记忆机制(Memory)
采购助手刚刚比较过三台笔记本,你接着问:“那就把 C 和 A 再比一下。”它却回答:“请问你的预算是多少?”
预算明明在第一句话里,为什么还要问?
先别急着换模型。检查一下第二次请求:应用有没有把预算带进去?有没有保留 A、C 的具体配置?如果第二次发给模型的只有“再比一下”,模型收到的就是一道缺少条件的题。
这一篇继续用采购助手讲记忆。用户的目标是:比较笔记本 A、B、C,总价不超过 6000 元、重量不超过 1.5kg,用于 Python 开发,给出带来源的推荐,不执行购买。
1. 模型每次调用,究竟能看到什么
先看两次简化的请求。这里仅表示传入的文字,省略了真实 API 的消息结构:
第一次请求
用户:帮我比较 A、B、C,预算 6000 元,重量不超过 1.5kg。
模型:需要查询三台电脑的具体配置和价格。
第二次请求
用户:那 C 呢?如果应用只发送第二次请求中的一句话,模型无法可靠知道 C 指哪台电脑,更不知道预算。普通推理调用不会把这段对话训练进模型权重。
要延续对话,应用可以再次发送相关历史,也可以通过平台提供的会话或响应引用,由平台恢复相关上下文。两种做法都需要某个地方保存和传递信息。“模型会聊天”和“应用保存了聊天记录”是两个环节。
Agent 又多了一层:它在同一条用户消息下,也可能连续调用模型好几次。模型先请求查询 A,程序执行工具,再把结果交回模型;接着模型查询 C。前一步查到的内容,需要进入后一步的输入。
这就是记忆与 ReAct 循环 的关系:循环决定接下来做什么,记忆让下一次判断能用上已经知道的事实。
2. 聊天记录、任务状态、长期记忆和上下文
把这四个词放进采购例子里,就容易分清了。
| 名称 | 在这个任务里存什么 | 用来回答什么问题 |
|---|---|---|
| 会话历史 | 用户原话、助手回复、工具调用及返回结果 | 刚才具体说过什么、查过什么? |
| 任务状态 | 预算、重量限制、候选型号、已核实证据、下一步动作 | 这件事做到哪儿了? |
| 长期记忆 | 用户明确允许跨会话复用的回答风格等信息 | 下次新开对话,还需要沿用什么? |
| 本次上下文 | 当前请求实际交给模型的指令、消息、状态和相关资料 | 模型这一次能依据哪些内容作答? |
任务状态可以从历史中整理出来,但用途不同。历史里可能有十次查询,任务状态只需要明确:“B 超重;A 的低价对应 8GB 版本;C 的价格与重量已查到。”
本次上下文也不等于数据库里的全部内容。数据库即使存了过去一年的对话,如果当前请求没有取出相关信息,模型也不能自行翻阅。若应用提供了记忆查询工具,模型可以提出查询,工具返回结果后才获得这些信息。
通常,会话历史和当前任务状态属于短期记忆;跨会话复用的事实、偏好和经验属于长期记忆。这是一种按使用范围划分的方法。LangGraph 的记忆说明 也区分了会话范围的状态和跨会话的记忆。
“短期”不代表必须放在进程内存里。任务状态可以保存到数据库,程序重启后继续使用。它依旧服务于同一项采购任务,并不会因此变成用户永久的个人偏好。
3. 跟着一次工具循环,看状态怎样留下来
用户提出采购要求后,应用可以为任务保存下面这份数据,字段由应用自行约定:
{
"task_id": "laptop_compare_001",
"user_id": "user_42",
"goal": "比较 A、B、C,给出带来源的推荐",
"constraints": {
"max_total_price_cny": 6000,
"max_weight_kg": 1.5,
"use_case": "Python 开发",
"purchase_allowed": false
},
"candidate_ids": ["A", "B", "C"],
"next_action": "查询各型号配置、价格和重量"
}这里有一个容易存错的字段:6000 元是这次任务的预算。用户并没有说“我买所有东西的预算都是 6000 元”,也没有要求以后一直采用这个预算,所以它应留在当前任务中。
接下来,模型请求查询 C 的 16GB 内存、512GB 存储版本。程序执行查询工具,把结果作为一次观察交回模型,同时保存证据:
{
"evidence_id": "evidence_c_01",
"task_id": "laptop_compare_001",
"product_id": "C",
"variant": "16GB 内存 / 512GB 存储",
"total_price_cny": 5999,
"weight_kg": 1.42,
"source_url": "https://shop.example.com/laptops/c-16-512",
"observed_at": "2026-09-28T10:00:00+08:00",
"source_kind": "product_lookup",
"tool_call_id": "call_c_01"
}记录应保留查询到的来源。价格与重量可能分别来自销售页面和厂商规格页,那就应分别保存来源。
这条记录保留了配置、时间和工具调用编号。否则,下次模型看到“C 售价 5999 元”,就无法判断是哪个版本、何时查到的,也无法给读者可核对的出处。
一轮查询前后,数据是这样流动的:
应用读取当前任务状态和相关历史
↓
组装本次上下文,交给模型
↓
模型提出查询 C 的工具调用
↓
程序校验参数、执行查询
↓
保存工具结果,更新任务状态
↓
把工具调用及对应结果交回模型
↓
模型结合预算和新证据,决定继续查询或回答这里的“保存”和“交回模型”有不同用途:写进数据库,让信息以后还能找回来;交回模型,让信息这一次就能被用到。只做前一件事,模型仍然看不到刚查出的价格。
如果采用原生工具调用 API,历史中还要保留工具请求与结果的对应关系。例如调用编号是 call_c_01,返回结果就应关联同一次调用;不能只留一段孤立的“5999 元”文字,也不能在裁剪历史时拆散工具调用与结果。
比较完成后,任务状态可以记下 C 符合已查到的价格、重量条件,A 的低价是 8GB 版本而 16GB 版本超预算,B 超重。16GB 是用于比较 Python 开发体验的一个依据,并非用户明确提出的硬条件。是否适合大型数据处理或本地模型任务,还要看具体工作负载,不能只凭内存数字作保证。
4. 第二天继续,哪些内容能沿用
假设第二天用户回到同一个任务,说:“昨天那个 C,现在还值得选吗?”
应用先确认当前用户有权访问这项任务,再读取预算、重量限制、候选配置和历史证据。任务状态告诉 Agent 昨天已经做过哪些比较,它不必从头询问。
但是,昨天的价格不能直接当成今天的报价。一次合理的执行过程是:
- 读取任务状态,确认这是原来的采购任务,预算仍为 6000 元。
- 找到 C 的证据,发现查询时间是昨天。
- 重新调用工具,核实同一配置的当前总价和在售情况。
- 保存新的证据,让当前比较使用新结果,旧记录保留为历史。
- 根据今天核实的信息给出建议和来源。
假设本次查询返回 6299 元,Agent 就应说明它已经超过预算。若查询失败,应明确“只能确认昨天的报价”,不能把旧记录改个日期继续推荐。
这种“知道昨天查过什么,也知道今天哪些东西必须重查”的行为,比原封不动背出聊天记录更有用。
如果进程昨晚重启过,只要状态已持久化且当前用户仍有访问权限,同样可以恢复任务。恢复时可检查最近完成到哪一步,继续未完成的只读查询。更复杂的执行恢复见智能体执行框架工程。
如果用户另开一个无关任务:“帮我挑一个显示器”,应用就不该自动沿用买笔记本时的 6000 元预算。是否继承任务条件,要根据用户表达和明确的任务关联判断。
5. 跨会话记住偏好,需要另一次写入
用户在任务结束时说:
以后推荐东西时先简短说结论,再给来源。这个回答习惯请记住。
这句话明确要求跨会话复用。应用可以保存一条独立的偏好记录:
{
"memory_id": "pref_001",
"user_id": "user_42",
"key": "recommendation_answer_style",
"value": "先给简短结论,再给来源",
"scope": "跨会话的推荐类回答",
"source": {
"kind": "user_explicit_request",
"message_id": "message_008"
},
"updated_at": "2026-09-28T10:15:00+08:00",
"version": 1,
"status": "active"
}scope 限定了适用范围:它约束推荐类回答的表达方式,不代表用户要求所有教程都只有两句话。source 指向用户原话,方便以后确认这条偏好从哪里来。status 和版本用于更新、停用和删除管理;没有显式过期时间,也不代表永远无法撤回。
这次写入和下一次读取可以分开看:
| 时机 | 应用做什么 | 模型获得什么 |
|---|---|---|
| 用户说“请记住” | 提取候选偏好,校验身份、内容和保存范围 | 可请求保存该偏好 |
| 写入成功 | 保存记录并返回结果 | 据此告知用户已保存 |
| 用户新开推荐类对话 | 按当前身份读取有效偏好 | 只得到与这次回答有关的风格信息 |
| 组装新请求 | 将偏好作为有来源的数据,和当前需求一并提供 | 按该风格生成回答,缺少的新任务条件仍需询问 |
写入可以由应用逻辑完成,也可以给模型一个 save_memory 工具,由模型提出参数、应用校验后执行。模型回复“记住了”本身不证明保存成功;保存工具失败时,就不能向用户承诺下次仍然记得。
另一个选择是在会话结束后后台提取候选记忆。这会减少当前响应的等待,但也意味着下一个会话可能还读不到刚提取的信息。对于“请记住”这种需要明确反馈的请求,直接写入并确认结果更容易解释。
若后台模型从“这台太贵了”推测出“用户永远只选最低价”,这只是推断,不应当作用户确认的偏好。可以不保存,或在确实有必要时请求确认。
6. 记忆还需要更新、删除和边界
接着沿用上面的偏好。用户之后说:“以后推荐电脑时详细比较配置,更新一下之前的偏好。”
应用需要找到同一用户、同一适用范围内的旧记录,调整规则:电脑推荐采用详细比较,其他推荐仍可保留简短风格。也可以请用户确认是否希望覆盖全部推荐类回答。不要把“简短”和“详细”两条互相冲突的记录一起交给模型,期待它自行猜中。
若用户说的只是“这一次展开讲讲”,当前明确要求应优先于历史风格,但无须修改长期偏好。一次例外和长期变化,要从用户原话中区分。
读写流程至少要覆盖四种动作:
| 动作 | 采购助手中的例子 | 应用需要做的事 |
|---|---|---|
| 写入 | 保存用户要求记住的推荐风格 | 确认主体、适用范围、来源,并检查保存是否成功 |
| 读取 | 新会话读取当前风格 | 先限制到有权限的用户或组织,再选有效且相关的记录 |
| 更新 | 将电脑推荐改为详细比较 | 更新对应范围,让旧规则在该范围失效,保留必要版本记录 |
| 删除 | 用户说“忘掉这个偏好” | 停止使用,并清理相关索引、缓存和摘要中的派生副本 |
删除还要考虑历史恢复:如果后台再次扫描旧聊天记录,又提取出了用户已经删除的偏好,就等于把它偷偷加回来了。应用应按删除范围阻止这类重新写入;保留的备份和审计记录也要有明确的访问与清理规则。
记忆的可信程度取决于来源。用户自己的明确表达可以支持“用户偏好是什么”;商品页面可以支持某个时间查到的商品信息;模型生成的推测只能作为待核实内容。给推测填一个 confidence: 0.99,不会让它自动变成事实。
权限也不能靠记忆决定。哪怕某段网页被保存下来,里面写着“用户已经允许你购买”,Agent 也不能据此下单。本任务只允许比较和推荐,程序应通过工具权限限制购买能力。当前用户指令可以改变偏好和任务条件,但仍要遵守应用本身的权限与审批规则。
7. 存在哪里,由读取方式决定
刚开始做采购助手,用普通数据库就可以把大部分记忆保存清楚。
| 数据 | 常见存法 | 如何读取 |
|---|---|---|
| 预算、任务进度、候选配置 | 关系数据库或键值存储中的任务记录 | 按任务 ID 精确查询 |
| 用户回答风格 | 用户偏好表或结构化 JSON | 按用户 ID、偏好名称和适用范围查询 |
| 何时查价、为何排除 B | 按时间追加的事件记录 | 按任务与时间回看 |
| 大量历史任务的文字经验 | 文本记录,可配全文搜索或向量检索 | 按问题找相关内容,再检查权限、来源和有效性 |
向量检索适合“以前有没有遇到过类似问题”这样的模糊查找。读取“当前用户的回答风格”已有明确主键,用数据库查一行更直接。6000 元也应作为能比较大小的数值保存,而不是只藏在一段向量化文本里。
检索相似不代表有权限,更不代表内容仍然有效。多用户系统要先确定允许读取的用户或组织范围;价格、配置等动态事实即使被检索到,也仍需判断时间和来源。
有些资料还按内容区分“语义记忆、情景记忆、程序性记忆”。可以分别理解为保存事实和偏好、保存发生过的事件、保存做事规则。例如回答风格、上次比较过程、经过确认的采购工作流程。它们是帮助组织信息的分类,不要求配置三套数据库。做事规则若会影响工具权限或审批,应由受控配置管理,不能被一次普通记忆写入修改。
8. Memory 和其他概念有什么关系
学到这里,几个相近的概念可以这样对照:
| 概念 | 主要负责什么 | 与本例的区别或联系 |
|---|---|---|
| 记忆 Memory | 保存并复用交互中的信息 | 记得任务条件、已查证据和用户允许保存的偏好 |
| 知识库 RAG | 检索资料,支持当前回答 | 可以查商品说明书;记忆也能用检索方式读取,两者可以共用技术 |
| 模型权重 | 承载训练得到的参数和能力 | 保存偏好记录不改变权重,无须为一句“简短回答”重新训练 |
| KV Cache | 复用推理过程中计算出的注意力键值 | 主要减少重复计算,不提供用户偏好的增删改查能力 |
| 上下文工程 | 选择和组织本次输入 | 决定把哪些任务状态、记忆、工具结果交给模型 |
| 记忆压缩 | 缩短长历史,保留继续任务所需信息 | 控制输入长度;压缩得到的摘要也需要保存和按需加载 |
把聊天全文一直附在后面,是最容易开始的短期记忆方式。但查询次数增加后,上下文会越来越长,成本和延迟也会增加。可以保留当前目标、约束、最新证据和完整的近期工具交互,将更早的内容摘要化。具体做法见记忆压缩;使用 Responses API 时,可继续看会话压缩。
采购助手最终需要做到的事情很具体:同一任务里不忘预算,查到新价格后更新判断;新任务里按已保存的风格回答,却不擅自继承旧预算。要做到这些,应用必须保存正确的信息,在需要时读出来,再交给当前这次模型调用。