V0.11 开发日志 — 论文复现
概述
V0.11 是连接 V0.1 概念探索与 V0.2 原创认知增强之间的桥梁。这个版本的目标很直接:忠实复现 Generative Agents 论文(Park et al., UIST 2023),使用现代技术和本地 LLM 推理。
V0.1 揭示了朴素的基于 prompt 的对话配合扁平记忆会导致僵硬、重复的行为。解决方案不是调参——而是架构层面的变革。V0.11 实现了论文中描述的完整认知架构,将基于云端的 GPT-3.5 调用(原作者每次 2 天仿真花费约 $1000)替换为通过 Ollama 的免费本地推理。
开发周期:2026年3月30日 – 4月1日
代码仓库:github.com/jeffliulab/ALICE_PROJECT
复现内容
完整认知循环
论文的架构以六阶段认知循环为核心,每个智能体在每个仿真步都执行完整循环。V0.11 实现了全部六个阶段:
-
感知(Perceive) — 每个智能体检测其空间区域内的附近事件。每个被感知的事件通过 LLM 评分为"触动度"(情感重要性),决定该事件对记忆的影响强度。这将 V0.1 简单的观察记录替换为具有上下文感知的事件过滤。
-
检索(Retrieve) — 三因子记忆检索系统,解决了 V0.1 "总是检索相同记忆"的问题:
- 时效性(Recency):指数衰减——越近的记忆得分越高
- 相关性(Relevance):当前上下文嵌入与存储记忆嵌入之间的余弦相似度
- 重要性(Importance):感知时赋予的触动度分数
每个因子独立归一化(在检索集内做最小-最大归一化),然后以可调权重组合(默认:时效性 0.5,相关性 3.0,重要性 2.0)。这确保检索能适应智能体当前的情境,而不是总是浮现相同的高重要性记忆。
-
规划(Plan) — 三级时间分解:
- 日计划:当天的宏观目标
- 时段安排:将每个日目标分解为小时级别的行动
- 微任务:将每个时段行动进一步拆分为 5-15 分钟的子任务
大多数复现跳过了微任务层。V0.11 实现了它,因为它对有机社交互动至关重要——当一个智能体正在执行 5 分钟的子任务时遇到另一个智能体,规划模块需要决定是参与、等待还是忽略。
-
反思(Reflect) — 当累积的重要性分数超过阈值时(默认:累积值降至零),智能体进入反思模式:
- 从近期高重要性记忆中生成焦点问题
- 检索与每个焦点问题相关的证据
- 综合产生更高层次的洞见,存储为"思想"节点
这创建了一个记忆层次结构:原始观察 → 反思想法 → 元反思。一个观察了许多小事件的智能体最终会将它们综合为更广泛的理解。
-
执行(Execute) — 将当前计划的目标位置解析为瓦片坐标,在碰撞网格上运行 A* 寻路,生成下一步移动和行动描述。智能体的精灵沿计算路径每步移动一个瓦片。
-
对话(Converse) — 逐轮对话,带有结构化知识提取。当两个智能体相遇并决定交谈时:
- 每轮为当前对话上下文检索相关记忆
- LLM 以角色身份生成回应
- 对话结束后,双方智能体提取关键信息并存储为新的记忆节点
- 关系上下文在多次对话间保持
记忆架构
V0.11 实现了三种不同的记忆结构,取代了 V0.1 的扁平时间戳列表:
-
关联记忆(Associative Memory):使用概念节点的长期存储。每个节点存储:
- 主语-谓语-宾语三元组(如"薇薇安——听到——来自森林的奇怪声音")
- 关键词索引,用于快速查找
- 语义嵌入向量(由本地运行的 sentence-transformers MiniLM 计算)
- 创建时间戳、最后访问时间和重要性分数
这本质上是一个知识图谱,每个记忆都是一个丰富索引的节点,而不仅仅是一个文本字符串。
-
空间记忆(Spatial Memory):层级树结构——世界 → 区块 → 区域 → 物品。智能体知道有哪些建筑、建筑内有哪些房间、每个房间里有什么物品。这使得导航决策更加真实("我需要去教堂——它在神圣区域——我应该往北走")。
-
工作记忆(Scratch):动态状态容器,捕捉智能体当前的认知上下文:身份、日计划、时段安排、当前行动、对话伙伴和各种阈值。这相当于一个不断演化的"系统提示词",每个仿真步都会更新。
世界:Smallville
V0.11 复现了原始论文的世界环境:
- 25个智能体,各有独特的人设、日常作息和关系网络
- 140 × 100 瓦片网格,多层地图:
- 碰撞层(墙壁、障碍物)
- 区块层(命名区域)
- 区域层(区块内的具体房间/区域)
- 物品层(可交互物品)
- 出生点层(智能体初始位置)
- 285个命名地址
- 带碰撞感知的 A* 寻路
技术栈
与原始论文最大的不同在于技术栈。原始版本使用云端 API 和 Django 单体架构;V0.11 使用现代的本地优先工具:
| 组件 | 原始论文 | V0.11 |
|---|---|---|
| LLM | GPT-3.5-turbo(云端,约 $1000/2天) | Ollama + Qwen 14B(本地,免费) |
| 嵌入 | OpenAI ada-002(云端) | sentence-transformers MiniLM(本地) |
| 后端 | Django + 基于文件的 IPC | FastAPI + REST API |
| 前端 | Phaser.js + Django 模板 | React + Phaser 3 + Vite |
| 仿真 | 仅实时(慢,耦合 UI) | 仿真/回放分离 |
| 数据 | 自定义文件格式 | 全程 JSON |
为什么选择本地 LLM?
三个原因:
- 成本:原始论文在单次 2 天仿真中花费约 $1000 的 GPT-3.5 API 调用费。Ollama 本地推理在下载模型后完全免费。
- 可复现性:云端 API 的行为随时间变化。固定权重的本地模型产生确定性的结果。
- 隐私:所有智能体数据——记忆、对话、反思——都保留在本地机器上。
代价是推理质量。Qwen 14B 在某些方面不如 GPT-3.5,但对于认知循环中使用的结构化 prompt,它的表现足够好,能够复现论文的关键发现。
架构:仿真/回放分离
V0.11 最重要的设计决策之一是将仿真与可视化分离。
仿真模式(python -m backend.simulate):
- 在终端无界面运行
- 每步:全部 25 个智能体执行认知循环
- 输出
master_movement.json——完整记录每个智能体在每个时间步的位置、行动和状态 - 100 步仿真需要 30-60 分钟(主要被 LLM 推理时间占据)
回放模式(基于浏览器):
- FastAPI 通过 REST API 提供仿真数据
- React + Phaser 3 前端渲染瓦片地图和精灵动画
- 播放控制:播放/暂停、进度条、可变速度(1×–20×)
- 逐智能体状态检查(点击智能体查看其当前想法、计划和记忆)
这种分离意味着:
- 可以在过夜时运行实验而无需 UI
- 通过加载不同的回放文件来比较多次仿真
- 回放查看器即时加载——无需等待 LLM 推理
代码结构
ALICE_PROJECT/
├── backend/
│ ├── simulate.py # CLI 仿真运行器
│ ├── main.py # FastAPI 回放服务器
│ ├── world_engine.py # 核心仿真引擎 + 全局时钟
│ ├── maze.py # 140×100 瓦片世界地图
│ ├── recorder.py # 保存回放数据为 JSON
│ ├── path_finder.py # A* 寻路算法
│ ├── config.py # LLM 和仿真配置
│ ├── llm/
│ │ ├── llm_client.py # OpenAI 兼容 LLM 接口
│ │ └── embedding.py # 本地 sentence-transformers 嵌入
│ ├── persona/
│ │ ├── persona.py # 智能体认知循环编排器
│ │ ├── cognitive_modules/
│ │ │ ├── perceive.py # 事件感知 + 触动度评分
│ │ │ ├── retrieve.py # 三因子记忆检索
│ │ │ ├── plan.py # 三级计划分解
│ │ │ ├── reflect.py # 重要性触发的反思
│ │ │ ├── execute.py # 移动 + 行动执行
│ │ │ └── converse.py # 逐轮对话引擎
│ │ └── memory_structures/
│ │ ├── associative_memory.py # 概念节点知识图谱
│ │ ├── spatial_memory.py # 层级世界模型
│ │ └── scratch.py # 工作记忆 / 状态
│ └── data/
│ └── the_ville/ # Smallville 世界数据(25个智能体)
├── frontend/
│ ├── src/
│ │ ├── App.tsx # 回放查看器界面
│ │ ├── GameScene.ts # Phaser 3 地图渲染
│ │ └── api.ts # 后端 REST 客户端
│ └── public/assets/ # 瓦片集和角色精灵
└── paper-generative-agent/
├── ANALYSIS.md # 15 节深入分析原始代码
└── reverie/ # 原始论文源代码(参考)
关键技术细节
记忆检索公式
对于查询上下文 q 和候选记忆集 M,每个记忆 m ∈ M 获得评分:
score(m) = w_recency × norm(recency(m)) + w_relevance × norm(relevance(m, q)) + w_importance × norm(importance(m))
其中:
recency(m) = e^(-λ × (t_now - t_created)),衰减率 λ = 0.995relevance(m, q) = cosine_similarity(embedding(m), embedding(q))importance(m)= LLM 评定的触动度分数(1-10)norm()= 在当前检索集内的最小-最大归一化- 默认权重:
w_recency = 0.5,w_relevance = 3.0,w_importance = 2.0
反思触发机制
当新记忆的重要性分数累积和降至零时触发反思(概念上:智能体已积累了足够多的重要经历,值得进行综合)。系统:
- 从近期高重要性记忆中生成 3 个焦点问题
- 对每个问题,检索最相关的 top-k 记忆
- 将每组综合为一个带有证据指针的"思想"节点
- 将思想节点存回关联记忆(使未来循环中的元反思成为可能)
计划分解
三个时间层级处理不同的时间尺度:
- 宏观(日级):"起床、吃早餐、在铁匠铺工作、吃午饭、逛市场、回家、睡觉"
- 中观(时级):"9:00-10:00: 加热锻炉并准备材料;10:00-12:00: 制作委托的剑"
- 微观(5-15分钟):"9:00-9:05: 拨旺煤炭;9:05-9:15: 整理金属原料;9:15-9:30: 开始加热锻炉"
当意外事件发生时(另一个智能体到来、巨大响声),规划模块评估是否:
- 继续当前任务
- 暂停并参与事件
- 放弃当前任务并重新规划
经验教训
论文做对了什么
-
三因子检索效果出奇的好。 时效性、相关性和重要性的组合产生了自然的记忆访问模式。智能体记住近期事件、回忆相关的过去经历、保留情感上重要的时刻——就像人类一样。
-
反思创造了真正的深度。 当足够多的观察累积后,智能体综合出有意义的洞见。一个多次对话失败的铁匠可能反思:"我觉得人们躲避我,因为我说了太多关于我工作的事情。"
-
计划层次防止了漫无目的的游荡。 没有计划,智能体只会对刺激做出反应。三级分解给了他们目标、常规,以及在有趣的事情发生时打破常规的能力。
出乎意料的发现
-
本地 LLM 表现超出预期。 Qwen 14B 很好地处理了结构化 prompt。关键洞见:认知架构将 LLM 的输出限制在可管理的、聚焦的任务中。每个模块问一个具体的问题,有明确的格式要求。LLM 不需要聪明绝顶——它需要在狭窄范围内可靠。
-
仿真/回放分离是必要的。 早期带 UI 的实时仿真尝试极其缓慢(25 个智能体每步需要数分钟)。将两者分离使实验变得实际可行。
-
智能体对话是最薄弱的环节。 即使有记忆检索和关系上下文,对话在几轮后倾向于礼貌和重复。这就是 LLM 权重静态性最明显的地方——智能体无法真正从对话中学习,只能记录它们。
与 V0.1 和 V0.2 的关系
V0.1 提出了问题:"两个 LLM 驱动的智能体能进行有意义的对话吗?"
答案是:"用朴素 prompt 和扁平记忆,不能。"
V0.11 回答了:"如果我们实现论文中的完整认知架构呢?"
答案是:"可以——有了感知、结构化检索、规划和反思,智能体的行为可信度大大提高。"
V0.2 追问:"我们能超越论文吗?智能体能成长、遗忘、做梦和反叛吗?"
V0.2 在 V0.11 坚实的基础上添加了受生物学启发的增强:短期/长期记忆分裂、基于梦境的记忆巩固、Ego 演化、能力检定,以及异见机制。它还从 Smallville 转移到原创世界塔纳波西亚——乌瓦村,一个充满中世纪奇幻设定、宗教张力和隐藏血脉的舞台。