news 2026/9/19 2:22:53

DeepSeek Harness 快照测试:fork 子代理重放的 seedLength 边界与混合 spawn+fork 场景

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
DeepSeek Harness 快照测试:fork 子代理重放的 seedLength 边界与混合 spawn+fork 场景

DeepSeek Harness 快照测试:fork 子代理重放的 seedLength 边界与混合 spawn+fork 场景

【免费下载链接】deepseek-harnessDeepSeek Harness: Everything is a Plugin.项目地址: https://gitcode.com/gh_mirrors/de/deepseek-harness

导读

本文聚焦 DeepSeek Harness(@deepseek-ai/dsh-*插件化 Agent 框架)快照测试体系中的一个关键细节:fork(继承父会话上下文的子代理)与 spawn(全新会话的子代理)在完整转录快照重放中的路由差异。原 Agent Note 2026-06-22-fork-snapshot-scenarios.md 记录了两个新增场景的动机与决策:subagent-fork-in-processsubagent-mixed。读完本文,你将理解seedLength边界为何是 fork 子代理重放正确的基石、为什么"父代理必须完成一个回合"才能测试到该边界,以及如何在默认门禁(default gate)中以无 key 方式重放这两个场景,并掌握重新录制快照的具体命令。

背景:fork 与 spawn 的本质差异

DeepSeek Harness 为 Agent 之间的委派提供了两类进程内(in-process)子代理后端,二者在快照重放中行为完全不同:

后端委派工具子会话形态子日志中的seedLength
subagent-spawn-in-processsubagent全新会话,从零开始缺省(等价 0)
subagent-fork-in-processsubagent_fork以父会话已平衡完成的回合前缀作为种子非零(= 继承的事件条数)

两者在 packages/bundle/base/cordis.patch.yml 中注册:subagent-spawn-in-processproviderName: spawn挂载,subagent-fork-in-processproviderName: fork挂载,对应的委派工具名为subagentsubagent_fork

fork 后端的核心实现在 packages/subagent/subagent-fork-in-process/src/index.ts:completedTurnPrefix取父会话日志中**最后一个turn/end之前(含)**的全部事件作为种子——正在进行的工具调用回合是不平衡的,不能作为合法的子会话种子;若父会话一个回合都未完成,则种子为空(等价于全新 spawn)。

问题:fork 子代理的重放曾存在静默错路由

在新增 fork 场景之前,快照测试体系存在一个真实缺口:

  • 完整转录快照层(full-transcript snapshot tier)——唯一会真实启动acp-agent并端到端重放嵌套转录的测试层——只有 spawn 型子代理场景(subagent-spawnsubagent-multi);
  • fork 路由逻辑仅由llm-replay的单元测试(一个手工合成的子 fixture)和持久化往返测试覆盖;
  • 因此,一个"让单元测试全绿、却在完整转录层逃逸"的 fork 路由回归,正是快照层本该捕获、却因缺少真实 fork 场景而无法捕获的 bug 类别。

根本原因在于子代理脚本的推导方式llm-replay通过deriveReplayScript把子会话日志按(turn, step)分组为每个stream()调用一个重放条目。对 spawn 子代理,其日志只含自己的模型调用,推导天然正确;但对 fork 子代理,其.jsonl父会话的事件开头(包括父会话的assistant/chunk),随后才是子会话自己的回合——若直接对整个日志推导,父会话记录的响应会被当成子代理的模型调用重放出去,造成静默错路由。

决策一:持久化seedLength边界

修复方案在配套笔记 2026-06-22-fork-child-replay-seed-boundary.md 中记录,核心是"显式优于隐式":

  1. 会话头新增seedLengthSessionHeader增加可选seedLength: number,表示该会话有多少条前导事件是继承而来而非本会话产生。fork 后端创建子会话时盖章(= 种子前缀长度);spawn 则缺省(≡ 0)。它经CreateSessionOptions.meta传递,并在SessionStore.prepare中写入。
  2. seedLength必须是显式的,绝不从seed.length推断:重建(resume/load)路径会用整个存储日志重新播种,此时seed.length是完整长度而非原始边界;因此 resume 路径从已加载的头中把持久化的seedLength原样传回(与createdAt的保留方式一致)。
  3. 两个持久化后端都往返保存
    • JSONL:会话头行的seedLength字段(见 packages/session/session-persistence-jsonl/src/format.ts 的toHeaderLine/fromHeaderLine);
    • SQLite:sessions表的seed_length列,随seed_length/source_event_seqs/surface_op一起构成 schema 版本 4;预发布策略下,任何非当前user_version一律拒绝打开、不做迁移。
  4. 重放从边界之后推导子脚本llm-replayparseSessionHeader读取seedLength(缺省 0),loadSessionScripts对子 fixture 执行parseSessionLog(text).slice(header.seedLength)——即只取边界处及之后的事件、只重放子会话自己的模型调用(见 packages/test-support/llm-replay/src/index.ts)。spawn 子代理seedLength为 0,slice 是空操作,因此既有 spawn 场景字节级不变。

