DSHarness 系统拆解 固定基线 47f943859b · 36 已复核 / 0 撰写中 / 36 章
English
编排·第 25 章

Workflow 与 Ralph

模型编写的 JavaScript 编排怎样桥接到 Agent 调用

已复核上游 47f943859b范围: 分析 worker-thread engine、agent() bridge、fan-out、结果聚合、事件、错误与固定 Ralph loop。

结论:Workflow 是前台编排 VM;Ralph 是建立在它之上的固定策略

DeepSeek Harness 把编排分成三层:ctx.workflowEngine 定义由持有者负责的 run 合同;worker-thread engine 执行一段模型编写的 JavaScript,并把 agent() 桥接到 subagent seam;面向模型的 consumer 决定模型能够控制哪些脚本和策略。通用 workflow 工具暴露脚本,而 ralph 不暴露——它提供一段构建时固定的循环,只接受目标和受限轮数。

这让 Workflow 足够强,但不伪装成持久化基础设施。它是一个具有明确并发、取消、JSON 和生命周期边界的前台扇出/聚合机制,而不是 scheduler、可恢复 job 系统、安全 sandbox 或独立 verifier。

1. 三层架构把策略留在 engine 之外

负责不负责
Workflow seam启动请求、live run、结果词汇、生命周期事件、fatal error 分类JavaScript 实现、面向模型的 schema、持久化策略
Worker-thread providerVM hooks、worker/host 协议、child admission、上限、取消、清理模型何时应该使用编排,或由哪个 consumer 暴露
Tool consumer可见 schema、prompt 指引、结果投影、可选 Session 记录ctx.workflowEngine 后面的执行机制

请求携带纯 JavaScript body、JSON metadata/args、可选 provider 与 child ceiling,以及一个必需的 live parent Agent。返回的 run 暴露已验证 metadata、永不 reject 的结果 Promise、取消与幂等 dispose。WORKFLOW-SEAM

设计解读

专门策略可以复用同一 engine,而无需给 Agent loop 再加分支。Ralph 正好证明了这一点:它组合已有 workflow/subagent 服务,而不是在核心 runtime 中创造“Ralph 模式”。

2. Live run 是由持有者负责的资源,不是 fire-and-forget Promise

start(request)
  → 发布前验证
  → 返回 WorkflowRun
       result: Promise<WorkflowResult>  // 永不 reject
       cancel(reason?)
       dispose()                        // 幂等、有界清理

这个 seam 明确了所有权。拿到 run 的 caller 必须 dispose;取消与处置有界;listener failure 被隔离;workflow/end 在结果 settle 时恰好触发一次。普通失败通过 completedcancellederror 数据表达,而不是让公开 result Promise reject。WORKFLOW-LIFECYCLE

这避免了一个含混状态:Promise reject 究竟表示“脚本失败”,还是“runtime 丢失了所有权”?它也迫使每个 consumer 正视清理,而不是把 child 当作脱离管理的工作。

3. 发布前做请求验证;发布后用结果表达运行失败

Worker provider 会验证并归一化 metadata,用 worker 将要编译的同一个 async wrapper 做 parse check,解析 child provider,并在生成 ID 或发出 workflow/start 之前约束 per-run child override。未知 metadata 字段与畸形 phase 会一次性报告;旧式 export const meta header 会收到有针对性的 parse error。WORKFLOW-START-VALIDATIONWORKFLOW-META

提交点

start() 返回之前,无效请求不存在已发布 run;返回以后,script error、worker death、cancellation 与 boundary failure 都通过 run result settle。这与仓库其他位置的事务形状一致:候选对象在变得可观察之前就被拒绝。

4. 每个 run 一个全新 Worker Thread:提供 containment,不提供安全边界

每次运行都会获得新的 Node worker。其 ambient environment 被清空,loader flag 被移除,初始化数据通过 structured clone 跨界。模型编写的代码随后在 worker 内的 vm.Context 中执行。这可防止同步脚本阻塞 host event loop,也给 host 一个可以强制终止的线程。WORKFLOW-WORKER-SPAWN

5. 脚本世界被刻意压得很小

globals = {
  agent(prompt, options?),
  parallel(thunks),
  pipeline(items, ...stages),
  phase(title),
  log(message),
  args
}

其中没有 filesystem、network、timer、Cordis context、Node API 或直接 Session handle。初始同步片段受 syncTimeoutMs 限制;第一次 await 之后,协作式 hook check 与 host 的 cancellation grace 共同提供停止机制。一旦 cancellation 或 disposal 开始,若脚本永久停在不再触及 hook 的 Promise 上,host 会在 grace 之后终止它;若没有这种触发,本 engine 并未定义通用 wall-clock deadline。WORKFLOW-VM-GLOBALSWORKFLOW-CANCEL-BOUNDARY

