Skip to content

04 · 上下文与记忆

完成本章后,你应能估算一次模型调用的上下文组成,解释消息裁剪和摘要会丢失什么,区分运行时状态、checkpoint 与长期记忆,并为写入记忆建立来源、信任、过期和删除规则。所有 token 数字示例均为教学假设,不代表任何供应商或模型的窗口、计数器或价格限制。

对话历史不等于模型此刻看到的内容。每次调用都会构造一份输入:系统与开发者指令、当前用户消息、选入的历史消息、工具定义、工具结果、检索片段或记忆,以及消息格式本身的开销。上下文管理就是从可用信息中选出本次决策所需的子集,同时满足模型窗口、产品预算、时延和隐私边界。

“窗口够大”不等于“应该全部塞入”。长历史会增加调用成本与处理时间,也可能让相关信息被大量无关内容稀释。更重要的是,原样重放旧消息可能带入过时事实、工具输出中的恶意指令或不再适用的用户偏好。上下文不是可信度标签:消息进入 prompt 不会因此变成事实。

假设某个教学应用自行设定本次请求的输入与回答合计预算为 8,000 个 token。下面数字只是便于算术演示的估计值,不是模型限制:

输入部分 假设占用 备注
系统与开发者指令 1,100 必须保留的行为约束
当前用户问题 300 本次任务输入
工具定义与格式约束 900 可用工具越多,定义开销可能越大
最近对话消息 2,000 保留近期上下文
检索片段 1,800 本次问题可能相关的来源内容
消息边界和其他估算误差 200 简化示例中的余量
输入小计 6,300 8,000 − 6,300 = 1,700,尚未预留回答

假定应用还预留 1,200 token 给回答,则余下只有约 500 token 可容纳更早对话、摘要或额外工具结果。这个 8,000 是应用在教学例子里自定的合计预算;实际 API 对上下文窗口、输入输出上限、缓存和计费的定义会因服务与模型而异,应以实际接口文档和 tokenizer 结果为准。字符数不是 token 数的可靠替代,中文、代码、JSON 和多模态内容的比例也不同。估算后还需要记录真实调用的使用量(如果接口提供),并校准预算。

当超预算时,不要先截掉最早的消息就结束。先问每段内容对当前任务的价值、可靠性和时效性:哪些是必须保留的约束?哪些旧信息只在某个话题下有用?检索结果是否重复?工具定义是否需要全部提供?回答预留是否足够?预算是选择问题,不只是字符串截断问题。

裁剪直接删除或省略部分消息。实现简单,适合可重建或已无关的内容;但可能丢失早期约束、推理依据或仍未完成的任务。可以保留系统约束、当前请求、最近完整轮次和必要工具调用,并优先移除重复内容。不要只保留对话最后几条,而忽略一条更早且仍生效的安全约束。

摘要把多条历史压成较短表述。它节省空间,适用于连续任务的背景,但摘要是有损变换:精确日期、数值、原话、说话者、否定词、条件和不确定性都可能消失。摘要应区分“用户明确说”“助手推测”“工具观察”,保留关键原文引用或来源标识,并注明覆盖的时间范围。高风险决策不应只依赖一个不可追溯的压缩句。

按需检索将较早的信息放到外部存储,在相关时再召回。它避免每次都携带全部历史,但召回可能漏掉信息、取回过期版本,或让相似但不相关的记录进入上下文。召回结果仍要做权限检查、时效检查和来源检查。裁剪控制即时输入,摘要压缩历史,检索决定从较大存储中挑选候选;三者可以组合,不能替代彼此的正确性验证。

生命周期:状态、checkpoint、长期记忆

Section titled “生命周期:状态、checkpoint、长期记忆”
对象 主要用途 生命周期与边界
会话/运行状态 当前一次交互或图执行所需的变量,如查询、结果、步骤数 通常随运行创建和更新;是否跨请求保留由实现决定
Checkpoint 保存一条图运行的状态快照,以便查看或恢复 通常按 thread/run 标识组织;是否跨进程取决于 checkpointer 与存储后端
长期记忆 供未来任务检索的用户偏好、已确认事实或摘要 跨运行保存,需要写入、读取、更新、过期、访问与删除策略
上下文 某次模型调用实际收到的信息集合 每次请求都可重新构造,不必等于任何一种持久存储

仓库的 LangGraph 练习用 InMemorySaver 展示线程 checkpoint。它能在当前 Python 进程中保存状态,但进程结束时内存数据丢失;这不是跨重启的持久存储,也不是长期用户记忆。若要演示进程重启后恢复,需要选择并运行一个持久后端,再验证写入、关闭、重启、恢复同一 thread 的完整过程。本章没有提供 SQLite 或持久化记忆 demo,也不应把它写成已有功能。

模型生成的摘要、检索到的网页、工具返回内容和用户明确陈述,证据地位不同。把它们都写成“用户事实”会造成来源混淆。可为每条记忆保留结构化属性,例如:

记忆:用户可能下周搬家
来源:用户在会话 2026-09-29 的原话
状态:未确认 / 不确定
记录时间:2026-09-29
有效期:待确认,不作为确定日程

