聊天记录明明还在,助手却忘了前面讨论过的条件;换一个会话,它又能记住用户的某项偏好。这两种现象并不矛盾,因为保存聊天记录、组织模型上下文和维护长期记忆,处理的是不同的问题。
最近在开发的 Agent 工作平台中,聊天已经接入业务查询、代码分析、文件和图表生成。用户连续完成取数、分析、画图、修改报告时,系统既要延续先前确定的条件,又要避免工具结果和过程记录无限增长。原始记录、摘要、用户记忆和计算文件,因此分别进入了对话运行时的管理范围。
Claude Code、Codex 及其官方 API 文档提供了有代表性的处理方式。下面先解释这些机制各自解决什么问题,再介绍平台已经落地的做法。产品内置行为、API 可选能力和本文归纳的工程经验分别说明,避免将某个产品的实现细节当成通用标准。
从 Agent 工作循环理解上下文
Agent 接到任务后,会不断收集信息、执行行动、检查结果。一次查询产生的数据,可能成为下一次计算的输入;一次验证发现的问题,又会触发新的搜索与修改。用户也可能中途补充条件,改变后续工作的方向。Claude Code 的官方文档用这一循环解释其工作过程。参考:How Claude Code works
上下文管理发生在整个循环中。它需要回答:当前任务是什么,哪些条件仍然有效,哪些信息已经过时,接下来必须重新读取什么。只保留最近几轮,可能丢掉最初的约束;一直堆积全部历史,又会让早已无用的日志占据大量输入。
例如,用户最初要求“分析上个月办公楼的能耗,排除节假日”,之后连续讨论了十几轮图表颜色。下一次重新计算时,“排除节假日”仍然重要,而某次绘图失败的完整堆栈可能已经没有必要继续携带。决定保留什么,需要结合信息在当前任务中的作用。
先区分六种信息
| 信息 | 回答什么问题 | 典型内容 | 生命周期 |
|---|---|---|---|
| 原始会话记录 | 实际发生过什么 | 用户消息、回答、工具调用与结果 | 按会话保留策略保存 |
| 当前模型上下文 | 这一轮需要知道什么 | 指令、有效条件、近期消息、必要证据与工具定义 | 一次模型请求 |
| 任务摘要 | 当前工作进行到哪里 | 目标、决定、对象标识、已完成与待办事项 | 当前任务的后续执行 |
| 持久指令 | 项目怎样工作 | 构建命令、代码规范、固定操作约定 | 项目或用户主动维护 |
| 长期记忆 | 以后什么还值得记住 | 稳定偏好、用户反馈、可复用经验 | 按用户或项目跨会话保存 |
| 外部产物与执行状态 | 数据和成果在哪里 | 数据文件、报告、代码、沙箱变量 | 文件保留期或执行环境生命周期 |
当前模型上下文是一份经过选择的输入,原始会话记录则是可供追溯的过程。任务摘要用于续接工作,长期记忆用于未来复用。已经确认的业务结果还有自己的报告或数据文件,不能只存在于一句“记住了”的回答里。
LangGraph 也按作用域区分线程内的短期状态与跨线程的长期记忆,并通过命名空间组织长期存储。这说明,先确定信息属于哪个用户、哪个项目、哪条线程,是后续存储与检索的基础。参考:Memory overview
会话保存与模型输入分开管理
一种常见的架构是:先保存事件,再从事件中构造本轮上下文。历史还在,不代表本轮会将每个事件重新发给模型;模型输入被压缩,也不代表原始记录被删除。
Anthropic 的 Managed Agents 将会话设计为独立于运行框架和沙箱的持久事件日志,运行框架按需读取事件,再决定怎样组织模型输入。这个设计把“历史能恢复”与“本轮怎样使用历史”分开了。参考:Managed Agents 设计说明;本站也有中文收藏版。
OpenAI Responses API 则提供手动回传历史、通过 previous_response_id 续接,以及使用 Conversations 对象保存会话状态等方式。这些解决的是状态如何衔接,应用仍然需要处理上下文长度和信息选择。参考:Conversation state
由此得到的工程原则是:原始存储保留可追溯依据,模型请求只携带当前所需内容。持久保存也有保留期限、访问权限和删除规则,并不意味着无限期保存所有数据。
长对话通常组合使用四种处理方式
裁剪、清理工具结果、压缩和按需读取可以配合使用,但它们保留信息的方式不同。
| 方式 | 怎样处理 | 适合什么情况 | 主要限制 |
|---|---|---|---|
| 近期窗口 | 只将最近一部分历史加入请求 | 短任务、早期内容已不再相关 | 可能裁掉仍有效的早期条件 |
| 工具结果清理 | 从后续输入移除冗长、过时的工具内容 | 大量搜索、日志与查询结果 | 需要保留关键结论和必要引用 |
| 会话压缩 | 用较小的状态表示替代较早历史 | 同一任务持续多轮、接近上下文上限 | 压缩有损,可能漏掉细节 |
| 按需读取 | 先保留路径、索引和线索,需要时再取内容 | 大文件、代码库、大型知识集合 | 依赖读取能力、权限与引用有效性 |
Anthropic 的 Context editing 提供工具结果清理等策略,编辑发生在模型接收输入之前,客户端仍可维护完整历史。这里清理的是后续请求携带的内容。参考:Context editing
Vercel AI SDK 的 pruneMessages 也提供针对推理内容、工具调用及结果等消息片段的裁剪能力。这是上下文构造工具,应用仍需决定原始记录怎样保存、裁剪规则怎样制定,以及用户数据怎样隔离。参考:pruneMessages
工程上可以先减少冗余内容,再对仍然过长的任务历史做压缩。重要约束则需要单独识别,避免在一轮普通的“取最近消息”处理中悄悄丢失。具体顺序取决于任务和接口要求,不能对所有消息统一截取字符数。
Claude Code:任务压缩与项目记忆分别处理
Claude Code 接近上下文上限时会自动压缩,也提供 /compact 让用户指定保留重点。压缩维持当前任务的连续性,但早期细节仍可能丢失,长期约定不能只寄托在聊天历史里。参考:Claude Code 的上下文管理
其跨会话信息主要通过两类文件提供:CLAUDE.md 保存明确的项目指令,Auto memory 保存从用户反馈中积累的信息。自动记忆使用 MEMORY.md 索引和主题文件;启动时加载索引前 200 行或 25KB,以先达到的限制为准,主题详情按需读取。作用域以项目组织,具体加载行为也受配置影响。参考:Claude Code memory
这个模式适合解释两个容易混淆的动作:压缩回答“当前任务怎样继续”,项目记忆回答“以后开始工作时需要带上什么”。一个短索引让系统先知道信息在哪里,详情不必全部常驻。将这种方式用于业务平台时,还需要按租户和用户确定可见范围,不能直接套用单机项目目录的隔离假设。
Codex:线程续接、压缩和跨会话提炼
Codex 的 App Server 提供线程恢复和主动压缩能力,公开配置也提供自动压缩阈值。这些能力用于控制当前线程的上下文增长,不等于把整个线程都变成长期记忆。参考:Codex App Server、配置参考
在跨会话记忆方面,当前配置明确包含线程级提炼、全局整合、候选历史筛选和未使用记忆淘汰,并将生成记忆与使用记忆分别控制。启用情况与具体版本、配置有关。它体现的是一个有筛选和维护过程的记忆系统。
项目约定还可以通过 AGENTS.md 按全局、仓库、目录层级加载。明确指令、从历史中提炼的经验,以及当前线程的压缩状态,各自承担不同职责。参考:AGENTS.md
OpenAI Responses API 的 compaction 另有一项值得区分的能力:返回的压缩窗口可以包含加密、不可直接阅读的压缩项,用于后续推理续接。调用方需要按协议保留返回内容,不能只提取其中看起来像摘要的文本。这个 API 能力也不能用来推断所有 Codex 客户端的内部实现完全一致。参考:Compaction
对应用开发者来说,可读摘要更容易检查和编辑,提供方的原生压缩项则有自己的续接协议。选用哪一种,需要考虑模型支持、可观察性和恢复方式,不能把两者都当成任意可改写的字符串。
一份好摘要保存任务状态
“讨论了能耗分析,生成了一张图”是对经过的概括,却不足以支撑继续工作。下一轮重新计算时,模型还需要知道采用哪份数据、哪些条件有效、哪里尚未确认。
用于续接任务的摘要,可以按下面的结构组织。这里是业务示例,不是某个产品的固定内部格式:
目标:生成办公楼上个月的能耗分析报告。
有效约束:排除节假日;金额使用含税口径;报告使用中文。
已确定:按照楼栋与工作日类型分组,柱状图用于比较。
数据引用:本线程工具结果文件 energy-2026-10.json。
当前状态:完成数据清洗与分组,尚未确认缺失记录的处理方式。
未完成:与用户确认缺失值规则,重算统计,再更新报告。
已被替代的决定:不再使用早先的自然月同比口径。
已确定和待确认的内容要分开。用户已经撤回的要求也要有明确状态,否则旧约束可能在后续摘要里重新出现。路径、对象编号和数据版本帮助系统重新取证,摘要本身不承担保存全部原始数据的职责。
Anthropic 的上下文工程文章将压缩、结构化笔记和子 Agent 作为长任务的不同手段,并提醒过度压缩可能丢失关键细节。本文将其归纳为两项实践:先保证目标、有效约束和未完成事项的覆盖,再减少冗余;需要精确引用的内容保留可回读的来源。参考:Effective context engineering
长期记忆经历写入、读取和修订
模型说出“我记住了”,并不能证明跨会话存储已经更新。应用需要实际完成写入,在以后请求中按范围读取,还要处理纠正、过期和删除。
Claude 的 Memory tool 是一个清晰的接口例子:模型请求对记忆文件进行操作,真正的操作由应用执行,/memories 可以映射到应用控制的目录或数据库。模型提出操作,应用负责存储与访问边界。参考:Memory tool
从业务平台的角度,一条可维护的记忆除了内容,还适合带有以下信息。这是本文归纳的记录建议,不代表前述产品都采用同样字段:
| 信息 | 用途 |
|---|---|
| 所属工作区、用户或项目 | 决定谁可以读取和修改 |
| 来源消息或用户确认 | 能解释这条记忆从哪里来 |
| 创建、确认与更新时间 | 判断当前是否仍然有效 |
| 状态与替代关系 | 处理纠正、过期、撤回与删除 |
| 适用范围 | 区分通用偏好和某个项目的特殊要求 |
例如,用户说“这次汇报用英文”,属于本次任务约束;用户说“以后给客户的月报都用英文”,才可能成为长期偏好。用户后来改口,旧事实就需要更新或失效。后台自动抽取可以辅助发现候选内容,但不能把模型的猜测直接当成已确认事实。
OpenAI 的记忆与压缩实践示例,也将当前工作状态、可复用经验和最终审核产物分开。这个例子强调记忆保存工作方法,而具体业务结论保留在审查产物中。它提供了一个边界清晰的参考,而非对所有场景规定唯一的记忆内容。参考:Building Reliable Agents with Memory and Compaction
大结果留在计算环境,模型读取必要部分
一份包含几千行数据的查询结果,如果完整放进上下文,后续每一轮都可能继续承担它的输入成本。如果简单截断,模型又可能将预览当成全量数据,产生错误统计。
更合适的链路是保存完整结果,再交给可访问这些数据的计算工具处理。模型接收文件引用、字段结构、行数、预览,以及明确的“预览不代表全量”说明。分组、筛选、聚合在代码执行环境中完成,结果和必要证据再进入对话。
Anthropic 关于 MCP 与代码执行的文章,展示了在执行环境中处理工具数据、只向模型返回所需结果的方式。它减少了中间数据反复通过模型上下文的负担,但也依赖可靠的执行环境与数据访问控制。参考:Code execution with MCP
这类引用应有来源和有效期。临时文件已经回收时,系统需要能够重新查询,或明确告诉用户该结果无法复用。对需要直接观察的图片、音频等内容,也不能仅保留路径就期待模型知道其中细节。
当前平台怎样组合这些机制
平台采用的是近期窗口、后台摘要、有界文本记忆和大结果落盘的组合,当前聊天链路没有引入全历史向量召回。Vercel AI SDK 负责模型调用和工具循环,保存什么、取出什么、怎样控制大小,由平台规则决定。
下面的数量和时长是当前实现参数,便于说明具体取舍,并不是业界统一标准。前述产品的原生压缩和记忆机制用于对照理解,也不意味着平台已经接入了它们的全部能力。
原始记录完整保存,模型每轮只接收其中一部分
原始聊天记录按工作区和线程追加保存为 JSONL 文件,消息包含角色、时间、正文,以及必要的工具调用和结果。页面展示、历史查询和模型输入都以这些记录为基础,但使用时各自进行转换。
工作区
├─ .logs/
│ ├─ conv-<threadId>.jsonl 原始聊天记录
│ └─ conv-<threadId>.summary.json 该会话的历史摘要
└─ .memory/
└─ conv-<userId>.json 该用户的长期记忆
每轮提问时,后端重新加载会话记录,先处理过大的工具结果和旧图片,再从新到旧选取历史消息。窗口之外的记录仍保留在磁盘,模型输入不会随着聊天文件无限增长。
当前历史预算默认是约 32,000 个估算 token。这个值只控制历史消息,系统提示、工具定义、用户记忆和当前问题另外占用空间,所以不能把它理解为整次请求的总长度。平台采用字符估算来控制历史量,也保留了预算可能存在偏差这一限制。
从完整请求看,还需要为输出和模型相关的推理空间留出余量。只检查历史是否小于 32,000,并不能单独证明整次调用不会超限。这里保留历史预算与整次请求预算的区别。
一轮请求的上下文大致由以下部分组成:
系统部分
平台基础规则
工作区提示词
当前会话的历史摘要
可用技能目录
当前用户的长期记忆
消息部分
压缩和裁剪后的近期历史
当前问题与图片
工具部分
当前用户获准使用的工具定义
技能先提供名称和简短说明,模型使用时再读取完整指令。这种按需加载减少了无关技能长期占据上下文的情况。
工具调用占用的上下文需要单独处理
数据类助手的一次查询可能返回很大的 JSON。即使只聊天几轮,这些结果也可能超过历史预算。仅按轮数保留消息,无法处理这种大小差异。
平台在历史进入窗口前,先进行轻量压缩:最近两轮的工具结果正文最多保留 8,000 字符,更旧的结果截取到 400 字符;历史图片只保留最近 4 张的实际图片内容,其他图片改为文字标记。磁盘记录保持原样。
消息结构也一起处理。一次模型步骤可能并行调用多个工具,结果却按不同顺序返回。回放时按调用标识重建对应关系,缺少结果的调用明确标记为未完成,避免一条不完整记录使后续对话持续报错。
这里的经验是,聊天历史不只是文本列表。工具调用、结果和必要的模型返回字段有结构关系,裁剪时需要一起考虑。删除了结果却保留对应调用,或者把另一轮的结果接到旧调用后面,都会改变模型收到的含义。
大查询结果通过文件进入计算环境
截断能够控制长度,却不能让模型完成整份数据的统计。假设查询返回了几千条设备记录,模型只看到开头一部分,就无法据此可靠地计算总量或分组结果。
对超过阈值、适合落盘的 MCP 文本结果,平台将完整数据保存为按用户和线程隔离的文件,模型收到文件位置、数据结构和简短预览。随后通过代码执行工具读取全部数据,在沙箱中筛选、聚合和画图。
业务工具返回大量数据
↓
完整结果写入当前用户、当前线程的文件
↓
模型收到结构、预览和文件位置
↓
Python 读取完整文件并计算
↓
将统计结果和产物交回对话
没有代码执行权限时,平台返回截断内容并提示缩小查询范围,不生成一份模型无法读取的落盘文件。图像、音频等需要直接感知的结果,以及有明确返回契约的第一方工具,也没有被统一替换成文件引用。
这些文件属于计算缓存,有数量和容量管理,旧文件会被回收。模型拿到的是可使用的引用,不是永久保留承诺;文件已经过期时,通过重新查询恢复数据。
历史摘要保留已经作出的决定
近期窗口处理了输入大小,但早期讨论仍然可能影响后续工作。比如对话已经转到图表样式,用户最初要求的“排除节假日”仍然有效。
平台在旧消息被裁出窗口后,后台生成结构化摘要,重点保存目标、已作决定、文件和对象标识、未完成事项及当前状态。摘要带有覆盖位置,记录它对应到原始历史的哪一部分。
这项能力默认开启。已有摘要后,通常累计新增至少 4 条被裁掉的消息记录才刷新,输出上限为 1,500 token。摘要在本轮回复结束后异步生成,减少对正常回答的等待影响;当前请求使用已有摘要,之后的请求再读更新结果。因此首次溢出时可能出现摘要尚未生成的空档,后台失败也会继续保留旧版本。
这也是当前异步方案的明确边界:它降低了本轮等待,却没有提供“新摘要一定先于历史裁剪完成”的保证。对于必须连续保留的约束,裁剪前同步确认覆盖,或者独立维护任务状态,是可对照的处理方式;这里不将这些进一步的保证写成平台已经实现的行为。
异步写入还处理了两个顺序问题。用户清空聊天后,旧摘要任务不能再把内容写回来;较早启动、较晚完成的摘要,也不能覆盖已经包含更多消息的新版本。平台通过失效代次和覆盖位置检查保护这两种情况。
摘要只保留必要线索,详细数据仍在原始记录或文件里。文件名、对象编号和已确定的条件,比一段笼统的“前面讨论了能耗”更有助于后续继续工作。
长期记忆保存跨会话仍然适用的事实
长期记忆按“工作区+用户”保存,同一用户在该工作区新建线程后继续使用,不会自动跨应用共享。其他用户也不会因为使用同一个聊天应用就收到这份记忆。
平台选择有界的文本事实列表。每轮把有效事实直接放入系统提示,不再通过相似度搜索决定取哪几条。对于数量较少的偏好和背景,这样能直接看到当前保存内容,也方便用户查看和删除。
当前上限是 50 条有效事实,单条最多 500 字符,有效内容合计最多 8,000 字符。重复检查处理文本一致的事实;容量不足时,按重要性和保存时间计算分数,让低分事实失效。语义相近但措辞不同的记录,以及互相矛盾的事实,仍由模型在维护过程中判断,程序去重没有被当作完整的事实校验。
记忆的写入有两条路径:
- 对话内调用
remember和forget,保存新事实或使旧事实失效。 - 开启后台自动抽取后,在每轮结束后追加一次模型调用,从本轮交流中提炼耐久事实。
长期记忆默认关闭,后台自动抽取也独立默认关闭。自动抽取开启时,每轮最多新增 5 条事实,同一工作区内同一用户的抽取串行执行,减少前后两轮完成顺序颠倒造成的覆盖。
写入指令要求跳过秘密、临时任务细节和用户明确要求保密的内容;召回块也标明这些是用户派生信息,可能过时或不准确。模型提炼出的记忆没有被赋予系统规则或授权记录的地位。
文本列表与向量检索的选择
这里没有把“做记忆”直接等同于“建立向量库”。两种方式对应的信息规模和查询目的不同。
当前的用户记忆是有限的稳定事实,全量注入的成本可控,也不用承担检索漏掉重要偏好的风险。按用户和工作区隔离的 JSON 文件,能够沿平台现有文件存储和备份方式管理。
如果问题是“找出过去半年讨论过的某个方案”,所需能力就接近历史检索:先按权限和时间缩小范围,再用关键词或语义方式召回原文。它的目标是找到证据,不能简单用几十条用户事实代替。
平台目前的实现集中在对话连续性和有限的跨会话事实,全历史检索没有被混入这条默认链路。这个选择也明确了能力范围:记住用户常用什么格式,与随时翻出所有旧对话,是两件分别管理的事。
代码执行状态和提示缓存另有生命周期
用户先让助手读取 CSV,接着要求“按刚才的数据画图”,Python 变量和临时文件也在帮助维持连续性。平台为同一工作区、用户和线程复用暖沙箱,默认空闲 15 分钟或累计 4 小时后回收。
这部分状态随沙箱存在,不是长期记忆。沙箱因超时、故障或回收而重建后,模型需要根据仍可用的文件重新加载数据,不能默认先前的变量还在。
提示缓存又是另一回事。它让支持缓存的上游复用符合条件的输入前缀,主要影响计算开销,并不决定应用保存了哪些事实。OpenAI 官方说明中,前缀变化会影响后续部分的缓存复用,具体还受模型、缓存边界和请求设置影响。参考:Prompt caching
平台根据前缀是否稳定决定缓存策略;后台记忆频繁变化时,当前默认关闭这条显式缓存路径。这是本实现的取舍,并不意味着记忆更新必然导致所有上游缓存失效。通用的布局思路是尽量保持可复用部分稳定,把经常变化的内容与其区分,再根据实际用量检查收益。缓存不能替代上下文压缩,也不承担长期记忆存储职责。
清空聊天和删除记忆分别处理
清空当前聊天会删除该线程的原始记录、历史摘要和落盘工具结果,用户的长期记忆保留。用户从记忆面板删除单条或全部事实,则是独立的硬删除;模型调用 forget 使用软失效,暂时保留有限旧记录。
普通清空聊天目前没有同时销毁暖 Python 沙箱。已经进入沙箱的变量和暂存文件仍按沙箱生命周期回收。长期记忆、聊天记录和执行状态因此有不同的清理入口,“清空”不能被理解成所有相关数据都在同一时刻消失。
异步任务的完成顺序同样是这套管理中的边界。历史摘要已经使用失效代次阻止清空后的迟到写入;长期记忆抽取采用按用户串行和写前检查用户存在的方式,其删除一致性不能直接套用摘要的保证。文件存储、后台任务和用户操作各自成功,并不自动组成一次完整的清理事务。
从这些实践中归纳出的处理原则
将公开方案与已落地链路放在一起,可以得到一组职责较明确的处理原则。
| 处理环节 | 值得保留的做法 | 避免的问题 |
|---|---|---|
| 原始记录 | 与本轮模型输入分开保存,保留可回读来源 | 把上下文裁剪误当成数据删除 |
| 当前任务 | 显式保留目标、有效约束、决定与待办 | 只取最近消息,遗失最初条件 |
| 会话压缩 | 区分已确认、待确认和已撤回内容 | 摘要把推测写成事实,或恢复旧决定 |
| 工具结果 | 大结果落盘计算,小结果按需要回传 | 让模型根据局部预览计算全量统计 |
| 长期记忆 | 按范围提炼,支持纠正、过期与删除 | 无差别保存临时要求,跨用户混用 |
| 内容加载 | 少量稳定事实直接提供,详情按需读取 | 将整个知识库长期塞进每轮输入 |
| 异步处理 | 检查覆盖范围、版本和删除后的迟到写入 | 旧任务覆盖新状态,删除后内容再次出现 |
| 执行与缓存 | 分别记录文件、沙箱和缓存的生命周期 | 把暖环境或缓存误当成持久记忆 |
这些原则还需要通过长对话中的实际行为验证。例如,讨论了多轮图表样式后,重新计算是否仍然排除了节假日;用户纠正偏好后,新线程是否仍然读取旧值;工具结果文件过期后,助手是否明确重新查询,而不是凭摘要补造数据;清空期间已经启动的后台任务,是否会把旧内容写回来。
检查结果不只看回答是否流畅,也看约束是否保留、来源是否可追溯、召回范围是否正确,以及恢复和删除是否符合产品承诺。这些观察比单独比较“摘要压缩了多少 token”更接近业务中的连续性。
分层管理已经进入实际聊天流程
当前平台已经把原始记录、近期窗口、任务摘要、长期记忆和计算文件接入同一条对话链路。用户连续完成查数、分析、画图和报告调整时,近期上下文承接当前讨论,摘要延续早先决定,完整数据保留在文件中计算。新建线程后,适用范围内的用户偏好可以继续加载,前一线程的数据和运行状态仍按各自范围管理。
与 Claude Code 和 Codex 的公开实践相对照,这套实现已经采用了记录与输入分离、任务状态压缩、有限记忆注入和大结果外置等方式。采用后台摘要减少等待,也留下了首次摘要空档;使用有界事实列表便于检查和删除,也保留了语义冲突主要依赖模型处理的限制。平台目前的能力和边界,都能对应到具体的存储、请求组装与后台任务流程。
聊天的连续性由这些环节共同承担。原始记录保存依据,任务摘要维持进度,长期记忆复用稳定信息,文件与计算环境承载数据和产物。它们分别管理后,助手为什么记得一种偏好、为什么需要重查旧数据,以及清空后哪些状态还会保留,都有了可以解释和核对的来源。