第 11 章

Dynamic Workflows

大规模子代理编排——让几十个代理协同完成一个任务

脚本化编排、对抗性验证、可恢复执行

一个代理不够

当任务大到单次对话装不下的时候,怎么办?

全仓库审计

500 个文件逐一检查安全漏洞。一个代理读到第 30 个文件时,上下文已经塞满了,最早发现的问题被挤出记忆。

大规模迁移

把整个项目从 ESLint 迁移到 Oxlint——配置文件、插件、规则覆盖,每个包都需要独立处理。一个代理跑不完。

结果可信度

让 Claude 做了一次深度研究,返回了一份报告。但你不知道哪些结论站得住脚——它没有自己验证过自己的结论。

Workflow 就是为这些场景设计的:它把"谁做什么、什么时候做"写成 JavaScript 脚本,让几十到几百个子代理并行执行,中间结果存放在脚本变量里而非上下文窗口,还能让代理之间互相验证彼此的发现。

四种方式对比

Subagents、Skills、Agent Teams、Workflows 各有分工

SubagentsSkillsAgent TeamsWorkflows
本质Claude 生成的工人Claude 遵循的指令主代理监督对等会话运行时执行的脚本
谁决定下一步Claude,逐轮Claude,遵循提示主代理,逐轮脚本
中间结果上下文窗口上下文窗口共享任务列表脚本变量
可重复性工人定义指令团队定义编排本身
规模每轮几个同 Subagents少量长期会话数十到数百
中断恢复重启当前轮重启当前轮同伴继续可恢复
核心区别:Workflow 把计划移入代码。其他三种方式中,Claude 是编排者——逐轮决定下一步做什么,所有结果堆积在上下文中。Workflow 脚本持有循环、分支和中间结果,Claude 的上下文只保留最终答案。

架构概览

用户会话、Workflow Runtime 和子代理的三层关系

图:Workflow 运行时架构——从用户输入到最终结果的三层结构

脚本怎么来的

从你输入提示词到 Workflow 运行,中间发生了什么

前提条件:要看到下方对话框,Workflow 功能必须先启用。需要两个条件:环境变量 CLAUDE_CODE_WORKFLOWS=1(字符串)且账户计划支持 Dynamic Workflows(Max/Team 默认开,Pro 需在 /config 手动打开)。详情见本章末尾的排错指南。
你输入:ultracode: 审查 src/routes/ 下每个路由文件是否缺少认证检查

Claude Code:检测到 "ultracode" 关键字 → 高亮显示 → 弹出确认提示
┌─────────────────────────────────────────────────┐
│  Dynamic workflow requested for this turn       │
│  Esc to ignore                                   │
│                                                 │
│  Phases:                                        │
│    1. Scan — 发现所有路由文件                     │
│    2. Review — 每个文件一个代理并行审查            │
│    3. Verify — 对抗性验证每个发现                 │
│    4. Report — 汇总报告                          │
│                                                 │
│  ⚠️ Token usage will be high                    │
│                                                 │
│  [Yes, run it] [View raw script] [No]           │
└─────────────────────────────────────────────────┘

你选择 "View raw script" → Claude 把生成的 JS 脚本展示给你
你选择 "Yes, run it"  → Workflow Runtime 在后台接管执行

你输入:/workflows    # 随时查看进度
# 你的会话保持响应,可以继续做其他事

你不需要自己写脚本

Claude 根据你的任务描述即时生成 JavaScript 脚本。你只需要用自然语言描述要做什么,Claude 决定怎么编排。脚本写好后保存在会话目录下,你可以查看、编辑、重新运行。

为什么是 JavaScript

脚本需要表达循环、分支、并行这些确定性的控制流——而不是让模型逐轮决定下一步。JavaScript 是 Workflow Runtime 能执行的唯一语言。脚本里没有文件系统访问,所有读写操作都由子代理完成。

脚本结构

Claude 生成的脚本长什么样

// 1. 元数据块(必须是纯字面量,不能有变量或计算)
export const meta = {
  name: 'my-workflow',
  description: '做什么事',
  phases: [
    { title: 'Phase1', detail: '描述' },
    { title: 'Phase2', detail: '描述' },
  ],
};

// 2. 脚本主体
phase('Phase1');
const results = await parallel(items.map(item => () =>
  agent(`处理 ${item}`, { schema: MY_SCHEMA, phase: 'Phase1' })
));

phase('Phase2');
const final = await agent('综合所有结果', { phase: 'Phase2' });

return { final, count: results.length };

脚本能做什么

使用 agent()、parallel()、pipeline()、phase()、log()、workflow() 编排子代理。读取 args 获取外部输入,通过 return 返回结果。

