ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

DeepSeek Harness 会话持久化兼容层:react-loop 重构前 v0 会话日志的加载与恢复实战

DeepSeek Harness 会话持久化兼容层:react-loop 重构前 v0 会话日志的加载与恢复实战 人工智能AI AgentAgent 框架DeepSeek【免费下载链接】deepseek-harnessDeepSeek Harness: Everything is a Plugin.项目地址https://gitcode.com/gh_mirrors/de/deepseek-harness点击查看免费下载本文基于.agents/notes/implemented/bug-fix/2026-08-04-load-pre-react-loop-sessions.md展开。react-loop 简化重构在保持SESSION_FORMAT_VERSION不变的情况下替换了持久事件词汇导致该重构基线生成的旧 v0 会话日志无法被当前表层与轮次不变量直接回放。本文完整解析PersistenceCoordinator的只读投影方案如何识别旧形状、如何逐事件迁移到当前格式、如何在loadinspectreadFromHMR 接管中统一生效以及为什么恢复后的 agent 以空 inbox 待处理列表起步。读完你将掌握 DeepSeek Harness 同版本格式导入边界的完整设计以及如何用测试契约验证旧会话的可恢复性。背景一次不升版本号的持久事件重构DeepSeek Harness 的会话持久化以事件溯源event-sourced为模型追加式append-only的日志是唯一事实来源LLM 历史由deriveMessages()从日志派生见 事件溯源会话架构 与 以抽象服务实现会话持久化。react-loop 简化重构对 AgentLoop 的轮次执行模型做了大幅裁剪随之改变了持久事件的形状但头部的格式版本号始终保持在 0——即SESSION_FORMAT_VERSION 0定义于 packages/core/session/src/types.ts。这意味着该重构变更基线base之前已经落盘的 v0 会话日志头部版本号与当前版本一致但事件体是旧词汇当前的表层surface与轮次turn不变量无法直接回放这些记录直接拒绝又会搁浅 PR 基线期间产生的真实会话数据。正是这种版本号相同、词汇已变的困境催生了本文要讲的窄导入兼容层。与它同族的前一个修复是 加载消息标识机制引入前持久化的会话——它处理的是消息标识MessageId出现之前的另一批 v0 旧载荷两者共用同一条只读导入边界可以一并理解。问题全景旧记录与当前不变量之间的四类差距该重构基线存储的会话preReactLoopLog()见 coordinator-contract.ts包含四类当前无法直接消费的旧形状steering/message中途引导事件重构前用独立的 steering 事件表达中途插入的引导消息重构后统一为带标识的user/message。turn/start.trigger字段旧turn/start携带trigger触发来源如{ kind: message }、{ kind: retry }当前格式的turn/start只保留turn。粗粒度aborted终止原因旧记录{ kind: aborted }没有说明由谁中止调用方已不可考。独立的disposed终止原因旧记录用{ kind: disposed }表达轮次被销毁当前格式没有该终止 kind。两种旧错误载荷{ kind: error, step, failure: { message, code, ... } }详细失败事实与{ kind: error, step, message, code? }扁平错误需要映射为当前的结构化error。另外需要澄清一个容易误会的点新的持久 inbox待处理列表并不属于本兼容问题。重构基线只发出进程内process-local的 inbox 通知从未产生agent/inbox/*会话事件。因此把旧历史回放成待处理工作会让已经领取或丢弃的提示词被再次执行——这正是为什么决策中明确不合成 inbox splice下文详述。兼容方案后端解码后、验证前的只读投影核心决策落在PersistenceCoordinator实现于 packages/session/session-persistence/src/coordinator.ts上在后端解码之后、进入当前消息验证之前识别旧形状的确切模式并将其投影为当前读取视图。投影后的结果才参与当前格式验证从而让旧日志合法地通过当前不变量。投影流水线在源码中表现为一条明确的迁移链snapshotStoredEvents与adoptStoredEvents共用同一顺序assertSupportedEvents拒绝不可回放的旧词汇 → migrateLegacyTurnStartEvent剥离 trigger → migrateLegacyTurnEndEvent终止原因归一 → migrateLegacySteeringEventsteering → user/message → migrateLegacyMessageEvent消息标识补齐来自前一修复 → snapshotSessionEvent / adoptSessionEvent冻结或接管映射规则一移除turn/start.triggermigrateLegacyTurnStartEvent在完整校验旧信封后才剥离trigger要求data恰含turn≥1 的安全整数与trigger两个键且trigger.kind为非空字符串否则抛malformed pre-react-loop turn/start at seq ...。合法输入{ turn: 1, trigger: { kind: message, ... } }投影为{ turn: 1 }。当前形状的turn/start不含trigger原样通过。映射规则二steering/message→ 带标识的user/messagemigrateLegacySteeringEvent支持两种基线形状已包装形状{ turn, message }且message是记录 → 直接取message作为新user/message的data该形状下消息已带标识无需补 id扁平形状{ turn, content, source }→ 投影为{ ...message, id: legacyMessageId(id, seq), role: user }即补上确定性标识与role: user。两者的共同点是steering/message事件被整体改写为user/message语义上中途引导与用户消息在重构后统一为同一种消息。映射规则三旧错误事实 → 当前结构化错误migrateLegacyTurnEndEvent对turn/end的reason分派旧reason.kind旧载荷投影后completed/blocked/max-tokens/interrupted仅{ kind }原样通过aborted仅{ kind: aborted }无reason子字段{ kind: aborted, reason: { kind: legacy } }aborted已含reason当前形状原样通过disposed仅{ kind: disposed }{ kind: aborted, reason: { kind: disposed } }error{ step, failure: { message, code, [status], [providerRetryAfterMs], [requestId] } }{ kind: error, error: failure }error{ step, message, [code] }{ kind: error, error: { message, code: code ?? UNKNOWN } }error已含error当前形状原样通过其他未知 kind如扩展原因原样通过保留给扩展注意disposed被折叠为aborteddisposed原因当前词汇表没有独立disposed终止但需要保留销毁导致中止这一事实分类。而纯aborted使用仅供持久化导入的{ kind: legacy }原因——这是全文的关键设计之一旧记录没有注明调用方任何把它归因到user、parent或hook的映射都是在伪造审计事实因此用一个专用legacy原因保住停止分类、同时明确调用方不可考。映射规则四消息标识的确定性补齐来自前一修复steering/message扁平形状与旧消息事件user/message、assistant/message、tool/result的无id载荷统一由migrateLegacyMessageEvent补齐标识规则是确定性公式legacy-message:session-id:event-seq即legacyMessageId(id, seq)。这个公式保证了重复加载、混合旧/新日志追加、inspect、resume、重启都会复现同一批消息 id无需后端专用改写事务。特别地旧tool/result若是对某个更早消息做内容替换surfaceOp.op replace其导入标识继承替换目标的消息 id保持仅内容可替换、标识稳定的当前不变量。边界原则确切形状识别绝不猜成合法上述所有迁移函数都先做严格键集合与类型校验见hasOnlyKeys/asRecord辅助函数长得像当前形状但缺字段、字段错误的记录不会被修复而是原样交给当前验证器拒绝。同理不支持的旧词汇如request/header-delta、mode/set、request/header的fallback原因由assertSupportedEvents给出带 seq 的明确诊断。这条原则贯穿始终兼容层只恢复完整且无歧义可映射的受支持第一方数据绝不把损坏伪装成旧数据。投影的生效面load、inspect、接管、HMR 与 readFrom投影不是只在load时执行一次而是统一应用于所有读取入口保证任何视角看到的都是同一份规范化视图load走prepareCore→adoptStoredEvents就地接管零拷贝返回可发布的恢复视图inspect走snapshotStoredEvents深快照非破坏返回不可变检查视图无主状态接管adoptiononCreated中seedMatchesPersisted用snapshotStoredEvents归一化存储前缀后与活体会话种子做逐事件 JSON 相等比较seedCoversPrefix——前缀比较必须在同一规范化视图上进行否则旧前缀永远匹配不上当前种子HMR 前缀采纳adoptLivePrefix同样先snapshotStoredEvents再比对避免热替换把旧前缀误判为冲突readFrom见下一节的特殊处理。readFrom的后缀回退旧事件需要更早的替换标识readFrom(id, fromSeq)是从指定 seq 起读取后缀的原语。可寻址后端SQLite 实现loadStoredFrom钩子通常只读后缀使读放大随后缀长度缩放。但这里有一个精妙的兼容细节readFromCore实现先取后缀若后缀中不存在任何需要旧前缀事实的事件needsLegacyPrefix判断steering/message或缺少id/message的旧消息载荷则直接规范化并返回后缀若后缀中包含这类旧事件——例如一个旧tool/result内容替换需要追溯到前缀中替换目标的标识——则放弃后缀捷径加载并规范化完整前缀再按seq fromSeq切片返回。也就是说seek 优化在后缀自洽时才生效一旦旧事件破坏了后缀的自洽性协调器自动退化为全量规范化路径保证标识继承关系不被破坏。这一行为同时被写进了PersistenceBackend.loadStoredFrom钩子的契约注释coordinator.ts使两种后端行为一致。恢复后从空 inbox 起步不合成 splice 的取舍决策明确导入器不合成 inbox splice。恢复后的 pre-react-loop agent 从空的待处理列表nextTurn、nextStep均为空开始这与基线运行时无法持久化待处理 inbox 工作的行为一致——旧通知本就不是会话事件无法构成可信的待处理状态快照若在无法获知每一次领取与丢弃的情况下推断插入会让已消费的工作重新执行。这一行为在 agent-loop 恢复端到端测试 中有直接断言handle.agent.inbox.nextTurn与handle.agent.inbox.nextStep恢复后均为[]历史 transcript含legacy-message:标识的三条消息完整可见且followup新消息可正常续写当前格式的turn/end。存储契约仅追加、不改写、后续事件用当前格式投影是纯只读升级已存储的旧 JSONL / SQLite 记录原封不动不做原地改写改写会违反仅追加契约还需要 JSONL 与 SQLite 各自的后端专用原子迁移机制恢复后的会话只在旧记录之后追加当前格式事件追加入口appendCore对旧词汇保持拒绝防止一个过时的 JS 插件把已退役形状重新写入本后端。正因如此这被明确定位为一个显式的同版本导入例外而不是通用的 v0 兼容层SESSION_FORMAT_VERSION 0预发布格式本就不承诺广泛兼容每次额外例外都必须提供完整且无歧义的持久化边界映射。被否决的替代方案与理由原文档记录了四条被否决的路线各自有清晰的取舍依据把同版本记录视为不支持符合预发布默认立场但会搁浅 PR 基线的真实会话——而旧 steering 内容与终止事实均有完整映射拒绝是不必要的损失。把旧 inbox 通知回放为持久 splice通知不是会话事件无可信待处理状态快照推断插入会重跑已消费工作。把粗粒度中止记录归因到现有调用方userparenthook凭空指定旧记录未注明的调用方制造虚假审计事实{ kind: legacy }专用原因既保留分类又不撒谎。重写已存储的 JSONL / SQLite 记录违反仅追加契约且需为读取兼容边界引入后端专用原子迁移设施复杂度与收益不成比例。测试验证跨后端的共享契约兼容行为由共享契约测试套件统一验证runPersistenceContract见 coordinator-contract.ts该套件同时跑在内存参考实现、JSONL 与 SQLite 三个后端上。preReactLoopLog()构造了覆盖全部旧形状的 7 个轮次的完整日志测试断言包括归一化后不存在任何steering/message全部turn/start的data均为纯{ turn }7 个turn/end的终止原因依次投影为completed、error/SERVER、aborted/legacy、aborted/disposed、error/UNKNOWN旧扁平载荷、error/RATE_LIMIT含status: 429、providerRetryAfterMs: 1000、requestId的完整失败事实、error/CODEDinspect、readFrom(id, 0)、load三个入口得到一致的规范化视图deriveMessages()正确还原[old prompt, old steering]两条消息readFrom(id, 3)后缀读取返回的steering/message已被替换为带原消息标识的user/message扁平steering/messagecontentsource直存形式得到legacy-message:id:0的确定性标识未知扩展终止原因如{ kind: extension-reason }原样保留不被误伤。边界与限制只支持基线格式本例外只支持重构的基线格式不支持重构开发期间产生的中间格式尤其没有为更早的实验性agent/inbox/spliced载荷定义迁移——该事件目前仍属于当前词汇webhook 不变量、客户端会话节点 都在消费它但本兼容层明确不为旧实验载荷背书当前形状外观相似但结构错误的记录仍走拒绝路径不会被猜测性转换为有效记录恢复后的 agent 从空待处理列表开始这是与基线运行时行为对齐的有意限制不是缺陷。小结PersistenceCoordinator用一次后端解码后、验证前的只读投影把 react-loop 重构基线的 v0 旧会话平滑接回当前 AgentLoopsteering/message统一为带确定性标识的user/message废弃的turn/start.trigger被剥离两类旧错误载荷折叠进当前结构化errordisposed与粗粒度aborted分别以disposed与legacy原因保留停止分类且全程不触碰仅追加存储、不为 inbox 合成 splice。它证明了一个可复用的工程原则当格式版本不能变而词汇已经演进时兼容层应当是一组在统一边界上、形状严格可识别、只读且确定性的投影规则而不是一次宽松的尽力修复。延伸阅读加载消息标识机制引入前持久化的会话另一项同版本格式变更的确定性标识与通用只读导入边界。以抽象服务实现会话持久化仅追加后端存储、恢复边界与SESSION_FORMAT_VERSION 0的定位。将每条消息创建为带标识的不可变值当前消息标识与不可变契约的归属。协调器实现packages/session/session-persistence/src/coordinator.ts共享契约测试packages/session/session-persistence/tests/coordinator-contract.ts恢复端到端测试packages/core/agent-loop/tests/resume.spec.ts。赞分享人工智能AI AgentAgent 框架DeepSeek【免费下载链接】deepseek-harnessDeepSeek Harness: Everything is a Plugin.项目地址https://gitcode.com/gh_mirrors/de/deepseek-harness点击查看免费下载相关推荐DeepSeek Harness Turn 包裹不变式会话事件日志的持久化与崩溃恢复边界设计DeepSeek Harness Turn 包裹不变式会话事件日志的持久化与崩溃恢复边界设计 本篇技术指南讲解 DeepSeek Harness 核心架构决策人工智能AI AgentAgent 框架DeepSeekDeepSeek Harness 会话持久化设计基于 SessionEvent 事件溯源日志的抽象持久化服务DeepSeek Harness 会话持久化设计基于 SessionEvent 事件溯源日志的抽象持久化服务 本文是 DeepSeek Harness一切皆人工智能AI AgentAgent 框架DeepSeekDeepSeek Harness TUI 自动会话标题默认开启与恢复会话重推导的实现决策DeepSeek Harness TUI 自动会话标题默认开启与恢复会话重推导的实现决策 本文基于开源仓库 DeepSeek HarnessEverythi人工智能AI AgentAgent 框架DeepSeek上一篇Rclone云存储同步技术解决分布式数据管理的终极方案下一篇如何在Vue.js应用中集成KaTeX快速实现数学公式渲染的完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表