ByteNoteByteNote
DeepSeek Harness 子智能体接缝:委派与停稳
字

字节笔记本

2026年10月6日 · 约 11 分钟读完

DeepSeek Harness 子智能体接缝:委派与停稳

API中转
¥120

DeepSeek 开源的 agent 框架 dsh(DeepSeek Harness)信奉一切皆插件,它的仓库 docs/subsystems 目录里,每个文件讲一个子系统的设计约定。subagent 这一篇讲的是让一个 agent 把工作委派给子 agent 的那条接缝。它和 bash 一样属于可选能力,不在 agent 循环之内,因此类型定义放在核心文档之外;它又和 bash 不一样:bash 全局只允许一个执行器,而 subagent 接缝允许同一上下文中并存多个提供方实现,按名称注册进 ctx.subagents 注册表,对齐的是模型适配器注册表的做法,而不是单一 bash 执行器。

包结构照着这个思路铺开:服务定义在 dsh-subagent 包,六个兄弟包各是一种提供方,分别是进程内 spawn、fork、acp、codex、claude-code 与 dsh-sdk。面向模型的消费方有三个:dsh-tool-subagent 负责按提供方委派,dsh-tool-subagent-control 提供可选的全局控制工具 send_message、interrupt_agent 与 list_agents,dsh-tool-subagent-report 提供可选的 child 作用域 report 返回通道。

DeepSeek Harness 子智能体接缝全景:请求、能力校验、六个提供方与两条路径

两类能力,两种发现方式

提供方用一个静态描述符公布自己启动时支持的功能,服务在单次 run 存在之前就完成检查:请求依赖提供方不具备的能力时,直接以类型化错误 SubagentError 拒绝,错误码为 UNSUPPORTED_CAPABILITY,绝不会先接受再静默忽略。这套 flag 只有四个:outputSchema、depthLimit、toolFilter、persona,与启动请求的可选字段一一对应。值得注意的是,这些 flag 只描述一次性 start 路径,也就是由提供方组装子 agent 的那条路径;可继续子 agent 由继续执行管理器自行组装,能力发现换成另一个机制:唯一一个可选方法 prepareContinuable,方法存在即为具备能力,TypeScript 的类型收窄就是发现手段。

一次性委派:从请求到运行句柄

工具层根据模型输入和自身配置构建启动请求:必填的 parent 提供会话工作目录、谱系与委派深度;prompt 作为子 agent 的用户消息;signal 是启动前后唯一的取消通道,提供方在 run 发布前收到取消,会清理已创建的部分资源再拒绝,发布后收到取消则取消剩余轮次工作。可选的 outputSchema 要求对象根 JSON Schema,不支持的 schema 在启动时即失败;进程内后端把工具过滤实现为子 agent 创建窗口内的作用域限制,被点名的工具从子 agent 提示词里消失且拒绝执行,两种表现是一体的,未知工具名会响亮报错;persona 则只遮蔽该子 agent 自己的部署级 persona。

请求经过服务校验并解析出持久化描述符后,才交给所选提供方。提供方返回的运行句柄由消费方持有:本地 run 的 id 必须等于已发布子会话的 id,localAgent 暴露确切的子 agent;远程 run 则没有 localAgent,也不会出现在持久化枚举结果里。终态由结果对象描述:output 取最后一条非空 assistant 消息的内容;structured 只在请求了 outputSchema 且成功满足时存在,请求 schema 并不保证一定拿到它;stopReason 是可合并扩展的联合类型,已知取值为 completed、aborted、error、max-tokens、refusal,消费方应对已知分支处理并把未知终态视为失败。非 completed 意味着输出可能不完整,应映射为 isError 的工具结果,而不是把半截输出当成功。run 没有 steering,也没有恢复;可继续对话根本没有 run,因为它们的句柄直接由继续执行管理器持有,每个轮次都通过子 agent 自己的收件箱排序。

可继续子智能体:持久会话与激活

可继续子智能体的准入、激活路由与结算流程

可继续后台子智能体的底座是一份持久化子会话,至多关联一个 Activation,即被重建的子 agent 处于驻留状态的时段。Activation 不是请求、结果、取消或任务,它可以执行多个先进先出轮次,并在自己创建的后代仍在运行时保持驻留。文档里用一行链式图讲清了层次:持久化会话,到可选的存活激活,到一个保留的 AgentHandle,到作为唯一轮次队列的 Agent 收件箱,再到零或多个自己拥有的子激活。

startContinuable() 先预留稳定的子 agent id,对版本化的描述符载荷建立快照,向提供方索取分离的创建输入,通过私有的 activation-owner 作用域创建子 agent,建立所有权并提交初始提示词。收件箱准入产出消息 id 的那一刻,调用就以 childId 和 messageId 两个值完成,无需等待轮次开始;准入之前的任何失败都两个 id 都不返回,并回滚激活与父级所有权。后续消息只有唯一入口 followup(),路由只看激活的驻留状态:running 就在同一激活中入队,waiting 就唤醒同一激活,没有激活就冷恢复一个新的。这三个状态由管理器从 agent 的完全停稳情况与其拥有的子级集合推导而来,不是第二套执行状态机。