脚本不能做什么

没有文件系统或 Node.js API 访问。代理负责读写文件和运行命令,脚本只负责协调。Date.now()、Math.random() 会直接抛错——为了保证可缓存和可恢复。

agent() 原语

派生一个子代理执行任务,返回结果

// 最简用法:返回代理的文本输出
const result = await agent('审查 src/auth.ts 的安全性');

// 带 schema:强制返回结构化 JSON(模型会自动重试直到匹配)
const bugs = await agent('找出这个文件里的 bug', {
  schema: {
    type: 'object',
    properties: {
      bugs: { type: 'array', items: {
        type: 'object',
        properties: {
          file: { type: 'string' },
          line: { type: 'number' },
          severity: { type: 'string', enum: ['high','medium','low'] },
        }
      }}
    }
  }
});

// 常用选项
await agent('审查代码', {
  label: 'review:auth',       // 进度视图中的显示名
  phase: 'Review',            // 分配到进度组
  isolation: 'worktree',      // 在独立 git worktree 中运行
  agentType: 'general-purpose' // 使用自定义子代理类型
});
关键:schema 是 Workflow 的核心机制之一。它迫使子代理返回符合 JSON Schema 的结构化数据,而不是自由文本。后续阶段可以直接用这些数据做判断,不需要再解析。

并行原语

parallel() 是屏障,pipeline() 是流水线——默认用 pipeline

parallel() — 屏障式

等所有任务完成才返回。适合需要看到全部结果才能做决定的场景。

const all = await parallel([
  () => agent('搜索角度A'),
  () => agent('搜索角度B'),
  () => agent('搜索角度C'),
]);
// 这里拿到全部 3 个结果
const deduped = dedup(all);

pipeline() — 流水线

每个 item 独立流经所有阶段,无屏障。Item A 在阶段 3 时,Item B 可能还在阶段 1。

const results = await pipeline(
  files,
  (file) => agent(`审查 ${file}`),
  (review) => agent(`验证 ${review.file}`)
);
// 更快,因为没有等待屏障
常见误区:不要因为"阶段在概念上是分开的"就用 parallel()。分开 ≠ 需要同步。pipeline() 的默认性能更好。只有当后续阶段真的需要跨项合并或去重时,才用 parallel()。

选择决策

什么时候用 parallel,什么时候用 pipeline

✅ 用 parallel 的理由

  • 去重/合并:需要跨全部结果去重后再处理
  • 提前退出:根据总数决定是否跳过("0 个发现,跳过验证")
  • 跨项引用:后续提示词需要引用"其他发现"

❌ 不该用 parallel 的理由

  • "我先 flatten 一下"→ 在 pipeline 的 stage 里做就行
  • "阶段在概念上是分开的"→ 分开 ≠ 需要同步
  • "代码更整洁"→ 屏障有真实的延迟代价
经验法则:默认用 pipeline()。只有当你发现自己在写"先 parallel() → 做 transform → 再 parallel()",而中间的 transform 不依赖跨项数据时,你应该用 pipeline() 并把 transform 放在 stage 里。

辅助原语

phase、log、workflow、budget、args

原语作用示例
phase(title)设置进度分组phase('Verify')
log(msg)输出进度消息log(`${bugs.length} bugs found`)
workflow(name, args?)嵌套调用另一个工作流await workflow('deep-research', { question: topic })
budget.total总 token 预算(未设则为 null)while (budget.remaining() > 50000)
budget.spent()已消耗 token见上
args外部传入的参数args.weekStart
// 按预算控制深度
const bugs = [];
while (budget.total && budget.remaining() > 50000) {
  const result = await agent('Find bugs', { schema: BUGS_SCHEMA });
  bugs.push(...result.bugs);
  log(`${bugs.length} found, ${Math.round(budget.remaining()/1000)}k remaining`);
}

// 嵌套调用内置工作流
const report = await workflow('deep-research', 'Claude Code hooks 原理');
注意:workflow() 只支持一层嵌套。子工作流内再调用 workflow() 会抛错。子工作流共享父级的并发上限、代理计数和 token 预算。

对抗性验证

让 N 个"怀疑者"代理力图反驳每个发现

// 对每个发现,派 3 个独立怀疑者
const VOTES = 3;
const REFUTATIONS_TO_KILL = 2;

