一句话结论:Claude Code 的动态工作流(Dynamic Workflows)让你把「编排多个 AI 子代理」这件事从「逐句对话」变成「一段可复用的脚本」。脚本在后台跑,几十个 agent 并行干活,最后只把结论吐回给你。本文讲清它是什么、怎么装、怎么写、怎么定时跑,并用一个「每日热点雷达」实战把它串起来。
本文依据:官方文档
code.claude.com/docs/en/workflows与scheduled-tasks(2026-09 快照)。该功能高频迭代,具体版本号请以官方 changelog 为准。
0. 先看结果:你要的东西长什么样
不铺垫,直接上目标。搭好之后,每天一条指令(或干脆定时),Claude 在后台并行扫一圈信息源,回来给你这么一份清单:
今日热点(2026-09-08)
1. [某开源库发布 2.0 大版本](https://github.com/...) —— 社区 700+ star,重构了核心 API
2. [某框架的并发模型深度解析](https://...) —— 讲清了事件循环与协程的关系
3. [某工具架构演进笔记](https://...) —— 作者拆解了它的模块划分与取舍
...
每一条都是简要信息 + 原始链接,等你自己点进去查阅。没有冗长的正文,没有自动替你产出内容——采集、去重、排序这堆脏活交给脚本,决策权留在你手里。
这就是「动态工作流」能干的事。下面拆开讲。
1. 什么是动态工作流,为什么值得学
Claude Code 里做「多步任务」有四种工具,官方文档给了张对比表,我提炼成一句话:
| 工具 | 谁来决定下一步 | 中间结果放哪 | 能扩展多大 |
|---|---|---|---|
| 子代理 Subagent | Claude 逐轮决定 | Claude 上下文窗口 | 每轮几个 |
| Skills | Claude 按提示词走 | 上下文窗口 | 同子代理 |
| Agent Teams | 领队 agent 逐轮决定 | 共享任务列表 | 几个长期 peer |
| Workflows | 脚本代码 | 脚本变量 | 几十到几百个 agent/轮 |
核心区别在于**「谁持有计划」。前三者都是 Claude 边想边干,每个结果都塞进上下文窗口,任务一大上下文就爆。而 Workflows 把循环、分支、中间结果**全部下沉到一段 JavaScript 脚本里,Claude 的上下文里只装最终答案。
两个直接好处:
- 规模:一个 agent 装不下的任务(全库 bug 扫描、几百个文件迁移、多源交叉核验),workflow 能并行铺开。
- 可复现 + 可上质量模式:脚本里可以写「对抗验证」——让独立 agent 互相审查对方的发现,投票过滤后再上报,比单次跑一遍更可信。
什么时候用:任务大到单个 agent 塞不下、或同一个步骤要在很多条目上重复执行。反之,一次性小活就别上 workflow,杀鸡用牛刀。
2. 安装与开启
动态工作流不是独立安装的包,它内置于 Claude Code,但要满足两个前置条件。
2.1 版本与计划
- 引入版本:v2.1.154(2026-05-28),后续功能持续迭代。
- 计划要求:所有付费计划 + Anthropic API + Amazon Bedrock / Google Cloud / Microsoft Foundry 都可用。Pro 用户需要在
/config里手动开启(Dynamic workflows 那一行)。
2.2 开启与关闭开关
# 开启(Pro 用户)
/config → 找到 "Dynamic workflows" 打开
# 关闭(个人)
/config → 关闭
# 或写入 ~/.claude/settings.json
"disableWorkflows": true
# 或环境变量(启动时读取)
CLAUDE_CODE_DISABLE_WORKFLOWS=1
团队管理员可用 managed settings 全局关闭,此处不展开。
2.3 触发关键词
在提示词里加 ultracode 关键词,Claude 就会为这个任务写一个 workflow,而不是逐轮干:
ultracode: 扫描 src/routes/ 下所有 API 端点,检查有没有缺失鉴权
- v2.1.160 之前,字面关键词是
workflow;之后改成ultracode。自然语言说「use a workflow」「run a workflow」两种版本都认。 - 想取消这次触发:macOS 按
Option+W,Windows/Linux 按Alt+W。 - 想彻底关掉关键词触发:
/config里关掉 "Ultracode keyword trigger"。
注意关键词的生效范围:只在你自己手动输入的提示词里触发。
claude -p传参、Agent SDK 非 human 来源、定时任务 prompt、webhook 转发的内容都不会触发工作流(v2.1.210 之前会)。
3. 脚本核心 API 速查
保存下来的 workflow 就是一段普通 JS。官方文档给的骨架长这样:
export const meta = {
name: 'audit-routes',
description: 'Audit every route handler for missing auth checks',
}
const found = await agent('List every .ts file under src/routes/.', {
schema: { type: 'object', required: ['files'], properties: { files: { type: 'array', items: { type: 'string' } } } } },
})
const audits = await pipeline(found.files, file =>
agent(`Audit ${file} for missing authentication checks.`, { label: file }),
)
return audits.filter(Boolean)
一个 workflow 脚本 = meta 块 + 一段顶层 await 的 JS 正文。运行时注入的原语:
| API | 作用 |
|---|---|
agent(prompt, opts) | 派一个子代理。opts 可带 phase / label / schema / agentType / model |
parallel(thunks) | 并行跑一组 agent 任务,等全部结束(屏障) |
pipeline(items, stage1, stage2, ...) | 每个 item 独立走完所有阶段,item 之间没有屏障(默认首选,更省墙钟时间) |
phase(title) | 在 /workflows UI 里给后面的 agent 分组打标题 |
log(msg) | 在进度树上方打一条信息 |
args | 运行时的入参(全局变量) |
budget | token 预算:total / spent() / remaining() |
workflow(nameOrRef) | 内联调用另一个工作流(只支持一层嵌套) |
agent() 的 schema 参数是重点:传一个 JSON Schema,运行时就会强制该子代理调用结构化输出工具,返回的是校验过的对象,不是要你手动 parse 的文本。校验失败会自动带纠正提示重试。
3.1 确定性约束(踩坑重灾区)
脚本必须「纯净」,否则没法安全 resume:
- ❌
Date.now()、Math.random()、无参new Date()—— 直接抛错(时间戳通过args传进来) - ❌
import()/require/fs/process/ 网络 —— 脚本正文没有这些。要文件或网络,让 agent 去干 - ❌ 运行中用户输入(除了权限提示)
- ⚠️ 单次
parallel()/pipeline()最多 4096 个 item,超了直接报错(不静默截断) - ⚠️ 并发 agent 上限
min(16, cpu核数-2),超出排队 - ⚠️ 单次运行 agent 总数上限 1000(防失控死循环)
3.2 agent() 返回 null 的含义
中途 stop、或遇到不可恢复的 API 错误时,agent() 解析为 null。所以拿到结果后记得 .filter(Boolean),否则后面处理会踩空。
4. 从 0 创建一条工作流
4.1 三种创建方式
- 让 Claude 现写:提示词加
ultracode(或说「use a workflow」),Claude 为当前任务写一个临时脚本跑掉。 - 让 Claude 决定:
/effort ultracode,让 Claude 对会话里每个实质任务自动规划 workflow(token 消耗更大,慎用)。 - 跑现成命令:内置的
/deep-research <问题>,或你自己保存过的/<name>。
4.2 运行前的确认
CLI 下每次跑会弹一个确认,几个选项:
- Yes, run it —— 开始
- Yes, and don't ask again —— 以后这个 workflow 在此项目里不再问(只对按名字跑的内置/已保存 workflow 出现)
- View raw script —— 先看脚本再决定(
Ctrl+G直接在编辑器打开) - No —— 取消
确认与否取决于权限模式:Auto 模式只问第一次(记录同意);Bypass / claude -p / Agent SDK 不弹。
4.3 保存为可复用命令
跑完满意后,在 /workflows 里选中该 run,按 s 保存。对话框里 Tab 切换两个位置:
- 项目级
.claude/workflows/—— 随仓库共享 - 个人级
~/.claude/workflows/—— 每个项目都可用,仅自己可见
保存后变成 /<name> 命令,出现在 / 自动补全里。
4.4 传参
保存的 workflow 可以通过 args 接参数(脚本里读全局 args):
Run /triage-issues on issues 1024, 1025, 1030
args 以结构化数据传入,脚本里能直接 .filter / .map,不用手动 parse。
5. 实战:每日热点雷达
现在把前面的串起来,写一个真正能跑的 workflow。需求一句话:
每天扫几个信息源,抓技术相关的热点,去重排序后,把每条热点的简要信息 + 原始链接列给我,等我自己查阅。
注意:这里只做采集、去重、排序,不接着往下产出内容。机器干脏活,最终看什么、追什么由你拍板——这是控制工作流复杂度的关键,也是「简单性优先」的落地。
5.1 完整脚本
export const meta = {
name: 'hot-radar',
description: '扫多个信息源抓当日技术热点,去重排序后返回简要信息与原文链接',
phases: [
{ title: 'Sweep', detail: '并行扫描各信息源' },
{ title: 'Rank', detail: '去重、按相关度与热度排序' },
],
}
// 入参:args = { date: 'YYYY-MM-DD' }(脚本内禁 Date,日期由调用方传入)
const input = typeof args === 'string' ? JSON.parse(args) : args
const date = input?.date || '今天'
// 采集阶段的结构化输出契约
const SWEEP_SCHEMA = {
type: 'object',
properties: {
ok: { type: 'boolean', description: '该源是否成功抓到' },
items: {
type: 'array',
items: {
type: 'object',
properties: {
title: { type: 'string' },
url: { type: 'string' },
heat: { type: 'string', description: '热度信号:排名/分数/星数等' },
summary: { type: 'string', description: '一句话摘要' },
},
required: ['title', 'url', 'summary'],
},
},
},
required: ['ok', 'items'],
}
const SOURCES = [
{ key: 'zhihu', name: '知乎热榜', hint: 'WebSearch「知乎热榜 今天」;或 curl https://api.zhihu.com/topstory/hot-list?limit=50' },
{ key: 'hn', name: 'Hacker News', hint: 'curl "https://hn.algolia.com/api/v1/search?tags=front_page&hitsPerPage=50"' },
{ key: 'github', name: 'GitHub Trending', hint: 'https://github.com/trending?since=daily,筛技术相关仓库' },
]
phase('Sweep')
const sweeps = await parallel(
SOURCES.map((s) => () =>
agent(
`你是热点采集单元,抓取「${s.name}」当前技术相关的热点。\n` +
`入口提示:${s.hint}\n` +
`要求:每条必须带真实可访问的 URL,禁止编造标题或链接;抓到多少给多少,不硬凑。`,
{ label: 'sweep:' + s.key, phase: 'Sweep', schema: SWEEP_SCHEMA }
)
)
)
// 合并所有源的结果(注意 agent 可能返回 null)
const allItems = sweeps.filter(Boolean).flatMap((s, i) =>
(s.items || []).map((it) => ({ ...it, source: SOURCES[i].name }))
)
if (allItems.length === 0) {
return { ok: false, reason: '所有源都没抓到数据', topics: [] }
}
phase('Rank')
const RANK_SCHEMA = {
type: 'object',
properties: {
topics: {
type: 'array',
items: {
type: 'object',
properties: {
rank: { type: 'number' },
title: { type: 'string' },
url: { type: 'string' },
heat: { type: 'string' },
why: { type: 'string', description: '为什么值得关注,一句话' },
},
required: ['rank', 'title', 'url'],
},
},
},
required: ['topics'],
}
const ranked = await agent(
`你是热点主编。对下面的原始候选跨源去重合并(同一话题多个源报道时并成一条,url 取最权威的),\n` +
`按「相关度 × 热度」综合排序,取前 10 条。\n` +
`日期:${date}\n` +
`原始候选 JSON:\n${JSON.stringify(allItems, null, 1)}`,
{ label: 'rank', phase: 'Rank', schema: RANK_SCHEMA }
)
// 直接返回排序后的列表,由主会话把每条格式化列给用户
return { ok: true, date, topics: ranked?.topics || [] }
5.2 逐段拆解
① meta 块:name 是保存后的命令名(/hot-radar),description 出现在 / 自动补全里,phases 里的标题必须和正文 phase('...') 的字符串完全一致,否则会各分各的组。
② 日期从 args 传:脚本里禁用 Date.now(),所以日期由调用方(比如定时任务)传进来,脚本只负责读。这是「确定性约束」的直接体现——同一脚本、同一入参,重跑能得到同样结果,resume 才能靠缓存重放。
③ 采集用 parallel + schema:三个源并行扫,各自返回结构化的 items,不用我 JSON.parse 文本。schema 里的 required: ['title','url','summary'] 保证每个 agent 要么给全字段、要么被运行时强制重试。
④ 合并时 .filter(Boolean):挡住返回 null 的 agent,避免 .flatMap 踩空。
⑤ Rank 单 agent 汇总:把去重、排序、打分这步交给一个「主编」agent,输入是所有原始候选,输出是排序后的 top 10。这一步是串行瓶颈,但因为只跑一次,不影响整体。
⑥ return 直接回列表:workflow 的最后返回值会回到主会话,Claude 把它格式化成开头那张「排名 + 链接」清单给你。没有写文件——你要的就是「列给我等查阅」。
5.3 如何跑起来
# 方式一:临时跑(不保存)
ultracode: 扫知乎热榜、Hacker News、GitHub Trending 的技术热点,去重排序后列前10条给我,附原文链接
# 方式二:保存后复用(推荐)
# 先让 Claude 写并跑一次,满意后在 /workflows 里按 s 保存为 /hot-radar
# 以后直接:
/hot-radar 今天
6. 定时调度:让它「每天」自己跑
workflow 本身不内置定时,但 Claude Code 有三种调度方式,官方文档给了对比:
| 方案 | 跑在哪 | 需要本机开机 | 需要会话开着 | 跨重启持久 | 最小间隔 |
|---|---|---|---|---|---|
| Cloud(Routines) | 云端,Anthropic 托管 | 否 | 否 | 是 | 1 小时 |
| Desktop 定时任务 | 你的机器 | 是 | 否 | 是 | 1 分钟 |
/loop | 你的机器 | 是 | 是 | --resume 恢复 | 1 分钟 |
6.1 最省事:/loop
/loop 1d /hot-radar 今天
/loop 把间隔转成 cron 表达式,每天到点自动跑一次 /hot-radar。支持 s/m/h/d 单位;不整除的间隔(如 7m)会取最近可整除值并告诉你。
6.2 精确到分钟:cron 工具
要「每天 9:03 跑」这种精确时刻,直接用 cron 工具(5 字段 cron,本地时区):
# 每天 9:03 跑一次
cron: "3 9 * * *" → 每天追一次热点
提示:别用
0 9 * * *这种整点——全球用户都爱用整点,会让请求扎堆。错开几分钟体验更好。
6.3 关键限制
/loop和 cron 任务都是会话级:关掉 Claude Code 就停。--resume/--continue能带回来未过期的任务。- 7 天过期:循环任务 7 天后自动终止(会再跑最后一次然后删除)。要长期跑,得续期。
- 要跨重启真正持久:用 Desktop 定时任务或云 Routines——但云方案拿不到本机文件、最小间隔 1 小时。
如果你的「每日追热点」需要本机文件、又不想每天手动续期,最稳的组合是:Desktop 定时任务(本机、持久、1 分钟粒度)驱动 /hot-radar。
7. 运行监控与成本控制
7.1 看进度
/workflows
进度视图按 phase 显示 agent 数、token 总数、耗时。按键:↑↓ 选、Enter/→ 钻入单个 agent 看它的 prompt 和结果、p 暂停/恢复、x 停止、r 重启、s 保存、f 按状态过滤。
7.2 成本:workflow 烧 token 很快
一次 run 会派一堆 agent,token 消耗显著高于普通对话,会计入你的计划用量。几个控制手段:
- 小切片试水:先跑一个目录而不是整个仓库,先跑窄问题而不是宽问题。
/workflows实时看每个 agent 的 token,随时停(停掉通常不丢已完成的结果)。- 规模档位:
/config workflowSizeGuideline=small(<5 agent)/medium(<15,默认)/large(<50)。这是给 Claude 的建议值不是硬上限,prompt 明确要更大规模会覆盖它。 - 给阶段指定小模型:低风险阶段(采集)用小模型,验证/排序用强模型。
Large workflow警告:单次调度超过 25 个 agent、或预估 token 超 150 万,进度条会提示,可去/workflows停掉。
7.3 失败恢复
- 同一会话内 resume:
/workflows选 run 按p,已完成 agent 返回缓存结果,只重跑失败及之后的。 - 跨会话:退出后新会话从头开始;
--resume的会话能靠~/.claude/projects/下的缓存重放。 - 改了脚本再跑:从「第一个 prompt 不同的 agent」开始,连同它之后的全重跑。
8. 踩坑清单(都是实测血泪)
Date.now()/Math.random()会直接抛错——脚本里想用时间/随机,一律通过args传时间戳进去,随机就用「在 agent prompt 里做随机」绕。meta.phases标题要和phase()严格一致——对不上会各开各的分组,进度视图乱。agent()可能返回null——stop 或 API 错误都返回 null,.filter(Boolean)是标配。meta必须是纯字面量——里面有变量/函数调用/展开,命令会从/自动补全里消失。- 旧闻重炒陷阱(本实战直接相关):知乎热榜这类源,「如何评价 X」式的问题可能是几个月前的旧闻被重新顶起。采集 agent 容易把「正在被热议」当成「事件是新的」。实战脚本里要么在采集阶段标注发布时间,要么在 Rank 阶段做「新鲜度硬过滤」,否则你每天看到的第一条可能是三个月前的旧新闻。
- 改了脚本要
/reload-skills——否则当前会话读的还是旧版本。 - 并发上限是
cpu核数-2——在 CPU 受限的容器里会更少,别指望 always 16 路并发。
9. 总结
动态工作流的本质一句话:把「编排」从 Claude 的脑子搬进代码。换来的是规模(几十到几百 agent)、可复现(脚本可保存、可 resume)、可上质量模式(对抗验证)。
对「每日追热点」这类每天重复、多源采集、可去重排序的任务,它是天然适配的:
parallel并行扫多源 → 2.schema强制结构化输出 → 3. 单 agent 去重排序 → 4.return列表回主会话,简要信息 + 原链接列给你查阅。
采集、去重、排序交给脚本,看什么由你拍板。这就是「机器干脏活,人做决策」的边界,也是让自动化真正可长期跑下去的关键。
附:参考资料
- 官方文档 · Dynamic Workflows:code.claude.com/docs/en/wor…
- 官方文档 · Scheduled Tasks(/loop、cron、Routines、Desktop 对比):code.claude.com/docs/en/sch…
- 官方博客 · Introducing dynamic workflows:claude.com/blog/introd…
- 完整文档索引(发现更多页面):code.claude.com/docs/llms.t…