Claude Code Dynamic Workflow + Ultracode:编排外置与确定性调度
一句话定位:Dynamic Workflow 是 Claude Code 的”编排外置”层——把原本消耗 token 的调度逻辑移到确定性 JavaScript 脚本中,让模型只负责执行具体任务,实现 1000 个子 agent 的并行编排。
调研时间:2026-06-22
依据版本:泄露源码包(2026-03-31 stub)+ 官方文档(v2.1.154+)+ 社区实战
核心文件:src/tasks.ts、src/Task.ts、src/tasks/LocalWorkflowTask.js(DCE 剔除)
证据等级汇总
| 结论 | 来源 |
|---|---|
| TaskType 含 local_workflow(ID 前缀 w/) | [源码] Task.ts |
| WORKFLOW_SCRIPTS flag 守护 LocalWorkflowTask 加载 | [源码] tasks.ts |
| 实现文件被 DCE 剔除,泄露包中仅有 stub | [源码] tasks.ts |
| 5 个原语 API(agent/parallel/pipeline/phase/log) | [官方] code.claude.com/docs/en/workflows |
| 受限 JS 沙箱(无 fs/net/child_process) | [官方] 文档 + [社区] 实战验证 |
| 并发上限 16 / 总 agent 上限 1000 | [官方] 文档 |
| Ultracode = effort xhigh + 自动 workflow 决策 | [社区] 多源交叉验证 |
| Resume 机制(确定性重放) | [官方] 文档 |
| v2.1.154(2026-05-28)正式发布 | [官方] 博客 |
| Bun Zig→Rust 移植案例(75 万行,11 天) | [社区] 知乎 |
日常类比
传统模式像一个人独自装修房子——自己量尺寸、买材料、刷墙、装灯,每件事串行做,而且每做一步都要在脑子里记住”接下来该干什么”(消耗 context)。
Dynamic Workflow 像请了一个工头——工头手里有一张施工计划表(JS 脚本),按计划派电工、水管工、油漆工同时开工。工头自己不搬砖(不消耗 model token),只负责看计划表决定”谁先谁后、谁能并行”。工人(子 agent)各自独立干活,完成后向工头汇报。
类比边界:真实系统中”施工计划表”是 Claude 自己写的(或用户预定义的),而且工头可以根据中间结果动态调整后续步骤——这比纸质计划表灵活得多。
架构全景
graph TB
subgraph "用户层"
User[用户 prompt<br>"ultracode: 审计这个项目"]
PreDef[".claude/workflows/*.js<br>预定义脚本"]
end
subgraph "决策层"
PUI[processUserInput] --> Decision{适合 Workflow?}
Decision -->|是| Gen[Claude 生成 workflow.js]
Decision -->|否| Normal[普通 queryLoop]
PreDef -->|"斜杠命令触发"| Exec
end
subgraph "编排层(确定性 JS)"
Gen --> Exec[Workflow 沙箱执行]
Exec --> Phase["phase('分析')"]
Exec --> Agent1["agent('分析前端')"]
Exec --> Agent2["agent('分析后端')"]
Exec --> Par["parallel([...])"]
Exec --> Pipe["pipeline(items, stages)"]
end
subgraph "执行层(子 agent)"
Agent1 --> QL1[queryLoop #1<br>独立 context]
Agent2 --> QL2[queryLoop #2<br>独立 context]
Par --> QL3[queryLoop #3..#N]
QL1 --> Tools1[Read/Write/Bash/...]
QL2 --> Tools2[Read/Write/Bash/...]
end
subgraph "汇总层"
QL1 -->|结果| Exec
QL2 -->|结果| Exec
QL3 -->|结果| Exec
Exec -->|最终报告| User
end
核心概念:编排外置
传统模式的瓶颈
在普通 queryLoop 中,编排逻辑(”先做 A,再做 B,C 和 D 可以并行”)存在于模型的 context window 中:
[System Prompt] + [对话历史] + [编排推理] + [执行结果]
↑
消耗 token,且随任务复杂度线性增长
当任务需要协调 10+ 个步骤时,编排推理本身就占据大量 context,导致三个结构性问题:
- 提前收工:模型”觉得差不多了”就停止,实际还有未完成的子任务
- 目标漂移:长 context 中早期目标被后期信息冲淡
- 自我评估偏向:模型倾向于认为自己的输出是正确的
Workflow 的解法
编排逻辑 → 确定性 JS 脚本(零 token 成本)
执行逻辑 → 子 agent 的独立 queryLoop(各自有完整 context)
关键洞察:编排是确定性的(”先 A 后 B”不需要 AI 推理),执行才需要 AI。把确定性部分移出模型,让模型专注于它擅长的事。
五个原语 API
| 原语 | 签名 | 作用 |
|---|---|---|
agent |
agent(prompt, opts?) → string |
派发一个子 agent,返回其文本输出 |
parallel |
parallel(thunks[]) → string[] |
并行执行多个 thunk,等待全部完成 |
pipeline |
pipeline(items, ...stages) → string[] |
数据流式经过多个 stage |
phase |
phase(name) |
声明当前阶段(UI 进度展示) |
log |
log(message) |
输出日志到用户界面 |
agent() 详解
const result = await agent('分析 src/auth/ 目录的安全漏洞', {
// 可选参数
model: 'sonnet', // 子 agent 使用的模型(默认继承)
permissionMode: 'acceptEdits', // 权限模式
maxTurns: 50, // 最大轮次
tools: ['Read', 'Grep', 'Glob'], // 限制可用工具
})
底层实现:每个 agent() 调用创建一个 TaskType='local_agent' 的任务,注册到 taskStates Map 中,启动独立的 queryLoop 循环。子 agent 拥有完整的工具集(除非通过 tools 参数限制)。
parallel() 详解
const [frontend, backend, infra] = await parallel([
() => agent('审计前端代码'),
() => agent('审计后端代码'),
() => agent('审计基础设施配置'),
])
// 三个子 agent 同时启动,全部完成后继续
并发控制:实际并发数 = min(16, CPU核数 - 2)。超出的任务排队等待。
pipeline() 详解
const results = await pipeline(
['file1.ts', 'file2.ts', 'file3.ts'], // items
(file) => agent(`分析 ${file} 的类型安全问题`), // stage 1
(analysis) => agent(`为以下问题生成修复方案: ${analysis}`), // stage 2
(fix) => agent(`验证修复方案的正确性: ${fix}`), // stage 3
)
语义:每个 item 依次经过所有 stage,但不同 item 之间可以并行(流水线并行)。
Ultracode 模式
不是独立功能,而是设置组合
| 组件 | 值 |
|---|---|
| effort 级别 | xhigh(非 max) |
| 自动 workflow | 模型自主判断是否需要编排 |
| 触发方式 | /effort ultracode 或 prompt 含 “ultracode” |
与其他模式的关系
graph LR
subgraph "执行模式谱系"
Plan["/plan<br>只读规划"] --> Auto["/auto<br>自动执行"]
Auto --> Ultra["Ultracode<br>多 agent 编排"]
end
subgraph "编排方式"
Single["单 agent<br>queryLoop"] --> Coord["Coordinator<br>主从模式"]
Coord --> Swarm["Swarm<br>对等协作"]
Swarm --> Workflow["Dynamic Workflow<br>确定性编排"]
end
Ultra -.->|"自动选择"| Workflow
Ultra -.->|"简单任务退化为"| Single
Ultracode 的决策流程
用户 prompt 进入
→ processUserInput 检测 "ultracode" 关键词
→ 模型评估任务复杂度
→ 复杂(多文件/多维度/大规模): 生成 workflow.js 并执行
→ 简单: 退化为普通 queryLoop(但 effort 仍为 xhigh)
受限沙箱
Workflow 脚本运行在严格受限的 JavaScript 沙箱中 [官方]:
可用
- 5 个原语 API
- 基本 JS 语法(变量、循环、条件、字符串操作)
JSON.parse/JSON.stringify- 模板字符串
- async/await
不可用
require()/import(无模块系统)fs/path(无文件系统访问)child_process(无命令执行)fetch/http(无网络)Date.now()/Math.random()/new Date()(破坏确定性)setTimeout/setInterval(无定时器)process/global(无全局对象)
为什么禁止 Date/Random:Resume 机制依赖确定性重放。如果脚本中有随机数或时间依赖,中断后重放会走不同路径,无法正确恢复。
Resume 机制
首次执行:
agent('A') → 结果缓存 → agent('B') → 中断!
Resume:
agent('A') → 命中缓存,直接返回 → agent('B') → 重新执行
实现原理:
- 相同脚本 + 相同输入参数 = 可恢复
- 每个
agent()调用的结果被持久化 - Resume 时重放脚本,遇到已完成的调用直接返回缓存
- 只对未完成的部分启动新的子 agent
确定性要求:脚本必须是纯函数——给定相同输入,执行路径完全相同。这就是为什么禁止 Date.now() 和 Math.random()。
运行限制
| 限制项 | 值 | 原因 |
|---|---|---|
| 最大并发 agent | min(16, CPU-2) | 避免 CPU 过载 |
| 单 workflow 总 agent | 1000 | 防止失控 |
| pipeline 单次 items | 4096 | 内存限制 |
| 脚本最大长度 | 524288 字符 | 沙箱解析限制 |
| 无默认 token 上限 | — | 用户可通过 +500k 指令设置 |
源码中的痕迹
Task.ts 中的类型定义 [源码]
type TaskType =
| 'local_bash' // ID: b/
| 'local_agent' // ID: a/
| 'remote_agent' // ID: r/
| 'in_process_teammate' // ID: t/
| 'local_workflow' // ID: w/ ← Workflow 任务
| 'monitor_mcp' // ID: m/
| 'dream' // ID: d/
tasks.ts 中的条件加载 [源码]
const LocalWorkflowTask = feature('WORKFLOW_SCRIPTS')
? require('./tasks/LocalWorkflowTask.js')
: null
关键发现:泄露源码(2026-03-31)中 WORKFLOW_SCRIPTS flag 为 false,LocalWorkflowTask.js 被 DCE 完全剔除。正式发布是 2026-05-28(v2.1.154),晚于泄露时间点近两个月。
Workflow 脚本 meta 结构 [官方]
export const meta = {
name: 'audit-security',
description: '全项目安全审计',
phases: [
{ title: '扫描', detail: '识别潜在漏洞' },
{ title: '分析', detail: '评估风险等级' },
{ title: '修复', detail: '生成修复方案' },
],
}
七个质量模式 [社区]
社区实践中总结的高效 Workflow 编写模式:
| 模式 | 核心思想 | 示例 |
|---|---|---|
| Adversarial Verify | 用独立 agent 验证另一个 agent 的输出 | agent('验证: ' + result) |
| Divide & Conquer | 大任务拆成独立子任务并行 | parallel([...files.map(f => () => agent(f))]) |
| Pipeline | 数据流式经过多个处理阶段 | pipeline(items, analyze, fix, verify) |
| Loop-Until-Budget | 设置 token 预算,循环直到用完 | while(budget > 0) { agent(...) } |
| Consensus | 多个 agent 独立解决同一问题,取共识 | parallel([solver1, solver2, solver3]) → 投票 |
| Escalation | 简单模型先做,失败时升级到强模型 | agent(p, {model:'haiku'}) → 失败 → agent(p, {model:'opus'}) |
| Checkpoint | 每阶段保存中间结果,支持断点续传 | 配合 Resume 机制 |
与其他编排方式对比
| 维度 | queryLoop 单 agent | Coordinator | Swarm | Dynamic Workflow |
|---|---|---|---|---|
| 编排位置 | model context 内 | 主 agent context | 对等协商 | 外置 JS 脚本 |
| 编排成本 | 消耗 token | 消耗主 agent token | 消耗所有 agent token | 零(确定性代码) |
| 并行能力 | 无 | 有限(主 agent 调度) | 有限(协商开销) | 最多 1000 agent |
| 确定性 | 否(模型决策) | 否 | 否 | 是(JS 脚本) |
| 可恢复 | 否 | 否 | 否 | 是(Resume) |
| 适用规模 | 小任务 | 中等任务 | 协作任务 | 大规模任务 |
| 用户控制 | 低 | 中 | 低 | 高(可预定义脚本) |
与开源框架对比
| 维度 | Claude Code Workflow | LangGraph | CrewAI |
|---|---|---|---|
| 编排语言 | 受限 JS(5 原语) | Python(完整) | Python(完整) |
| 编排生成 | AI 自动生成 | 用户手写 | 用户手写 |
| 沙箱隔离 | 严格(无 fs/net) | 无 | 无 |
| 确定性保证 | 是(禁止副作用) | 否 | 否 |
| Resume 支持 | 内置 | 需自行实现 checkpoint | 无 |
| 并发模型 | 进程级隔离 | 线程/协程 | 线程 |
| 最大并发 | 1000 agent | 无硬限制 | 无硬限制 |
| 学习成本 | 低(5 个 API) | 高(状态图 DSL) | 中(角色定义) |
核心差异:Claude Code 的 Workflow 是AI 生成的确定性编排——模型理解任务后自动写出调度脚本,而 LangGraph/CrewAI 需要开发者手动定义编排逻辑。这是”AI 写代码调度 AI”的范式。
ScheduleWakeup 与 /loop 的关系
Dynamic Workflow 还有一个时间维度的编排——/loop 命令配合 ScheduleWakeup 工具:
/loop "监控 CI,失败时自动修复"
→ Claude 执行一轮
→ ScheduleWakeup(delaySeconds=120, prompt="<<autonomous-loop-dynamic>>")
→ 120 秒后自动唤醒
→ 检查 CI 状态
→ 失败: 启动修复 workflow
→ 成功: ScheduleWakeup(delaySeconds=1800, reason="CI 绿了,降低频率")
间隔选择与 Prompt Cache 的关系:
- 60-270s:cache 热(5min TTL 内),唤醒成本低
- 300s:禁区!cache 刚过期,付全价
- 1200-1800s:cache 已过期但长间隔摊薄成本
实战案例
Bun 的 Zig→Rust 移植 [社区]
- 规模:75 万行 Rust 代码
- 耗时:11 天
- 测试通过率:99.8%
- 方法:Dynamic Workflow 拆分为模块级子任务,每个模块独立 agent 处理
大规模代码审计
export const meta = {
name: 'security-audit',
description: '全项目安全审计',
phases: [
{ title: '扫描' },
{ title: '分析' },
{ title: '报告' },
],
}
phase('扫描')
const files = await agent('列出所有处理用户输入的文件路径,每行一个')
const fileList = files.trim().split('\n')
phase('分析')
const findings = await parallel(
fileList.map(f => () => agent(`分析 ${f} 的安全漏洞,重点关注注入和越权`))
)
phase('报告')
const report = await agent(`汇总以下发现,按严重程度排序:\n${findings.join('\n---\n')}`)
log(report)
局限性
- 源码实现不可见:
LocalWorkflowTask.js被 DCE 剔除,无法确认底层调度算法细节 - 成本不可控:社区报告”62 个子 agent,18 分钟打满 5 小时用量上限”
- 时间差:泄露源码(2026-03-31)早于正式发布(2026-05-28),中间有大量实现变更
- 沙箱边界:无法确认沙箱的具体实现方式(V8 isolate? Worker thread? 进程隔离?)
- 错误处理:官方文档未详细说明子 agent 失败时的重试/降级策略
下一步
- Claude Code 全景 — Workflow 在 Agent 生态中的独特定位
- 对比矩阵 — 编排策略的框架间深度对比
- 多 Agent 架构 — Workflow 与 Coordinator/Swarm 的互补关系