const verified = await parallel(findings.map(f => () =>
  parallel(Array.from({ length: VOTES }, (_, i) => () =>
    agent(`Be SKEPTICAL. Try to REFUTE this claim:
      "${f.claim}"
      Source: ${f.source}
      Return refuted: true/false with evidence.`, {
      schema: VERDICT_SCHEMA
    })
  )).then(verdicts => {
    const valid = verdicts.filter(Boolean);
    const refuted = valid.filter(v => v.refuted).length;
    const survives = valid.length >= REFUTATIONS_TO_KILL
                     && refuted < REFUTATIONS_TO_KILL;
    return { ...f, survives, refutedVotes: refuted };
  })
));

const confirmed = verified.filter(f => f.survives);
为什么有效:单个代理倾向于相信自己产出的内容。让 3 个独立"怀疑者"从不同角度反驳——如果 2 个以上认为站不住,就丢弃。这比单纯增加代理数量更能提升结果可信度。

循环与多视角

Loop-Until-Dry + Perspective-Diverse Verify

const seen = new Set();
const confirmed = [];
let dry = 0;

while (dry < 2) {  // 连续 2 轮没新发现就停
  const found = (await parallel(FINDERS.map(f => () =>
    agent(f.prompt, { phase: 'Find', schema: BUGS })
  ))).filter(Boolean).flatMap(r => r.bugs);

  const fresh = found.filter(b => !seen.has(key(b)));
  if (!fresh.length) { dry++; continue; }
  dry = 0;
  fresh.forEach(b => seen.add(key(b)));

  // 每个发现用 3 个不同视角独立评判
  const judged = await parallel(fresh.map(b => () =>
    parallel(['correctness','security','repro'].map(lens => () =>
      agent(`Judge "${b.desc}" via the ${lens} lens`, {
        phase: 'Verify', schema: VERDICT
      })
    )).then(vs => ({
      b,
      real: vs.filter(Boolean).filter(v => v.real).length >= 2
    }))
  ));
  confirmed.push(...judged.filter(v => v.real).map(v => v.b));
}
去重的关键细节:对"已见"去重(seen),不是对"已确认"去重。否则被拒绝的发现在下一轮会重新出现,循环永远不收敛。

质量模式总结

四种模式可以根据任务自由组合

🎯 对抗性验证

每个发现 → N 个怀疑者反驳 → 低于多数存活则丢弃。

适用:审计、研究——防止"听起来对但实际错"的结论。

🔍 多视角验证

每个验证者用不同视角(正确性、安全性、性能、可复现性)评判。

适用:代码审查——比 N 个相同视角的冗余验证更强。

⚖️ 评判团

N 个独立方案 → 并行评分 → 从获胜方案综合,嫁接亚军优点。

适用:架构设计、方案选型——解空间很大时。

🔄 循环直到枯竭

持续派生发现器,直到 K 轮没有新发现。

适用:Bug 扫描、安全审计——不知道总量有多大时。

/deep-research

Claude Code 内置的深度研究工作流——也是最好的学习范例

图:/deep-research 五阶段流程——从问题分解到带引用报告

研究脚本详解

/deep-research 的核心逻辑拆解

// 阶段1:分解问题
phase("Scope");
const scope = await agent(
  "Decompose this question into 5 search angles...\n" + QUESTION,
  { schema: SCOPE_SCHEMA }  // 强制返回结构化角度列表
);

// 阶段2+3:搜索 + 获取(pipeline,无屏障延迟)
const searchResults = await pipeline(
  scope.angles,
  (angle) => agent(SEARCH_PROMPT(angle), { schema: SEARCH_SCHEMA }),
  (searchResult) => {
    // URL 去重 + 预算控制
    const novel = dedupAndBudget(searchResult.results);
    return parallel(novel.map(source => () =>
      agent(FETCH_PROMPT(source), { schema: EXTRACT_SCHEMA })
    ));
  }
);

// 阶段4:3 投对抗性验证
phase("Verify");
const voted = await parallel(rankedClaims.map(claim => () =>
  parallel([0,1,2].map(v => () =>
    agent(VERIFY_PROMPT(claim, v), { schema: VERDICT_SCHEMA })
  )).then(verdicts => ({
    ...claim,
    survives: valid >= 2 && refuted < 2
  }))
));

// 阶段5:综合报告
phase("Synthesize");
const report = await agent("Synthesize...", { schema: REPORT_SCHEMA });

触发方式

三种方式让 Claude 写出并运行一个 Workflow

方式一:提示词触发

在提示词中包含 ultracode 关键字,或用自然语言说 "use a workflow"。

ultracode: 审查 src/routes/ 下
每个端点是否缺少认证检查

Claude Code 会高亮关键字并显示确认提示,按 Esc 取消。如果快捷键无效,用鼠标点击 "No" 按钮也可以取消。