边界含义

脚本负责协调 capability,但本身并不拥有这些能力。真实工作属于 child Agent,它会进入自己正常的 tool、permission 与 execution world。

6. agent() 是通往既有 Subagent seam 的 RPC bridge

每次调用验证非空 prompt 与一个封闭 option set:labelphaseschemaprovidermodel。明确 deferred 的 effortisolationagentType 会直接失败。Host 随后通过配置好的 subagent provider 启动 child,并传入 workflow 的 live parent、run 级共享取消信号、可选 output schema,以及可选 provider/model override。WORKFLOW-AGENT-OPTIONSWORKFLOW-CHILD-BRIDGE

没有 schema 时,agent() 只拼接最终 text block;有受支持的 object-rooted schema 时,它返回 structured value。一个 clean child 若缺少承诺的 structured output 会变成 null,每个 child handle 都在 finally 中 dispose。WORKFLOW-AGENT-RESULT

7. 存在两个相互独立的规模 backstop

上限默认值控制对象检查时点
maxConcurrentAgentsmin(16, max(1, cores - 2))同时活跃的 agent() 调用FIFO slot acquisition
maxTotalAgents1000单个 run 生命周期内接受的调用等待 slot 之前
maxItemsPerCall4096一次 parallel()pipeline() 的 item 数combinator 入口

Total counter 包括正在排队等待 concurrency slot 的调用,因此堵住了 runaway loop 把无界任务塞在小 pool 后面的常见漏洞。Slot waiter 是 FIFO,取消时会 reject。Consumer 可以降低单次 run 的 total cap,但不能超过 engine ceiling。WORKFLOW-CAPSWORKFLOW-SLOTS

8. parallel() 是 barrier;pipeline() 是逐 item 流动

Combinator执行形状普通 throwFatal workflow error
parallel([f, g])调用所有 thunk 并等待 Promise.all该位置变成 null整个脚本失败
pipeline(items, s1, s2)每个 item 独立执行 s1 → s2;stage 之间没有全局 barrier该 item 变成 null,跳过剩余 stage整个脚本失败

两者都经由 Promise.all 返回,所以保留输入位置。Pipeline 因此并不表示“所有 item 完成 stage 1 后再开始 stage 2”;快 item 可以进入下一 stage,而慢 sibling 仍停在上一 stage。WORKFLOW-COMBINATORS

9. Error taxonomy 将工作失败与合同失败分开

普通 child stop reason 会变成 null,让脚本能够筛选或聚合部分工作;combinator 内普通用户脚本 throw 也只会把该 item 置 null。但是参数错误、不支持的 option/schema、cap violation、provider start failure、child-result transport rejection、不可序列化输出与 cancellation 都是 typed fatal WorkflowError,不能溶解成 null。WORKFLOW-ERROR-TAXONOMYWORKFLOW-FATALITY

为何重要

“一个 reviewer 没有发现结果”与“编排合同已经坏了”不能长得一样。Fatality check 是 host realm 的 instanceof,模型脚本无法伪造一个 plain object 冒充可忽略或 fatal 的 engine error。

10. 每个跨 realm 结果都必须是无损 JSON

Materializer 接受有限数值、boolean、string、null、dense array 与 plain object;拒绝 bigint、function、symbol、嵌套 undefined、非有限数、cycle、sparse array、exotic prototype、symbol key 与 array extra property。它用 defineProperty 复制 __proto__,因此数据 key 不会改变 host copy 的 prototype。WORKFLOW-JSON-BOUNDARY

Child result 从 host 进入 worker 之前还会单独 snapshot。无法完成这一跨越的 child value 会成为 infrastructure failure,而不是被静默损失。WORKFLOW-CHILD-SNAPSHOT

11. Cancellation 是只有一个赢家的竞态,随后进入有界 quiescence

cancel(reason)
  → 关闭新工作 admission
  → 通知 worker
  → abort pending/published child 共享的唯一 signal
  → reject queued slot 与未来 hook call
  → grace timer
      └─ 若仍未 settle:补齐缺失 agent-end,settle cancelled,
         terminate worker

首个 worker result、worker death 或 cancellation grace expiry 拥有 terminal settlement。已经获胜的 result 不会被 cleanup callback 改写;竞争中的非 cancel result 若晚于已接受的 cancellation,就会报告 cancelled。Host 会抑制迟到的 phase/log narration,但为每个已发布 child start 保留或合成恰好一个 end event。WORKFLOW-CANCEL-RACEWORKFLOW-EVENT-PAIRING

