AGENTUPDATE 技术博客

起底 Claude Code Dynamic workflow工作流的上下文管理艺术

起底 Claude Code Dynamic workflow工作流的上下文管理艺术
目录

面向 Claude Code 用户与初学者的深度图解教程

你有没有过这样的体验:让 AI Agent 帮你重构代码,刚开始它聪明绝顶,但随着修 Bug 轮次增加、报错日志越堆越长,AI 突然开始"断片"——忘记最初的要求,甚至胡编乱造?

这在 AI 领域有个专门的名字:上下文污染(Context Contamination)

为了解决这个行业死穴,Anthropic 在 Claude Code 中引入了 Dynamic Workflows(动态工作流) 架构。今天这篇文章,我们用大白话 + 架构图 + 代码 + ASCII 图,彻底拆解它是如何通过"即用即弃"的上下文管理艺术,干翻传统 Subagent 模式的。


目录


一、问题的根源:为什么 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.tspayment.tslogger.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 做一次完整的开发流程:

  1. Agent A:重构 auth.ts 的认证逻辑
  2. Agent B:基于 A 的修改,编写对应的单元测试
  3. 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 用户的实践建议

  1. 复杂任务才用 workflow:简单任务不需要,workflow 会消耗更多 token
  2. 用"ultracode"关键词触发:确保 Claude Code 创建 workflow 而不是直接执行
  3. 配合 /loop 做持续任务:比如持续监控、定期审查
  4. 配合 /goal 设定完成条件:防止 workflow 无限运行
  5. 善用 token 预算:在 prompt 中指定 "use 10k tokens" 来控制消耗

延伸阅读

  • 📖 官方博客:A Harness for Every Task: Dynamic Workflows in Claude Code
  • 🛠️ 保存你的 workflow:在 workflow 菜单按 s 即可保存,文件位于 ~/.claude/workflows/
  • 🔄 中断恢复:如果 workflow 被中断(关闭终端等),重新打开会话会自动从断点继续