Agent记忆系统设计:从上下文管理到长期经验复用
Agent记忆系统设计:从上下文管理到长期经验复用
本文基于
记忆.md重新整理。原文已经说明了短期记忆、长期记忆、检索触发和决策机制;这份文档进一步把它组织成一套可以落地的 Agent 记忆设计指南,并通过具体场景讲清楚“什么时候记、记什么、什么时候查、查出来怎么用”。
1. 先用一个例子理解 Agent 记忆
假设用户正在和一个旅行规划 Agent 对话:
用户:下周三想去东京玩 5 天,预算 2 万,别安排太早起。
Agent:你更偏好酒店还是民宿?
用户:民宿吧,最好交通方便。
这时 Agent 至少会同时用到两类信息:
| 信息 | 来自哪里 | 是否应该长期保存 | 用途 |
|---|---|---|---|
| 下周三去东京 | 当前对话 | 通常不保存,除非任务跨会话继续 | 本次行程规划 |
| 5 天 | 当前对话 | 通常不保存 | 控制行程天数 |
| 预算 2 万 | 当前对话 | 看语义决定;如果是“这次预算”则不保存,如果是“我旅行通常预算 2 万”则保存 | 约束推荐范围 |
| 别安排太早起 | 当前对话,也可能变成偏好 | 可以保存为偏好,但要标注置信度 | 避免安排早班机、早市、清晨景点 |
| 偏好民宿 | 当前对话 | 可以保存为用户偏好 | 未来旅行推荐时优先考虑 |
| 上次去了大阪 | 历史对话 | 已在长期记忆 | 避免重复推荐或提供连续路线 |
这就是 Agent 记忆的核心:
- 短期记忆负责“当前正在做什么”。
- 长期记忆负责“这个用户、这个项目、这个世界过去有哪些可复用信息”。
- 记忆调度器负责判断“现在要不要查长期记忆,要不要把当前信息写进去”。
如果只靠短期记忆,Agent 只能在本轮对话里连贯;如果长期记忆随便注入,又会把无关、过时甚至错误的信息带进回答。真正可用的记忆系统,不是“记得越多越好”,而是“该记的记,该忘的忘,该查的时候查,查到后谨慎使用”。
2. 原文核心观点重组
原文可以提炼成四个工程问题:
- 记忆分层:短期记忆和长期记忆分别承担什么职责?
- 写入策略:哪些信息值得进入长期记忆?哪些只应该留在当前上下文?
- 检索策略:什么时候需要从长期记忆中取信息?由谁决定?用什么算法决定?
- 治理策略:如何处理上下文腐烂、信息冲突、隐私风险和成本失控?
这四个问题对应一套完整闭环:
用户输入
-> 更新短期记忆
-> 判断是否检索长期记忆
-> 召回、排序、过滤相关记忆
-> 注入当前上下文
-> 生成回答或执行动作
-> 提取值得保存的信息
-> 去重、冲突检查、写入长期记忆
注意,这里有两条路径:
- 读路径:从长期记忆检索信息,帮助当前回答。
- 写路径:从当前对话提取信息,沉淀为未来可用的长期记忆。
很多 Agent 记忆设计失败,不是因为没有向量数据库,而是只做了“存”和“查”,没有认真设计读写时机、记忆质量、冲突规则和验证机制。
3. 记忆分层:不要把所有信息塞进一个篮子
3.1 短期记忆:当前任务的工作台
短期记忆是 Agent 处理当前任务时可直接看到的信息,通常由以下内容组成:
- 系统指令和开发者约束。
- 当前用户请求。
- 本轮或最近多轮对话历史。
- 工具调用结果。
- 当前任务计划、已完成步骤和中间产物。
它的特点是:
- 强相关:主要服务当前任务。
- 高时效:当前轮次结束后价值快速下降。
- 容量有限:受上下文窗口限制。
- 容易污染:塞太多无关内容会降低模型注意力质量。
例子:代码修复 Agent 正在处理一个测试失败。
短期记忆中应保留:
- 用户要求:修复登录接口 500 错误。
- 当前报错:TypeError: cannot read property 'id' of undefined。
- 已读文件:auth_controller.ts、session_service.ts。
- 已尝试方案:补充空值判断,但单测仍失败。
- 下一步计划:检查 middleware 是否注入 user。
这些信息不一定适合长期保存,因为它们大多只对当前修复任务有意义。
3.2 长期记忆:跨会话可复用的经验库
长期记忆是外部持久化存储中的信息,不会天然出现在模型上下文里,需要按需检索。它通常存储在:
- 普通文件或 Markdown 笔记。
- 关系型数据库或键值数据库。
- 向量数据库。
- 知识图谱。
- 事件日志和摘要库。
长期记忆适合保存:
| 类型 | 例子 | 使用价值 |
|---|---|---|
| 用户偏好 | 用户喜欢 Python、回答要直接、不要太多概念 | 个性化输出 |
| 项目事实 | 项目使用 FastAPI、数据库是 PostgreSQL | 降低重复询问 |
| 历史决策 | 上次已决定不用 Redis,原因是部署复杂 | 避免反复讨论 |
| 经验教训 | 某接口超时通常是供应商 API 慢 | 加速排障 |
| 摘要记忆 | 某次长会话的目标、结论、待办 | 支撑跨会话任务 |
| 领域知识 | 公司报销规则、内部命名规范 | 提高一致性 |
例子:一个长期协作的编程 Agent 可以记住:
{
"type": "project_fact",
"scope": "project:CheckMCP",
"content": "该项目的文档主要放在 book/cn 目录,中文长文倾向使用章节式标题和表格对比。",
"source": "用户历史任务和文件结构",
"confidence": 0.82,
"updated_at": "2026-08-26"
}
这条记忆对未来写同类文档有价值,但不应该每次都注入,只有当用户要求整理该项目文档时才检索。
3.3 中间层记忆:摘要、缓存和元记忆
实际工程中,不建议只分“短期”和“长期”两层。更实用的分层是:
| 层级 | 名称 | 生命周期 | 典型内容 |
|---|---|---|---|
| L0 | 当前上下文 | 当前请求 | 用户输入、工具结果、当前计划 |
| L1 | 会话摘要 | 当前会话 | 本次会话的压缩进展 |
| L2 | 近期缓存 | 最近几次会话 | 最近任务标签、常用实体、未完成事项 |
| L3 | 长期记忆 | 长期保存 | 用户偏好、项目事实、历史决策 |
| L4 | 元记忆 | 长期保存 | 哪些记忆可信、哪些记忆常被误用、哪些信息需要确认 |
例子:用户说“继续上次那个 MCP 文档”。
合理的检索顺序不是直接查全量长期记忆,而是:
1. 查 L1:当前会话是否已经有“上次 MCP 文档”的摘要?
2. 查 L2:最近几次会话是否有 MCP 文档任务标签?
3. 查 L3:长期记忆里是否有项目文档结构、用户写作偏好、历史决策?
4. 查 L4:是否存在“这个用户不喜欢纯概念说明,偏好实例”的元偏好?
这样可以降低成本,也能减少无关记忆污染。
4. 写入策略:什么时候应该记住
写入长期记忆的关键不是“能不能存”,而是“值得不值得存”。可以把每条候选信息问五个问题:
- 未来是否会复用?
- 是否足够稳定?
- 是否与用户、项目、任务或领域有关?
- 是否已经存在同类记忆?
- 是否涉及隐私、敏感信息或用户不希望保存的内容?
4.1 应该保存的信息
下面这些信息通常值得进入长期记忆:
| 候选信息 | 是否保存 | 原因 |
|---|---|---|
| “以后回答我尽量多举例,不要只讲概念。” | 保存 | 明确、稳定、未来高频复用 |
| “这个项目的中文文档要用章节式结构。” | 保存 | 项目级风格偏好 |
| “我们决定用 SQLite 做本地缓存,不引入 Redis。” | 保存 | 重要历史决策,未来可能影响设计 |
| “用户正在写 Agent 相关教程。” | 可保存 | 如果未来持续相关,可作为任务背景 |
| “上次线上故障是 Nginx 超时配置导致。” | 保存 | 可复用的排障经验 |
4.2 不应该保存的信息
下面这些信息不应直接长期保存:
| 候选信息 | 不保存原因 | 更好的处理 |
|---|---|---|
| “今天心情不好。” | 可能只是短期状态 | 仅用于当前对话语气调整 |
| “这次旅行预算 500 美元。” | 单次任务约束,不一定是长期偏好 | 保留在任务状态中 |
| “我的银行卡号是…” | 高敏感信息 | 不保存,必要时提醒安全风险 |
| “我可能喜欢 Go。” | 不确定、低置信度 | 暂存为低置信候选,后续确认 |
| “随便吧。” | 没有稳定语义 | 不保存 |
4.3 写入流程示例
用户说:
以后帮我写技术文档时,尽量先给真实场景,再解释概念。
记忆系统不应该直接把整句话塞进数据库,而应提取成结构化记忆:
{
"type": "user_preference",
"scope": "user",
"key": "technical_writing_style",
"value": "技术文档应先给真实场景或案例,再解释概念。",
"evidence": "用户明确要求:以后帮我写技术文档时,尽量先给真实场景,再解释概念。",
"confidence": 0.95,
"policy": "inject_when_task_is_technical_writing",
"updated_at": "2026-08-26"
}
这样做有三个好处:
- 未来检索更准确。
- 可以处理冲突更新。
- 注入上下文时更短、更清晰。
4.4 写入不是立即永久保存
比较稳妥的工程做法是把写入分成三段:
原始对话
-> 候选记忆
-> 经过过滤和确认的长期记忆
例子:
用户:我最近都在写 Python,感觉 TS 太麻烦。
候选记忆可以是:
{
"type": "candidate_preference",
"content": "用户近期更偏好 Python,对 TypeScript 有一定抵触。",
"confidence": 0.55,
"ttl": "30d"
}
但不应立刻升级为“用户长期偏好 Python,不喜欢 TypeScript”。因为这句话可能只是某个项目中的临时情绪。后续如果用户多次表达类似偏好,或者明确说“以后默认用 Python”,才适合升级为长期偏好。
5. 读取策略:什么时候应该检索长期记忆
长期记忆不应该每次都查,也不应该查了就全塞进提示词。读取策略要解决两个问题:
- 是否需要查?
- 查到哪些内容应该注入?
5.1 触发检索的典型信号
| 信号 | 用户表达或系统状态 | 例子 | 应对 |
|---|---|---|---|
| 显式回忆 | 用户提到“上次”“之前”“还记得” | “按上次的结构继续写。” | 检索相关历史摘要 |
| 个性化需求 | 用户要求“按我的习惯” | “按我喜欢的风格改。” | 检索用户偏好 |
| 项目上下文缺口 | 当前任务依赖项目背景 | “继续整理这个文档体系。” | 检索项目事实和历史决策 |
| 任务跨阶段 | 多步任务进入新阶段 | 从调研进入写报告 | 检索目标、约束和已完成内容 |
| 上下文窗口将满 | token 占用达到阈值 | 长对话超过 70% 到 80% 水位 | 压缩摘要,替换低价值历史 |
| 事实缺口 | 当前上下文缺少必要事实 | “公司内部报销规则是什么?” | 检索知识库或外部事实源 |
5.2 由谁决定是否检索
一个成熟系统通常不是让 LLM 单独决定,而是多层共同判断。
| 层级 | 决策主体 | 适合做什么 |
|---|---|---|
| 底层系统钩子 | 规则、向量相似度、关键词检测 | 低成本预检、主动预取 |
| 中层编排器 | Router、Orchestrator、LangGraph 条件边 | 根据任务状态强制检索或跳过 |
| 顶层 Agent | LLM 自主推理、工具调用 | 处理复杂语义、模糊请求和特殊场景 |
例子:用户说“按照我上次喜欢的方式写一个 README”。
底层钩子:检测到“上次”“喜欢的方式”,命中回忆信号。
中层编排器:当前任务类型是文档写作,决定检索 writing_style 相关记忆。
顶层 Agent:判断还需要项目 README 历史结构,于是调用 search_memory("README 文档风格 项目结构")。
5.3 怎么决定:四种常用机制
机制一:语义路由
把用户输入转成向量,与长期记忆索引做相似度计算。
if max_similarity(user_query, memory_index) > 0.75:
retrieve_top_k_memories()
else:
skip_memory_retrieval()
例子:
用户:这篇文档还是按我喜欢的那种写法来。
可能召回:
用户偏好:技术文档应先给真实场景,再解释概念。
用户偏好:希望回答更详细,但不要堆概念。
机制二:规则和水位线
规则适合处理确定性强的场景。
if token_usage > 0.8 * context_window:
summarize_old_messages()
if user_query contains ["上次", "之前", "继续", "按我的习惯"]:
retrieve_user_or_project_memory()
例子:长文档写作对话已经进行了 60 轮,这时继续把所有历史消息塞进上下文会造成上下文腐烂。更好的做法是保留:
- 当前目标。
- 已确认的大纲。
- 用户明确修改意见。
- 未完成章节。
- 关键术语和风格要求。
低价值内容,比如寒暄、已废弃方案、重复解释,可以被摘要替换。
机制三:LLM 自反判断
把“是否需要记忆”交给 LLM 做二分类判断。
输入:
- 当前用户问题
- 当前上下文摘要
- 可用记忆类型列表
输出:
- need_memory: true/false
- memory_scope: user/project/domain/task
- query: 用于检索的短查询
- reason: 为什么需要
例子:
用户:帮我继续昨天那份市场报告,把风险部分补上。
LLM 可能输出:
{
"need_memory": true,
"memory_scope": "task",
"query": "昨天 市场报告 风险部分 大纲 结论",
"reason": "当前请求依赖历史任务状态,当前上下文没有报告内容。"
}
这种方式更聪明,但成本更高,适合复杂任务或高价值任务。
机制四:缓存漏斗
不要一上来就查全量向量库。可以按成本从低到高逐级扩大:
L1 当前会话摘要
-> L2 最近任务标签
-> L3 用户画像和项目记忆
-> L4 全量向量库或知识图谱
例子:用户说“继续刚才那个”。
- 如果 L1 已经知道“刚才那个”是
Agent 记忆设计文档,就不需要查长期库。 - 如果 L1 不知道,但 L2 最近任务标签有
记忆.md 重构,再查对应摘要。 - 如果 L2 也没有,再查 L3/L4。
这比每次都做全量检索更便宜,也更不容易召回无关记忆。
6. 检索出来后怎么用
检索不是结束,真正影响效果的是注入方式。一个常见错误是把 Top-K 结果原样塞进 prompt,导致上下文臃肿甚至互相矛盾。
6.1 推荐使用“记忆卡片”
把检索结果整理为短小、可审计的记忆卡片:
相关记忆:
1. [用户偏好][高置信] 用户希望技术文档多用实例,不要只讲概念。
2. [项目事实][中置信] 当前项目文档多采用“章节标题 + 表格 + 示例”的中文教程风格。
3. [历史决策][高置信] 本次任务目标是把 book/记忆.md 重构为 Agent记忆设计.md。
使用要求:
- 只使用与当前文档重构直接相关的记忆。
- 如果记忆与用户当前明确要求冲突,以当前要求为准。
这种形式比原始日志更适合放进上下文,因为它短、结构清晰、置信度明确。
6.2 注入时要区分事实、偏好和建议
例子:检索到三条记忆。
A. 用户喜欢 Python。
B. 用户正在写 Agent 文档。
C. 建议使用向量数据库。
它们不能被同等对待:
- A 是用户偏好,影响示例语言选择。
- B 是任务背景,影响上下文理解。
- C 只是历史建议,不一定仍然成立,不能当作硬约束。
因此,注入时应标明类型:
[偏好] 用户偏好 Python 示例。
[任务背景] 用户近期在整理 Agent 文档。
[历史建议] 曾讨论过向量数据库,但当前任务未确认采用。
6.3 记忆不能覆盖当前用户指令
如果长期记忆说“用户喜欢详细解释”,但当前用户说“这次只给简短结论”,应优先当前指令。
推荐优先级:
当前用户明确指令
> 当前任务约束
> 项目级规则
> 用户长期偏好
> 历史建议或低置信记忆
例子:
长期记忆:用户喜欢详细讲解。
当前请求:只要一个 5 行总结。
正确行为:输出 5 行总结,而不是展开长篇说明。
7. 冲突处理:记忆会过期,也会互相打架
长期记忆如果没有治理,很快会变成“错误长期保存系统”。
7.1 典型冲突类型
| 冲突类型 | 例子 | 处理方式 |
|---|---|---|
| 用户偏好更新 | 原来喜欢 Java,现在明确说以后默认 Python | 更新主记忆,保留历史变更记录 |
| 任务约束变化 | 预算从 500 美元改为 800 美元 | 当前任务状态中覆盖旧值 |
| 项目事实变化 | 项目从 Flask 迁移到 FastAPI | 标记旧记忆过期,写入新事实 |
| 低置信误判 | 系统误以为用户讨厌 TypeScript | 降低置信度或删除 |
| 当前指令冲突 | 长期偏好详细,当前要求简短 | 当前指令优先 |
7.2 预算更新例子
错误做法:
长期记忆 1:用户东京旅行预算 500 美元。
长期记忆 2:用户东京旅行预算 800 美元。
这会让后续 Agent 不知道该相信哪一个。
更好的做法:
{
"type": "task_state",
"key": "tokyo_trip_budget",
"value": "800 USD",
"previous_value": "500 USD",
"change_reason": "用户在当前会话中更新预算",
"valid_for": "current_trip_planning_task",
"updated_at": "2026-08-26"
}
如果用户说的是“以后我旅行预算通常按 800 美元算”,才适合进入长期偏好。
7.3 用户偏好变化例子
用户过去多次说喜欢 Python,今天说:
这个项目以后全部用 TypeScript,不要再给 Python 示例。
正确处理不是删除“用户喜欢 Python”,而是加上作用域:
{
"type": "project_preference",
"scope": "project:current",
"content": "当前项目默认使用 TypeScript 示例,不使用 Python 示例。",
"overrides": "user_preference:python_examples",
"confidence": 0.96
}
这样未来在当前项目中优先使用 TypeScript,在其他泛化场景仍可参考用户的 Python 偏好。
8. 记忆系统的推荐架构
一个可落地的 Agent 记忆系统可以拆成七个模块。
输入流
-> Memory Collector 收集对话、工具结果、任务状态
-> Memory Extractor 提取候选记忆
-> Memory Validator 去重、过滤、权限和敏感性检查
-> Memory Store 持久化存储
-> Memory Retriever 按需检索
-> Memory Ranker 相关性、时效性、置信度排序
-> Memory Injector 把记忆压缩成可用上下文
-> Agent Response/Action 生成回答或执行动作
8.1 Memory Collector:收集原始材料
收集内容包括:
- 用户消息。
- Agent 输出。
- 工具调用和结果。
- 任务状态变更。
- 用户确认或否定。
例子:用户说“对,这个结构就按这个版本定稿”。Collector 应捕获这是一个确认信号,而不是普通闲聊。
8.2 Memory Extractor:提取候选记忆
Extractor 把原始文本变成结构化候选。
原始文本:以后写这种 Agent 文档,最好先讲真实例子,再讲架构。
候选记忆:
- 类型:用户偏好
- 范围:技术文档写作
- 内容:先用真实例子引入,再讲架构和概念
- 置信度:高
8.3 Memory Validator:判断能不能存
Validator 需要回答:
- 是否重复?
- 是否冲突?
- 是否敏感?
- 是否需要用户确认?
- 是否只是当前任务状态?
例子:
用户:这次就写详细点。
这不应该升级成“用户永远喜欢详细文档”。Validator 应把它标为当前任务约束。
8.4 Memory Store:选择合适存储
| 存储 | 适合内容 | 不适合内容 |
|---|---|---|
| JSON/Profile | 用户偏好、项目配置 | 大量非结构化日志 |
| SQL | 结构化事实、任务状态、审计记录 | 语义模糊的长文本检索 |
| 向量数据库 | 摘要、片段、经验、非结构化知识 | 强一致更新和精确约束 |
| 知识图谱 | 实体关系、组织结构、依赖关系 | 简单偏好和临时状态 |
| 文件/Markdown | 人可读项目记忆、设计记录 | 高频实时更新 |
一个实用 MVP 可以从“JSON 用户画像 + Markdown 项目记忆 + 向量摘要库”开始,不必一开始就上复杂知识图谱。
8.5 Memory Retriever 和 Ranker:召回后还要排序
排序可以综合四类分数:
final_score = semantic_similarity
+ recency_weight
+ frequency_weight
+ confidence_weight
- sensitivity_penalty
- staleness_penalty
例子:同样都命中“文档风格”,下面哪条更应注入?
A. 2 年前:用户喜欢非常长的解释。
B. 今天:用户要求这篇 Agent 记忆文档多举实例。
C. 上周:用户希望技术文档按章节和表格组织。
对于当前任务,排序应是 B、C、A。因为 B 最新且最具体,C 与任务相关,A 太泛且可能过期。
8.6 Memory Injector:只注入必要内容
Injector 的目标不是“把检索结果放进去”,而是“把当前任务真正需要的记忆压缩成清晰约束”。
例子:
当前任务相关记忆:
- 用户要求本次文档多举实例,避免纯概念说明。
- 原文重点包括短期记忆、长期记忆、检索触发、Router/LLM/Hook 三层决策。
- 周边文档风格偏章节式教程,适合加入表格和案例。
这比塞入三段历史聊天记录更有效。
9. 三个完整案例
9.1 案例一:旅行规划 Agent
场景
用户希望规划东京旅行。
用户:下周三去东京,5 天,预算 2 万,别太累。
短期记忆
{
"destination": "东京",
"start_date": "下周三",
"duration_days": 5,
"budget": "2 万",
"pace": "不要太累"
}
长期记忆检索
触发原因:旅行规划高度依赖用户偏好。
可能召回:
- 用户偏好民宿。
- 用户讨厌早起。
- 用户上次去大阪时喜欢本地小店,不喜欢网红打卡点。
最终使用
Agent 不应说:“我记得你讨厌早起,所以绝不安排上午活动。”因为这可能太绝对。
更稳妥的回答是:
我会按轻松节奏安排,上午不放太早的活动,住宿优先考虑交通方便的民宿。第一天建议只安排抵达和附近用餐,第二天再开始主景点。
写入长期记忆
如果用户后续确认:
对,我旅行一直不喜欢早起,住宿也更喜欢民宿。
则可以写入:
[
{
"type": "user_preference",
"scope": "travel",
"content": "用户旅行时不喜欢早起。",
"confidence": 0.95
},
{
"type": "user_preference",
"scope": "travel",
"content": "用户旅行住宿更偏好民宿。",
"confidence": 0.95
}
]
9.2 案例二:编程 Agent
场景
用户说:
继续修昨天那个登录接口的问题。
当前上下文没有“昨天那个问题”的细节,因此应触发长期记忆检索。
检索查询
query = "昨天 登录接口 问题 报错 修复进展"
scope = "project + recent_task"
召回记忆
- 昨天定位到登录接口 500 错误与 session middleware 未注入 user 有关。
- 已尝试在 controller 层加空值判断,但单测仍失败。
- 下一步计划是检查 auth middleware 的执行顺序。
- 项目使用 TypeScript + Express,测试命令为 npm test。
注入上下文
任务恢复摘要:
用户要继续修复登录接口 500 错误。上次进展显示 controller 层空值判断不是根因,下一步应检查 auth middleware 是否在路由前执行。项目使用 TypeScript + Express,验证命令是 npm test。
为什么这样设计
如果没有记忆,Agent 可能重新读一遍所有文件,浪费时间;如果注入完整昨天聊天记录,又会浪费上下文。摘要式任务恢复最合适。
写入长期记忆
修复完成后,值得保存的是“经验教训”,不是所有调试日志。
{
"type": "debug_lesson",
"scope": "project:login",
"content": "登录接口 500 错误曾由 auth middleware 执行顺序错误导致;排查时应先检查路由注册顺序和 user 注入。",
"confidence": 0.9
}
9.3 案例三:客服 Agent
场景
用户再次咨询 SaaS 产品的账单问题:
用户:为什么这个月又多扣了一笔?
需要的记忆
客服场景不能只靠用户偏好,还要检索历史工单、订阅状态和账单事实。
短期记忆:用户当前询问本月多扣款。
长期记忆:上月用户曾升级团队套餐,享受过 14 天试用。
外部事实:账单系统显示本月从试用转为正式订阅。
正确回答方式
我查到你上月把账号升级为团队套餐,当时有 14 天试用。本月这笔费用是试用结束后的正式订阅扣款。我可以继续帮你核对扣款日期和套餐人数,如果人数不对,再进一步处理退款或调整。
记忆治理重点
客服 Agent 的记忆需要更严格:
- 账单事实必须来自可信系统,不应仅凭历史对话。
- 敏感信息不应直接写入长期记忆。
- 用户争议和投诉要保存审计记录,但回答时要避免泄露内部备注。
10. 常见误区
10.1 误区一:上下文窗口就是记忆
上下文窗口只是短期记忆的载体。它能让模型看到当前材料,但不能天然跨会话持久化,也不能自己决定什么值得保存。
10.2 误区二:有向量数据库就有记忆
向量数据库只是存储和检索工具,不等于记忆系统。完整记忆系统还需要:
- 信息提取。
- 语义去重。
- 冲突更新。
- 置信度管理。
- 权限控制。
- 注入策略。
- 评估指标。
10.3 误区三:记忆越多越智能
记忆过多会导致:
- 检索噪声变大。
- 上下文成本升高。
- 过时信息污染回答。
- 用户隐私风险增加。
好的 Agent 应该像一个靠谱助手,不是什么都记,而是知道哪些信息会影响未来决策。
10.4 误区四:摘要一定安全
摘要会压缩上下文,但也可能丢掉关键细节。
例子:
原文:用户不喜欢早起,但如果是筑地市场这种特殊体验,可以接受 7 点以后。
错误摘要:用户不喜欢早起。
错误摘要会让 Agent 永远不安排上午活动。更好的摘要是:
用户一般不喜欢早起;旅行中可接受少量 7 点以后的特殊体验,但不应连续安排早起。
11. 最小可用方案:从简单系统开始
如果要从 0 到 1 搭建 Agent 记忆,不建议一开始做很复杂。可以先实现一个 MVP。
11.1 MVP 架构
短期记忆:当前 messages + task_state
会话摘要:每 10 到 20 轮压缩一次
长期记忆:user_profile.json + project_memory.md + vector_summaries
检索触发:关键词规则 + 相似度阈值
注入策略:最多注入 3 到 5 条记忆卡片
写入策略:只保存明确偏好、项目事实、历史决策和任务摘要
11.2 MVP 数据结构
{
"id": "mem_001",
"type": "user_preference",
"scope": "technical_writing",
"content": "用户希望技术文档多用真实案例,不要只讲概念。",
"tags": ["writing", "agent", "examples"],
"confidence": 0.95,
"source": "explicit_user_instruction",
"created_at": "2026-08-26",
"updated_at": "2026-08-26",
"ttl": null
}
11.3 MVP 检索规则
触发检索,如果满足任一条件:
1. 用户输入包含“上次、之前、继续、按我的习惯、记得”。
2. 当前任务类型属于长任务:写报告、写代码、调试、研究、规划。
3. 当前上下文 token 水位超过 80%,需要摘要替换。
4. 当前问题与某条记忆向量相似度超过 0.75。
11.4 MVP 写入规则
允许写入:
- 用户明确表达的长期偏好。
- 项目中已确认的技术事实。
- 跨会话任务的阶段性摘要。
- 经过验证的问题原因和解决方案。
禁止写入:
- 敏感凭据。
- 单次闲聊情绪。
- 未确认的猜测。
- 与当前用户指令无关的原始对话全文。
12. 评估记忆系统是否好用
记忆系统必须评估,否则很容易“看起来聪明,实际上乱记”。
12.1 读路径指标
| 指标 | 问题 | 示例 |
|---|---|---|
| 召回率 | 该查的有没有查到? | 用户说“继续上次报告”,是否找到报告摘要? |
| 精确率 | 查到的是不是相关? | 是否把旅行偏好注入到代码任务里? |
| 新鲜度 | 是否用了过时信息? | 是否仍使用旧预算、旧技术栈? |
| 成本 | 检索和注入是否过多? | 每轮都注入 20 条记忆就是危险信号 |
| 延迟 | 记忆检索是否拖慢响应? | 是否需要预取或缓存? |
12.2 写路径指标
| 指标 | 问题 | 示例 |
|---|---|---|
| 写入质量 | 保存的信息是否结构化、可复用? | “用户喜欢例子”比整段原话更好 |
| 去重能力 | 是否重复保存相同偏好? | 同一偏好不应出现 10 个版本 |
| 冲突处理 | 新旧信息冲突时是否更新? | 预算从 500 改 800 是否覆盖? |
| 隐私合规 | 是否保存了不该保存的信息? | 密码、银行卡、身份证号不应入库 |
| 可解释性 | 能否说明为什么使用这条记忆? | 回答可追溯到来源和置信度 |
12.3 测试用例示例
可以为记忆系统设计测试集:
用例 1:用户明确说“以后文档多举例”。
期望:写入用户偏好,未来写文档时召回。
用例 2:用户说“这次预算 500,等等改成 800”。
期望:当前任务状态更新为 800,不生成两条互相冲突的长期偏好。
用例 3:用户问“继续昨天的登录 bug”。
期望:召回最近任务摘要,而不是全量用户画像。
用例 4:用户当前要求“只给简短答案”。
期望:即使长期偏好是详细解释,也应服从当前指令。
用例 5:用户输入银行卡号。
期望:不写入长期记忆,必要时提示敏感信息风险。
13. 设计检查清单
在实现 Agent 记忆前,可以用下面的清单快速检查方案是否完整。
13.1 写入检查
- 是否区分了短期任务状态和长期偏好?
- 是否有候选记忆到正式记忆的过滤流程?
- 是否处理重复、冲突和过期?
- 是否对敏感信息默认不保存?
- 是否记录来源、时间、置信度和作用域?
13.2 读取检查
- 是否只在需要时检索?
- 是否有关键词、相似度、任务状态等触发规则?
- 是否限制注入数量和长度?
- 是否区分事实、偏好、历史建议和低置信信息?
- 是否让当前用户指令优先于长期记忆?
13.3 治理检查
- 用户能否查看、修改或删除记忆?
- 系统能否解释为什么使用某条记忆?
- 是否有定期清理低价值记忆的机制?
- 是否有评估数据集验证召回准确率?
- 是否监控 token 成本、延迟和错误注入?
14. 总结
Agent 记忆不是单纯的“把聊天记录存起来”,而是一套围绕任务连续性、个性化和可靠性设计的工程系统。
最核心的设计原则可以概括为:
- 短期记忆管当前任务,长期记忆管可复用经验。
- 写入前先判断价值、稳定性、作用域和风险。
- 读取时先低成本预检,再按需召回和压缩注入。
- 记忆必须有来源、时间、置信度和冲突处理规则。
- 当前用户指令永远优先于长期记忆。
如果要做一个稳妥的第一版,可以从最小闭环开始:
会话摘要 + 用户偏好 JSON + 项目记忆 Markdown + 向量摘要检索 + 记忆卡片注入
等读写策略、评估指标和冲突治理跑稳后,再扩展到更复杂的知识图谱、主动预取、多级缓存和元记忆体系。
- 点赞
- 收藏
- 关注作者
评论(0)