Deepseek Harness 四大执行模式对比(架构师视角)
·
四大执行模式对比(架构师视角)
当前在 DSH 里有四种"派活"的方式,它们在上下文继承、执行模型、生命周期、阻塞性、适用规模五个维度上有本质区别。下面我从架构师的角度讲清楚区别、用法和陷阱。
一、TL DR 对照表
| 维度 | subagent | subagent_fork | workflow | ralph |
|---|---|---|---|---|
| 上下文继承 | � 完全隔离 | ✅ 继承本会话已完成轮次 | ❌ 脚本编排,与本会话隔离 | ❌ 全新空白,无任何上下文 |
| 执行模型 | 单次/多轮委派 | 单次/多轮委派 | 流水线脚本(agent/pipeline/parallel) | 循环迭代(每轮全新子代理) |
| 生命周期 | 持久 subagent_id,可 send_message 续聊 | 持久 subagent_id,可 send_message 续聊 | 一次性脚本,跑完即结束 | 多轮循环,由 worker 报告完成/阻塞 |
| 默认阻塞 | 后台(fire-and-forget) | 后台 | 前台(脚本跑完才返回) | 前台(每轮循环跑完才返回) |
| 共享"长期记忆" | 无(靠 subagent 自己产物) | 无(靠 subagent 自己产物) | 无(靠产物) | ✅ 共享 workspace 作为唯一长期记忆 |
| 典型用法 | 独立研究、范围内实现 | 复用上下文做后续分析 | 大规模扇出(审计、迁移、多角度) | 干净迭代、避免上下文污染 |
二、逐个详解
1. subagent — 标准委派(无状态承包商)
核心契约:子代理拥有全新的、独立的上下文,看不到本会话的任何历史。它收到的是一个"自包含的 prompt"。
// 典型签名
subagent({
description: "研究 X",
prompt: "请调研 X 的实现方案…(必须给出完整背景,因为子代理看不到父会话)",
run_in_background: true // 默认 true
}) → 返回 subagent_id
架构要点:
- prompt 必须是自包含的:子代理不知道"我们之前聊过什么"。所以 prompt 里要把背景、约束、产出格式全写清楚。
- 后台为主:
run_in_background: true时立刻返回 id,不阻塞主线程;只有当你下一步动作依赖它的结果时才用run_in_background: false(同步等待)。 - 可续聊:返回的
subagent_id是持久的,后续用send_message在同一子会话里继续派活。 - 结果是一次性返回的:你拿到的是最终答复,看不到中间步骤。
适用场景:
- 独立研究:“调研一下 React 19 的新特性”
- 范围内实现:“实现 user 模块的 CRUD(已有 schema 在 …)”
- 隔离分析:“分析这个文件为什么慢”
- 任何和当前会话上下文无关的工作
反模式:
- ❌ 在 prompt 里写"就像我们刚才讨论的那样…"——它不知道你讨论过什么。
- ❌ 把它当成同步函数用却不关心 id——后台跑完你可能错过通知。
2. subagent_fork — 上下文继承委派(带备忘录的承包商)
核心契约:和 subagent 类似,但子代理能看到本会话的所有已完成轮次(不包括当前正在生成的这轮)。
subagent_fork({
description: "基于我们刚才的讨论做 X",
prompt: "在刚才讨论的基础上,请你…",
run_in_background: true
}) → 返回 subagent_id
架构要点:
- prompt 可以省略大量背景:因为子代理继承了你之前所有的输入输出,包括已读过的文件、做过的决定。
- 节省本会话 context:虽然子代理看到了全部历史,但它的"思考+产出"不会回流到你的主会话——这是它的关键价值。你能继续在主会话工作而不被它的中间步骤淹没。
- 生命周期与 subagent 完全一致:同样后台、同样
send_message可续聊。
适用场景:
- 基于当前进度的下一步:“基于我们刚才定下来的 schema,实现 DAO 层”
- 复用上下文的审查:“review 我们刚写完的这套方案,给出风险清单”
- 承接式分析:“把刚才那份调研再深挖一层”
- 任何显式需要本会话上下文的工作
何时选用 vs subagent:
- 默认用
subagent_fork当你需要它"知道上下文"。 - 默认用
subagent当你想要"干净隔离"(避免子代理被本会话历史污染/分心)。
反模式:
- ❌ 在长会话里 fork 出 N 个 subagent_fork——每个都继承全部历史,子代理端 token 成本会高。
- ❌ 期望"子代理的中间步骤进入主会话"——不会,它只把最终答复交给你。
3. workflow — 流水线编排(CI/CD 思维)
核心契约:你写一段 JavaScript 编排脚本(顶层 await),用工作流提供的 hooks(agent()、pipeline()、parallel()、phase()、log())来调度多个子代理。前台执行,整段脚本跑完才返回。
workflow({
script: `
const files = args.files;
const results = await parallel(files.map(f => () => agent("分析文件 " + f)));
return results.filter(Boolean);
`,
meta: { name: "codebase-audit", description: "代码库审计" }
}) → 返回 JSON 序列化的值
架构要点:
- 脚本本身就是编排层:子代理是"工人",脚本是"产线"。
parallel()是 barrier:所有 thunk 全部完成才继续——只在某阶段确实需要全部前置结果时用。pipeline(items, ...stages)是无 barrier 的:每个 item 独立跑完所有阶段,阶段间不互相阻塞——批处理首选。- 结构化产出:
agent()支持opts.schema(受限 JSON Schema),可以拿到强类型结果而不是自然语言。 - 前台阻塞:跑完才返回,意味着你不能同时做别的事。适合可预期的批量任务。
- 没有持久子代理:脚本结束,所有临时子代理就散了——不要指望"回头再问某一步"。
适用场景:
- 大规模扇出:审计 100 个文件、迁移 50 个模块、跨 10 个角度并行调研。
- 多阶段流水线:每个文件先 lint、再测、再生成报告。
- 对抗性验证:让多个 agent 从不同视角审同一个方案,汇总分歧。
- 结果需要强结构:用 schema 收口,避免自然语言解析。
反模式:
- ❌ 单任务用它——杀鸡用牛刀,直接
subagent。 - ❌ 用
parallel()包单个 agent——它和agent()没区别还多一层开销。 - ❌ 期望子代理跨阶段共享上下文——每个
agent()调用都是新的、独立上下文。
4. ralph — 全新迭代循环(每日立会 + 黑板)
核心契约:前台循环。每一轮启动一个全新的、没有任何对话历史的子代理。子代理的"记忆"只有共享 workspace 里的文件。子代理跑完会报告 completion 或 blocker,循环据此决定下一轮或终止。
ralph({
objective: "把 src/legacy/ 全部迁移到新架构", // 不可变目标
maxRounds: 20 // 轮数上限
}) → 返回最终报告
架构要点:
- 每轮全新:子代理不知道上一轮说过什么——这是故意的设计,用来对抗上下文累积带来的偏见/分心/锚定。
- workspace 是唯一长期记忆:每轮子代理必须从文件里读出"项目当前状态"才能继续。这就是为什么这种模式适合有明确可验证产物的任务。
- 不可变 objective:循环开始后目标不能改(除非显式 edit)。
- worker 自行判断完成:完成/阻塞由子代理报告,不是独立评估——这意味着你需要把"如何判断完成"写进 objective 里。
- 前台阻塞:每轮跑完才进下一轮,你看不到中间过程,但每轮结束后会拿到子代理报告。
适用场景:
- 长跑、有明确终止条件的实现任务:“把 X 模块全部重构完,每完成一个文件就在 progress.md 划掉”
- 需要"清零"避免上下文污染:“调试这个诡异 bug,每次试一个新假设”
- 探索式 + 有工件:任务产物能落盘,让下一轮子代理有据可查。
反模式:
- ❌ 一次性研究——用
subagent。 - ❌ 任务没有可写产物——子代理下轮会"失忆",循环会卡。
- ❌ 期望循环"智能调度"——它只是反复启动新子代理,子代理自己决定下一步。
- ❌ 在会话里讨论细节——子代理听不到,细节必须写到 workspace 文件里。
三、选用决策树(架构师速查)
要派活给 AI 子代理?
│
├─ 是一次性、可预期的批量任务(≥3 个独立子任务)?
│ └─ YES → workflow(流水线,前台跑完拿结构化结果)
│
├─ 是长跑、有可写产物的迭代任务?
│ └─ YES → ralph(每轮全新,靠 workspace 续命)
│
├─ 工作需要看到本会话上下文?
│ ├─ YES → subagent_fork(继承历史,省主会话 context)
│ └─ NO → subagent(干净隔离)
│
└─ 你的下一步依赖子代理结果?
└─ YES → run_in_background: false(同步等待)
└─ NO → run_in_background: true(后台跑)
四、高级程序员使用建议
-
默认组合:
subagent_fork(后台)+ 必要时send_message续聊。90% 的"派活"场景适用。 -
隔离 vs 继承的选择:
- 子任务和当前讨论直接相关(review、扩展、追问)→
subagent_fork - 子任务是独立研究 / 干净实现 →
subagent(避免被本会话历史带偏)
- 子任务和当前讨论直接相关(review、扩展、追问)→
-
批量任务三步走:
- 1~2 个:
subagent - 3~10 个、结构相似:用
workflow+pipeline()或parallel() - 长跑迭代:有可写产物 + 明确终止条件 →
ralph
- 1~2 个:
-
成本意识:
subagent_fork子代理会"重读"父会话全部历史,token 成本高;上下文真的需要才用它。workflow编排脚本本身便宜,但 N 个子代理的并发可能撞到部署上限(部署有 cap)。ralph每轮全新子代理 + 重读 workspace,单轮成本固定,总成本 = 轮数 × 单轮成本,要控制 maxRounds。
-
不要混用语义:四种模式不是"调用同一个东西的不同写法",它们各自有独立的生命周期和上下文契约。把它们当成四种不同的架构组件来用——就像 RPC、消息队列、定时任务、事件循环的关系,而不是"四个名字一样的函数"。
更多推荐


所有评论(0)