恢复历史可以改变模型下一轮看到的上下文,却不能把已经发出的业务动作变回未发生。
长任务助手常见的故障不是"历史完全丢了",而是会话恢复成功后,产品把历史当成了业务事实。resume、fork、restore 只改变会话上下文;审批、工单、通知和外部写操作的状态仍由业务系统确认。开发时应先设计这两条状态链的映射,再设计恢复按钮。
先设计四个标识的映射
| 标识 | 作用 | 不能替代什么 |
|---|---|---|
userId |
归属应用中的最终用户 | 不能直接当作会话或业务权限 |
sessionId |
一条对话和运行历史 | 不能证明外部动作已经完成 |
checkpointId |
会话历史中的共同节点 | 不能回滚外部数据库或第三方系统 |
businessTaskId |
业务系统中的任务事实 | 不能独自恢复模型上下文 |
应用至少要能从方案列表找到 sessionId、来源检查点和 businessTaskId。方案标题可以由模型生成,但归属与关联关系必须由应用持久化;不要根据标题猜会话属于谁。
三个 SDK 动作对应三种历史语义
resume从SessionStore读取会话历史。要让恢复后的新消息继续落盘,创建会话时还要同时传入store;只配置读取不等于继续保存。restore在同一会话中用检查点历史替换当前历史。它要求空闲,运行中会返回turn_running,不会替调用方排队。fork从检查点创建新会话,源会话保持不变。它需要会话存储;新会话打开后,应用要记录来源会话和检查点。
官方检查点文档对三者的范围有明确区分:检查点与恢复。这些动作改变的是模型下一轮看到的历史,不是业务系统的事务控制。
恢复流程应先读回,再对账
再次打开一个方案时,可以按下面顺序组织应用逻辑:
- 读取应用自己的方案记录,拿到
sessionId、checkpointId和businessTaskId。 - 用
resume读回会话,或由用户明确选择restore/fork。 - 单独查询
businessTaskId的权威状态,处理已完成、处理中、已拒绝和未知四类结果。 - 根据业务状态决定是否显示执行按钮;会话末尾停留的工具调用只作为上下文,不能作为完成凭证。
- 业务动作需要重试时,沿用业务系统的唯一键和确认流程;不要因为新会话产生了新的模型调用就创建新的业务意图。
createSessionView 可以把会话事件归约为消息、工具、错误和连接状态,但业务任务的状态投影仍应来自业务数据源。会话与事件
自建 SessionStore 时,必须处理 rewritten
检查点恢复和历史压缩可能改写历史前缀。自建 SessionStore 收到 commit 的 rewritten: true 时,应按完整新历史替换保存内容,不能只按消息数量追加;否则用户回到共同节点后,旧方案可能在重启时重新出现。
恢复目标不存在与存储损坏也要分开处理。session_not_found 应让用户选择重新开始或修复关联;存储损坏应保留问题上下文并进入修复流程。把两种错误都吞成空白会话,会让用户以为自己的方案从未保存。
业务状态不要藏在会话历史里
以"提交供应商审批"为例,业务表至少应独立保存任务 ID、申请人、当前状态、受理号、最后查询时间和必要的幂等键。会话消息可以说明模型为何建议提交,不能代替业务表的状态迁移。若 fork 出方案 B,B 若要进入真实业务流程,应创建新的草稿或意图;若只是比较文案,就不要误写入正式任务。
同理,restore 只是恢复讨论上下文。它不会撤销已经发出的审批、短信或外部预订。若产品需要撤销,必须通过有授权的业务接口执行,并留下独立回执。
什么时候不必引入分支与恢复?
一次性文本生成、没有跨天历史、没有外部工具副作用的功能,直接调用模型或现有框架通常更简单。只有当产品确实需要长期讨论、多方案比较、可解释的历史切换和业务状态对账时,才值得接入检查点、SessionStore 和分支机制。
验收时检查:A、B 从共同节点开始且互不污染;恢复后新消息继续保存;rewritten 提交覆盖完整历史;业务状态查询与会话恢复独立发生;运行中点击恢复得到明确错误,而不是假装成功。这样恢复能力才不会被误解为业务回滚。