12. dispose() 立即开始 child cleanup,并与 worker 共享同一个 grace

Disposal 被 memoize。它移除 input signal、按需取消、立刻开始 dispose 所有已登记 child,等待“result + child quiescence”或 grace 两者中先到者,然后无条件 terminate worker。Per-child disposal 同样 memoize,因此 worker RPC、host disposal 与 death cleanup 会汇合到一个操作。超过 grace 的慢清理会被刻意停止等待,而不是让 run 永远存活;若它之后 reject,则会被记录并隔离。WORKFLOW-DISPOSEWORKFLOW-CHILD-QUIESCENCE

HMR 生命周期

Engine 在返回 run 之前捕获一个 holder-bound subagent service。卸载 engine 会移除启动新 workflow 的能力,但不会让已接受 run 失去启动和清理 child 的能力。WORKFLOW-HMR-HOLDER

13. Workflow event 只供观察,并刻意省略返回值

Seam 发出 start、phase、log、agent-start、agent-end 和 end。Phase 只是进度标签,不会调度工作。Listener 收到借用的 identity/outcome snapshot,而非 control handle;listener failure 会被捕获并记录。最终 event 不含 script value:只有 run holder 可以 await 并消费它。WORKFLOW-EVENTS

保留所有权

Observer 可以构建进度 UI 或 metrics,但不能取消 run、修改结果,也不会意外持有 live child-control capability。

14. 通用工具增加模型合同和一份窄耐久投影

workflow 工具暴露 scriptmeta 与可选 object-shaped args。Description 就是 authoring specification;独立 system-prompt section 则要求只有在人类明确要求 workflow 或大规模多 Agent 编排时才使用。工具等待前台结果,把非 completed outcome 映射为错误,只限制渲染后的 JSON,并始终 dispose run。WORKFLOW-TOOL-CONTRACTWORKFLOW-TOOL-EXECUTE

仅对 root transport call,它会把 run start/end 与配对 member start/end 投影进 parent Session。Nested dispatch 正常执行,但不写这些记录。Append 失败会禁用该 run 后续 recording,却不影响执行,因此只会留下“无记录”或一个合法连续前缀。WORKFLOW-DURABLE-RECORD

15. 耐久记录是 audit trail,不是 workflow recovery

四种 Session event 记录 run name、member sequence/label/phase/child ID、member outcome 与 terminal reason。Package invariant 会拒绝重复 start、未配对 end、有开放 member 的 terminal event 和 terminal 之后的更新,同时允许 crash 后缺少 terminal suffix。WORKFLOW-RECORD-TYPESWORKFLOW-RECORD-INVARIANT

16. Ralph 把 script、provider 与 report schema 收回部署控制

面向模型的 Ralph schema 只有必填 objective 和可选 maxRounds。Plugin 拥有固定 JavaScript body、固定 metadata、structured round schema、provider route、handoff limit 与 terminal validation。配置的 provider 必须存在、支持 structured output,并声明自己不继承 parent context。RALPH-CONTRACTRALPH-FRESH-PROVIDER

安全与产品差异

通用 Workflow 让 parent model 编写 control flow;Ralph 只让模型提供数据,并运行部署方审核过的 control flow。两者都使用同一个 escapable VM,但 Ralph 暴露给 calling model 的攻击面与可靠性表面小得多。

17. Ralph 状态机很小,也很明确

previous = none
for round in 1..maxRounds:
  report = await fresh agent(objective, round, previous, workspace rules)
  null      → round-failed
  complete  → complete
  blocked   → blocked
  continue  → previous = report
loop exhausted → budget-limited(previous)

Report 包含 status、summary、evidence、next steps 与 blocker text。Continue 要求有 next steps 且无 blocker;complete 要求有 evidence、无 next steps、无 blocker;blocked 要求具体 blocker。所有字符串必须 normalized,每个 serialized handoff 必须落在 maxHandoffChars 内。RALPH-SCRIPT

18. Fresh-context 路由由 provider 声明门控;连续性保存在 workspace

每一轮 prompt 都说明它既不接收 parent conversation,也不接收之前的 child Session。它只携带 immutable objective、当前 round/cap、上一个 bounded report,以及“把 shared workspace 当长期事实源、先检查再验证”的指令。Live parent 仍向 subagent seam 提供 cwd 与 lineage,但所选 provider 必须承诺 inheritsParentContext:falseRALPH-ROUND-PROMPT

