Workflow 基础:用代码调度多个 Agent

Workflow 基础:用代码调度多个 Agent

系列第 3 篇 · 前置:第 1 篇第 2 篇


前两篇讲的是单个 Agent 怎么定义、Prompt 怎么写。这篇开始讲怎么把多个 Agent 串起来干活。

Shell 脚本也能串(claude -p 一个接一个),但数据靠文件传,并行写起来麻烦。

Workflow 解决的就是这个:用一段 JS 代码决定什么时候派谁、数据传给谁,你只需要说一句"开始"。

别怕,不需要会 JS。这篇看完你就知道,写 Workflow 只需要四样东西。


一、先看效果:一个完整的 Workflow 长什么样

这是我实际用过的自动写文章流程:三源并行搜索 → 汇总列大纲 → 写初稿。

javascript 复制代码
export const meta = {
  name: 'auto-article',
  description: '三源并行搜索 → 汇总大纲 → 写初稿',
  phases: [
    { title: '搜集资料' },
    { title: '列大纲' },
    { title: '写初稿' },
  ],
}

const TOPIC = args.topic || 'Claude Code Agent 编排'

// 阶段 1:三个 Agent 同时搜(搜资料用 haiku,便宜)
phase('搜集资料')
const [chinese, english, github] = await parallel([
  () => agent(`搜索「${TOPIC}」的中文资料,找 3 个最有价值的来源`, { label: '搜中文', model: 'haiku' }),
  () => agent(`Search for "${TOPIC}" in English, find 3 sources`, { label: '搜英文', model: 'haiku' }),
  () => agent(`搜索「${TOPIC}」相关的开源项目和官方文档`, { label: '搜开源', model: 'haiku' }),
])

// 阶段 2:汇总资料,列大纲
phase('列大纲')
const allMaterials = [chinese, english, github].join('\n---\n')
const outline = await agent(
  `基于以下资料列出文章大纲:\n${allMaterials}`,
  { phase: '列大纲' }
)

// 阶段 3:写初稿
phase('写初稿')
const draft = await agent(
  `根据大纲写一篇 1200 字初稿:\n${outline}`,
  { phase: '写初稿' }
)

return draft

这段代码做的事:

arduino 复制代码
你说"开始" → 三个 Agent 同时搜三个源
           → 结果拼在一起,派一个 Agent 列大纲
           → 大纲喂给另一个 Agent 写初稿
           → 返回文章

二、你只需要会四样 JS

写 Workflow 不需要学完整的 JS,简单总结了一下:这四样基本就够了:

1. await agent():派活,等结果

vbnet 复制代码
const result = await agent('帮我搜一下 Claude Code Workflow 的用法')

读作:派一个子 Agent 去做引号里的事,等它干完,把结果(一段文字)存进 result 变量。

await 就是"等它回来再往下走"。没有它,代码不会等 Agent 干完就直接执行下一行了。

2. 反引号和 ${}:拼字符串

ini 复制代码
const topic = 'Claude Code'
const prompt = `帮我搜一下${topic}的用法`
// prompt 的实际内容是:帮我搜一下Claude Code的用法