这里最重要的一条设计是:Agent 收件箱是唯一的队列。每条继续执行消息都成为一个先进先出轮次,已接受的消息共享同一个可观测顺序,后来的消息无法改变进行中的轮次。调用方的取消信号只在收件箱接受之前掌管查找、物化与准入,此后管理器独立掌管激活,这条接缝不对外暴露任何中途引导操作。

中断、上报与结算

interrupt() 是唯一的公开停止操作,鉴权要求确切:要么携带人工客户端给出的持久化直接父地址,要么携带确切的存活祖先 agent 对象,错误的父地址或不在存活祖先链中的调用方会被 UNAUTHORIZED 拒绝;目标不存在、是一次性的或已结算时,中断是被接受的空操作。中断对目标发出保留收件箱的取消,已领取进中断轮次的工作不会重新入队,被中断的驱动进入空闲后,一次唤醒发送即可恢复被暂停的队列。

上报是 child 自己的选择。reportFrom() 由确切的存活 child agent 授权,调用方不能指定接收方,管理器从 child 持久化的父会话记录推导唯一接收方,并要求父 agent 在线。静默投递用 inject,不产生父轮次;唤醒投递用 followup,产生一个普通的父轮次;两种模式都不会结束 child 的轮次,最终回答也不会被隐式当作上报。因为上报是 child 的选择,管理器还保有一份自己的记账:激活结算时,它无条件向每个调用方拿到过 id 的 child 的持久化直接父级投递一条结算通知,说明这个 epoch 如何结束。这条通知使用与上报不同的来源类型,文档对理由说得很直白:上报是 child 选择的内容,结算通知是运行时对 child 下场的陈述,混用两种来源会让文本记录把运行时的话记到 child 头上。

结算的顺序也有讲究:只有当子 agent 完全停稳、它的每个子级都已释放、尽力而为的最终会话 flush 结算完毕且句柄完成释放之后,这个子 agent 才算释放。flush 被拒绝只记录、不让激活失败,此后持久化状态在恢复时可能缺失或陈旧,这是文档明说的代价。

不加载进程的持久化枚举

listChildren() 从会话存储与可选持久化的实时优先合并中枚举直接子代理,全程不查询服务、不加载也不恢复任何 agent。每行的模式与标签由注册的投影单元供值,走三级阶梯:存活 child 用注册表水位缓存,零日志读取;冷 child 先读可选的投影 checkpoint 缓存,通过序列门才可用;否则在一次持久化检查读取上经注册表折叠。折叠规则是 last-wins 且没有失败通道,child 自己的描述符覆盖 fork 种子里祖先的描述符,格式错误或版本不认识的载荷折叠为可序列化的空哨兵。结果是按创建时间排序的条目数组,条目有 child、corrupt、unavailable 三种形态:冷检查失败生成 unavailable 并在下次列表自然重试,因此一个损坏的兄弟不会遮住健康的 child。缺少持久化时,枚举退化为仅存活枚举而不是报错;缺投影注册表或缺会话存储则抛出带专属错误码的异常,且在任何读取之前检查,零 child 的部署同样确定失败。面向模型的 list_agents 适配器只保留可继续条目,并把状态细化为自己的 running、idle、ready 词汇,ready 把仅存于存储的 child 命名为可恢复而非终态。listDescendants() 把同一套解释应用到完整后代树,普通会话和一次性 child 仍作为遍历节点,因此藏在它们下面的可继续后代仍然可发现。

深度与 fork 种子:复用既有词汇

委派深度由持久化的会话头字段与可合并扩展的运行时选项共同表示,两者都归这条接缝所有,循环既不设置也不读取;进程内子 agent 持久保存父深度加一,冷恢复无法降低深度,每次启动都拒绝超出安全整数域或高于请求绝对上限的派生深度。fork 的种子注入复用创建 agent 的既有原语:传入父日志的一段平衡的已完成轮次前缀,直到并包括最后一个轮次结束事件,从序列零开始连续,进行中的未平衡轮次被排除在外,不变量回放可以接受它。

整篇文档读下来,最值得带走的不是某个接口签名,而是几条一以贯之的原则:能力先验证、失败要响亮;多条路径共享唯一队列,顺序才可观测;提供方在可继续路径上只贡献数据、不贡献能力,子 agent 的整个生命周期归管理器所有;运行时的记账与 child 自己的话在来源类型上分开,让文本记录永远诚实。对一个想把多 agent 编排做扎实的团队来说,这篇子系统文档是难得的参考实现说明。

相关文章

分享: