Skip to content

3.5 工作流 Workflow 与 Ralph

本章概览:Workflow 能力缝让模型编写一段 JavaScript 编排脚本,向多个子代理扇出工作。本章覆盖脚本钩子与 meta 身份块、run 永不 reject 的失败纪律、worker-thread 引擎的隔离边界,以及固定目标的 Ralph 循环。

概述 / 定位

Workflow 与 Subagent 一样是可选能力缝,不属于 agent 循环;与 bash 一样,每个上下文只允许一个引擎实现提供 ctx.workflowEngine(没有命名提供方注册表,第二个引擎通过插件配置替换第一个)。能力缝三角色:Service Definition 是 dsh-workflow,Service Provider 是 dsh-workflow-worker-thread,模型侧 Consumer 是 dsh-tool-workflow(通用)与 dsh-tool-ralph(固定策略)。

出处:packages/workflow/README.mddeepseek-harness-src/packages/workflow/README.md)、docs/subsystems/workflow.mddeepseek-harness-src/docs/subsystems/workflow.md

模型写编排脚本

WorkflowStartRequest{ meta, script, args?, subagentProvider?, maxTotalAgents?, parent, signal? }script 是纯 JS 脚本体(顶层 await 可用,以 return <json-value> 结尾);meta 是身份数据块(必填 name/description,可选 whenToUse/phases),由引擎校验;argsargs 全局原样暴露给脚本。metaargs 是纯数据,从不求值脚本来获得它们parent 必填——脚本启动的每个子代理都归属它,cwd、lineage 与深度经 subagent 缝传递。

脚本内钩子:

钩子语义
agent(prompt, opts?)启动一个宿主侧子代理;带 schema 返回结构化值,否则返回最终文本;普通失败子代理解析为 null
pipeline(items, ...stages)每项依次过各阶段,(prev, item, index)无跨阶段屏障
parallel(thunks)并发执行(引擎配置的并发上限),等待全部
phase(title) / log(message)观察者进度叙述

subagentProvidermaxTotalAgents 是引擎级策略:可选地为单次运行路由子代理/降低总子代理上限,脚本既观察不到也不能替换。

出处:docs/subsystems/workflow.md(同上)

run 永不 reject 与失败纪律

WorkflowRun.result 永不 reject:执行失败解析为 stopReason: 'error',取消在引擎有界宽限内解析为 cancelledWorkflowResult{ value, stopReason, error?, agentsStarted }。消费方把非 completed 的原因映射为 isError 工具结果,绝不把部分输出报成成功。

WorkflowError 携带 codefatal 标志。fatal 错误总是从 parallel()/pipeline() 逃逸,而不是变成普通逐项 null

错误码含义
SCRIPT_PARSE / META_INVALID工作流无法启动
INVALID_ARGUMENT / UNSUPPORTED_OPTION / UNSUPPORTED_SCHEMA钩子调用违反引擎契约
AGENT_CAP / ITEM_CAP配置的安全上限被超
AGENT_START提供方异步 start 拒绝
AGENT_RESULT已发布子代理结果以基础设施故障拒绝
RESULT_UNSERIALIZABLE脚本/worker 值不是纯 JSON 数据
CANCELLED取消占有运行,待决/未来钩子拒绝

区分的关键:子代理以非 completed 的 stop reason 正常结束不是基础设施异常——agent() 返回 null,脚本可自行处理;而钩子误用(拼错的选项、越界 schema、超限)必须大声杀死脚本,绝不溶解为看起来像普通子代理失败的东西。

worker-thread 隔离

dsh-workflow-worker-thread 每次运行使用一个 Node worker 线程,脚本的 vm 上下文在其中执行;子代理仍在宿主侧,经类型化 host/worker 协议走 ctx.subagents。隔离有两个目的:同步脚本循环不能阻塞宿主事件循环;无视取消的脚本可随 worker 终止。它不是安全边界——脚本与模型既有的 bash 访问同信任前提,逃逸 node:vm 的脚本可恢复 Node 能力。worker 仍提供有用包含:脚本 CPU 工作离开宿主事件循环、worker.terminate() 提供真实最终停止、worker 以空环境启动(环境凭据不跨 process.env 泄漏)、跨边界走结构化克隆 + 纯 JSON 校验(materializeFromRealm 拒绝函数、符号、循环、稀疏数组与非有限数)。取消时 WorkflowRun.cancel() 记录首个原因、abort 共享信号、在 disposeGraceMs(默认 5000ms)内强制结算并终止 worker。

出处:packages/workflow/workflow-worker-thread/README.mddeepseek-harness-src/packages/workflow/workflow-worker-thread/README.md

Ralph:固定目标的 fresh-agent 循环

dsh-tool-ralphralph 工具是"固定策略工作流"作为普通插件:把一个不可变目标交给一系列全新子代理。与普通 workflow 的差异:脚本固定、目标由模型提供、每轮一个新鲜子代理(无会话种子,父对话不复制)、共享工作区作为跨轮长期记忆、轮间只传一份有界结构化报告(status: continue | complete | blocked + 摘要/证据/下一步/阻塞文本,maxHandoffChars 默认 16384)。

约束:subagentProvider(默认 spawn)必须存在、支持结构化输出并报告 inheritsParentContext: false;轮次上限以 WorkflowStartRequest.maxTotalAgents 与引擎全局后盾协调;maxRounds 默认 256 且是部署上限。完成与阻塞是工作报告,不是独立认证——父代理只看到最后一份报告与轮次数。Ralph 与同会话 goal 域相互独立:普通长跑目标用 goal 工具,一次性委派用 plain subagent 或 workflow。

出处:packages/workflow/tool-ralph/README.mddeepseek-harness-src/packages/workflow/tool-ralph/README.md

事件与观察

workflow/* 事件(workflow/startworkflow/phaseworkflow/log、按 seq 配对的 workflow/agent-start/workflow/agent-endworkflow/end)全部 observe-only:载荷携带 WorkflowRunInfo(id + meta)而非活 run,订阅者拿不到 cancel/disposeworkflow/end 刻意省略结果值(观察者不得拿到调用方结果的易变别名)。每个监听器独立包含,每个监听器收到自己的载荷克隆。

关键设计决策

  • 模型写代码、引擎定策略:提供方选择与上限不进脚本可见面。
  • run 永不 reject:取消与失败都以结果解析,消费方按 stopReason 映射。
  • fatal 与 null 分层的失败纪律:拼写错误必须杀死脚本,普通子代理失败可被脚本处理。
  • worker 线程隔离而非沙箱:保持与 bash 相同的信任前提,只防事件循环阻塞与取消僵局。
  • Ralph 以插件实现固定编排策略:不给 agent-loop 加"Ralph 模式",goal 域保持独立。

扩展点

  • 新引擎:在 workflow seam 后实现另一个 WorkflowEngine(独立进程或沙箱引擎),模型侧工具不变。
  • 脚本作者:组合 agent/pipeline/parallel;结构化输出走核心工具的 schema 子集。
  • 观察者:订阅 workflow/* 事件做进度展示(dsh-client-ui-workflow-run 把四个事件折叠为一个 Chat 节点)。
  • 新固定策略:仿 tool-ralphworkflowEngine + subagents 之上写专用工具。

参考资料

本文档为学习用途的原创讲解,基于 MIT 许可的开源项目 deepseek-ai/deepseek-harness 编写;所有引用均注明出处。