LLM之Agent(五十三)|Claude Workflow 完整技术指南:多智能体编排深度解析
原文:Claude Workflow Dynamics: A Complete Technical Guide to Multi-Agent Orchestration
作者:Roan Brasil Monteiro
翻译 & 解读
核心理念: 动态工作流并不会让 Claude 变得更聪明——它重新定义了"智能"在哪儿落地。计划从 Claude 的上下文窗口迁移到一段确定的 JavaScript 脚本中,这种转变才是解锁规模化、可验证、高可靠性任务的关键——这是任何单智能体会话无法达到的。
📖 目录
- 动态工作流要解决什么问题
- 三大编排原语
- 架构深度解析
- 核心 API:agent()、pipeline()、parallel()
- Ultracode:自动工作流编排
- 对抗验证与 /deep-research
- 编写你的第一个工作流
- 生产模式与质量门禁
- 成本、可恢复性以及何时不该用工作流
- 失败模式
一、动态工作流要解决什么问题
任何一个有经验的 Claude Code 用户都遇到过同一个天花板:任务本身是真实的、意图是清晰的,但作业太大,一次会话搞不定。
你想在整个分布式服务中追一个 bug。你想做一个涉及上千个文件的框架迁移。你想要一份经过全方位压力测试的研究报告再做决策。你发出请求,Claude 开始干活,然后在第 80 个文件或第 12 个数据源附近,上下文窗口满了,注意力开始下降,会话变成了部分结果和飘忽逻辑的坟场。
传统的变通方案——手动拆解任务再拼凑结果——是可行的,但它把你变成了编排者本身。你成了那个循环。 你要记住每个子智能体返回了什么,决定下一个处理哪个文件,判断结果是否收敛。这是认知开销,它随任务复杂度增长,而不是随你的意图增长。
动态工作流(Dynamic Workflows)由 Anthropic 于 2026 年 5 月 28 日以研究预览形式推出,通过逆转模型解决了这个问题。Claude 不再逐回合决定下一步做什么(每个中间结果都堆积在上下文中),而是由一个生成的 JavaScript 脚本来承载所有循环、分支逻辑和中间变量。Claude 的上下文窗口只看到最终验证过的答案。编排负担从人类的认知转移到确定的代码中——正是这种转变让扩展到数百个智能体变得协调有序而非混乱不堪。
Bun 的创建者 Jarred Sumner 展示了这个天花板:动态工作流 + 对抗性代码审查使将 Bun 从 Zig 移植到 Rust 成为可能——大约 75 万行 Rust,99.8% 的现存测试通过,从第一次提交到合并在大约 11 天内完成。一个工作流映射了 Zig 代码库中每个结构体字段的 Rust 生命周期。第二个工作流将每个 .zig 文件写成了行为相同的 .rs 移植版本,数百个智能体并行工作,每个文件配两个审查员。一个修复循环驱动构建和测试套件,直到两者都干净通过。所有这些都不需要 Jarred 手动管理编排。
这就是天花板。本指南从地基开始,教你理解和运用这套机制。
二、三大编排原语
在写一行工作流代码之前,你需要一个清晰的思维模型来理解有什么以及为什么。Claude Code 暴露了三种不同的协作原语。选错了就会浪费 token、时间和信任。问题不再是"我该怎样提示 Claude?"——而是"什么样的执行模型最适合这个问题的结构?"
2.1 子智能体(Subagents)
子智能体是从当前会话中启动的临时工作实例。每个实例都有自己的隔离上下文窗口,执行任务后只返回结果摘要。调用方会话不会吸收子智能体的中间日志或原始输出——只有最终答案。这让编排会话保持干净、上下文清晰。
Claude Code 内置三种子智能体类型:
- Explore — 只读;用于快速代码库搜索和上下文收集,不会造成写操作。用在发现阶段。
- Plan — 只读;在 Plan 模式下收集背景信息,主会话继续工作。用于为决策提供输入的并行研究。
- 通用(General-purpose) — 读写访问;适用于混合探索和执行的场景。
你也可以在 .claude/agents/(项目级)或 ~/.claude/agents/(用户级)中用带 YAML front matter 的 Markdown 文件定义自定义子智能体。description 字段是主要的触发信号——Claude 读取它来决定何时自动调用。精度直接决定可靠性。模糊的描述如"处理代码任务"会不可预测地触发。精确的描述如"在用户请求对 Go 服务进行安全审计后运行;执行静态分析并发现 SQL 注入向量"会在预期时触发。
子智能体擅长需要一致、成本可预测的明确定义任务。规范用例包括执行特定团队风格指南的代码审查、自动生成变更日志、以及遵循已知模式的针对性重构。
✅ 用子智能体: 任务能塞进一个上下文窗口,拆分策略显而易见,token 预算是个硬约束。
❌ 不用子智能体: 任务需要数百个独立操作,拆分策略事先不清楚,或结果需要对抗验证。
2.2 智能体团队(Agent Teams)
智能体团队是彼此协调的独立 Claude Code 会话。与子智能体不同(由父会话产生和监管),智能体团队是对等的。它们通过共享文件、消息队列或定义好的交接点进行通信。每个成员有自己的上下文窗口、工具访问权限和生命周期。
智能体团队适合你有足够理解的长期并行任务,其结构可以提前定义,且不同部分真正独立。一个进行安全审计的团队可能有一个会话扫描认证代码,第二个扫描输入验证,第三个扫描数据访问层——全部同步进行,互不阻塞。
对比让区别一目了然:
- 子智能体: Claude 是编排者,逐回合决策,结果在上下文窗口中累积
- 智能体团队: 协调分布在多个会话中
- 工作流: 编排逻辑在脚本中——Claude 只看最终答案,计划在确定代码中
智能体团队相对于动态工作流的关键限制:你必须事先知道团队结构。 成员数量、专长领域、通信协议——全部必须在运行前决定。动态工作流在运行时根据实际任务动态决定。
2.3 动态工作流(Dynamic Workflows)
最新、最强大的原语。2026 年 5 月 28 日以研究预览形式推出,是 Claude Code 针对单智能体处理不了的大问题的新编排层。核心机制:当你在提示词中包含 "workflow" 这个词,或 /effort ultracode 设置激活时,Claude 不是直接响应,而是即时生成一段 JavaScript 编排脚本。该脚本承载所有循环、分支逻辑和中间变量。然后 Claude 执行这个脚本,将工作分发到数十到数百个并行子智能体,让对抗智能体尝试反驳发现,迭代直到答案收敛,最终只返回验证过的结果。
一句话区分:
- 标准 Claude Code:Claude 是 编排者——逐回合决策,中间结果在上下文窗口中累积
- 动态工作流:一段生成的 JS 脚本 是 编排者——Claude 的上下文窗口只保留最终验证过的答案,而非每个中间步骤的废气
操作规则很简单: 如果任务计划两三个步骤就能搞定,Claude 自己就能记着,用子智能体或技能就够了。一旦计划变成了代码——可重复、可扩展、能支持数百个独立操作——那就是工作流该上场的时候了。
三、架构深度解析
脚本的生成过程
当你触发一个工作流(在提示词中包括"workflow"或激活 ultracode)时,Claude 不会立即开始产生智能体。它首先生成一个 JavaScript 编排脚本。这个脚本就是计划本身,以可执行代码的形式呈现。
脚本必须以一个 meta 块开头,并且必须是纯字面量——没有动态表达式,没有函数调用。这一点很重要:运行时在智能体启动之前读取 meta 块来展示进度信息并规划执行。纯字面量保证运行时可以在不运行任何代码的情况下解析计划。
export const meta = {
name: "security-audit",
description: "全代码库安全审计,带对抗验证",
phases: [
{ title: "探索", detail: "跨所有模块并行读取" },
{ title: "审计", detail: "按模块分析漏洞" },
{ title: "反驳", detail: "对抗智能体挑战发现" },
{ title: "报告", detail: "合成已验证的发现" },
],
};
meta 块之后,脚本体使用编排原语:agent()、parallel()、pipeline()、phase()、log()、args 和 budget。
关键技术属性
动态工作流有几个关键的技术特性,将其与更简单的编排方法区分开来:
规模: 最多 1000 个总智能体每次运行,最多 16 个并发。16 个并发的上限是为了限制本地资源使用——同时运行数百个会耗尽 CPU、内存和 API 速率限制。工作流运行时自动管理队列:16 个槽位中一个完成后,下一个排队智能体启动。
对抗验证: 独立智能体被分配任务去相互反驳对方的发现。只有通过交叉审问的结论才被采纳。这不是一个装饰性功能——它直接解决了智能体系统中的幻觉问题。一个找到"bug"的智能体可能是错的。一个经过多次尝试仍无法推翻发现的对抗智能体提供了有意义的置信度。
收敛驱动迭代: 工作流持续产生智能体并迭代,直到答案稳定。通过次数由工作本身决定,而非用户预设。对于你事先不知道需要多少次通过的任务(在陌生代码中找 bug、攻击面未知的安全审计),这是正确的设计。
缓存备份的可恢复性: 如果运行被暂停或中断,已完成的智能体在恢复时直接从缓存返回结果。没有工作被重复。这对于不能假设在单次不间断会话中完成的长时间运行任务(数小时,甚至可能数天)极其重要。
后台执行: 运行不阻塞 CLI;你可以继续工作。用 /workflows view 深入任何阶段、检查提示、检视工具调用,无需停止引擎。
禁止非确定性: Date.now()、Math.random() 和无参数的 new Date() 在工作流脚本中是被禁止的。工作流会记录每次 agent() 调用以便恢复,而非确定性会失效该缓存。如果你需要时间戳,通过 args 传递。如果你需要在智能体间变化,按索引变化提示或标签。这会绊倒那些期望 JavaScript 环境表现正常的经验丰富的开发者。记住:脚本不仅仅是运行一次的代码——它是一个可恢复、可缓存的执行计划。
审批流程: 工作流生成后,Claude Code 会在实际启动执行前向用户展示计划的阶段。可以一次批准,或针对该特定工作流在该项目中永久批准。
可用性
动态工作流在 Max、Team 和 Enterprise 计划上可用(如果管理员启用),覆盖 Claude Code CLI、VS Code 扩展、桌面应用和 Claude API——包括 Amazon Bedrock、Vertex AI 和 Microsoft Foundry。Pro 和基础计划不可用。
四、核心 API:agent()、pipeline()、parallel()
理解这三个函数是编写有效工作流的基石。其他所有模式都从它们构建。
4.1 agent(prompt, opts?)
agent() 产生一个子智能体并返回其结果。不加选项时返回智能体的最终文本。带 schema 选项时强制子智能体返回符合 JSON Schema 的结构化 JSON——运行时在匹配失败时自动重试,所以你永远不需要防御性地解析自由文本。
// 最小化使用
const result = await agent("分析认证模块的 SQL 注入漏洞");
// 带结构化输出
const FINDING_SCHEMA = {
type: "object",
properties: {
severity: { type: "string", enum: ["critical", "high", "medium", "low", "none"] },
location: { type: "string" },
description: { type: "string" },
cwe: { type: "string" }
},
required: ["severity", "location", "description"]
};
const finding = await agent("分析认证模块", {
label: "audit:auth",
phase: "审计",
schema: FINDING_SCHEMA,
agentType: "explore", // 只读
model: "claude-opus-4-8" // 为此调用升级模型
});
关键选项:
schema— JSON Schema,强制结构化输出,验证失败自动重试label— 进度 UI 中的显示名称。用描述性标签如 "audit:auth" 而非 "agent1"phase— 将智能体分配给某个进度组。在parallel()和pipeline()内部使用,避免与全局phase()状态竞争model— 覆盖本次调用的模型。默认从会话继承agentType— "explore" 只读,"general-purpose" 读写
结构化输出是可靠路径。schema 选项强制子智能体调用结构化输出工具,验证发生在工具调用层,模型在匹配失败时自动重试。这远比让智能体"请返回 JSON"并指望它靠谱多了。当下游阶段消费结果时,始终使用它。
4.2 parallel() — 基于屏障的扇出
parallel() 接收一个 thunk(不接受参数、返回 agent() 调用的函数)数组,并发运行它们。它是一个屏障——运行时等待组内所有智能体完成后才执行下一行代码。
const files = ["auth.go", "data.go", "api.go", "config.go"];
// 四个智能体同时启动;parallel() 在所有完成时返回
const results = await parallel(
files.map(f => () => agent(`审计 ${f} 的安全漏洞`, {
label: `audit:${f}`,
phase: "审计",
schema: FINDING_SCHEMA,
agentType: "explore"
}))
);
// results 是数组;每个元素对应一个文件
parallel() 的屏障行为是其决定性特征。所有智能体同时启动,parallel() 之后的代码直到每个智能体都返回才执行。当下一个阶段需要所有结果才能推进时(例如运行去重或排序步骤,需要看到完整发现集),这是正确的原语。
并发上限: 即使你传了 200 个 thunk 给 parallel(),也只有 16 个同时在跑。运行时将其余排队,槽位空出时启动下一个。这是自动且透明的——你不需要管理队列。
4.3 pipeline() — 无屏障的流式处理
pipeline() 独立处理每个项目,按顺序通过一系列阶段流动——它不是屏障,而是流。
// 文件独立处理;没有阶段等待所有文件
await pipeline(
files,
async (f) => agent(`读取并总结 ${f}`, { agentType: "explore" }),
async (summary) => agent(`为以下内容生成修复:${summary}`),
async (fix) => agent(`将修复写入磁盘:${fix}`)
);
在 pipeline() 中,文件 1 可能在阶段 3(写入修复)时,文件 2 还在阶段 1(读取)。没有跨项目的同步。当项目独立时,这比 parallel() 更快,因为你不需要等待每个阶段中最慢的那个项目。
工作流设计中最重要的决策:默认用 pipeline()。 只有当一个阶段需要所有前期结果时,才在阶段之间使用 parallel() 屏障。检验标准:如果你写了 parallel() → transform → parallel(),而中间那个 transform 没有跨项目依赖,那你应该用 pipeline()。
4.4 phase() 和 log()
phase(title) 启动一个命名的进度组,在 Claude Code UI 中可见。log(msg) 在当前阶段下发射一条叙述性消息。这些是显示原语,不是执行原语——它们不影响哪些智能体运行或何时运行。
用好它们,让长时间运行的工作流可调试。没有 phase() 调用的工作流是个黑盒;有清晰阶段的工作流是你能够观察和理解的东西。
phase("研究");
log(`并行扫描 ${sources.length} 个数据源`);
const raw = await parallel(sources.map(s => () => agent(s.prompt, { phase: "研究" })));
log(`收集了 ${raw.filter(Boolean).length} 个有效响应`);
phase("合成");
const report = await agent(`根据以下内容撰写报告:${JSON.stringify(raw)}`, { phase: "合成" });
4.5 args 和 budget
args 携带启动工作流时传入的 JSON。用它在运行间传递可变值——目标目录、日期范围、分析深度、特性开关。
const { targetDir, depth = "full", startDate } = args;
budget 暴露用 --budget 设置的 token 目标。未设定时它为 null。在使用之前始终用 budget.total 保护循环:
if (budget && budget.used > budget.total * 0.8) {
log("接近预算上限——正在收尾");
break;
}
五、Ultracode:自动工作流编排
什么是 Ultracode
将 effort 设置为 ultracode 会激活自动工作流编排。用 /effort ultracode 启用。它将 xhigh 推理 与自动工作流编排相结合——开启 ultracode 后,Claude 评估每个请求并自行决定任务是否需要工作流。如果需要,Claude 规划一个理解-修改-验证循环:一组子智能体集群映射架构,第二组执行修改,第三组验证结果。这个循环自动运行。
单个请求可以依次变成多个工作流——一个用于理解代码,一个用于应用修改,一个用于验证。Ultracode 仅在支持 xhigh effort 的模型上可用,在当前会话期间有效,每次新会话重置。
理解-修改-验证循环
ultracode 生成的循环遵循一致的三阶段结构:
Phase 1 — 理解: 一组只读的 explore 智能体映射代码库、识别相关文件、追踪数据流、建立上下文模型。该阶段严格非破坏性。智能体产生结构化输出:哪些组件与任务相关、它们的依赖关系是什么、修改必须尊重哪些约束。
Phase 2 — 修改: 第二组通用智能体(读写访问)根据理解阶段的输出执行转换。这些智能体接收 Phase 1 的结构化上下文,而非原始文本。它们写文件、调用工具、产生实际的变更。
Phase 3 — 验证: 第三组对抗智能体尝试破坏 Phase 2 所做的修改。它们运行测试、追踪边界情况、专门探测修改智能体变更的区域。如果验证失败,循环迭代——新的修改阶段以验证失败为额外上下文运行。
循环在验证通过或达到预算上限时终止。 你不需要设置迭代次数。工作流自行判断结果何时收敛。
何时用 Ultracode
Token 成本和时间高于标准提示,但在多阶段工程任务上的成功率显著更好。日常简单工作用 /effort high 就够,做完繁重任务后降回来。
✅ 适用场景:
- 生产代码的关键修改,出错代价大
- 跨多个模块、依赖关系不明显的重构
- 在不熟悉的代码中修 bug,不确定修复是否会破坏其他东西
- 任何独立验证结果值得额外 token 成本的任务
❌ 不适用场景:
- 小型 PR 的常规代码审查
- 范围清晰、有边界的单文件编辑
- 需要 token 预算可预测的任务
- 探索性工作(错误答案修正成本低)
六、对抗验证与 /deep-research
对抗验证为何重要
智能体系统中的幻觉问题是结构性的,而非偶然性的。一个搜索 bug 的智能体会找到 bug——包括不存在的 bug。一个生成研究发现结果的智能体会产出结果——包括没有来源支持的结论。单智能体验证(让同一个智能体"检查自己的工作")不可靠,因为智能体在检查时带着与原始生成时相同的偏见和盲点。
对抗验证通过将提出者和反驳者分离来打破这个模式。 提出者的任务是发现和表述结论。反驳者的任务是找到能推翻这些结论的任何方式。这是两种根本不同的认知任务,将它们分配给不同的智能体(带不同的上下文和提示)能大幅提升输出可靠性。
/deep-research 的内部机制
/deep-research 命令是首要的捆绑工作流。与接受第一个合理答案的标准搜索工具不同,/deep-research 的设计目标就是反驳自己的发现。它从多个角度扇出搜索,跨来源交叉验证,对每个声明进行内部投票,只包括经受住对抗性审查的声明。输出是一份引用过的研究报告,可靠性门槛高于单次 AI 搜索。
内部结构严格遵循对抗验证模式:
- 扇出 — 多个智能体从不同角度搜索(不同查询、不同来源、问题的不同框架)
- 声明提取 — 每个智能体产出带来源引用的结构化声明
- 交叉检查 — 声明互相测试;冲突的声明触发额外搜索来解决矛盾
- 对抗性轮次 — 专门的反驳智能体尝试找到反驳每个幸存声明的来源
- 投票 — 根据未能推翻声明的反驳智能体比例给声明评分
- 报告生成 — 只有高置信度的声明进入最终报告,附引用
这就是为什么 /deep-research 与让 Claude "搜索 X 的信息"有本质区别——搜索过程的架构不同,而非输出长度不同。
在自定义工作流中实现对抗验证
关键洞察:验证不是一个事后贴上去的独立步骤——它内嵌在工作流的结构中。
export const meta = {
name: "verified-audit",
description: "带对抗验证的安全审计",
phases: [
{ title: "审计", detail: "发现漏洞" },
{ title: "反驳", detail: "挑战每个发现" },
{ title: "报告", detail: "仅包含已验证的发现" }
]
};
// 阶段 1:审计
phase("审计");
const rawFindings = await parallel(
modules.map(m => () => agent(`审计 ${m} 的安全问题`, {
schema: FINDING_SCHEMA,
agentType: "explore"
}))
);
// 扁平化并去重
const findings = deduplicateFindings(rawFindings.filter(Boolean).flat());
log(`${findings.length} 个候选发现(去重后)`);
// 阶段 2:对抗性反驳
phase("反驳");
const verdicts = await parallel(
findings.map(f => () => agent(
`你是一个挑剔的安全审查员。尝试证明这个发现是误报。发现:${JSON.stringify(f)}`,
{
schema: VERDICT_SCHEMA, // { survived: boolean, reason: string }
label: `refute:${f.location}`
}
))
);
// 阶段 3:仅幸存发现
phase("报告");
const verified = findings.filter((f, i) => verdicts[i]?.survived);
log(`${verified.length} / ${findings.length} 个发现通过了反驳`);
const report = await agent(
`为以下已验证发现编写安全审计报告:${JSON.stringify(verified)}`,
{ phase: "报告" }
);
return { report, verified, totalCandidates: findings.length };
七、编写你的第一个工作流
工作流文件结构
工作流文件放在 .claude/workflows/(项目级)或 ~/.claude/workflows/(用户级)。必须是有效的 JavaScript ES 模块,带有命名的 meta 导出和作为工作流体的默认导出函数。
// .claude/workflows/dependency-audit.js
export const meta = {
name: "dependency-audit",
description: "审计所有 npm 依赖的已知 CVE 和过时版本",
phases: [
{ title: "盘点", detail: "列出所有依赖" },
{ title: "审计", detail: "并行检查每个依赖" },
{ title: "优先级", detail: "按严重程度排序发现" },
{ title: "报告", detail: "生成可操作的报告" }
]
};
export default async function ({ agent, parallel, pipeline, phase, log, args, budget }) {
const { packageJsonPath = "package.json", threshold = "high" } = args;
// 阶段 1:盘点
phase("盘点");
const inventory = await agent(
`读取 ${packageJsonPath} 并返回所有依赖及其版本约束`,
{
schema: {
type: "object",
properties: {
dependencies: {
type: "array",
items: {
type: "object",
properties: {
name: { type: "string" },
version: { type: "string" },
isDev: { type: "boolean" }
},
required: ["name", "version", "isDev"]
}
}
}
},
agentType: "explore"
}
);
log(`找到 ${inventory.dependencies.length} 个依赖`);
// 阶段 2:并行审计
phase("审计");
const auditResults = await parallel(
inventory.dependencies.map(dep => () => agent(
`研究 npm 包 ${dep.name}@${dep.version} 的已知 CVE。返回严重程度和 CVE ID。`,
{
schema: {
type: "object",
properties: {
package: { type: "string" },
hasVulnerabilities: { type: "boolean" },
severity: { type: "string", enum: ["critical", "high", "medium", "low", "none"] },
cves: { type: "array", items: { type: "string" } }
}
},
label: `audit:${dep.name}`
}
))
);
// 阶段 3:优先级排序(纯 JS,不需要智能体)
const vulnerabilities = auditResults
.filter(r => r?.hasVulnerabilities)
.sort((a, b) => severityRank(b.severity) - severityRank(a.severity));
log(`${vulnerabilities.length} 个有漏洞的包`);
// 阶段 4:报告
phase("报告");
const report = await agent(
`编写依赖安全报告。阈值:${threshold}。发现:${JSON.stringify(vulnerabilities)}`,
{ phase: "报告" }
);
return { report, vulnerabilitiesFound: vulnerabilities.length };
}
function severityRank(s) {
return { critical: 4, high: 3, medium: 2, low: 1, none: 0 }[s] ?? 0;
}
触发工作流的四种方式
- 在提示词中引用名称:
创建一个工作流来审计我们的 npm 依赖中的 CVEClaude 会生成一个工作流或调用匹配的已有
.claude/workflows/文件。 - 引用特定工作流文件:
用 threshold=critical 运行 dependency-audit 工作流 - 用 CLI 的
--workflow标志启动:claude --workflow dependency-audit --args '{"threshold":"critical","packageJsonPath":"services/api/package.json"}' - 激活 ultracode 自动触发:
/effort ultracode 审计我们的 npm 依赖在 ultracode 模式下,Claude 自行判断是否值得使用工作流,如果是则生成一个。
完整实例:Vue 新闻简报工作流
Alexander Opalic 记录了一个完整的真实世界示例,清晰地展示了扇出 → 归约 → 合成模式。任务是:"本周 Vue 和 Nuxt 生态发生了什么。"许多独立数据源需要检查,然后合并,再写一篇总结。工作流并行产生 9 个智能体,每个挖掘不同的来源,将发现收集到同一个列表中,按影响力排序,然后写出一份摘要。
关键教训:
- 用
parallel()扇出: 所有 9 个数据源同时命中。每个返回验证过的 JSON。如果一个智能体失败或返回垃圾数据,filter(Boolean)干净地丢弃。 - 用纯 JavaScript 归约: 去重、扁平化和过滤是确定性操作——不需要智能体。用智能体来"去重这个列表"耗费 token、引入非确定性,严格劣于三行 JavaScript。
- 用顺序
agent()调用合成: 策展智能体和写作智能体顺序运行,因为每一步需要前一步的完整输出。当每一步依赖前一步时,用顺序智能体,而非并行智能体。
这次运行汇总了 Nuxt UI 发布、Vue Router v5 小版本、Vue 核心补丁和马德里会议回顾——九个来源的 17 个项目,约三分钟完成。
八、生产模式与质量门禁
循环直到收敛模式
对于事先不知道需要多少次迭代的任务,在工作流内部实现一个收敛循环:
let iteration = 0;
let remainingIssues = await findInitialIssues();
while (remainingIssues.length > 0 && iteration < 10) {
phase(`修复迭代 ${iteration + 1}`);
log(`还有 ${remainingIssues.length} 个问题需要处理`);
// 并行修复问题
await parallel(
remainingIssues.map(issue => () => agent(
`修复这个问题:${JSON.stringify(issue)}`,
{ agentType: "general-purpose" }
))
);
// 验证 — 运行测试、静态分析等
const verification = await agent(
"运行测试并报告剩余问题",
{ schema: ISSUES_SCHEMA, agentType: "explore" }
);
remainingIssues = verification.issues;
iteration++;
}
log(`在 ${iteration} 次迭代后收敛。剩余 ${remainingIssues.length} 个问题。`);
这是 ultracode 验证循环的内部结构——你可以在自定义工作流中显式实现,当需要对收敛标准有更多控制时。
结构化输出作为质量门禁
agent() 的 schema 选项是一个质量门禁,而非格式化便利。当你指定 schema 时,运行时会:
- 指示智能体调用结构化输出工具
- 针对 schema 验证工具输出
- 验证失败自动重试
这意味着下游阶段可以始终信任 schema 验证过的结果具有预期形状。无需防御性的 if (result?.findings?.length) 检查。无需用 try/catch 包裹的 JSON.parse。Schema 就是契约。
✅ 使用场景: 下游阶段消费输出、输出将被归约或聚合(去重、排序、过滤)、输出将被存储或展示给用户
❌ 不用场景: 生成人类可读文本的终端阶段、内容比结构更重要的阶段
预算感知循环
const MAX_ITERATIONS = 20;
let i = 0;
while (i < MAX_ITERATIONS) {
if (budget && budget.used > budget.total * 0.85) {
log(`预算已达 ${Math.round(budget.used/budget.total*100)}% — 提前停止循环`);
break;
}
// ... 循环体
i++;
}
budget 对象提供 budget.used(已消耗的 token)、budget.total(预算目标)和 budget.remaining(差值)。未设定预算时为 null。
异构模型选择
不同阶段对智能的要求不同。你不需要用 claude-opus-4-8 来做文件列表任务,也不该用 claude-haiku-4-5 来做对抗性安全分析。用 agent() 的 model 选项匹配模型能力与任务复杂度:
// 阶段 1:探索 — Haiku 快而便宜,适合只读发现
const fileMap = await parallel(
directories.map(d => () => agent(`列出 ${d} 中的相关文件`, {
model: "claude-haiku-4-5",
agentType: "explore"
}))
);
// 阶段 2:分析 — Sonnet 负责主要工作负载
const analyses = await parallel(
files.map(f => () => agent(`分析 ${f} 的数据流问题`, {
model: "claude-sonnet-4-6",
schema: ANALYSIS_SCHEMA
}))
);
// 阶段 3:合成 — Opus 处理高风险最终推理
const report = await agent("将所有发现合成为一份执行层风险报告", {
model: "claude-opus-4-8"
});
核心原则:小模型用于路由和格式化,大模型用于推理。 在工作流上下文中,按阶段而非按智能体类型应用。
九、成本、可恢复性以及何时不该用工作流
理解 token 成本
Anthropic 明确描述动态工作流"消耗的 token 远超典型的 Claude Code 会话"。这不是个 bug——这正是工作流的直接后果:它们运行多个智能体,每个都有各自的上下文、提示和工具调用。
成本驱动因素:
- 智能体数量: 越多 = 越多 API 调用 = 越多 token。100 智能体的工作流比 10 智能体的贵。
- 模型选择:
claude-opus-4-8每 token 比claude-haiku-4-5贵。异构模型选择是主要的成本控制杠杆。 - Schema 验证重试: 如果智能体频繁验证失败需要重试,成本倍增。
- 对抗性轮次: 每个反驳智能体大约使提出者的成本翻倍。
实用建议: 开始新的工作流任务时,先用一个有限定范围的版本。在子集文件或单个模块上运行,为完整运行建立成本基线。
可恢复性的实际运用
可恢复性是动态工作流最有实际价值的属性之一。运行进度会持续保存,因此被中断的任务可以从断点恢复,而非从头开始。
这很重要因为:
- 长达数小时的工作流有网络中断、机器休眠和 API 超时的风险
- 因临时网络问题重新启动 200 智能体的工作流是不可接受的
- 缓存备份的恢复意味着无论发生多少次中断,你为每个智能体只付一次费
实践中: 长时间工作流用 --background 启动(如果可用),或在你不打算关闭的会话中运行。用 /workflows list 和 /workflows view 从另一个终端监控进度。
何时不该用动态工作流
三个条件同时满足时才用: 任务太大放不进一个上下文窗口、拆分策略事先不知道、结果质量比 token 经济更重要。
🚫 避免使用的情况:
| 场景 | 原因 | 替代方案 |
|---|---|---|
| 任务有边界且定义清晰 | 30 秒就能想清楚编排逻辑 | 子智能体或简单顺序调用 |
| Token 预算是硬约束 | 工作流成本难预测 | 带显式绑定提示的子智能体 |
| 需要立即得到结果 | 工作流可跑几分钟到几小时 | 不需要工作流 |
| 编排逻辑稳定已知 | 结构固定,不应每次动态生成 | 自定义子智能体或技能 |
| 原型探索阶段 | 工作流设置的开销是摩擦 | 交互式 Claude Code + 标准子智能体 |
十、失败模式
动态工作流很强大,但不是魔法。理解失败模式与理解能力同样重要。
⚠️ 大规模审计中的幻觉发现
对抗验证减少误报——但无法消除它们。在 10,000 个文件的全代码库安全审计中,即使提出者的误报率只有 1%,也会产生 100 个假发现。对抗验证可能捕捉其中 70%,剩下 30 个假发现留在报告中。规模越大,即使错误率低,绝对错误数也在增长。
缓解措施: 适当设置反驳智能体的阈值。对于关键安全发现,要求多个独立反驳智能体失败才能通过。对于低严重性发现,单轮反驳就够了。
⚠️ 智能体间的上下文丢失
工作流中的每个智能体都有自己的隔离上下文窗口。智能体不共享记忆。如果智能体 A 发现了一个对智能体 B 至关重要的架构细节,这个细节必须通过工作流脚本显式传递——结构化输出、args 或写入磁盘的共享文件。
依赖智能体隐式共享上下文的工作流会产生不一致的结果。**扇出 → 归约
更多推荐



所有评论(0)