第 6 章 · 记忆与上下文

01 · 三类记忆

Agent 需要三种不同时间尺度的记忆:短期执行链路、会话任务历史、项目级规则约定。

为什么需要记忆

到第 5 章结束时,agent 已经有了事件流、权限系统和完整的工具箱。但有一个明显的问题——每次 runAgent 调用都是独立的,没有记忆

这意味着:

  • agent 读过文件 A,下一轮对话不记得读过 A,可能重复读取
  • agent 执行 npm test 失败了,下一轮可能再执行同样的命令
  • agent 不知道项目的代码规范,每次都按通用方式写代码
  • 用户第二轮说"继续",agent 不知道上一轮做了什么

Claude Code、Codex CLI 等产品都没有这个问题——它们都能在多轮对话中保持上下文连续性。

三类记忆

按照时间尺度和用途,agent 的记忆可以分为三层:

类型持续时间内容类比
短期记忆单次 runAgent 调用对话历史、工具调用和结果工作记忆
会话记忆一次会话(多轮对话)读过的文件、失败的命令、最近搜索短期笔记
项目记忆跨会话代码规范、目录约定、常见命令项目文档

短期记忆已经存在了——allMessages 数组就是短期记忆,它包含了完整的对话历史。模型通过阅读这个数组来理解上下文。

会话记忆是这一章要实现的——一个 ContextManager,在 main.ts 的交互循环中持续存在,跨轮次记录 agent 的操作历史。

项目记忆通过加载项目根目录的 AGENTS.md 文件来实现——把项目的规则和约定注入到系统提示词中。

短期记忆 vs 会话记忆 vs 项目记忆

用一个具体例子区分三者:

场景: 用户让 agent "把测试框架从 Jest 迁移到 Vitest",agent 需要多轮操作。

短期记忆(单次调用): 模型看到了完整的对话历史——用户说"迁移到 Vitest",agent 搜索了配置文件,读取了 package.json,修改了配置。这些信息都在 allMessages 数组中。但这次调用结束后,下次调用不会保留这个数组。

会话记忆(多轮对话): 第二轮用户说"继续"。agent 知道第一轮读过 package.jsonvitest.config.ts,也知道 npm test 曾经失败过。这些信息帮助 agent 不重复劳动,直接从上次断点继续。

项目记忆(跨会话): agent 读到了 AGENTS.md 中的规则:"使用 ESM 模块格式"、"测试文件放在 test/ 目录"。这些规则在任何一次会话中都适用。

实现策略

三类记忆的实现方式各不相同:

短期记忆 → allMessages 数组(已存在,不需要改动)
会话记忆 → ContextManager 类(新建 src/memory/context.ts)
项目记忆 → ProjectRulesLoader 类(新建 src/memory/rules.ts)

ContextManager 是一个纯 JavaScript 对象,在 main.ts 中创建,通过 RunAgentOptions 传入 agent 循环。agent 在执行工具后,把关键信息记录到 ContextManager。下一轮调用时,ContextManager 的内容会被注入到系统提示词中。

ProjectRulesLoader 在 agent 启动时从工作目录读取 AGENTS.md,把内容注入到系统提示词中。整个会话期间保持不变。

这一章的重点是后两类——短期记忆已经通过对话历史自然实现了,不需要额外工作。

信息不是越多越好

有一个容易犯的错误:把所有工具调用结果都存到记忆里,越多越好。

实际上,太多信息反而有害:

  • token 成本增加——每次调用模型都要把上下文发过去,信息越多越贵
  • 噪音干扰——模型被大量无关信息淹没,可能忽略真正重要的线索
  • 上下文窗口有限——信息太多会挤占对话历史的空间

所以会话记忆的设计原则是只记录关键信息的摘要

  • 读过的文件 → 只记路径和前几行,不存完整内容
  • 失败的命令 → 只记命令和错误信息
  • 最近的搜索 → 只记关键词和结果摘要,最多保留 5 条

下一节会详细讲解 ContextManager 的实现。

登录以继续阅读

解锁完整文档、代码示例及更多高级功能。

立即登录

On this page