反引号 ````` 就是键盘左上角和 ~ 在一起的那个键。它和普通引号的区别是:可以跨行,可以用 ${变量名} 把变量插进去。

你在 Prompt 里塞资料、塞上一步结果,全靠这个。

3. parallel([]):同时派多个

scss 复制代码
const [a, b, c] = await parallel([
  () => agent('搜中文'),
  () => agent('搜英文'),
  () => agent('搜开源'),
])

两个要点:

  • 每个 agent() 外面包一层 () =>,意思是"先别跑,等 parallel 发令"。不包的话写的时候就执行了,变成串行。
  • const [a, b, c] 叫解构赋值,就是把三个结果分别放进三个变量。顺序和数组里的顺序对应。

parallel 会等所有 Agent 都回来才往下走。

4. return:交结果

kotlin 复制代码
return draft

最后把最终产物返回。你在对话里看到的输出就是它。

没了。变量声明用 const,字符串拼接用 +${},条件判断偶尔用个 ||(给默认值),其他 JS 特性一概不需要。


三、逐行拆解

回头看开头那段代码,一行行说。

meta 头

css 复制代码
export const meta = {
  name: 'auto-article',
  description: '三源并行搜索 → 汇总大纲 → 写初稿',
  phases: [
    { title: '搜集资料' },
    { title: '列大纲' },
    { title: '写初稿' },
  ],
}

告诉系统这个 Workflow 叫什么、分几个阶段。phases 只影响进度面板的显示,不写也能跑,写了能看到跑到哪一步。

phases 只影响进度面板的显示,不写也能跑,写了能看到跑到哪一步。想让某个阶段用便宜模型,在 agent() 调用里传 { model: 'haiku' }

注意:meta 必须是纯字面量,不能在里面写变量或函数调用。

接参数

ini 复制代码
const TOPIC = args.topic || 'Claude Code Agent 编排'

args 是系统给的全局变量。你运行时说"主题是 Remotion",args.topic 就是 "Remotion"|| 后面是默认值,不传就用它。

阶段切换和日志

scss 复制代码
phase('搜集资料')

切换当前阶段,后面的 Agent 会归到这个阶段下显示。和 meta 里的 title 对应。

三个 Agent 并行搜索

javascript 复制代码
const [chinese, english, github] = await parallel([
  () => agent(`搜索「${TOPIC}」的中文资料,找 3 个最有价值的来源`, { label: '搜中文' }),
  () => agent(`Search for "${TOPIC}" in English, find 3 sources`, { label: '搜英文' }),
  () => agent(`搜索「${TOPIC}」相关的开源项目和官方文档`, { label: '搜开源' }),
])

三个 Agent 同时启动,各搜各的。{ label: '搜中文' } 只影响进度面板上显示的名字。

三个都回来后,结果按顺序进 chineseenglishgithub 三个变量。

拼资料

csharp 复制代码
const allMaterials = [chinese, english, github].join('\n---\n')

把三个搜索结果用分隔线拼成一个大字符串。等价于 chinese + '\n---\n' + english + '\n---\n' + github,但更好读。

列大纲和写初稿

javascript 复制代码
phase('列大纲')
const outline = await agent(
  `基于以下资料列出文章大纲:\n${allMaterials}`,
  { phase: '列大纲' }
)

phase('写初稿')
const draft = await agent(
  `根据大纲写一篇 1200 字初稿:\n${outline}`,
  { phase: '写初稿' }
)

return draft

串行的两步:大纲 Agent 拿到全部资料,产出大纲;初稿 Agent 拿到大纲,产出文章。每一步的输出是下一步的输入,数据在变量之间传递,不写临时文件。


四、怎么运行

在 Claude Code 对话里直接说:

arduino 复制代码
运行 workflows/auto-article.js,主题是 Remotion 视频渲染优化

系统会读这个文件,把 { topic: 'Remotion 视频渲染优化' } 传给 args,然后执行。会看到进度面板实时显示三个阶段的状态。

不传主题就用默认值:

arduino 复制代码
运行 workflows/auto-article.js

五、Shell vs Workflow:什么时候用哪个

你可能已经在用 Shell 脚本调 claude -p 串流程了。两者不是替代关系,是场景不同。

维度 Shell claude -p Workflow agent()
运行条件 终端能跑就行,Claude Code 不用开着 必须在 Claude Code 会话里
定时任务 能,cron/launchd 直接调 不能
并行 for 循环串行,写并行麻烦 parallel() 原生支持
数据传递 写临时文件再读 JS 变量直接传
中间产物 每步落盘,可审计 只在内存里
进度显示 看终端输出 进度面板分阶段显示
一个 Agent 挂了 整条链路断 返回 null,其他继续

我的判断标准很简单:

复制代码
人不在电脑前(定时跑、无人值守) → Shell
人在电脑前,需要并行              → Workflow
人在电脑前,串行简单任务          → 随便,哪个顺手用哪个
需要中途看一眼再决定下一步        → 别写脚本,在对话里手动来

比如我那个每天自动生成视频的流水线,用 Shell(launchd 定时触发)。但我在电脑前要快速出一篇文章,用 Workflow(并行快)。


六、常见坑

1. 忘了 () =>,变成串行

scss 复制代码
// 错:写的时候就执行了,parallel 拿到的是结果不是任务
await parallel([
  agent('搜中文'),
  agent('搜英文'),
])

// 对:包成函数,parallel 内部统一发射
await parallel([
  () => agent('搜中文'),
  () => agent('搜英文'),
])

2. 忘了 await

csharp 复制代码
// 错:result 不是结果,是一个"还没干完的凭据"
const result = agent('搜一下')

// 对:等它干完
const result = await agent('搜一下')

3. meta 里写变量

meta 必须是纯字面量。想根据参数改阶段名做不到,阶段名是固定的。

4. 给 Agent 的 Prompt 里忘塞资料

javascript 复制代码
// 错:Agent 不知道你要它分析什么
const outline = await agent('列个大纲')

// 对:把资料拼进去
const outline = await agent(`基于以下资料列大纲:\n${allMaterials}`)

子 Agent 看不到主对话和其他变量,它只拿到你写在 Prompt 里的东西。


小结

javascript 复制代码
Workflow = 一段 JS 代码,决定什么时候派谁、数据传给谁

四样 JS 走天下:
  await agent('任务')           派活等结果
  `文字${变量}`                 拼字符串
  parallel([() => agent(), ...]) 同时派多个
  return 结果                   交差

和 Shell 的区别:
  定时无人值守 → Shell
  在电脑前要并行 → Workflow
  要中途人工判断 → 手动对话
相关推荐
XLYcmy40 分钟前
PDF论文处理器 - 完整使用指南
pdf·llm·json·agent·token·csv·dify
cuguanren42 分钟前
Agent 框架的会话记忆管理机制
人工智能·ai·大模型·llm·agent·上下文管理·会话记忆管理
SelectDB1 小时前
开源!Apache Doris 上线 Profile 可视化诊断:基于 Doris Skills 破解 AI 误诊难题
数据库·agent·自动化运维
0end12 小时前
AI Agent 学习笔记(七):Coding Agent 与代码生成——代码是 Agent 的元能力
ai编程
Lambert2812 小时前
Spring AI 2.0 vs LangChain4j 1.19:MCP 新规范竞速,Spring AI 慢在哪
langchain·ai编程
quarter_day2 小时前
DeepSeek Harness (DSH) 中配置 Google Gemini 与避坑实践
agent·deepseek
樊小肆2 小时前
DeepSeeker-Code源码导读12-Hooks四引擎
前端·人工智能·agent
Sword992 小时前
近 90 天用了 14 亿 Token,我拿 AI 干了些什么
ai编程
DigitalOcean2 小时前
GLM 5.3 已上线 DigitalOcean AI 推理云平台:Agent 任务的高性价比底座来了
llm·agent·chatglm (智谱)