Generative agents 代码分析 三

经过之前的摸索学习,现在终于可以来看一下整个论文中描述的最核心的部分 reverie

三个关键词:

  1. personas: generative agents,角色
  2. associative memory: memory stream,联想记忆
  3. reverie: 整个模拟框架

启动 reverie

之前提到整个系统分为 5 个部分,今天我们来看 2、3 部分。

  1. 启动 Environment 服务
  2. ☕ 启动 Simulation 服务
  3. ☕ 运行和保存 Simulation
  4. 重放之前保存的 Simulation
  5. 演示 Simulation

执行 pythone reverie.py启动 reverie,然后等待用户输入指令。

python 复制代码
  origin = input("Enter the name of the forked simulation: ").strip()
  target = input("Enter the name of the new simulation: ").strip()

  rs = ReverieServer(origin, target)
  rs.open_server()

初始化创建一个新的模拟实例,通常是从一个已有的模拟中派生。

python 复制代码
# fs_storage = "../../environment/frontend_server/storage"
# fs_temp_storage = "../../environment/frontend_server/temp_storage"

def __init__(self, 
               fork_sim_code,
               sim_code):
    copyanything(fork_folder, sim_folder)
    
    outfile.write(json.dumps(reverie_meta, indent=2))
    
    for persona_name in reverie_meta['persona_names']:
        persona_folder = f"{sim_folder}/personas/{persona_name}"
        p_x = init_env[persona_name]["x"]
        p_y = init_env[persona_name]["y"]
        curr_persona = Persona(persona_name, persona_folder)

        self.personas[persona_name] = curr_persona
        self.personas_tile[persona_name] = (p_x, p_y)
        self.maze.tiles[p_y][p_x]["events"].add(curr_persona.scratch
                                        .get_curr_event_and_desc())

    self.maze = Maze(reverie_meta['maze_name'])
    
    with open(f"{fs_temp_storage}/curr_sim_code.json", "w") as outfile: 
      outfile.write(json.dumps(curr_sim_code, indent=2))
    
    with open(f"{fs_temp_storage}/curr_step.json", "w") as outfile: 
      outfile.write(json.dumps(curr_step, indent=2))
    
        
  • 复制源模拟的所有文件到新目录。
  • 修改 meta.json 文件记录来源。
  • 加载起始时间、地图名称、秒每步等全局配置。
  • 实例化地图对象 Maze。
  • 🧑‍💼构造所有人物(Persona),并初始化它们的位置信息。
  • 写入当前模拟代码和步数到临时文件,供前端识别。

初始化完成后启动 server 控制台,server 主要提供命令行方式控制模拟,方便一步步模拟执行和调试。

python 复制代码
def open_server(self): 
    elif sim_command[:3].lower() == "run": 
          # Runs the number of steps specified in the prompt.
          # Example: run 1000
          int_count = int(sim_command.split()[-1])
          rs.start_server(int_count)

支持命令:

命令 功能
f , fin , finish , save and finish 保存并退出模拟
start path tester mode 进入路径测试模式(清空当前模拟)
exit 退出不保存,删除当前模拟数据
save 仅保存当前状态
run <steps> 自动运行指定步数
print persona schedule <name> 查看某个人物的日程安排
print all persona schedule 查看所有人物的日程摘要
print hourly org persona schedule <name> 查看原始日程安排(未分解)
print persona current tile <name> 查看人物当前位置
print persona chatting with buffer <name> 查看人物的聊天缓冲区
print persona associative memory (event/thought/chat) <name> 查看人物的记忆流(事件/思考/对话)
print persona spatial memory <name> 查看人物的空间记忆
print current time 查看当前模拟时间
print tile event <x,y> 查看某个瓦片上的事件
print tile details <x,y> 查看某个瓦片的完整信息
call -- analysis <name> 与人物开启一次分析会话(不保存记忆)
call -- load history <filename> 从 CSV 文件加载历史记忆

我们输入run 100启动服务。主要用来模拟的核心运行逻辑,处理环境输入、人物行为推理、输出下一步动作。

python 复制代码
def start_server(self, int_counter):
  while (True): 
    # 1
    curr_env_file = f"{sim_folder}/environment/{self.step}.json"
    check_if_file_exists(curr_env_file)
    # 5
    self.maze.add_event_from_tile(persona.scratch
                                         .get_curr_event_and_desc(), new_tile)
    
    next_tile, pronunciatio, description = persona.move(	# 这里移动就会触发感知 perceive
              self.maze, self.personas, self.personas_tile[persona_name], 
              self.curr_time)
  1. 等待前端环境文件更新(environment/.json)。
  2. 清理上一轮添加的游戏对象事件(如床铺是否被使用)。
  3. 更新人物位置:
  4. 从环境文件中获取每个角色的新坐标。
  5. 更新地图中的事件标记。
  6. 驱动每个人物进行决策(调用 persona.move(...)):
  7. 返回下一步目标位置、表情符号、描述文本、对话内容。
  8. 写入下一步动作文件(movement/.json)供前端使用。
  9. 更新时间与步数。
  10. 循环直到指定步数完成。

