面向 Claude Code 用户与初学者的深度图解教程
你有没有过这样的体验:让 AI Agent 帮你重构代码,刚开始它聪明绝顶,但随着修 Bug 轮次增加、报错日志越堆越长,AI 突然开始"断片"——忘记最初的要求,甚至胡编乱造?
这在 AI 领域有个专门的名字:上下文污染(Context Contamination)。
为了解决这个行业死穴,Anthropic 在 Claude Code 中引入了 Dynamic Workflows(动态工作流) 架构。今天这篇文章,我们用大白话 + 架构图 + 代码 + ASCII 图,彻底拆解它是如何通过"即用即弃"的上下文管理艺术,干翻传统 Subagent 模式的。
目录
- 一、问题的根源:为什么 AI 会越聊越蠢?
- 二、传统 Subagent 模式:滚雪球式的上下文灾难
- 三、动态工作流:无状态线程的"接力赛"
- 四、并行模式(Fan-Out):多线程同时开工
- 五、串行模式(Pipeline):增量接力赛
- 六、实战代码:一个动态工作流长什么样?
- 七、经典模式速查
- 八、新旧架构核心对比
- 九、终极启示
一、问题的根源:为什么 AI 会越聊越蠢?
大模型(LLM)的工作方式,可以简单理解为:你把所有聊天记录塞进一个"窗口",模型根据窗口里的内容来回答你的问题。
但这个窗口是有大小限制的。当窗口里 80% 都是报错日志、废代码、重复的试错过程时,模型就会:
| 失败模式 | 白话解释 | 真实表现 |
|---|---|---|
| Agentic Laziness(偷懒) | 干到一半说"我搞定了" | 安全审查 50 项,它只做了 35 项就交差 |
| Self-preferential Bias(自嗨) | 觉得自己写的都对 | 自己写的代码自己审查,永远"看起来没问题" |
| Goal Drift(目标漂移) | 忘了最初要干什么 | 改着改着 Bug,开始重构起了无关的代码 |
| Lost in the Middle(迷失中段) | 注意力被垃圾信息淹没 | 忘记了第 3 条需求,因为中间夹了 200 行日志 |
一句话总结:上下文窗口就像你的桌面,垃圾文件越多,你越找不到真正重要的东西。
二、传统 Subagent 模式:滚雪球式的上下文灾难
在过去,主 Agent 遇到复杂任务时,会召唤子 Agent 去干活。问题出在收尾方式上:
传统模式的工作流程
┌─────────────────────────────────────────────────┐
│ 主会话上下文(越来越臃肿) │
│ │
│ 用户提问 │
│ ├─ 主 Agent 派生指令 → 子 Agent │
│ │ ├─ 子 Agent 翻了 10 个文件 │
│ │ ├─ 编译报错 5 次 × 每次日志 60 行 │
│ │ ├─ 跑测试 3 次 × 每次输出 40 行 │
│ │ └─ 终于修好了! │
│ │ │
│ ◄── 💥 子 Agent 结束,180+ 行垃圾全部塞回 ────── │
│ │ │
│ ├─ 主 Agent 又派生另一个子 Agent │
│ │ ├─ 又是 100+ 行日志... │
│ │ └─ 又全部塞回来... │
│ │ │
│ ⚠️ 上下文像滚雪球,越来越大,主模型智商直线下降 │
└─────────────────────────────────────────────────┘
Mermaid 架构图
graph TD
User([用户提问]) --> Main[主智能体 Main Agent]
subgraph ctx["主会话上下文 Context Bloat"]
Main -- "1. 派生指令" --> Sub[子智能体 Subagent]
Sub -- "2. 翻文件 / 报错 / 跑测试" --> Sub
Sub -- "3. 💥 全部日志塞回主会话" --> Main
end
style Main fill:#ffd93d后果:主会话变成了垃圾场。改 2-3 个文件还行,改 10 个文件上下文就爆了。
三、动态工作流:无状态线程的"接力赛"
动态工作流彻底重构了游戏规则。主 Agent 不再自己冲锋,而是升级为 "指挥官(Orchestrator)",通过一个运行时控制脚本(Harness)来调度一切。
核心思路只有三个字:用完即弃。
核心三步法:Fork → Drop → Fan-In
┌──────────────────┐
│ 100% 干净的 │
│ 主会话 Session │
│ │
│ 只有: │
│ · 用户需求 │
│ · 最终结果 │
└────────┬─────────┘
│
① 动态生成控制流
│
┌────────▼─────────┐
│ │
│ Harness │
│ (运行时控制) │
│ │
└───┬──────────┬───┘
│ │
② Fork │ │ ② Fork
(干净分身) │ │ (干净分身)
┌────▼───┐ ┌───▼────┐
│ 子Agent │ │ 子Agent │
│ A │ │ B │
│ │ │ │
│ 翻文件 │ │ 翻文件 │
│ 报错! │ │ 报错! │
│ 再改 │ │ 再改 │
│ 改好了! │ │ 改好了! │
└────┬───┘ └───┬────┘
│ │
③ Drop │ │ ③ Drop
(销毁垃圾)│ │ (销毁垃圾)
┌────▼───┐ ┌───▼────┐
│只返回 │ │只返回 │
│Git Diff │ │Git Diff │
└────┬───┘ └───┬────┘
│ │
└────┬─────┘
│
④ Fan-In 收敛
│
┌────────▼─────────┐
│ 主会话只收到: │
│ ✅ A 的 Git Diff │
│ ✅ B 的 Git Diff │
│ 垃圾日志?不存在的│
└──────────────────┘
Mermaid 架构图
graph TD
User([用户提问]) --> Main["🧠 主智能体 Claude Code"]
subgraph clean["干净的主会话 Session"]
Main
end
Main -- "1. 生成控制流" --> Harness["⚙️ Harness 运行时控制"]
subgraph iso["隔离的临时工作区"]
Harness --> SubA["子智能体 A"]
Harness --> SubB["子智能体 B"]
SubA -- "调试/报错/测试 循环" --> SubA
SubB -- "调试/报错/测试 循环" --> SubB
end
SubA -- "2. 🗑️ 销毁日志 只返回 Git Diff" --> Harness
SubB -- "2. 🗑️ 销毁日志 只返回 Git Diff" --> Harness
Harness -- "3. 合并最终成果" --> Main
style Main fill:#4ecdc4,color:#fff
style Harness fill:#45b7d1,color:#fff
style SubA fill:#96ceb4
style SubB fill:#96ceb4关键区别:子 Agent 返回的是 Git Diff(代码补丁),不是聊天记录。就像你去餐厅点菜,厨师在厨房里试了 5 次才炒好,但端上来的只有那盘成功的菜,你不会看到烧焦的废料。
四、并行模式(Fan-Out):多线程同时开工
当多个任务之间互不依赖时,动态工作流会让它们同时并行执行。
场景:同时修改 3 个无关文件
假设你的项目要改三个文件:auth.ts、payment.ts、logger.ts,它们互不相关。
传统模式(串行,上下文累积)
时间 ─────────────────────────────────────────────►
主 Agent: [改 auth.ts ──── 报错日志塞回 ────] [改 payment.ts ──── 报错日志塞回 ────] [改 logger.ts]
│ 上下文膨胀 │ 上下文爆了 │
└── 50行垃圾 ──────────────────┘── 50行垃圾 ───────────────────┘
动态工作流(并行,上下文稳定)
时间 ─────────────────────────────────────────────►
子 Agent A: [改 auth.ts ──── 报错 ──── 修好 ────] 🗑️ → 只返回 Diff
子 Agent B: [改 payment.ts ─ 报错 ──── 修好 ────] 🗑️ → 只返回 Diff
子 Agent C: [改 logger.ts ── 报错 ──── 修好 ────] 🗑️ → 只返回 Diff
└─────────── 同时并行 ───────────────┘
主 Session: [干净] ──────────────────────── [收到 3 个 Diff,合并完成]
上下文始终干净!
Mermaid 并行图
graph LR
Main["🧠 主 Agent"] -->|"Fan-Out"| Harness["⚙️ Harness"]
Harness -->|"Fork"| A["🔧 改 auth.ts"]
Harness -->|"Fork"| B["🔧 改 payment.ts"]
Harness -->|"Fork"| C["🔧 改 logger.ts"]
A -->|"Drop + Diff"| Harness
B -->|"Drop + Diff"| Harness
C -->|"Drop + Diff"| Harness
Harness -->|"Fan-In"| Main
style A fill:#96ceb4
style B fill:#96ceb4
style C fill:#96ceb4结果:并行执行比串行快 3 倍,而且主会话上下文只增加了 3 个 Git Diff,而不是几百行垃圾日志。
五、串行模式(Pipeline):增量接力赛
当任务之间有先后依赖时(A 做完 B 才能开始),动态工作流采用"增量接力"的方式传递上下文。
场景:改代码 → 写测试 → Code Review
假设你要对 auth.ts 做一次完整的开发流程:
- Agent A:重构
auth.ts的认证逻辑 - Agent B:基于 A 的修改,编写对应的单元测试
- Agent C:对 A 的代码 + B 的测试做 Code Review
ASCII 流程图
┌──────────────┐
│ Agent A │ 上下文 = CLAUDE.md + auth.ts + 任务说明
│ 重构代码 │
│ │
│ · 修改函数签名 ×
│ · 编译报错 ×
│ · 再改 ×
│ · 又报错 ×
│ · 终于通过! ✓
│ │
└──────┬───────┘
│ 🗑️ 销毁所有报错日志
│ ✅ 只保留 Git Diff
▼
┌──────────────┐
│ Agent B │ 上下文 = CLAUDE.md + A 的 Diff + "写测试"
│ 写测试 │
│ │ ⚠️ B 完全看不到 A 的 4 次失败!
│ · 看到干净的 │ B 眼里的世界:
│ 代码变更 │ "哦,A 把函数签名改成了这样,
│ · 直接写测试 │ 那我就按新签名写测试呗"
│ · 一次通过! ✓
│ │
└──────┬───────┘
│ 🗑️ 销毁中间过程
│ ✅ 只保留测试代码的 Diff
▼
┌──────────────┐
│ Agent C │ 上下文 = CLAUDE.md + A 的 Diff + B 的 Diff + "Review"
│ Code Review │
│ │ ⚠️ C 看到的只有成品,没有过程
│ · 审查代码 │ "这个命名不太规范"
│ · 审查测试 │ "这里缺个边界测试"
│ · 给出建议 │
│ │
└──────┬───────┘
│ 🗑️ 销毁中间过程
│ ✅ 只返回 Review 报告
▼
┌──────────┐
│ 主会话 │ 总共只增加了 3 份高密度成果
│ 收到: │ · A 的代码 Diff
│ 最终成果 │ · B 的测试 Diff
└──────────┘ · C 的 Review 报告
Mermaid 串行图
graph TD
Main["🧠 主 Agent"] -->|"派发任务"| Harness["⚙️ Harness"]
Harness --> A["Agent A 重构代码"]
A -->|"🗑️ 销毁日志 保留 Diff"| B["Agent B 写测试"]
B -->|"🗑️ 销毁日志 保留 Diff"| C["Agent C Code Review"]
C -->|"🗑️ 销毁日志 保留报告"| Harness
Harness -->|"Fan-In"| Main
style A fill:#96ceb4
style B fill:#87CEEB
style C fill:#DDA0DD上下文传递的数学公式
每一步,下一个 Agent 收到的上下文都是全新拼装的:
$$\text{Agent B 的上下文} = \underbrace{\text{CLAUDE.md}}{\text{项目规则}} + \underbrace{A{\text{diff}}}{\text{上一步成果}} + \underbrace{\text{B 的任务说明}}{\text{当前目标}}$$
$$\text{Agent C 的上下文} = \underbrace{\text{CLAUDE.md}}{\text{项目规则}} + \underbrace{A{\text{diff}} + B_{\text{diff}}}{\text{前两步成果}} + \underbrace{\text{C 的任务说明}}{\text{当前目标}}$$
注意:每一步的上下文都是按需拼装的,不是把历史记录全部堆上去。这就是"增量接力"的精髓。
六、实战代码:一个动态工作流长什么样?
动态工作流的底层就是一个 JavaScript 脚本,Claude 会根据你的需求自动生成这个脚本。你通常不需要手写,但了解它的结构有助于理解原理。
示例:并行修改多个文件的工作流脚本
// 每个工作流必须以 meta 开头
export const meta = {
name: 'rename-user-to-account',
description: '批量将 User 重命名为 Account',
phases: [
{ title: 'Find', detail: '找出所有 User 引用' },
{ title: 'Rename', detail: '逐文件替换' },
{ title: 'Verify', detail: '验证修改正确性' },
],
}
// Phase 1: 找出所有需要改的文件
phase('Find')
const files = await agent(
'在项目中搜索所有包含 "User" 的文件,返回文件路径列表',
{
schema: {
type: 'object',
properties: {
files: {
type: 'array',
items: { type: 'string' },
},
},
},
}
)
// Phase 2: pipeline — 每个文件一个子 Agent,并行修改
// 每个子 Agent 拿到的是干净上下文,互不干扰
phase('Rename')
const results = await pipeline(
files,
(file) =>
agent(`将 ${file} 中的 "User" 重命名为 "Account"`, {
label: `rename:${file}`,
phase: 'Rename',
isolation: 'worktree', // 每个子 Agent 在独立工作区中修改
}),
// pipeline 让每个文件独立走完整个流程
// 文件 A 在 Phase 2 时,文件 B 可能还在 Phase 1
// 不需要等最慢的文件,先完成的先往下走
)
// Phase 3: 验证 — 对每个修改做对抗性审查
phase('Verify')
const verified = await parallel(
results
.filter(Boolean)
.map((r) => () =>
agent(`检查这次重命名是否遗漏了任何 User 引用`, {
label: `verify:${r.file}`,
phase: 'Verify',
})
)
)
// 返回最终结果
return {
totalFiles: files.length,
renamed: results.filter(Boolean).length,
verified: verified.filter(Boolean).length,
}
代码中的上下文管理要点
// 要点 1:agent() 返回的是结构化结果,不是聊天记录
const files = await agent('搜索文件', { schema: FILES_SCHEMA })
// ^ 返回 { files: ['a.ts', 'b.ts', 'c.ts'] }
// 不是 "好的,我帮你搜索了文件,找到了以下..." + 200行日志
// 要点 2:isolation: 'worktree' 确保并行修改不会互相冲突
agent('修改文件', { isolation: 'worktree' })
// 每个子 Agent 在独立的 git worktree 中工作
// 就像给每个厨师分配了独立的厨房
// 要点 3:pipeline() 天然支持串行传递
// 上一步的输出自动成为下一步的输入
const results = await pipeline(
items,
stage1, // stage1(item) → result1
stage2, // stage2(result1, item) → result2
stage3, // stage3(result2, item) → result3
)
七、经典模式速查
根据官方博客,动态工作流有几种经典模式,每种对应不同的上下文管理策略:
模式总览
┌───────────────────────────────────────────────────────────────┐
│ 动态工作流经典模式 │
├───────────────┬───────────────────────────────────────────────┤
│ │ │
│ Fan-Out │ A ──► [子1, 子2, 子3] ──► 合并 │
│ 并行分发 │ 多个无关任务同时干,最后汇总 │
│ │ 适用:批量修改文件、多源搜索 │
│ │ │
├───────────────┼───────────────────────────────────────────────┤
│ │ │
│ Pipeline │ A ──► B ──► C │
│ 串行流水线 │ 上一步的成果是下一步的输入 │
│ │ 适用:改代码 → 写测试 → Review │
│ │ │
├───────────────┼───────────────────────────────────────────────┤
│ │ │
│ Tournament │ [子1, 子2, 子3] ──► 对决 ──► 胜者 │
│ 锦标赛 │ 多个 Agent 竞争同一任务,选最优 │
│ │ 适用:方案比选、命名、设计决策 │
│ │ │
├───────────────┼───────────────────────────────────────────────┤
│ │ │
│ Adversarial │ A 产生 ──► B 对抗验证 │
│ 对抗验证 │ 一个干活,一个专门挑刺 │
│ │ 适用:安全审查、代码审查、事实核查 │
│ │ │
├───────────────┼───────────────────────────────────────────────┤
│ │ │
│ Loop-Until │ while (没搞定) { 派 Agent 去干 } │
│ 循环直到完成 │ 不知道要干多少轮,直到满足停止条件 │
│ │ 适用:修 Bug、深度搜索、根因分析 │
│ │ │
└───────────────┴───────────────────────────────────────────────┘
Mermaid 模式总览图
graph TB
subgraph fanout["Fan-Out 并行"]
FO_Main["Main"] --> FO_A["A"]
FO_Main --> FO_B["B"]
FO_Main --> FO_C["C"]
FO_A --> FO_Merge["合并"]
FO_B --> FO_Merge
FO_C --> FO_Merge
end
subgraph pipeline["Pipeline 串行"]
PL_A["A 改代码"] --> PL_B["B 写测试"] --> PL_C["C Review"]
end
subgraph tournament["Tournament 锦标赛"]
T_A["方案 A"] --> T_J1["对决"]
T_B["方案 B"] --> T_J1
T_C["方案 C"] --> T_J2["对决"]
T_J1 --> T_W["🏆 胜者"]
T_J2 --> T_W
end
subgraph adversarial["Adversarial 对抗"]
AV_A["A 产生方案"] --> AV_B["B 挑刺验证"]
AV_B -->|"通过"| AV_Done["✅"]
AV_B -->|"驳回"| AV_A
end实际触发方式
作为 Claude Code 用户,你不需要手写这些模式。只需在提示词中描述你的需求:
# 触发并行修改
"用 workflow 把 User 重命名为 Account"
# 触发对抗验证
"用 workflow 审查这个 PR,对每个发现做对抗性验证"
# 触发深度搜索
"用 workflow 搜索过去 6 个月的 incident 报告,找出反复出现的根因"
# 触发锦标赛
"帮我给这个 CLI 工具起个名字,用 workflow 多方案竞争"
# 万能触发词
"ultracode" # 加上这个词,Claude Code 就会创建 workflow
八、新旧架构核心对比
对比表
| 维度 | 传统 Subagent 模式 | 动态工作流模式 |
|---|---|---|
| 上下文结构 | 垂直堆叠(滚雪球) | 树状分发(即用即弃) |
| 子 Agent 返回内容 | 全部聊天记录 + 日志 | 只有 Git Diff / 结构化结果 |
| 试错垃圾日志 | 永久粘在主会话中 | 隔离区用完即删 🗑️ |
| 主模型智商 | 随轮次增加而下降 | 始终保持最高水平 |
| 并行能力 | 有限,上下文互相污染 | 天然支持,互不干扰 |
| 项目规模上限 | 2-3 个文件就爆 | 1000 个文件依然稳定 |
| 防止偷懒 | ❌ 无保障 | ✅ 每个子 Agent 有独立目标 |
| 防止自嗨 | ❌ 自己验证自己 | ✅ 独立 Agent 对抗验证 |
| 防止目标漂移 | ❌ 上下文丢失后漂移 | ✅ 每个子 Agent 任务聚焦 |
上下文体积对比(同一任务)
传统模式上下文变化:
│
█ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ █ ← 爆了
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
│ └─ 子Agent 1 的 180 行日志 ─┘│└─ 子Agent 2 的日志 ─┘│
│ │ │
└── 用户提问 ──────────────────┴────────────────────────┘
动态工作流上下文变化:
│
█ █ █ █ █ █ █ █ ← 始终稳定
│ │ │ │ │ │ │ │
│ │ │ └─ Diff 1 (30 行) ──┘
│ │ └── Diff 2 (25 行) ────┘
│ └── 用户提问 ─────────────┘
图例:█ = 一个上下文 token
九、终极启示
Claude Code 的动态工作流让我们明白一个道理:
真正的顶级 AI 架构,不是比谁的上下文窗口能塞进更多垃圾,而是比谁能更优雅地"克制"与"裁剪"上下文。
通过把子 Agent 当成 "无状态的瞬时线程"——给它一个干净的任务,让它在隔离区里尽情试错,干完活就擦干现场,只留代码。这种"断舍离"的艺术,正是 AI Agent 迈向企业级超大型工程的必经之路。
给 Claude Code 用户的实践建议
- 复杂任务才用 workflow:简单任务不需要,workflow 会消耗更多 token
- 用"ultracode"关键词触发:确保 Claude Code 创建 workflow 而不是直接执行
- 配合
/loop做持续任务:比如持续监控、定期审查 - 配合
/goal设定完成条件:防止 workflow 无限运行 - 善用 token 预算:在 prompt 中指定 "use 10k tokens" 来控制消耗
延伸阅读
- 📖 官方博客:A Harness for Every Task: Dynamic Workflows in Claude Code
- 🛠️ 保存你的 workflow:在 workflow 菜单按
s即可保存,文件位于~/.claude/workflows/ - 🔄 中断恢复:如果 workflow 被中断(关闭终端等),重新打开会话会自动从断点继续