方式二:全局 ultracode

设置 /effort ultracode,Claude 自动为每个重要任务规划 Workflow。

/effort ultracode

每个请求可能变成多个连续 Workflow:理解 → 实现 → 验证。更费 token,适合关键任务。

方式三:运行命令

直接运行内置或已保存的 Workflow 命令。

# 内置
/deep-research Claude Code hooks 原理

# 保存过的自定义
/vue-newsletter {"weekStart":"2026-06-04"}

保存与复用

一次写好、反复运行的 Workflow 才是真正的价值

# 查看运行中的工作流
/workflows

# 选择一个成功的运行,按 s 保存
# 保存位置选择:
  .claude/workflows/   ← 项目级(团队共享,随仓库分发)
  ~/.claude/workflows/ ← 个人级(所有项目可用,仅自己可见)
# 保存后像 slash command 一样调用
/my-code-review

# 传入参数
/triage-issues issues 1024, 1025, and 1030

# 同名时,项目级优先于个人级
为什么保存很重要:动态 Workflow 是 Claude 每次即时生成的脚本。如果你发现某次运行的编排恰好是你想要的——保存它。下次直接按名字调用,保证每次用完全相同的编排结构,而不是让 Claude 重新规划。

实战:代码审查

多维度审查 + 对抗性验证的完整 Workflow

export const meta = {
  name: 'review-changes',
  description: 'Review changed files across dimensions, verify each finding',
  phases: [
    { title: 'Review', detail: '三个维度并行审查' },
    { title: 'Verify', detail: '对抗性验证每个发现' },
  ],
};

const DIMENSIONS = [
  { key: 'bugs',     prompt: 'Review for correctness bugs...' },
  { key: 'perf',     prompt: 'Review for performance issues...' },
  { key: 'security', prompt: 'Review for security vulnerabilities...' },
];

const results = await pipeline(
  DIMENSIONS,
  (d) => agent(d.prompt, {
    label: `review:${d.key}`,
    phase: 'Review',
    schema: FINDINGS_SCHEMA
  }),
  (review) => parallel(review.findings.map(f => () =>
    agent(`Adversarially verify: ${f.title}`, {
      label: `verify:${f.file}`,
      phase: 'Verify',
      schema: VERDICT_SCHEMA
    }).then(v => ({ ...f, verdict: v }))
  ))
);

const confirmed = results.flat()
  .filter(Boolean)
  .filter(f => f.verdict?.isReal);
return { confirmed };

运行管理

查看进度、暂停恢复、成本控制

# 查看所有运行
/workflows

# 在进度视图中可用的快捷键:
↑/↓        选择阶段或代理
Enter/→    展开查看详细内容
p          暂停或恢复运行
x          停止选中的代理或整个工作流
s          保存脚本为命令
Esc        返回上一层
注意:快捷键可能因版本而异。如果 p 暂停无效,用 /workflows 进入视图后点击暂停按钮即可。

暂停与恢复

停止后可以恢复。已完成的 agent() 调用从 Journal 缓存直接返回,只重新执行未完成或被修改的部分。

注意:恢复只在同一会话内有效。退出 Claude Code 后再启动会重新开始。

成本控制

一个 Workflow 可能消耗大量 token。建议:

  • 先在小范围试跑(一个目录而非整个仓库)
  • /workflows 视图显示每个代理的 token 用量
  • 可随时停止,已完成的工作不会丢失
  • 用 /model 确认当前模型

运行约束

Workflow Runtime 的安全边界

约束原因
无运行中用户输入只有代理权限提示可以暂停运行。需要人工审批的阶段,拆成独立的 Workflow
无直接文件系统或 Shell 访问代理负责读写和执行命令,脚本只负责协调代理
最多 16 个并发代理限制本地资源使用(CPU 核心数较少时自动降低)
每次运行最多 1000 个代理防止失控循环
Date.now() 等抛错保证 Journal 缓存的可恢复性——非确定性函数会使缓存失效
为什么禁止 Date.now():Workflow 用 Journal 记录每个 agent() 调用的输入和输出。暂停后恢复时,相同输入直接返回缓存结果。如果脚本里用了 Date.now(),每次恢复都会得到不同值,缓存全部失效。如果需要时间戳,通过 args 从外部传入。

关闭 Workflow

三种方式禁用 Dynamic Workflows

方式持久性说明
/config 切换跨会话在设置中关闭 "Dynamic workflows" 开关
settings.json跨会话设置 "disableWorkflows": true
环境变量启动时读取CLAUDE_CODE_DISABLE_WORKFLOWS=1