核心 Persona

这是 Reverie 模拟系统中所有角色(智能体)的核心类。这个类实现了生成式智能体(Generative Agent)的行为逻辑,包含感知、记忆、规划、执行和反思等认知模块。

Perceive(感知) → Retrieve(检索) → Plan(计划) → Reflect(反思) → Execute(行动)

整体结构概览

模块 描述
初始化 (init) 加载或创建人物的记忆与状态
保存 (save) 将当前状态保存到磁盘
感知 (perceive) 获取周围环境事件
记忆检索 (retrieve) 根据感知获取相关记忆
规划 (plan) 制定长期与短期计划
执行 (execute) 将计划转化为具体动作
反思 (reflect) 更新记忆流中的深层思考
移动 (move) 综合调用以上模块,返回下一步动作
对话会话 (open_convo_session) 开启一个临时对话会话

另外可以通过 Maze 模块访问地图信息

详细分析

1. init 初始化

python 复制代码
def __init__(self, name, folder_mem_saved=False):
  f_s_mem_saved = f"{folder_mem_saved}/bootstrap_memory/spatial_memory.json"
  self.s_mem = MemoryTree(f_s_mem_saved)
  # <s_mem> is the persona's associative memory. 
  f_a_mem_saved = f"{folder_mem_saved}/bootstrap_memory/associative_memory"
  self.a_mem = AssociativeMemory(f_a_mem_saved)
  # <scratch> is the persona's scratch (short term memory) space. 
  scratch_saved = f"{folder_mem_saved}/bootstrap_memory/scratch.json"
  self.scratch = Scratch(scratch_saved)
  1. folder_mem_saved="environment/frontend_server/storage/July1_the_ville_isabella_maria_klaus-step-3-20/personas/Isabella Rodriguez/"
  2. 加载 spatial(空间) 记忆,associative(联想)记忆,scratch(暂时)记忆

2. save 保存记忆到文件

python 复制代码
def save(self, save_folder):

将当前 Persona 的状态保存到指定目录。

  • 空间记忆树(JSON 文件)
  • 联想记忆(CSV 文件)
  • 临时记忆(JSON 文件)

3. perceive 感知环境

当角色移动的时候,调用 persona.move,然后就会调用 persona.perceive开始感知

python 复制代码
def perceive(self, maze):

此函数获取当前 Maze,并返回角色周围发生的事件。重要的是,感知是由以下因素引导的

角色的两个关键超参数:

  • att_bandwidth: 控制最多感知多少个事件。
  • retention: 控制多久内不重复感知同一事件。

首先,<att_bandwidth> 决定了角色可以感知的附近事件的数量。假设有10个事件在角色的视野范围内------感知这10个事件可能太多了。因此,在事件太多的情况下,角色会感知到最接近 att_bandwidth 数量的事件。

其次,角色不想在每个时间步都感知和思考同一事件。这就是 发挥作用的地方------角色记忆的内容有时间顺序。因此,如果角色的记忆中包含了最近一次记忆中发生的当前周围事件,就没有必要再去感知。

4. retrieve 检索记忆

python 复制代码
def retrieve(self, perceived):

作用:从联想记忆中检索与感知相关的记忆。

输入:感知到的事件列表。

输出:字典结构,包含每个事件相关的上下文记忆(包括事件、思考、对话等)。

5. plan 规划行为

python 复制代码
def plan(self, maze, personas, new_day, retrieved):

作用:制定长期与短期计划。

输入:

  • maze: 当前地图。
  • personas: 所有人物字典。<font style="background-color:rgba(255, 255, 255, 0.04);">new_day</font>: 是否为新的一天(或第一天)。
  • <font style="background-color:rgba(255, 255, 255, 0.04);">retrieved</font>: 检索到的相关记忆。

输出:目标动作地址(如 <font style="background-color:rgba(255, 255, 255, 0.04);">"Isabella Rodriguez is idle"</font>)。

6. execute 执行计划

python 复制代码
def execute(self, maze, personas, plan):

作用:将抽象计划转化为具体动作(如移动坐标、使用的对象等)。

输入

  • maze: 地图。
  • personas: 所有人物。
  • plan: 目标动作地址。