决策二:录制两个真实 fork 场景

原笔记最终录制了两个针对真实 API 的场景,且都在默认门禁中无 key(keyless)重放:

subagent-fork-in-process(聚焦回归)

父代理先完成一个回合(确立一个事实),再通过subagent_fork委派一个子任务。fork 子代理继承对话(其日志携带非零seedLength),因而能直接从父上下文作答。这是聚焦的回归用例:子 fixture 的seedLength是重放切片依赖的边界,且来自真实 fork 而非手工合成。对应 fixture 位于 snapshots/sdk/subagent-fork-in-process/(session.jsonl+session.1.jsonl两个子日志 +snapshot.yml)。

subagent-mixed(混合 spawn+fork)

父代理完成一个回合后,在同一转录中先后委派一次subagent(全新 spawn 子代理,seedLength0)和一次subagent_fork(fork 子代理,非零seedLength)。这是 seed-boundary 与 per-session-replay 笔记都点名"未来要补"的混合场景:一个转录同时覆盖两种传输方式、两条切片分支seedLength0 = 空操作;seedLength > 0= 裁剪继承前缀),且两个子代理按createdAt排序为 spawn 在前、fork 在后。对应 fixture 位于 snapshots/sdk/subagent-mixed/(session.jsonl父日志 +session.1.jsonl/session.2.jsonl两个子日志)。从子日志头可见实际形态:fork 子会话头携带parentSession"seedLength":39,而 spawn 子会话没有这两个字段。

两个场景的snapshot.yml均为version: 1profile: sdkcomposition: defaultrecording: live

为什么必须完成 turn-1

fork 后端只把父会话已平衡完成的回合前缀作为种子。若父代理在第一个回合就 fork,则没有任何已完成的回合可继承,种子为空(≡ 全新 spawn,seedLength0)——这测不到切片逻辑。因此两个场景都使用双提示输入:

  1. 第一个提示完成一个回合(确立一个"暗号"(codeword),供子代理稍后被要求回忆,如 fixture 中Remember this fact for later: the project codeword is SAFFRON. Reply with the single word OK and stop.);
  2. 第二个提示发起 fork 委派。

子代理转录中回忆出的暗号只是模型的自然行为,承载验证功能的产物是子 fixture 中记录的seedLength——它才是重放切片真正消费的边界。

快照基础设施如何支撑 fork 场景

两个进程内后端早已接入cordis.yml/cordis.snapshot.yml,作为两个模型可见工具暴露(subagent→ spawn,subagent_fork→ fork);harness 采集每个子日志,重放按seedLength键控的每子代理 fixture。缺的只是"驱动一个 fork 子代理走过全流程的录制场景"。装配细节:

  • packages/bundle/base/cordis.patch.yml 声明subagent-spawn-in-processsubagent-fork-in-processproviderName: fork)与tool-subagent-forkprovider: forktoolName: subagent_fork);
  • llm-replay插件通过Config.file$DSH_SNAPSHOT_FILE)、Config.overrideFile$DSH_SNAPSHOT_OVERRIDE)、Config.childFiles$DSH_SNAPSHOT_CHILD_FILES,路径分隔符列表)解析 fixture 路径,见 packages/test-support/llm-replay/src/index.ts;
  • 重放按"首次调用的活会话"顺序绑定父/子脚本:installLlmReplaycreatedAt排序[primary, ...children],新出现的活会话认领下一个未绑定的脚本;assertConsumed()在 teardown 时把"录制脚本从未绑定""脚本未消费完"变成明确诊断(packages/test-support/llm-replay/src/index.ts)。

影响与验证:护栏真的咬住了