关闭后:ultracode 关键字不再触发、/deep-research 不可用、/effort ultracode 从菜单中移除。

排错指南

ultracode 不触发

症状:提示词包含 ultracode 但 Claude 没有生成 Workflow。

修复:Workflow 触发需要同时满足两个条件:

  1. 环境变量 CLAUDE_CODE_WORKFLOWS=1(字符串,不是数字)
  2. 账户计划支持 Dynamic Workflows(Max/Team/Enterprise 默认开启,Pro 需在 /config 手动启用)

用 echo $CLAUDE_CODE_WORKFLOWS 确认变量已设置。如果用 /config 找不到 "Dynamic workflows" 开关,说明当前账户未支持该功能。

运行卡住不动

症状:Workflow 启动后进度不动。

修复:可能是权限提示在等待你批准。用 /workflows 查看状态。把需要的命令提前加入 allowedTools。

schema 匹配失败

症状:代理反复重试,消耗大量 token。

修复:简化 schema 定义,减少 required 字段,或去掉嵌套层级。过于复杂的 schema 会导致模型反复尝试。

token 消耗过快

症状:一次运行消耗远超预期。

修复:先在小范围试跑。检查 /model,考虑对简单阶段用更小的模型。停止运行不会丢失已完成的工作。

Date.now() 报错

症状:脚本执行时报 "Date.now is not available"。

修复:这是设计限制。通过 args 传入时间戳。用 index 值(而非随机数)来区分代理。

恢复后结果不同

症状:暂停后恢复,结果跟之前不一样。

修复:如果修改了脚本,被修改的 agent() 调用会重新执行。只有参数不变的调用才使用缓存。

动手练习

让 Claude 为你的项目生成一个 Workflow

任务:用 Workflow 对你当前项目做一次多维度代码审查。
  1. 在 Claude Code 中输入带 ultracode 关键字的审查请求
  2. 选择 "View raw script" 查看生成的脚本
  3. 运行 Workflow,用 /workflows 监控进度
  4. 运行完成后按 s 保存为可复用的命令
提示词参考: ultracode: 审查 src/ 目录下所有文件的代码质量,从正确性、安全性、性能三个维度并行审查,每个发现经过 3 个独立验证者确认

参考答案

一个典型的审查 Workflow 运行过程

# 步骤 1:触发 Workflow
> ultracode: 审查 src/ 下所有文件,从正确性、安全性、性能
> 三个维度并行审查,每个发现经过独立验证

# 步骤 2:Claude 生成脚本,确认运行
Dynamic workflow requested for this turn · Alt+W to ignore

# 步骤 3:查看运行进度
/workflows
# 输出示例:
┌─ Review ────────── 3/3 agents ──── 45,230 tokens ──
│  review:bugs     ✅ 12 findings
│  review:perf     ✅ 8 findings
│  review:security ✅ 5 findings
├─ Verify ────────── 25/25 agents ─── 183,400 tokens ─
│  verify:auth.ts:42   2-1 ✓ survives
│  verify:api.ts:108   1-2 ✗ killed
│  ...
└─ Complete ─ 11 confirmed findings

# 步骤 4:保存成功运行
Press s → save as "code-review" → .claude/workflows/

# 下次直接调用:
/code-review
关键步骤:触发 → 确认脚本 → 运行并监控 → 验证结果质量 → 保存可复用命令。

课程总回顾

十一章走完,从入门到大规模编排

到这里,你已经能

  • 理解 Workflow 与 Subagents/Teams 的区别
  • 读懂和编写 Workflow 脚本
  • 使用 agent/parallel/pipeline 编排子代理
  • 应用对抗性验证等质量模式提升结果可信度
  • 保存和复用 Workflow 命令
  • 管理运行、控制成本、排查问题

完整课程路线

  • 01 Slash Commands — 自制命令
  • 02 Memory — 长期上下文
  • 03 Skills — 可复用能力包
  • 04 Subagents — 角色分工
  • 05 MCP — 外部工具接入
  • 06 Hooks — 事件驱动自动化
  • 07 Plugins — 方案打包分发
  • 08 Checkpoints — 快照回退
  • 09 Advanced — 高级特性
  • 10 CLI — 工程化入口
  • 11 Workflow — 大规模子代理编排
核心结论:Workflow 是 Claude Code 的编排引擎。它把"谁做什么、什么时候做"从模型的大脑移到了代码中。你写控制流,代理做思考,中间结果不占上下文。配合对抗性验证等质量模式,Workflow 产出的结果比单次对话更可信——这就是从"AI 助手"到"AI 工程团队"的关键一步。

延伸阅读

官方文档

社区资源