编码 Agent 最尴尬的体验,是它每次都像第一天上班:你昨天才解释过这个仓库的构建脚本为什么要临时翻转 skipTests,今天它又踩同一个坑。根因在于大模型本身是无状态的——它只看得到当前请求里的上下文窗口。所谓"记忆",不是模型长出了海马体,而是工程上为它搭了一套外部状态层,在每轮请求时把相关历史重新喂回去。本文讨论如何为编码 Agent 设计这套记忆系统。
为什么上下文窗口不等于记忆
很多人以为把窗口拉到 1M token 就解决了记忆问题,其实不然。原因有三:
- 会话边界:窗口只在单次会话内有效,进程退出、
/clear或跨天重开,历史就没了。 - 成本与延迟:每轮请求都重发全部历史,token 线性增长,长会话后期既慢又贵。
- 信噪比:窗口里塞满几十轮琐碎对话,真正关键的约束反而被稀释,模型的注意力会被噪声拉偏。
所以记忆的本质是有选择地持久化、有选择地召回:把值得记的东西落盘,在需要时只取相关的一小部分塞回窗口。
记忆的三个层次
按生命周期和粒度,编码 Agent 的记忆大致可分三层:
- 工作记忆(Working):当前会话的对话历史,活在上下文窗口里。它最完整也最易失。
- 情景记忆(Episodic):一次具体任务的过程与结果——“上次重构鉴权模块时,发现
os.path.basename是清洗下载文件名的关键”。通常按时间或任务 ID 归档。 - 语义记忆(Semantic):从多次情景里沉淀出的稳定结论——项目约定、用户偏好、架构事实。这是跨会话最值钱的部分,例如"master 是工作分支,但新功能走 feature/* 分支"。
工程上不必严格三分,但要意识到:情景需要遗忘,语义需要长存。把两者混在一个文件里,要么无限膨胀,要么把重要约定淹没。
写入:什么时候记、记什么
记忆系统最难的不是存储,而是决定写什么。两种主流策略:
- 显式写入:模型在对话中主动调用一个
save_memory工具,把它认为重要的事实落盘。优点是模型自己判断相关性;缺点是需要在系统提示里引导,且容易记冗余。 - 后处理提炼:会话结束(或被压缩)时,用一次单独的"反思"调用把整段历史蒸馏成几条结构化记忆。这样写入时机集中、可控,也能去重。
提示词在这里很关键。按编码 Agent 的调参经验,不要写成过激的 “CRITICAL!你必须在每轮都保存记忆”——新一代模型对这类措辞会过度触发工具,结果是满屏噪声记忆。改成温和的条件式更稳:
1 | 当出现以下情况时,调用 save_memory: |
记忆条目本身建议结构化,便于后续检索与淘汰:
1 | { |
召回:把对的那几条塞回窗口
下一轮会话开始时,不能把全部记忆一股脑倒进去——那又退化成"窗口即记忆"了。常见召回方式:
- 索引/目录式:维护一个轻量级
MEMORY.md索引(一行摘要 + 指向详情文件的链接),开场只注入索引,模型按需再读详情。适合条目不多、人也要看的场景。 - 检索式(RAG):对记忆向量化,用当前任务描述做相似度检索,取 top-k 注入。适合记忆量大、需要语义匹配的场景。
- 元数据过滤:按
scope、kind等字段精确筛选,比如"只取当前仓库的 convention"。常与上面两种叠加用。
一个实用的 manual loop 召回骨架:
1 | def build_system_context(task: str, repo: str) -> str: |
遗忘:记忆系统的下半场
只写不删的记忆系统会慢慢腐烂——过期的临时结论、已被推翻的假设、重复条目,都会污染召回。需要主动维护:
- TTL / 陈旧淘汰:情景类记忆设过期时间;
last_used_at长期不更新的条目降权或归档。 - 去碎片(defrag):定期把膨胀的文件拆分、把重复条目合并、把已完成任务的记忆移到归档目录。这件事可以由一个专门的维护流程周期性触发。
- 失效标记:当某条约定被新决策推翻,不是悄悄删除,而是标记状态迁移(active → archived),保留可追溯性。
安全:记忆是被持久化的、也可被注入读取
记忆层天然是持久化存储,这带来一条硬性安全约束:密钥、令牌等敏感凭据绝不能进记忆,正如它们不该进系统提示或消息历史。任何写进记忆的东西,都可能在未来某轮被重新注入窗口,从而被一次 prompt injection 诱导吐出。需要鉴权的第三方调用,应把凭据留在 orchestrator 侧(用客户端自定义工具承载),记忆和沙箱里只出现占位符。同理,写入前最好对记忆内容做一道清洗,避免把用户粘贴的密钥顺手存了下来。
小结
Agent 记忆不是给模型加内存条,而是工程上搭一套"写入—召回—遗忘"的外部状态闭环:选择性地落盘语义结论、按相关性召回一小部分、并主动淘汰陈旧内容,同时严守凭据不入库的底线——做好这三件事,编码 Agent 才真正从"每天第一天上班"变成"记得住项目脾气的老同事"。