dshkit

DeepSeek Harness 的子 agent、workflow 与 Ralph 循环

dsh 怎么把活分出去:subagent 与 subagent_fork、后台 agent 控制、workflow 的 agent/pipeline/parallel 原语、job 管理,以及 Ralph 循环到底是什么。

更新于 2026-08-133 min
一句话结论

dsh 里的委派是四组彼此独立的工具:subagent 与 subagent_fork 负责派生,subagent-control 负责发消息和打断,jobs 管理所有类型的后台工作,workflow 用 agent()、pipeline()、parallel() 做编排。ralph 是另一种形状——面向一个不可变目标、由全新子会话组成的前台循环。

多数编程 agent 的「委派」不过是把自己再 prompt 一遍。DeepSeek Harness 提供了四种实质不同的委派机制, 而选错的代价,是「能收敛的编排」和「在原地烧 token 的编排」之间的差别。

派生:subagentsubagent_fork

@deepseek-ai/dsh-tool-subagent 提供 subagent;挂载了 fork 后端时,还会注册 subagent_fork

区别在于子 agent 从什么起步。普通子 agent 从你交给它的任务开始;fork 从当前会话状态开始——当子 agent 需要你已经建立起来的上下文时很有用,不需要时就是浪费。

一般规则:上下文本身就是任务时用 fork(「顺着这条路继续,但换个修法试试」);任务可分离时开全新的 (「去读这三十个文件,把发现报回来」)。

控制在跑的东西

@deepseek-ai/dsh-tool-subagent-control 给父级三个动词:

  • send_message——给正在后台跑的子 agent 追加一条消息
  • interrupt_agent——打断它当前这一轮
  • list_agents——看看到底哪些还活着

@deepseek-ai/dsh-tool-subagent-report子级一个动词:report,把结论传回父级。它只在可继续 的进程内子会话中可见——一个无法继续的子会话,也没有可以汇报进去的对象。

这是一个真正的消息传递面,而不是发完不管。父级可以起五个子 agent,用 list_agents 盯着,用 send_message 纠偏其中一个,杀掉跑歪的另一个——不必等五个全部结束。

Job

@deepseek-ai/dsh-tool-jobs——job_listjob_outputjob_kill——管理所有类型的后台工作: 用 run_in_background 起的 bash、常驻终端、子 agent,一视同仁。

这种统一是有意义的。否则你得为「我丢到后台的构建」和「我派生的 agent」维护两套心智模型。在这里, 它们都是 job。

Workflow

@deepseek-ai/dsh-tool-workflow 用三个原语编排多 agent 工作:

  • agent()——跑一个 agent
  • pipeline()——让每个条目流过各个阶段
  • parallel()——并发跑一批任务

结构上最关键的选择是 pipeline()parallel() 之争。pipeline 让每个条目独立走完全部阶段——条目 A 可以在第三阶段,而条目 B 还在第一阶段。parallel() 是屏障:全部完成才能往下走。

只有当后一阶段确实需要前一阶段的全部结果同时在手时才用屏障——比如跨全集去重,或者决定要不要继续。 否则 pipeline 的耗时是「最慢的那条单链」,而不是「每阶段最慢者之和」。

Ralph 循环

@deepseek-ai/dsh-tool-ralph 不是 workflow 的变体,而是另一种形状。harness 自己的术语表定义得很精确:

  • Ralph loop:面向一个不可变目标的前台 fresh-agent workflow 运行。
  • Ralph round:该循环中的一个全新子会话,不带父会话的对话种子
  • Ralph handoff:从上一个继续中的 round 传给下一个的、规范化的、有界的、结构化报告。

三条定义连起来读,设计意图就很清楚了。目标不会漂移,因为它不可变。上下文不会膨胀,因为每轮从头开始。 而连续性不靠搬运对话来维持,靠的是搬运一份有界的结构化报告

这是对「长 agent 会话」失效模式的正面回答:一个进行到第三十轮的 agent,是在一堆考古材料里做推理。 Ralph 每轮把考古材料丢掉,只留 handoff。

适用:目标确实固定、且轮与轮之间进展可衡量——把测试套件磨绿、把一个清单干到空。

不适用:目标本该随着你的认知而改变的场合。不可变是它的卖点,而对探索来说这是错的属性。

Goal,不是 todo

还有两个概念容易混:

todo_write@deepseek-ai/dsh-tool-todo)是会话内草稿状态——pendingin_progresscompleted, 每次调用整体替换。

create_goal / get_goal / update_goal@deepseek-ai/dsh-tool-goal)是持久的。术语表把 goal 定义为 挂在某个已有会话上的一个持久完成目标,带阶段和轮次上限,支持暂停、恢复、完成、阻塞状态。 goal round 是为当前 goal 准入的一次续跑循环,goal activation 则是准入下一轮的进程内许可。

值得注意的是那个轮次上限。一个没有边界的持久目标,就是 agent 循环变成无上限账单的方式。

Scope:为什么子 agent 的工具不一样

harness 的 scope 模型解释了那个让人意外的现象——子 agent 的工具集和父级不同。

scope 是工具、提示词这类贡献的按-agent 注册单位,要么全局,要么属于某一个 agent。shadowing 是最具体者胜的名字解析:scope 内的同名项只在该 scope 内替换全局项。restriction 则通过取交集, 为某个 scope 过滤全局工具集。

所以子 agent 拿到的不是你工具的副本,而是它自己 scope 下组装出的一套,可以遮盖、限制或扩展全局注册的 东西。这就是「只读不写的 reviewer」这种预设背后的机制——它是对某个 scope 的一条限制,而不是另写一个程序。

常见问题

subagent 和 subagent_fork 有什么区别?

两者都委派给独立 agent,fork 后端会额外注册为 subagent_fork。fork 让子 agent 从当前会话状态起步,而不是从零开始。

Ralph 循环是什么?

面向一个不可变目标的前台 fresh-agent workflow 运行。每一个 Ralph round 都是全新子会话,不带父会话的对话种子;轮与轮之间靠一份规范化、有界、结构化的报告(handoff)传递上下文。

为什么每轮开新会话,而不是继续同一个?

长会话会堆积大量与当前步骤无关的上下文,而无关上下文会稀释注意力。每轮重启、只带一份有界的结构化 handoff,能让每轮的上下文规模与眼前的活相称。

子 agent 和我共用工具吗?

注册是按 scope 的。scope 是工具、提示词这类贡献的按-agent 注册单位,而 scope 内的同名项只在该 scope 内遮盖全局项。

接着看