Generative Agents:用记忆、反思与规划维持长期一致性
原始来源
Joon Sung Park 等,UIST 2023:Generative Agents: Interactive Simulacra of Human Behavior,本页按 arXiv v2 阅读。
本页是原创学习导读,不含原文复刻;论文版权与完整内容以原始来源为准。
学完后你应该能做到什么
读完本篇,你应该能够:
- 画出“观察写入 → 记忆检索 → 反思 → 计划 → 行动 → 新观察”的完整循环。
- 说清 Memory Stream 中一条记录至少保存什么,以及观察、反思、计划为什么可以进入同一检索空间。
- 分别解释 recency、importance、relevance 解决哪一种检索失败,并手算一个简化排序例子。
- 说明反思何时触发、怎样从具体记录生成高层认识,以及来源回链为什么重要。
- 追踪一个粗粒度日计划怎样递归细化,并在新事件出现时被局部重排。
- 区分“行为看起来可信”“系统拥有真实人格”“模拟能代表现实社会”这三个完全不同的命题。
先把问题说清楚:一次回答正确,不等于长期行为一致
把人物设定、当前时间和眼前环境交给语言模型,它通常能生成一句看起来合理的行动。困难在于下一分钟、第二天以及多次社会互动之后:
- 角色可能忘记昨天接受过的邀请;
- 只按当前场景反应,可能在一小时内重复吃三次午饭;
- 历史记录越来越多,全部塞进上下文既放不下,也会淹没真正相关的信息;
- 一堆具体事件不能自动变成“我在乎什么”“我与谁关系密切”这样的高层认识;
- 新观察可能与原计划冲突,系统必须知道何时继续、何时重排。
论文把目标称为 believability:角色的行为在其经历、关系和当前环境下显得连贯可信。这里的“像一个角色”是交互系统目标,不代表角色具有人的意识或真实能动性。
一句话抓住它
Generative Agents 把不断增长的经历写入自然语言记忆流,用近期性、重要性和相关性选出当前需要的记录,周期性把具体经历合成为可追溯的高层反思,再用记忆与反思生成、细化并动态修改计划。
flowchart TD E["环境状态、他人行动与对话"] --> O["Observation:写成自然语言记录"] O --> M["Memory Stream"] M --> R["Retrieval:近期性 + 重要性 + 相关性"] M --> G{"累计重要性达到反思阈值?"} G -->|"是"| F["Reflection:问题 → 检索 → 高层认识"] F --> M R --> P["Planning:日计划 → 小时 → 5–15 分钟"] P --> A["当前行动或对话"] A --> E E --> D{"继续原计划还是响应新事件?"} D -->|"继续"| A D -->|"响应"| P
完整机制契约:输入、状态、输出与停止
| 部分 | 在论文系统中是什么 | 为什么不可缺 |
|---|---|---|
| 输入 | 当前时间、可感知环境、附近角色、人物设定与当前计划 | 决定 Agent 此刻真正能看到什么 |
| 长期状态 | Memory Stream 中的观察、反思和计划记录 | 让行为能受过去影响,而非每步重新开始 |
| 检索器 | 对查询与每条记忆计算三项分数并选择有限子集 | 避免把全部历史塞进提示 |
| 反思器 | 从近期高重要性事件提出问题,再形成带来源的高层认识 | 把“发生了什么”提升为“这说明什么” |
| 计划器 | 生成带时间、地点和时长的层级计划 | 让连续行动在长时间尺度上保持一致 |
| 反应器 | 判断新观察是否值得打断原计划 | 让计划既稳定又不僵死 |
| 输出 | 一条自然语言行动、对话或环境操作意图 | 交给沙盒引擎转换成移动与状态变化 |
| 局部停止 | 当前时间片行动完成,进入下一次感知 | 系统是持续运行的,而不是一次回答后永久结束 |
这里有两个反馈回路:快回路在每个时间步“感知—行动—再感知”;慢回路把多次经历积累成反思和未来计划。只实现其中一个,都无法复现论文关注的长期一致性。
核心机制一:Memory Stream 保存可以再次参与决策的经历
论文把 Memory Stream 实现为按时间增长的记忆对象列表。最基本的记忆是 observation,即 Agent 直接感知到的事件。每个对象至少包含:
- 自然语言描述,例如“Maria 正在咖啡馆复习化学”;
- 创建时间;
- 最近一次被访问的时间;
- 创建时得到的重要性分数;
- 用于相关性检索的文本表示;
- 如果是反思,还可保存它依据的下层记录引用。
观察并不是唯一记录类型。反思和计划也会重新写入 Memory Stream,所以后续查询可以同时取回事实经历、高层认识和未来安排。这种统一带来便利,也带来风险:如果不保留类型和来源,模型可能把“我推断他喜欢研究”误当成“他亲口说过喜欢研究”。
初学者可以先用一种表结构理解
id | type | text | created_at | last_accessed_at | importance | evidence_ids向量索引只负责候选相关性,原始表仍负责时间、类型、来源和审计。把“有向量库”等同于“有长期记忆系统”会漏掉大半机制。
核心机制二:三项检索分数补的是三种不同缺口
决策前,系统用当前情境构造查询,为记忆对象计算三个分量。
1. Recency:最近进入注意范围的事情不要立刻消失
论文实现按记忆自上次访问后经过的沙盒小时数做指数衰减,衰减因子为 0.995。刚发生或刚取用的事件分数较高,久未使用的记录逐渐变低。
只有 recency 会造成“近期偏见”:昨天的闲聊可能压过一年前但极重要的承诺。
2. Importance:重要事件不应被大量日常噪声淹没
记录创建时,论文让语言模型按 1~10 估计事件的 poignancy。整理房间可能很低,重大关系变化可能很高。importance 让较久以前但关键的经历仍有机会进入上下文。
它不是客观真值。评分模型可能受措辞、文化和人物设定影响,因此生产系统应记录评分来源,并允许规则或人工信号覆盖。
3. Relevance:与当前问题语义接近的记忆优先
论文把查询和记忆描述编码成 embedding,用余弦相似度衡量相关性。问“化学考试要复习什么”时,教师、课程和复习经历应高于早餐记录。
只有 relevance 也不够:一条语义相似但过期的安排,可能压过刚刚发生的新变化。
4. 合并与截断
论文先把三项值分别做 min–max 归一化,再计算:
其中实现里三个权重都取 1。按总分排序后,只把能装入语言模型上下文窗口的高排名记录送入提示。
下面是教学用简化例子,数字不是论文原始样本:
| 记忆 | recency | importance | relevance | 总分 | 是否优先 |
|---|---|---|---|---|---|
| 今天答应 17:00 帮忙布置聚会 | 0.95 | 0.85 | 0.92 | 2.72 | 是 |
| 刚刚喝了一杯水 | 1.00 | 0.10 | 0.05 | 1.15 | 否 |
| 去年参加过一次生日会 | 0.10 | 0.60 | 0.70 | 1.40 | 可能 |
| 明天有考试 | 0.45 | 0.95 | 0.15 | 1.55 | 查询改变时可能上升 |
关键不是死记权重,而是看懂:检索结果由当前查询和三个不同维度共同决定。查询写错、分数校准错误或上下文预算过小,都会让后续规划基于错误材料。
核心机制三:Reflection 把事件变成带证据的高层认识
如果只保存观察,系统能回答“发生过什么”,却很难回答“这些事情共同说明什么”。论文用反思建立第二层记忆。
反思何时触发
论文累加近期事件的重要性分数;总和超过实现阈值 150 时触发反思。在该沙盒设置中,Agent 大约每天反思两到三次。150 是论文系统的工程参数,不是所有记忆系统的通用标准。
一次反思怎样生成
- 取最近 100 条记忆记录。
- 让模型提出 3 个当前最值得回答的高层问题,例如“这个人最投入的主题是什么?”
- 把每个问题作为新查询,运行三项记忆检索。
- 根据取回记录生成若干高层 insight。
- 要求 insight 引用支撑它的记录编号。
- 把 insight 作为
reflection类型写回 Memory Stream。
反思可以检索并引用旧反思,因此会形成一棵树:叶子是直接观察,中间节点是较具体总结,更高节点是更抽象认识。
抽象层级越高,错误也越可能被放大
如果底层观察不完整,或模型在总结时添加了不存在的细节,高层反思会继续被后续检索和再反思。工程实现应保留
evidence_ids,允许从结论回到原始观察,并在来源变化后重新计算或失效旧总结。
核心机制四:Planning 让局部合理变成时间上连贯
只问“现在做什么”会得到每一步都像样、合起来却荒谬的轨迹。论文中的例子是角色可能在 12:00、12:30 和 13:00 重复吃午饭。计划提供跨时间约束。
一条计划包含地点、开始时间和时长。系统先根据人物摘要、近期经历和前一天摘要生成一天的粗计划,通常分成 5~8 个大块;再把大块递归细化到小时,最后细化到 5~15 分钟动作。计划本身也存入记忆流。
计划并非脚本锁死。每个时间步,Agent 感知附近对象和角色,把新观察写入记忆,再判断:
- 新观察只是背景,继续当前计划;
- 新观察与自己有关,生成反应;
- 新事件改变后续安排,从当前时刻开始重建余下计划;
- 如果涉及另一个 Agent,双方各自检索关系与当前话题,再生成对话。
因此计划承担“默认路径”,观察承担“中断信号”。没有计划会漂移,没有中断机制会僵化。
走完一次:一条聚会意图怎样跨两天变成集体行动
论文著名案例从一个很小的种子开始:Isabella 被赋予举办情人节聚会的意图。下面按系统机制而非故事效果拆开:
| 阶段 | 写入或取回了什么 | 组件怎样改变下一步 |
|---|---|---|
| 初始意图 | “在指定时间于咖啡馆办聚会”作为记忆/计划 | 重要性和相关性使它在日程规划中靠前 |
| 遇到熟人 | 感知对方并检索关系、聚会计划 | 反应器决定发起邀请对话 |
| 信息传播 | 被邀请者把对话写入自己的记忆流 | 聚会不再只存在于主办者上下文 |
| 日程生成 | 被邀请者生成当天粗计划并细化 | 邀请与其他安排共同竞争时间 |
| 布置准备 | 主办者在前一天下午安排装饰 | 长期计划约束多步准备,而非到点临时生成 |
| 新互动 | 角色基于已有关系邀请另一角色同行 | 关系记忆与当前聚会记忆共同进入对话 |
| 到点行动 | 当前时间触发已细化的地点与动作 | 多名角色分别导航到咖啡馆 |
| 后续记忆 | 到场、对话和关系变化继续写入 | 事件可影响未来检索、反思与计划 |
这个结果依赖许多环节同时成功:意图被保存、邀请被表达、听者正确记住、计划没有覆盖邀请、时间和地点解析一致、角色到点执行。所谓“涌现”不表示没有机制;它表示全局行为并非逐角色手写脚本,而是多个局部记忆—计划循环相互作用的结果。
用伪代码把快慢两个循环钉死
def tick(agent, world, now):
observation = perceive(agent, world)
agent.memory.append(make_observation(observation, now))
if recent_importance_sum(agent.memory) >= REFLECTION_THRESHOLD:
questions = propose_questions(last_records(agent.memory, 100))
for question in questions:
evidence = retrieve(agent.memory, question)
insights = infer_with_evidence(question, evidence)
agent.memory.extend(make_reflections(insights, evidence, now))
if needs_daily_plan(agent, now):
coarse = create_daily_plan(agent.profile, agent.memory)
agent.plan = recursively_decompose(coarse)
agent.memory.append(make_plan_record(agent.plan, now))
context = retrieve(agent.memory, query_from(observation, agent.plan))
decision = decide_continue_or_react(agent.plan, observation, context)
if decision.requires_replan:
agent.plan = revise_remaining_plan(agent.plan, now, context)
action = choose_current_action(agent.plan, decision, context)
return world.execute(action)这段伪代码故意保留两个预算:反思不能每步触发,检索不能装入全部记忆。否则系统会把成本花在不断总结自己,或再次退化成超长上下文。
证据该怎么看:论文证明的是组件贡献与行为可信度
作者在 Smallville 中放入 25 个 Agent,并进行了两类评估:一类通过自然语言访谈考察自我知识、记忆、计划、反应与反思;另一类观察两天沙盒运行中的个体和社会行为。控制评估让 100 名参与者对完整架构和多种消融条件的回答排序。
论文报告,完整架构的可信度评分最高;移除反思后下降,继续移除规划后进一步下降,完全不提供观察记忆、反思和规划的条件最差。这个消融支持“这些组件在该实现和访谈任务中各有贡献”。
证据边界
- 评测目标是角色回答和行为的 believability,不是事实准确率或通用任务成功率。
- 为便于控制比较,消融条件获得了同一次模拟产生的记忆;这不等于每种架构独立运行两天后的因果差异。
- 25 个沙盒角色和两天案例不能代表真实社会。信息扩散与协调是系统演示,不是社会科学预测。
- 论文记录了检索遗漏、记忆润色和过度正式说话等失败,说明“有记忆模块”并不自动保证忠实回忆。
论文也讨论寄生关系、深度伪造、定向说服等风险。角色越连贯,用户越可能把“可信表演”误认作真实理解或关系,因此产品设计要标示人工身份、保留来源日志,并避免用模拟取代真实利益相关者。
常见失败模式与调试顺序
| 现象 | 更可能坏在哪一层 | 先检查什么 |
|---|---|---|
| Agent 明明听过邀请却说不知道 | retrieval miss | 查询、三项分数、top-k 和上下文截断 |
| 记得聚会要做什么,却不确定聚会是否存在 | 只取回了关联片段 | 是否遗漏最初邀请记录,是否需要多跳/分组检索 |
| 反思出现原记录没有的事实 | synthesis embellishment | evidence_ids、生成约束和事实核验 |
| 日程反复做同一件事 | plan 不完整或时间状态未更新 | 起止时间、时长、已完成标记和细化边界 |
| 发生紧急事件仍照旧执行 | reaction/replan 判定失败 | 新观察是否可见,打断阈值与计划重建范围 |
| 旧认识长期支配行为 | 反思缺少过期/反证机制 | 来源记录是否更新,结论是否可撤销 |
| 成本随时间快速增长 | 无预算或重复总结 | 检索预算、反思频率、缓存、去重和归档 |
历史贡献与今天的工程解释
原典的历史贡献是把 Memory Stream、三因素检索、递归反思和层级计划组合为可运行的长期角色架构,并用消融与端到端沙盒展示这些组件如何共同支持连贯行为。
今天仍值得复用的工程思想包括:
- 把记忆视为“记录—评分—检索—综合—失效”的系统,而不是聊天记录数组;
- 将事实观察、高层推断和未来计划标成不同类型,并保留来源关系;
- 用粗到细的计划提供稳定默认路径,用环境观察决定是否中断;
- 分别监控 retrieval miss 和 synthesis hallucination,因为两者修法不同;
- 让记忆与上下文都服从明确预算,而不是不断增长。
不应直接推出:任何三项权重、阈值或提示都能迁移;语言模型模拟的人物等于真实人类;多个 Agent 形成故事就证明了真实社会规律。
与后续工作的关系
Reflexion 把记忆聚焦在“上一次为什么失败、下一次怎样改”;Voyager 把经过环境验证的成功行为保存成可执行代码技能。Generative Agents 保存的是维持人物和社会互动连贯性的经历、认识与计划。三者都叫“记忆”,但对象、触发条件和成功标准不同。
自测:确认你真的掌握了机制
- 为什么只按 relevance 检索不够?recency 与 importance 分别补什么缺口?
- 一条 reflection 与一条 observation 在证据地位上有什么不同?怎样保持可追溯?
- 从“今天要完成研究报告”到 14:10 的具体动作,计划器需要经过哪些层级?
- 如果 Agent 忘了邀请,怎样判断是没有写入、没有检索到,还是检索后被模型忽略?
- 聚会案例能证明什么,不能证明什么?
参考答案与判断要点
- relevance 只衡量语义接近,可能选中很旧或不重要的相似记录;recency 保留当前连续性,importance 保护较久但关键的事件。
- observation 是系统记录的直接感知,reflection 是根据若干记录生成的推断。应保存类型和 evidence_ids,并允许回到原始记录核验或让旧反思失效。
- 先生成日级粗计划,再分解为小时块,最后分解为 5~15 分钟动作;每条要带时间、地点和时长,并允许从中断点重排余下部分。
- 依次审计事件采集日志、Memory Stream 写入、检索候选与分数、送入模型的最终上下文以及生成决策。不要只看最后一句输出猜根因。
- 它展示局部记忆、对话和计划循环能够产生未逐角色脚本化的协调行为;不能证明 Agent 有真实意识,也不能把 25 个沙盒角色当作现实社会模型。
上一篇:Toolformer · 下一篇:Reflexion · 返回 里程碑原典目录