这形成一套刻意的 memory hierarchy:文件与 working tree 是耐久事实;一份小 report 是协调状态;未提交的对话推理随每轮消失。它可以减少长对话带来的锚定,也使 filesystem hygiene 与诚实 handoff 成为关键条件。

19. Ralph 在 trust boundary 两侧验证同一份 claim

固定脚本在携带 child report 进入下一轮之前先验证一次。Workflow 返回后,TypeScript 再验证 exact terminal object、允许的 key、round count、各 status report 语义与 size。模型选择的 round cap 必须是正 safe integer,且不超过 deployment ceiling;同一数值也成为 engine total-child ceiling。RALPH-DOUBLE-VALIDATIONRALPH-EXECUTE

两个不同的输出上限

maxHandoffChars 保护每个跨轮 report 与 canonical terminal report;maxResultChars 只截断 parent-facing rendered text,不会改写 canonical value。RALPH-RENDER

20. Completion 与 blockage 是 worker report,不是独立判断

Renderer 明确写的是“worker reported completion”或“worker reported a blocker”。Child failure 会立刻结束 run,返回 failed round 与最后一次成功 handoff;它不会重试。Cancellation 与 workflow infrastructure failure 都是错误,绝不是部分成功。只有在最后一个有效 report 仍然表示 continue 时,达到 round cap 才会得到 budget-limitedRALPH-OUTCOMES

21. 默认发行组合真实挂载两种工具,但仍要求显式使用

Base composition 以 spawn provider 挂载 worker engine、通用 workflow tool,以及 deployment ceiling 为 64 轮的 Ralph。Standard CLI preset 在 delegation group 内隔离 workflow service,并挂载同样三件套。Host-plane web bundle 会禁用这些 base row,让选定的 per-session preset 拥有它们。WORKFLOW-SHIPPED-BASEWORKFLOW-SHIPPED-PRESETWORKFLOW-WEB-ISOLATION

所以 availability 取决于所选 Agent preset,而 usage policy 仍然保守:普通一两个 child 的 delegation 应使用 subagent tool;Ralph 要求人类直接提出;通用 Workflow 用于明确的大型编排。

22. 聚焦 Codex 对比:相似的 delegation 目标,不同的编排产品

问题DeepSeek Workflow / RalphCodex 公开合同
谁编写编排?通用 Workflow:parent model 编写 JavaScript;Ralph:部署方发布固定 JavaScriptCodex 通过文档化的 subagent workflow 与 role config 做委派;其公开指南没有描述面向模型的 JavaScript workflow VM
主要 fan-out 单位一个前台 run 内的 agent() 调用专门 subagent thread,通常用于可并行、可分离工作
周期/后台工作本子系统不提供Scheduled tasks 是另一个文档化产品面
Worker 间连续性Ralph 使用显式 JSON handoff 与 shared workspace公开 multi-agent workflow 下的 parent/subagent summary 与 thread management

Codex 公开指南建议把 subagent 用于独立且以读取为主的探索、测试或 triage,并提醒重叠的 write-heavy 工作会冲突。DeepSeek 的通用 Workflow 增加可编程 parallel/pipeline 聚合,Ralph 则要求 provider 声明每轮使用 fresh context。反过来,Codex 把 scheduled tasks 文档化为独立的周期任务产品面;DeepSeek Workflow 明确只在前台运行。参见官方 Codex subagents 指南。检索于 2026-08-13。

23. 哪些值得复用,哪些不能泛化

1

复用 seam

让 live-run ownership、never-reject result、有界 dispose 和 observe-only event 独立于某一种 engine。

2

分离 policy

只在必要处暴露自由 control flow;可重复的运维策略应发布固定 workflow。

3

给 failure class 命名

部分工作失败可以是数据;合同与基础设施失败必须保持 fatal。

4

不要过度声称

Worker thread 不是 sandbox,event trace 不是 checkpoint,worker completion report 也不是 verification。

这个子系统最强的想法并不是 JavaScript 本身,而是把编排放进受所有权约束的生命周期,然后让不同 consumer 选择暴露多少控制权。

验证说明

本章在固定源码基线上追踪了 workflow seam、worker-thread host/runtime/realm、通用 workflow consumer、耐久 event invariant、Ralph consumer 与发行组合。定向 Vitest 覆盖 12 个文件,隔离临时根目录下 190 项测试全部通过。Claim 明确区分源码事实与设计评价;Codex 对比仅使用官方公开文档。

我的学习体会

内容仅自动保存到当前浏览器,不上传、不进入仓库。你可以导出 Markdown 自行归档。