聊天记录、上下文、记忆、检查点,Agent 怎样接上过去?
学习路线
建议下一站: 深入
昨天你让助手比较两版产品指南,已经读完 A。今天打开程序,说一句“继续”。助手应该重新读 A,接着读 B,还是直接打开昨天的报告?
只看这两个字,很难知道。需要先确定用户想接上什么,以及系统实际保存了什么。聊天记录、上下文、记忆和检查点,都与过去有关,却分别回答不同问题。
下面仍用 Atlas Notes 合成文档比较来解释。用户要求比较两版导出能力,并明确保存报告。我们把一次任务分成“读取 A、读取 B、比较、保存”几个便于理解的动作。实际 Agent 可以根据观察选择不同路径。
四种“留下来”,对应四种需要
| 用户的需要 | 主要依靠什么 | 能从中找到什么 |
|---|---|---|
| 回看昨天说过的话 | 聊天记录 | 原问题、补充要求与历史回答 |
| 让这一次模型接着判断 | 上下文 | 本轮挑选的要求、资料和最新观察 |
| 下次研究继续参考表达偏好 | 长期记忆 | 被保存且仍适用的跨任务信息 |
| 进程退出后接着执行 | 检查点及执行记录 | 原任务状态、已完成动作与接续位置 |
一份实现可以把它们存放在同一个数据库中,甚至共享部分数据。理解它们时,更重要的是看使用目的、更新时机和能支持的动作。
聊天记录怎样变成本轮上下文
用户在页面上看见昨天的完整对话,会自然地认为模型也看见了。但页面读取历史和下一次模型请求,是两条需要接通的路径。
应用可能只把最近几轮放进输入,也可能选择摘要,或者让模型按需回读旧内容。这样可以控制容量和成本,但也产生一个排查问题。“昨天已经说过”的要求,究竟保存在历史里,还是已经进入这次实际请求?
假设昨天用户补充了“只比较免费版”。若今天输入只有助手写的简短结论,模型可能无法恢复这项限制。保留用户原文的读取入口,再把相关修正装进本轮输入,能够减少这种失真。
聊天记录还保存了当时的判断。旧回答中的“当前版本支持 CSV”带着当时条件。新问题涉及 B 版时,模型需要核对原文与版本,才能决定旧判断是否适用。
回看、接着聊和重新研究,各需要怎样的过去
可以把昨天的工作拆成几份具体材料。用户原话里有“只比较免费版”,A 版原文里有“导出需要联网”,助手中途写下“已经取得 A”,最后还有一份比较报告。它们都属于过去,但下一次的用法各不相同。
用户想检查上次说过什么时,完整聊天记录最直接。用户问“为什么你上次说免费版能导出 PDF”,就需要把当时的问题、回答和相关依据一起找回来。只提供一份概括过的偏好,无法还原那次判断的条件。
用户要接着聊“那企业版呢”,当前请求可以携带上一轮问题、必要回答和相关原文,让模型理解新增的比较范围。这里关键在于实际请求有足够背景。把所有历史机械地追加到输入中,可能把旧问题的限制和新问题混在一起。
用户要重新研究“今天的最新版有没有变化”,则需要确认新的时间与版本范围。上次的报告可以帮助定位待查维度,最新事实仍需从适用原文取得。旧报告中的结论带着原任务条件,使用时要保留这个时点。
这些行为可以共享同一个聊天界面,后台读取的对象却可能不同。设计“历史”功能时,值得先想清楚用户要回看哪段交流、携带哪项背景,以及是否要重新获取来源。界面上显示了整段历史,只完成了可回看这一部分。
长期记忆为什么还要单独存在
有些信息需要跨越许多任务,例如用户明确保存的“先给比较表”。如果每次都从整段聊天中寻找,既费资源,也容易把一次临时要求误当成长期偏好。
长期记忆允许系统挑出适合积累的信息,保存范围与来源,并支持更正和遗忘。下次使用时,先取得相关且有效的记录,再决定是否加入当前输入。
于是,模型的一次请求里可以同时出现聊天历史与记忆。前者帮助理解刚才谈到哪里,后者提供经过选择的跨任务背景。当前用户提出新要求时,也需要在输入中明确呈现,让模型处理适用关系。
产品指南是另一个对象。它描述产品事实,应保留原始版本与可回读位置。可以把记忆和文档都交给检索,但仍要区分个人偏好、原文事实与模型派生的结论。
一条记忆从保存到使用,中间还有几步
以用户明确保存的“先给比较表”为例,可以把生命周期拆成保存、选择、使用、更正和遗忘。每一步都值得有清楚的含义。
保存时,需要知道这句话是谁提出的,适用于什么任务,以及用户是否希望长期保留。“这次先看表格”可能只服务当前阅读。系统若要把它变成未来默认偏好,应有对应的用户行为或已明确的产品约定。
使用时,系统先在合适范围内寻找相关记录,再把适用内容加入当前输入。搜索命中和模型实际收到之间,仍有一道选择过程。一条偏好存在数据库里,只能说明它被保存过。要确认本轮受它影响,应查看当时送出的输入与相应回答。
更正时,用户可能改为“以后先写三句话结论,再给表格”。若系统把新旧两条都召回,模型需要额外理解哪条生效。让旧记录和新记录保留更新关系,并在当前读取中取得有效版本,可以减少这种歧义。
遗忘还要明确影响范围。当前记忆库可以停止召回某条偏好,历史聊天中曾经出现过的文字却可能仍被保存。已经生成的旧报告也可能继续存在。用户界面中的“删除记忆”应说明对应对象,不能用一个动作名称混合这些不同结果。
公开示例会在专门选择的记忆库里依次保存、更正、遗忘一项合成偏好,分别返回回执,并检查当前读取。它保留历史修订记录,这使读者能观察“当前可召回内容变化”与“旧版本是否仍留存”的差别。固定记忆工具实现
长期积累也带来维护成本。偏好会变化,来源会失效,适用范围需要更新。如果用户只在一个短任务里临时指定格式,保留在当前任务上下文就足够。真正跨任务反复使用的要求,才更值得单独管理。
继续执行为什么需要额外的事实
现在把问题换成进程退出。A 已经读取完成,程序准备读取 B。若想从这里接上工作,需要知道的内容比聊天文字更多。
原任务及其身份
当前执行位置
已取得的 A 版结果与引用
已消费和仍可用的资源额度
待处理动作及实际执行结果
已保存的报告与回执检查点把受支持位置上的必要工作状态保存下来。执行记录、工具回执与产物存储则保留动作的实际结果。不同系统对这些对象的划分可能不同,检查点也可以引用外部记录。
恢复时,系统要从这些记录重建下一步所需状态。能够在哪个位置恢复、怎样处理进行中的工具调用,取决于实现提供的契约。仅保存消息列表,通常无法回答一项外部写入到底有没有发生。
检查点为什么要和执行记录一起看
可以用“读 A、读 B、保存报告”推演几次退出时机。下面讨论的是通用设计关系,具体可恢复位置以系统实际实现为准。
如果在读取 B 之前退出,记录已经保留 A 的结果与下一步位置,恢复时可以继续准备 B 的读取。若 A 的内容没有保存,只记下“读过 A”,则还需要重新取得它。是否允许读取新版本,也取决于原任务要比较固定快照还是最新状态。
如果 B 的请求已经发出,程序却在收到返回前退出,就出现另一种情况。请求可能尚未处理,也可能已经完成,只是结果没被当前进程记录。系统至少需要区分“准备调用”和“已发出但结果未知”。把两者都写成“尚未完成”,容易在恢复时重复发出原动作。
读取一般没有写入效果,但重读也可能得到变化后的内容。到了保存报告、发送消息等动作,重复执行还可能产生额外效果。恢复设计需要根据动作性质,保留操作身份、实际回执或可核对的产物,才能选择安全的后续处理。
如果报告已经保存,随后才退出,下一次可以核对保存回执并读取原产物。此时模型记忆里写“报告还没做好”,也不能改变真实文件已经存在的事实。派生摘要可以帮助组织工作,外部效果由实际执行记录说明。
一个检查点可以只保存关键状态和引用,也可以包含必要结果。全量保存占用更多空间,引用外部对象则依赖相应存储仍可读取。要保留多少,取决于支持从哪里接续,以及恢复时有哪些事实需要重新确认。
因此,“有检查点”还需要接上一句具体承诺。系统能够从哪个已记录位置恢复,怎样处理未知调用,是否保留原任务额度,以及保存后的成果怎样被重新取得。承诺足够具体,读者才知道一次重启能接上什么。
同一句“继续”,先看中断发生在哪里
情况一,A 的读取已经完成并保存。 可以核对原结果,在受支持位置继续取得 B。重新读取是否合适,还取决于任务需要原快照还是最新资料。
情况二,保存报告已经发出,但确认信息丢失。 文件可能已经写入。此时先核对原操作记录、产物与回执,再决定后续动作。直接重跑可能重复产生效果。
情况三,报告早已完成,用户只是回来阅读。 打开已保存的版本就足够。需要另外研究新问题时,再建立相应任务。
这三种情况分别涉及接续执行、核对未知效果和读取既有成果。一个统一的“继续”按钮也应在内部保留这些差异。
检查点本身不能提供新的权限与额度。沿原任务恢复时,需要使用原身份及其记录。新任务则按照新任务的入口准入。这里增加的持久化与一致性成本,适合有跨进程接续需求的工作。短小、可安全从头执行的计算,可以采用更简单的方式。
用一条时间线看“继续”的不同含义
把同一任务放到一条教学时间线上,可以更直观地看见状态差异。
| 中断位置 | 已知事实 | 下一步应先取得的依据 |
|---|---|---|
| A 已读取,B 尚未调用 | A 有已保存结果 | 原任务状态、A 的快照与 B 的读取位置 |
| B 的读取已发出,结果未记录 | 调用已经开始 | 原调用结果,或实现约定的未知结果处理方式 |
| 报告保存已确认 | 存在产物和回执 | 原报告及其读取入口 |
| 用户回来提出新问题 | 这是新一轮需求 | 新目标与需要携带的历史背景 |
这张表也能帮助产品设计。一个按钮可以叫“继续”,但用户应当知道它是恢复原任务、打开原报告,还是带着历史开始新研究。对长任务而言,显示“原报告已保存”往往比一条含糊的“已恢复记忆”更能说明实际进度。
额度也需要沿时间保留。原任务已经消费的请求与工具资源,不能通过重启自动回到初始值。绝对截止时间若已经过去,换一个进程也不会让原来的截止时间变晚。继续执行需要继承相应记录,额外投入则属于另一个明确决定。
权限同样来自原任务的授权记录。检查点可以保存或引用这项身份,恢复过程沿已有契约使用它。模型在聊天中写“我应该有权限”,无法替代实际授予。把执行身份和自然语言工作计划分开保存,有助于同时理解“想做什么”与“可以执行什么”。
压缩以后,哪些东西还应找得到
长任务可能需要把早期材料整理成便笺,再从当前输入中移出大块原文。此时摘要帮助下一步判断,原始资料承担回查依据,检查点承担接续状态。
如果便笺记着“B 的导出条件待核对”,下一步可以回读原页。如果记录表明报告已经保存,模型的摘要也不能把这项效果改成未发生。
压缩改变工作表示,持久记录保留对应事实。 区分这两类用途,才能同时获得紧凑输入与可回查的研究过程。我们的上下文压缩专文
摘要、原文和检查点,怎样在长任务里配合
继续设想一项已经读取许多页面的研究。为了给后续推理留出空间,系统把早期细节整理为“已取得 A,B 的导出条件待核对”。这段便笺能帮助确定下一步,但如果最终要写精确比较,仍需要取得对应原句。
一份有用的便笺可以同时保留已做事项、待查问题和原始位置。它不必抄写全部原文,却应让后续工作知道去哪里找。数值、套餐和联网条件对结论有影响时,应沿原页重新确认,尤其要防止把“暂未找到”压成“没有”。
检查点处理的则是执行接续。便笺说“下一步保存报告”,执行记录显示报告已经保存,恢复时应先核对已有产物。否则,一段过时的工作计划可能触发重复动作。这也是为什么摘要与工具回执应保留各自用途。
在公开压缩示例里,派生便笺和原始页面具有不同身份。程序先保存并读取便笺,再用原结果引用回读页面,最后保存基于原文的报告。读者可以观察两条读取路径,从而理解“保留一个简短表示”和“保留原文回查能力”怎样同时成立。便笺与回读示例
这套安排增加了存储、引用和读取步骤。短对话完整带入即可,通常没有必要先建复杂的压缩系统。需要长任务、多次回读或跨进程执行时,再围绕真实读取与接续需求逐步增加相应机制。
在 Open ETF Agent 中分别观察
本文以已发布的 v0.26.0 为固定版本。按输入与知识篇的安装步骤准备项目后,可以先运行两个独立的离线示例。
npm run demo -- --case compact-and-reread
npm run demo -- --case explicit-memory --memory-library runs/concept-memory-library第一个展示派生便笺与原文回读,第二个在一份专用合成记忆库中依次演示保存、更正与遗忘。记忆示例应选择新的空目录,查看 CLI 返回的保存、更正与遗忘回执。示例使用预设动作模拟模型选择。
进一步查看公开的跨进程接续测试,可以观察实际中断与接续位置的断言。它通过原运行系统验证受支持的恢复行为,仍属于离线机制检查。跨进程接续测试
node --test --test-concurrency=1 test/durable-runtime.test.mjs观察这些例子时,可以分别记下每种产物在回答什么。report.md 保存可供阅读的成果,过程记录反映工具动作与返回,记忆库当前记录体现保存、更正和遗忘后的内容,派生便笺帮助后续工作定位原文。跨进程测试则专门覆盖同一任务在支持位置上的中断与恢复。
在接续测试中,还能看到一种很有解释力的结果。动作已发出但缺少结果时,系统保留未知状态,并检查新进程没有再次执行原动作。这里的“未知”会影响后续安排,不能在界面里直接改写成“没有发生”。已保存报告的案例则核对原产物,避免把恢复理解为重新生成一份看起来相似的报告。
把这些观察带回自己的产品,可以从用户承诺倒推保存对象。要回看交流,检查历史读取。要沿用偏好,检查有效记忆怎样进入下一轮输入。要跨进程接续,检查执行身份、已完成动作、未知结果和额度怎样恢复。每项承诺都有相应可观察行为,讨论“Agent 记住了多少”时也就有了具体对象。
下一次设计“记住我”或“继续”时,先把承诺说具体。用户需要回看交流、携带偏好、重新取得材料,还是接着原执行位置工作?选定需要,才知道该保存哪些信息,以及怎样检验它真的生效。
接下来进入《Agent 深入:从一份报告到持续执行与专家协作》,沿完整任务看这些概念怎样落到系统设计。当前开源项目入口