第 4 课:Session 持久化——Transcript、Checkpoint 与 Memory
第 3 课已经把排查过程写进了 session.jsonl。现在增加一个需求:关闭程序,明天还能接着查同一个问题。
文件还在,只解决了记录没有丢。程序还要知道打开哪份记录、已经进行到哪一步,以及本次应该把什么交给模型。上一课的临时目录用于观察结果,长期续跑还需要稳定的存储位置。存了聊天记录,不等于接上了昨天的话。
1. Session:会话身份与存储边界
把同一段连续交互归在一起,这个单位叫 Session(会话)。应用可以给它一个编号,例如 session_id="order-debug-01"。第三课从用户提问到最终回答是一个 Turn;围绕这次故障继续追问,可以形成同一个 Session 中的多个 Turn。
一种本地存储布局可以是:
sessions/
|-- order-debug-01/
| |-- session.jsonl
| +-- checkpoint.json
+-- order-debug-02/
|-- session.jsonl
+-- checkpoint.json选择 order-debug-01,程序就去找这一组记录;开启新会话,则分配另一个编号。编号负责关联数据,不会让模型自动记住内容,也不代表访问授权。
这是布局示意,不是上一课 demo 已有的续跑入口。Session 是逻辑归属,可以落在目录、数据库记录里,也可以对接模型服务托管的对话状态。[1] 文件扩展名不是它的定义。
2. Transcript 与 Checkpoint:历史记录和恢复状态
Transcript 记录这段会话按顺序发生过什么,包括用户消息、模型调用申请和工具结果。Checkpoint 保存某个位置的状态,帮助程序从那里继续。
先用三笔流水看区别:存入 10 元,支出 3 元,再存入 5 元。完整流水能算出余额 12 元;如果已经保存了“处理完前两笔,余额 7 元”,恢复时只需再处理第三笔。
两次输出都是 12。processed=2 表示前两笔已经计入,切片从第三笔开始。快照中的状态必须和它标记的位置对应,否则会重复计算或漏掉事件。
这里是把已记录的第三笔算进余额,不是再存一次钱。同样,回放 Transcript 是重建应用状态,不代表把历史工具调用重新执行一遍。

快照已计入前两笔,恢复从余额 7 接着处理第三笔加 5。完整流水仍保留;这里重建的是状态,不是重做历史动作。
放回排查助手:
Transcript
当时问了什么,读到了什么?
完整的消息、调用编号、工具结果与相关事件
Checkpoint
程序最后保存到了哪里?
截至某个位置的消息或摘要、运行进度、恢复所需字段
一份快照可以包含部分历史,也可以包含已经整理好的状态,但它不一定能还原所有原始事件。保存哪些字段,要由程序准备怎样恢复来决定;Checkpoint 不要求复制整个 Python 进程。
3. JSON 与 JSONL:数据格式和文件组织
JSON 表示一份完整的结构化数据,适合保存一份当前快照;JSONL 则是一行一个独立 JSON 值,本书用每行一个对象记录事件,便于顺序追加。
下面用两条手工构造的短消息演示快照存取。它们只用来展示文件内容,不是模型运行实录。
输出为:
这份快照包含会话编号、截至第几条记录、两条消息和当前摘要。processed_entries 对应前面流水例子的 processed,这里计数的是会话记录。先写完临时文件,再在同一文件系统内替换目标,避免在覆盖过程中读到半份 JSON。示例只演示单写者的文件存取;断电持久性、并发写入还需要额外保证。临时目录会被清理,真实应用应使用长期保留的位置。
相同的两条消息,放进 Transcript 时可以逐条追加到 JSONL:
读取时逐行解析,每行都是完整 JSON;不能把整份多行文件当作一个 JSON 对象一次解析。完整 Transcript 也能存成 JSON 数组,这里选 JSONL 是为了方便逐条追加。本例只有两条消息事件;真实 Transcript 还可能记录其他事件,恢复位置必须按所用格式计算。第三课的 Tool Call 与 Tool Result 字段也必须完整保留。
Checkpoint 不一定要单独放文件。它也可以作为一种事件,写进同一个 JSONL:
程序找到这份快照后,再处理对应的后续事件。第 3 行是一条状态记录,不是多发生了一次用户交互。因此,Transcript 与 Checkpoint 描述用途,JSON 与 JSONL 描述数据怎样组织;同一个文件可以承担两种用途。
4. Memory 与 Context:跨会话信息和当次输入
继续 order-debug-01,需要知道上次停在“等待日志位置”。开启一个全新的会话,却不应该无缘无故继承这项进度。
有些信息适用得更久,例如用户确认的“默认使用中文”,或项目约定“诊断要注明文件位置”。这些经过选择、允许跨会话使用的信息,可以保存为 Memory(长期记忆)。
当前排查等待哪份材料
当前 Session
本项目的诊断报告约定
同一项目的多个 Session
用户默认使用哪种语言
适用该偏好的不同项目与 Session
沿用上一节读回的 restored,下面只比较本次选中的材料,不调用模型:
输出为:
同一份长期信息可以被再次选用,旧进度却只跟随对应会话。代码中的字符串列表只是材料清单;实际请求还要按模型协议组织指令、历史消息和工具说明,不能拿它替代完整的消息列表。
写到文件里,叫持久化;经过应用选择、实际放进这次模型请求的材料,才构成这次的 Context(上下文)。磁盘上保存了多少,不等于模型本次看到了多少。
Memory 不应照抄所有历史。临时日志、未确认的猜测和凭据,不应自动成为长期事实;已保存内容也要能更新或删除。用户本次要求英文,可以覆盖默认中文偏好;强制安全策略不能被新偏好覆盖,更不能靠记忆里一句“已经批准”获得权限。
5. 恢复边界与存储演进
快照只证明程序保存过某个状态,不证明外部世界仍停在那里。以后允许助手修改文件时,可能出现:
此时按旧快照重新执行,可能重复操作或覆盖用户后来的修改。第 6 课会用工具执行账本 Ledger 核对执行结果。只读工具也要分清:重新读取的是当前文件,不一定是当时的那份内容。
Checkpoint 可以在完整 Turn 结束后保存,也可以支持中途恢复,前提是保存了对应的现场。这里的文件读回还不是一次跨进程 Agent 续跑验证,不能把它当成完整故障恢复系统。
存储规模增长以后,再根据读写方式决定是否需要数据库。低频顺序追加与回放可以使用 JSONL;频繁查询、需要事务和唯一约束时,可以考虑 SQLite 等数据库。[2] 不必为了“到了十万行”就迁移,也不必等到十万行才处理并发问题。相关实现留在第 6 课和存储专项练习。
会话现在有了归属,历史与恢复点也能保存。下一课处理另一个问题:记录越积越多,哪些内容应该进入有限的模型窗口?
本课实践
进入第 4 课实践:保存并读回会话,增加一条消息,核对历史与快照位置。说明中列出必做步骤、完成标准和选做内容。
资料
Last updated
Was this helpful?