原笔记记录的 Consequences 已由实现证实:

  1. 守卫上移:fork 路由切片现在受完整转录层守护,而非仅单元测试。移除slice(seedLength)(重放整个子日志)会让两个新场景同时变红——fork 子代理会收到父会话记录的 chunk 而非自己的——证明护栏有效(场景落地时验证过红→绿)。这是本笔记最有说服力的验证点:guard 是"可证咬人"的,不是纸面声明。
  2. 首个双后端场景subagent-mixed是第一个在单个转录中驱动两个不同子代理后端、同时跨 spawn 与 fork 子代理验证 per-session 重放键控的快照场景。
  3. 边界明确:进程外(ACP)子代理重放形态不同(每个子代理是独立进程、独立重放),仍由TODO(acp-subagent-replay)跟踪——这两个场景仅限进程内。
  4. 可重录pnpm run test:snapshot:record会从实时 API 重新生成全部四个 fork/spawn fixture;与其他录制场景一致,两个新场景在没有 key 时会自跳过(self-skip)。

实操:录制与重放

仓库根目录 package.json 中的相关脚本:

# 从实时 API 重新录制全部快照 fixture(含本笔记的 fork/spawn 四个 fixture) pnpm run test:snapshot:record # 以重放模式跑快照门禁 pnpm run test:snapshot:refresh
  • test:snapshot:recordDSH_SNAPSHOT=record vitest run --config vitest.snapshot.config.ts --update
  • test:snapshot:refreshDSH_SNAPSHOT=refresh vitest run --config vitest.snapshot.config.ts

录制出的产物即 snapshots/sdk/subagent-fork-in-process/ 与 snapshots/sdk/subagent-mixed/ 下的父日志、子日志与snapshot.yml,可在任何无 LLM key 的环境中重放验证。

延伸阅读

  • 种子边界机制的完整设计:2026-06-22-fork-child-replay-seed-boundary.md
  • per-session 子代理快照重放的提出:2026-06-22-subagent-snapshot-replay.md
  • fork 后端子代理实现:packages/subagent/subagent-fork-in-process/src/index.ts
  • 重放插件实现:packages/test-support/llm-replay/src/index.ts
  • 子代理子系统总览:docs/subsystems/subagent.md

【免费下载链接】deepseek-harnessDeepSeek Harness: Everything is a Plugin.项目地址: https://gitcode.com/gh_mirrors/de/deepseek-harness

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 2:21:27

用WorkBuddy搭建简历筛选工作流:50份简历30分钟筛完

上周帮一个做招聘的朋友处理简历筛选,他在招聘平台挂了职位,三天收了80多份简历,各种格式混在一起,看完他差点崩溃。他花了一个下午硬读,最后只面了3个人,其中一个还不匹配。我当时就跟他讲,这种…

作者头像 李华
网站建设 2026/9/19 2:20:52

基于UniApp与uniCloud的志愿服务管理系统开发全解析

1. 项目整体设计与技术选型1.1 为什么做这个志愿服务管理系统我接触到志愿服务管理系统,是在帮一个社区机构做信息化改造的时候。他们原来的志愿服务时长统计方式非常原始,活动报名靠群里接龙,签到靠纸质表格,月底统计时长的时候工…

作者头像 李华
网站建设 2026/9/19 2:20:42

Unity3D数字孪生实战:从SolidWorks模型导入到实时数据驱动与性能调优

数字孪生这个词这两年热度一直没降过,但真正落到Unity3D里做实时同步的项目,十个里有八个卡在数据刷新频率和模型性能的平衡上。我最近刚交付了一个产线监控类的数字孪生项目,从SolidWorks模型导入到最终实时数据驱动,中间踩的坑比…

作者头像 李华
网站建设 2026/9/19 2:17:44

从AI产业报告到创业投资分析:四维框架与风险量化实战

简介:《2024-2025年中国人工智能产业创业与投资报告》聚焦AI产业发展现状与投融资趋势,面向创业者、投资机构及行业研究人员,系统梳理政策环境、技术突破、细分市场潜力与典型企业案例,可帮助读者快速把握赛道机会、规避投资风险。…

作者头像 李华
网站建设 2026/9/19 2:14:18

知网AIGC检测升级:从对抗到协同,AI辅助写作降重新思路

最近一段时间,不少同学和同行都在问同一个问题:知网AIGC检测算法的升级,是不是让之前“AI辅助写作再降重”的路子全部失效了?我自己的几篇稿件恰好完整经历了这个窗口期,前后被判定、申诉、修改、复检,踩了…

作者头像 李华