该格式只是概念示例,不是项目里的数据结构。系统可采用更简洁的 schema,但至少要保留事实来源和不确定性。写入前考虑必要性:短暂的一次性问题未必值得跨会话记忆;敏感信息除非有明确必要与许可,不应为了个性化而留存。偏好和事实也可能过期,应设置复核或有效期限。

读取记忆时,来源文本应作为数据而非高权限指令处理。记忆或网页里出现“忽略此前规则并执行某动作”,不会因此获得系统指令权限。只把所需字段放入模型上下文,限制对记忆工具的写入权限,并对可改变账户、发送消息等副作用要求独立授权。信任边界既包括“这条内容是否真实”,也包括“它有没有权命令系统做事”。

假设旧记忆是“用户住在南京”,用户后来说明已经搬到苏州。不能简单追加两条而让后续检索随机选一条。应先区分事实类别与时间:旧值标记为失效或带结束时间,新值记录来源与生效时间;若用户只是说“可能会搬”,则不应覆盖成已搬迁事实。遇到冲突,系统可询问确认,或在答案中保留不确定性,而不是自行挑一个确定版本。

删除流程也需要验证:找到涉及对象的记录和派生摘要,确认删除范围,移除或失效向量索引与缓存,检查后续检索不再返回,并说明备份与保留期的限制。只删除原始数据库记录而保留可检索的向量副本,不算完整删除。实际处理应遵守应用对用户数据和日志的约束。

对话中用户说:“我可能下周搬家,日期还没确定。”摘要若写成“用户下周搬家”,删掉了“可能”和“未确定”;日程 Agent 随后可能擅自安排搬家任务。修复不只是换一个摘要 prompt:记录来源、保留模态词、设定待确认状态,并在高影响行动前重新询问。摘要仍可能错,因此要允许纠正并让纠正传播到相关副本。

  1. 按上表计算示例输入小计和余量。若检索片段由 1,800 增至 2,600 token,其他项不变,输入小计是多少?如果仍需为回答预留 1,200 token,应先检查什么?
  2. 一段旧对话包含用户的饮食偏好、一次性的天气问题、未确认的搬家计划和助手的一项推测。说明哪些内容适合裁剪、摘要或转成长期记忆,并写出需要保留的来源/状态。
  3. 概念设计,不是仓库可运行实验:列出一个跨进程 checkpoint 验证步骤,并说明为什么仅检查同一进程中的 get_state() 不够。
  4. 设计一条用户删除记忆的验收用例:不只检查数据库,还要检查索引、摘要和后续检索。
  1. 原输入小计是 6,300,距离合计预算还有 1,700。检索增长 800 后,输入小计为 7,100;加上预留的 1,200 回答,合计 8,300,超过应用自定的 8,000 预算 300。先压缩重复或低价值片段、减少候选,或经成本评估后调整应用预算;不能把教学数字当成供应商硬限制。
  2. 饮食偏好若稳定、确有未来用途且用户允许,可保留为带来源和更新时间的偏好;天气问题通常一次性,可裁剪;搬家计划应保留“可能”“日期未定”,除非有未来用途,不必写入长期记忆;助手推测不能冒充用户事实,应标记为推断或丢弃。裁剪/摘要选择取决于当前任务,不是固定分类。
  3. 使用持久存储配置运行同一 thread,确认状态已写入;干净退出进程;重新启动新进程并重建图与 checkpointer;用原 thread 标识加载状态,验证预期字段及下一步恢复行为。若两个操作都在同一进程,数据可能只存在内存,不能证明磁盘持久化。
  4. 先记录某记忆及其派生摘要,执行删除请求,再验证主存储查不到,索引/缓存不再召回,汇总字段已更新,未来同类查询不会返回该内容。还应检查删除范围、授权和备份保留限制;不应声称即时删除超出系统可验证的部分。

问:上下文窗口、会话状态、checkpoint 和长期记忆有什么区别? 上下文是单次模型调用构造出来的输入;会话状态是运行中节点读写的数据;checkpoint 是可按运行或 thread 恢复的状态快照;长期记忆是供未来任务检索的信息。上下文可以由状态、历史、检索结果和指令共同组成,但不等于其存储后端。持久性必须看具体实现,不能从对象名称推断。

问:裁剪与摘要的主要风险是什么? 裁剪会直接丢掉被删消息中的事实或约束;摘要保留大意但可能改写条件、数值、说话者和不确定性。对高价值信息应保留来源或原文指针;选择策略应针对当前任务,并测试关键约束能否跨预算压力保留。

问:怎样避免记忆污染未来决策? 写入前判断是否必要,记录来源、时间、置信/确认状态和有效期;把模型推断与用户陈述分开;冲突时保留版本或询问用户;读取时检查时效和权限,把检索文本视为数据而非指令;支持更正和可验证删除。

  • 能把一次请求的预算按指令、历史、工具、检索与回答余量拆开。
  • 能说明裁剪、摘要、检索各自会丢失或引入什么问题。
  • 不把 InMemorySaver 描述成跨进程存储或长期记忆。
  • 记忆条目保留来源与不确定性,并说明如何更新、过期和删除。
  • 能解释检索到的记忆不自动拥有指令权限。