输出

  • 下一步位置(<font style="background-color:rgba(255, 255, 255, 0.04);">(x, y)</font> 坐标)
  • 表情符号(emoji)
  • 动作描述(如 <font style="background-color:rgba(255, 255, 255, 0.04);">"writing her next novel @ sofa"</font>

7. reflect 反思机制

python 复制代码
def reflect(self):

作用:回顾记忆并生成新的思考。

机制:

  • 如果记忆中有未处理的事件,则触发"反思"过程。
  • 生成新的思想节点,并加入联想记忆中。

8. move 主行为函数 认知流程的主要入口

执行 run 100会触发 persona 执行 move
动作。move
动作就是整个思考链条的起点。也是首页思考流程图的具体实现。

python 复制代码
def move(self, maze, personas, curr_tile, curr_time):
    """
    This is the main cognitive function where our main sequence is called. 

    INPUT: 
      maze: The Maze class of the current world. 
      personas: A dictionary that contains all persona names as keys, and the 
                Persona instance as values. 
      curr_tile: A tuple that designates the persona's current tile location 
                 in (row, col) form. e.g., (58, 39)
      curr_time: datetime instance that indicates the game's current time. 
    OUTPUT: 
      execution: A triple set that contains the following components: 
        <next_tile> is a x,y coordinate. e.g., (58, 9)
        <pronunciatio> is an emoji.
        <description> is a string description of the movement. e.g., 
        writing her next novel (editing her novel) 
        @ double studio:double studio:common room:sofa
    """
    # Updating persona's scratch memory with <curr_tile>. 
    self.scratch.curr_tile = curr_tile

    # We figure out whether the persona started a new day, and if it is a new
    # day, whether it is the very first day of the simulation. This is 
    # important because we set up the persona's long term plan at the start of
    # a new day. 
    new_day = False
    if not self.scratch.curr_time: 
        new_day = "First day"
    elif (self.scratch.curr_time.strftime('%A %B %d')
          != curr_time.strftime('%A %B %d')):
        new_day = "New day"
    self.scratch.curr_time = curr_time

    # Main cognitive sequence begins here. 
    perceived = self.perceive(maze)
    retrieved = self.retrieve(perceived)
    plan = self.plan(maze, personas, new_day, retrieved)
    self.reflect()

    # <execution> is a triple set that contains the following components: 
    # <next_tile> is a x,y coordinate. e.g., (58, 9)
    # <pronunciatio> is an emoji. e.g., "\ud83d\udca4"
    # <description> is a string description of the movement. e.g., 
    #   writing her next novel (editing her novel) 
    #   @ double studio:double studio:common room:sofa
    return self.execute(maze, personas, plan)

作用:综合调用上述模块,完成一次完整的认知循环。

流程:

  1. 更新当前时间与位置。
  2. 判断是否为新一天。
  3. 调用 perceive 感知环境。感知 perceive 的输入是地图 maze
  4. 调用 retrieve 检索记忆。检索 retrieve 的输入是感知 perceive
  5. 调用 plan 制定计划。计划 plan 的输入是地图 maze、角色 persona、时间 curr_time、检索 retrieve
  6. 调用 reflect 进行反思。
  7. 调用 execute 执行计划,返回下一步动作。

名词

  1. Congitive,形容词,认知的。侧重智力层面的认知能力或相关研究领域(如认知科学),不涉及感官感知
  2. Perceive,动词,感知/察觉。包含更广泛的意义,既包含感官感知(如视觉、听觉),也包含对抽象概念的认知(如理解理论、情感)。
  3. Retrieve,检索,取回
  4. Reflect,反射,反映,反思
相关推荐
AI大模型18 分钟前
RAG(检索增强生成)的深度解析——如何让人工智能告别“胡说八道”?
程序员·llm·agent
用户8485081469044 分钟前
OpenAI重返开源!GPT-OSS本地部署完全指南
openai
AscentStream2 小时前
技术文档 | 当 Agent 遇上 Pulsar:如何重构 A2A 协议,玩转事件驱动架构
agent
新智元2 小时前
GPT-5发布时间定了!ChatGPT年费只要1美元,OpenAI估值达5000亿
人工智能·openai
小厂永远得不到的男人2 小时前
我在AI公司的作死实录:实习生把Chatbot训练成祖安大师
agent
三花AI2 小时前
OpenAI 明日凌晨直播:GPT-5 或将正式亮相
gpt·openai
新智元4 小时前
刚刚,马斯克 Grok4 干翻谷歌 Gemini!o3 杀入首届大模型对抗赛决战
人工智能·openai
NocoBase5 小时前
GitHub 上 Star 数量前 20 的开源 AI 项目
langchain·开源·openai
Q同学5 小时前
阿里WebDancer:自主信息搜索Agent
人工智能·llm·agent
INFINI Labs16 小时前
Easysearch 集成阿里云与 Ollama Embedding API,构建端到端的语义搜索系统
阿里云·云计算·openai·embedding·easysearch