基于真实实践总结,面向初学者的完整教学指南
目录
- 1. 什么是 Dynamic Workflow
- 2. 核心概念速览
- 3. 目录结构
- 4. 第一个 Workflow:Hello World
- 5. 内置 API 详解
- 6. 实战:数学题 Pipeline
- 7. 在 Workflow 内定义 Agent
- 8. ⚠️ 限制与踩坑
- 9. 架构对比
- 10. 总结
1. 什么是 Dynamic Workflow
Dynamic Workflow 是 Claude Code v2.1.154+ 引入的多 agent 编排引擎。你写一段 JavaScript 脚本,它按你的逻辑自动 spawn 多个子 agent,并行或串行执行任务,最终汇总结果。
一句话:用代码控制多个 AI agent 协作干活。
适用场景
| 场景 | 示例 |
|---|---|
| 批量处理 | 8道数学题并行解 |
| 流水线 | 解题 → 验证 → 打分 |
| 大规模审查 | 100个文件并行 review |
| 多视角分析 | 同一问题从安全/性能/可读性角度分别评估 |
2. 核心概念速览
| 概念 | 说明 |
|---|---|
| meta | 脚本头部声明,定义名称、描述、阶段 |
| agent() | spawn 一个子 agent,返回结果 |
| pipeline() | 串行流水线:每项数据依次通过多个阶段 |
| parallel() | 并行执行:多个任务同时跑 |
| phase() | 声明当前阶段(用于 UI 进度展示) |
| log() | 输出日志到用户界面 |
| args | 外部传入的参数 |
| budget | 当前 turn 的 token 预算控制 |
执行模型
graph TD
A[用户触发 Workflow] --> B[解析 meta]
B --> C{编排逻辑}
C -->|串行| D[pipeline: item → stage1 → stage2 → stage3]
C -->|并行| E[parallel: task1 | task2 | task3 同时跑]
C -->|混合| F[parallel + pipeline 嵌套]
D --> G[汇总返回结果]
E --> G
F --> G3. 目录结构
.claude/
├── workflows/ # Workflow 脚本存放目录
│ └── math-problems.js # 一个 workflow = 一个 .js 文件
├── agents/ # 自定义 agent 定义(⚠️ workflow 无法引用)
│ ├── math-solver.md # 仅用于主会话自动路由
│ └── math-verifier.md
└── settings.json # 项目配置
Workflow 文件结构
每个 .js 文件固定格式:
export const meta = {
name: 'my-workflow', // 必填:唯一标识
description: '一句话描述', // 必填:说明用途
phases: [ // 可选:定义阶段(UI 展示用)
{ title: 'Analyze' },
{ title: 'Execute' },
],
}
// ... 你的编排逻辑
// 用 agent() / pipeline() / parallel() 编排
// 最后 return 结果
4. 第一个 Workflow:Hello World
最简版本
export const meta = {
name: 'hello',
description: '最简单的 workflow',
phases: [{ title: 'Greet' }],
}
const result = await agent('说一句 hello world', { phase: 'Greet' })
return result
带参数版本
export const meta = {
name: 'hello-name',
description: '打招呼',
}
// args 由外部传入
const name = (args && args.name) || 'World'
const result = await agent(`对 ${name} 说你好,介绍自己`)
return result
⚠️ 重要: 通过 name 引用 workflow 时 args 可能不传递!建议总是提供默认值,或使用内联 script 调用。
5. 内置 API 详解
5.1 agent() — 生成子 agent
// 基础用法
const result = await agent('你的提示词')
// 完整参数
const result = await agent('你的提示词', {
label: '显示名称', // UI 中显示的标签
phase: 'Solve', // 归属阶段
schema: { // 强制结构化输出(JSON Schema)
type: 'object',
properties: {
answer: { type: 'string' },
confidence: { type: 'number' }
}
},
model: 'sonnet', // 可选模型覆盖
agentType: 'Explore', // ⚠️ 仅支持内置类型,不支持自定义
})
5.2 pipeline() — 串行流水线
数据依次通过每个阶段,前一个阶段的输出是下一个阶段的输入:
graph LR
A[数据项] -->|stage1| B[中间结果1]
B -->|stage2| C[中间结果2]
C -->|stage3| D[最终结果]
style A fill:#e1f5fe
style D fill:#c8e6c9const results = await pipeline(
['题1', '题2', '题3'], // 数据数组
(item) => agent(`处理: ${item}`), // stage 1
(prev, item) => agent(`审核: ${prev}`), // stage 2
)
// results[0] = stage2(题1) 的结果
// results[1] = stage2(题2) 的结果
// 每个数据项独立走完整个 pipeline,不同项之间并行
关键特性: pipeline 内部每项数据独立流转,item A 在 stage 3 时 item B 可能还在 stage 1。总耗时 = 最慢的单项链路,而非所有阶段之和。
5.3 parallel() — 并行屏障
所有任务同时开始,全部完成后才返回:
graph TD
A[parallel 启动] --> B[Task 1]
A --> C[Task 2]
A --> D[Task 3]
B --> E[汇总结果]
C --> E
D --> E
style A fill:#fff9c4
style E fill:#c8e6c9const results = await parallel([
() => agent('分析前端代码'),
() => agent('分析后端代码'),
() => agent('分析数据库'),
])
// results = [前端分析, 后端分析, 数据库分析]
// 任一失败返回 null,用 .filter(Boolean) 过滤
5.4 phase() 和 log()
phase('Review') // 切换到 Review 阶段(UI 进度条分组)
log('已处理 3/10 个文件') // 输出进度信息
6. 实战:数学题 Pipeline
设计原则:题目直接写在 prompt 里。 每组题目作为一个整体,通过 prompt 模板传入 agent,初学者一眼就能看清分组结构和数据流转。
架构图
graph TD
subgraph "题目分组(直接写在 prompt)"
G1["第一组(基础计算)
12×15, √144+3², 2^10, ..."]
G2["第二组(进阶题目)
100!末尾0, sin30°×cos60°, ..."]
end
subgraph "Pipeline 流水线(每组整体处理)"
S1["🧮 Solve
整组题目写在 prompt"]
V1["✅ Verify
题目+解答写在 prompt"]
SC1["📊 Score
题目+验证结果写在 prompt"]
S1 --> V1 --> SC1
end
G1 -->|"prompt 含组1题目"| S1
G2 -->|"prompt 含组2题目"| S1
SC1 --> R["按组汇总结果"]
style G1 fill:#e3f2fd
style G2 fill:#fce4ec
style S1 fill:#bbdefb
style V1 fill:#c8e6c9
style SC1 fill:#fff9c4
style R fill:#f8bbd0数据流详解
每组的数据流(以第一组为例):
┌─────────────────────────────────────────────────┐
│ Stage 1: Solver │
│ prompt = SOLVER_PROMPT + 组1全部题目 │
│ output = 5道题的详细解答 │
└──────────────────────┬──────────────────────────┘
│ solution (上一阶段输出)
┌──────────────────────▼──────────────────────────┐
│ Stage 2: Verifier │
│ prompt = VERIFIER_PROMPT + 组1题目 + solution │
│ output = 逐题验证结果(✅/❌) │
└──────────────────────┬──────────────────────────┘
│ verification (上一阶段输出)
┌──────────────────────▼──────────────────────────┐
│ Stage 3: Scorer │
│ prompt = SCORER_PROMPT + 组1题目 + verification │
│ output = 每道题的分数和评语 │
└─────────────────────────────────────────────────┘
实际 Prompt 示例(模板替换后)
下面展示 .replace('{problems_block}', ...) 执行后,真正发给 agent 的完整 prompt。
Stage 1 — Solver 收到的 prompt(第一组)
你是一位数学解题专家。请逐一解答以下数学题。
1. 12 × 15 = ?
2. √144 + 3² = ?
3. 2^10 = ?
4. 1 + 2 + 3 + ... + 100 = ?
5. 1/2 + 1/3 + 1/6 = ?
输出格式(每道题):
- 题目:{原题}
- 解题过程:{逐步推导}
- 答案:{最终结果}
Stage 1 — Solver 收到的 prompt(第二组)
你是一位数学解题专家。请逐一解答以下数学题。
6. 100! 末尾有多少个 0?
7. sin30° × cos60° = ?
8. log₂1024 = ?
9. 一个圆的周长是 31.4cm,求面积(π≈3.14)
10. 解方程:2x + 5 = 17
输出格式(每道题):
- 题目:{原题}
- 解题过程:{逐步推导}
- 答案:{最终结果}
Stage 2 — Verifier 收到的 prompt(第一组,含解答)
你是一位数学验证专家。独立重新解题,对比下面的解答。
1. 12 × 15 = ?
2. √144 + 3² = ?
3. 2^10 = ?
4. 1 + 2 + 3 + ... + 100 = ?
5. 1/2 + 1/3 + 1/6 = ?
=== 待验证的解答 ===
[Solver 的完整输出会插入这里]
=== 要求 ===
- 独立计算,不可信任给出的解答
- 逐步对比,标注对错
- 有错误则精确指出位置
输出格式:
- 判定:✅ 正确 / ❌ 错误
- 验证过程:{独立推导}
- 错误原因:{如有}
Stage 3 — Scorer 收到的 prompt(第一组,含验证结果)
你是数学评分专家。综合解题和验证结果打分。
1. 12 × 15 = ?
2. √144 + 3² = ?
3. 2^10 = ?
4. 1 + 2 + 3 + ... + 100 = ?
5. 1/2 + 1/3 + 1/6 = ?
=== 验证结果 ===
[Verifier 的完整输出会插入这里]
评分:10=完美 | 7-9=正确可改进 | 4-6=部分正确 | 0-3=错误
输出格式:
- 分数:{0-10}
- 评语:{一句话}
关键点:
{problems_block}在每个阶段都被同一组题目替换,{solution}/{verification}被上一阶段的输出替换。每个 agent 都能看到完整上下文,不需要猜测题目是什么。
完整代码
export const meta = {
name: 'math-problems',
description: '数学题 workflow — 题目按组写入 prompt,3阶段 pipeline(解题→验证→评分)',
phases: [
{ title: 'Solve', detail: '每组并行解题' },
{ title: 'Verify', detail: '独立验证每组解答' },
{ title: 'Score', detail: '综合评分' },
],
}
// ─── Prompt 模板(顶部定义,一目了然)──────────────
// {problems_block} → 该组所有题目
// {solution} → Solver 的输出
// {verification} → Verifier 的输出
const SOLVER_PROMPT = `你是一位数学解题专家。请逐一解答以下数学题。
{problems_block}
输出格式(每道题):
- 题目:{原题}
- 解题过程:{逐步推导}
- 答案:{最终结果}`
const VERIFIER_PROMPT = `你是一位数学验证专家。独立重新解题,对比下面的解答。
{problems_block}
=== 待验证的解答 ===
{solution}
=== 要求 ===
- 独立计算,不可信任给出的解答
- 逐步对比,标注对错
- 有错误则精确指出位置
输出格式:
- 判定:✅ 正确 / ❌ 错误
- 验证过程:{独立推导}
- 错误原因:{如有}`
const SCORER_PROMPT = `你是数学评分专家。综合解题和验证结果打分。
{problems_block}
=== 验证结果 ===
{verification}
评分:10=完美 | 7-9=正确可改进 | 4-6=部分正确 | 0-3=错误
输出格式:
- 分数:{0-10}
- 评语:{一句话}`
// ─── 题目分组(直接写在这里,初学者直接改)──────────
const DEFAULT_GROUPS = [
{
name: '第一组(基础计算)',
problems: [
'1. 12 × 15 = ?',
'2. √144 + 3² = ?',
'3. 2^10 = ?',
'4. 1 + 2 + 3 + ... + 100 = ?',
'5. 1/2 + 1/3 + 1/6 = ?',
],
},
{
name: '第二组(进阶题目)',
problems: [
'6. 100! 末尾有多少个 0?',
'7. sin30° × cos60° = ?',
'8. log₂1024 = ?',
'9. 一个圆的周长是 31.4cm,求面积(π≈3.14)',
'10. 解方程:2x + 5 = 17',
],
},
]
// ─── 主流程 ─────────────────────────────────────────
const groups = (args && args.groups && args.groups.length)
? args.groups
: DEFAULT_GROUPS
log(`共 ${groups.reduce((n, g) => n + g.problems.length, 0)} 道题,${groups.length} 组`)
groups.forEach((g) => log(` ${g.name}: ${g.problems.join(', ')}`))
// 每组并行跑 pipeline,题目直接写在 prompt 里
const results = await parallel(
groups.map((group) => () => {
const problemsBlock = group.problems.join('\n')
return pipeline(
[group], // 整组作为一个单元
// Stage 1: 解题
(grp) => agent(
SOLVER_PROMPT.replace('{problems_block}', grp.problems.join('\n')),
{ label: `${grp.name}-解题`, phase: 'Solve' }
),
// Stage 2: 验证
(solution, grp) => agent(
VERIFIER_PROMPT
.replace('{problems_block}', grp.problems.join('\n'))
.replace('{solution}', solution),
{ label: `${grp.name}-验证`, phase: 'Verify' }
),
// Stage 3: 评分
(verification, grp) => agent(
SCORER_PROMPT
.replace('{problems_block}', grp.problems.join('\n'))
.replace('{verification}', verification),
{ label: `${grp.name}-评分`, phase: 'Score' }
),
)
})
)
// 按组返回
const output = {}
groups.forEach((g, i) => output[g.name] = results[i][0])
return output
调用方式
在 Claude Code 里直接说"跑 workflow math-problems"就行。 Claude 会调用
Workflow工具执行。
方式1:使用默认题目(10题2组)
直接让 Claude 跑:
跑 workflow math-problems
Claude 执行:
Workflow({ name: 'math-problems' })
方式2:自定义题目分组(通过 args 传入)
告诉 Claude 具体的题目和分组:
跑 workflow math-problems,8道题分4组:
12×15, √144+3², 2^10, 100!末尾几个0,
1+2+...+100, sin30°×cos60°, log₂1024, 1/2+1/3+1/6
Claude 执行:
Workflow({
name: 'math-problems',
args: {
groups: [
{ name: '组1', problems: ['1. 12×15', '2. √144+3²'] },
{ name: '组2', problems: ['3. 2^10', '4. 100!末尾几个0'] },
{ name: '组3', problems: ['5. 1+2+...+100', '6. sin30°×cos60°'] },
{ name: '组4', problems: ['7. log₂1024', '8. 1/2+1/3+1/6'] },
]
}
})
方式3:自定义分组(任意题目)
跑 workflow math-problems,分2组:
几何题:求半径5的圆面积、求边长3的正方体体积
代数题:解方程 x²-4=0、求 3x+2=14 的 x
Claude 执行:
Workflow({
name: 'math-problems',
args: {
groups: [
{ name: '几何题', problems: ['求半径5的圆面积', '求边长3的正方体体积'] },
{ name: '代数题', problems: ['解方程 x²-4=0', '求 3x+2=14 的 x'] },
]
}
})
提示: 用
/workflows查看实时进度。Workflow 跑完后结果自动返回到对话中。
为什么题目写在 prompt 里?
| 方式 | 初学者体验 | 维护性 |
|---|---|---|
| 动态轮询分组 | 看不出哪道题在哪个组,需脑补分组逻辑 | 改题需理解 round-robin |
| 题目写在 prompt(✅ 当前) | DEFAULT_GROUPS 直接展示分组,改题即改数组 | 改题目、改分组直观 |
7. 在 Workflow 内定义 Agent
Workflow 的 agentType 不支持 .claude/agents/ 自定义 agent。解决方案:在脚本内用 Prompt 模板 定义 agent 角色。
Prompt 模板模式(推荐)
把每个 agent 的完整指令写成字符串模板,用 {占位符} 标记数据插入点:
// ─── Prompt 模板(顶部定义,一眼看清所有 agent) ──────────
const SOLVER_PROMPT = `你是一位数学解题专家。请逐一解答以下数学题。
{problems_block}
输出格式(每道题):
- 题目:{原题}
- 解题过程:{逐步推导}
- 答案:{最终结果}`
const VERIFIER_PROMPT = `你是一位数学验证专家。独立重新解题,对比下面的解答。
{problems_block}
=== 待验证的解答 ===
{solution}
=== 要求 ===
- 独立计算,不可信任给出的解答
- 逐步对比,标注对错
输出格式:
- 判定:✅ 正确 / ❌ 错误
- 错误原因:{如有}`
const SCORER_PROMPT = `你是数学评分专家。综合解题和验证结果打分。
{problems_block}
=== 验证结果 ===
{verification}
评分:10=完美 | 7-9=正确可改进 | 4-6=部分正确 | 0-3=错误
输出格式:
- 分数:{0-10}
- 评语:{一句话}`
// ─── 模板结束 ─────────────────────────────────────────
使用:`.replace()` 填充数据
// pipeline 中用 .replace() 把数据填入模板
pipeline(
[group], // 整组数据
// Stage 1: 解题
(grp) => agent(
SOLVER_PROMPT.replace('{problems_block}', grp.problems.join('\n')),
{ label: `${grp.name}-解题`, phase: 'Solve' }
),
// Stage 2: 验证(solution 是上一阶段输出)
(solution, grp) => agent(
VERIFIER_PROMPT
.replace('{problems_block}', grp.problems.join('\n'))
.replace('{solution}', solution),
{ label: `${grp.name}-验证`, phase: 'Verify' }
),
// Stage 3: 评分(verification 是上一阶段输出)
(verification, grp) => agent(
SCORER_PROMPT
.replace('{problems_block}', grp.problems.join('\n'))
.replace('{verification}', verification),
{ label: `${grp.name}-评分`, phase: 'Score' }
),
)
三种 Agent 定义方式对比
| 方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Prompt 模板(✅ 推荐) | 指令和数据分离清晰,.replace() 一看就懂 |
多阶段时模板较长 | ≥2 阶段的 pipeline |
| Agent 对象 + buildPrompt() | 封装好,可复用 | 需理解 this 绑定 |
多处复用同一 agent |
| 字符串内联 | 最简单 | prompt 和逻辑混杂 | 单阶段、demo |
建议: 初学者用 Prompt 模板模式。模板在文件顶部,一眼看清所有 agent 角色和数据流向。
8. ⚠️ 限制与踩坑
已知限制
| 问题 | 说明 | 解决方案 |
|---|---|---|
| 自定义 agent 不可用 | agentType 只支持内置类型,.claude/agents/ 定义的被忽略 |
脚本内定义 Agent 对象 |
| args 可能丢失 | 通过 name 引用 workflow 时 args 可能为 undefined |
内联 script 或提供默认值 |
| 不支持 TypeScript | 脚本是纯 JS,类型注解会报错 | 用纯 JS,注释代替类型 |
| 无 Date/Math.random | Date.now()、Math.random() 等不可用(影响缓存) |
通过 args 传入时间戳 |
| 并发上限 | 同时运行 agent 数 ≈ min(16, CPU核心-2) |
自动排队,无需处理 |
| 总量上限 | 单次 workflow 最多 1000 个 agent | 合理分组 |
| 7天自动过期 | 循环任务 7 天后自动停止 | 需要时重新创建 |
agentType 支持列表(内置)
claude, claude-code-guide, Explore, general-purpose, Plan
gsd-*, glm-plan-usage:usage-query-agent, statusline-setup
❌ 不支持:
.claude/agents/里的自定义名称
9. 架构对比
Workflow vs 直接用 Agent Tool
graph TB
subgraph "Workflow 方式"
W1[JS 脚本控制流] --> W2[agent]
W1 --> W3[agent]
W1 --> W4[agent]
W2 --> W5[pipeline/parallel 编排]
W3 --> W5
W4 --> W5
W5 --> W6[结构化返回]
end
subgraph "Agent Tool 方式"
A1[主会话 prompt] --> A2[Agent subagent_type]
A1 --> A3[Agent subagent_type]
A2 --> A4[手动汇总]
A3 --> A4
end
style W1 fill:#bbdefb
style A1 fill:#fff9c4| 维度 | Workflow | Agent Tool |
|---|---|---|
| 编排方式 | JS 代码控制流 | Prompt 驱动 |
| 并行能力 | parallel() 原生支持 |
需手动 spawn 多个 |
| 流水线 | pipeline() 一行搞定 |
需手动串联 |
| 自定义 agent | ❌ 仅内置类型 | ✅ subagent_type 支持自定义 |
| args 传递 | ⚠️ name 引用时可能丢失 |
✅ 通过 prompt 传递 |
| 适用场景 | 批量、多阶段、结构化 | 灵活、少量 agent |
| 复杂度 | 需写 JS 脚本 | 简单 prompt |
pipeline vs parallel 选择
graph LR
Q{数据项之间
有依赖吗?} -->|是| P[用 pipeline]
Q -->|否| PR[用 parallel]
Q{需要所有结果
一起处理吗?} -->|是| PR
Q -->|否| P
style Q fill:#fff9c4| 模式 | 何时用 | 类比 |
|---|---|---|
| pipeline | 每项数据要经过多个阶段,项与项独立 | 工厂流水线 |
| parallel | 多个任务同时跑,全部完成后汇总 | 并行工程师团队 |
| pipeline + parallel | 分组并行,组内流水线 | 多条流水线同时开工 |
10. 总结
核心要点
- Dynamic Workflow = JS 脚本编排多 agent
- 三大原语:
agent()、pipeline()、parallel() - 自定义 agent 要定义在脚本内,不能引用
.claude/agents/ - args 传参不稳定,提供默认值或用内联 script
- 脚本是纯 JS,不支持 TS 语法、
Date.now()、Math.random()
推荐模板
export const meta = {
name: 'my-workflow',
description: '描述',
phases: [{ title: 'Phase1' }, { title: 'Phase2' }],
}
// 1. Prompt 模板(顶部定义,{占位符} 标记数据位置)
const AGENT_PROMPT = `你是一位专家。
{data_block}
请按要求处理以上数据。`
// 2. 数据分组(直接定义,初学者一眼看懂)
const DEFAULT_GROUPS = [
{ name: '组A', items: ['数据1', '数据2'] },
{ name: '组B', items: ['数据3', '数据4'] },
]
const groups = (args && args.groups) || DEFAULT_GROUPS
// 3. 编排:每组并行,组内 pipeline
const results = await parallel(
groups.map(group => () => pipeline(
[group],
(grp) => agent(
AGENT_PROMPT.replace('{data_block}', grp.items.join('\n')),
{ label: `${grp.name}-阶段1`, phase: 'Phase1' }
),
(prev, grp) => agent(
'基于结果进一步处理:\n' + prev,
{ label: `${grp.name}-阶段2`, phase: 'Phase2' }
),
))
)
// 4. 按组返回
const output = {}
groups.forEach((g, i) => output[g.name] = results[i][0])
return output
进一步学习
官方文档:
- Dynamic Workflows 官方文档 — 编排子 agent 的完整参考,涵盖运行、保存、管理、限制
- 官方文档 — Custom Subagents
- 官方文档 — Skills
社区资源:
📝 本指南基于 Claude Code Dynamic Workflow 实践总结,所有代码均经过实际运行验证。官方文档参考:Dynamic Workflows。