实战:从零写一条每日摘要工作流
跟着一个具体业务场景把所有核心概念串起来:拉数据 → 分类 → 汇总 → 投递。
这一篇把一个具体的业务场景从头到尾走一遍,让前面学过的所有概念(workflow / step / agent / variables / classifier / sub_workflow / credentials)在一条真实流程里对号入座。15 分钟之后,你会有"我可以独立做一条工作流"的感觉。
- 已经注册 Braidrun 账号并登录。不会的先看 注册与登录。
- 有一把 OpenAI / Anthropic / DeepSeek 任一家的 API Key。完全没有也能走前几步,只是到真跑时会跳过 LLM step。
- (可选)一个 Slack Incoming Webhook URL —— 想体验投递环节时需要。
业务场景:每日科技新闻摘要
需求:
- 每天早上 8:00 自动跑;
- 从 Hacker News / 36Kr / The Verge RSS 抓 10 条最新科技新闻;
- 对每条做"AI / 硬件 / 其它"三类分类;
- 仅对分类为 AI 的那几条生成 60 字中文摘要;
- 汇总成 Markdown,投递到 Slack 频道;
- 发送前要求我点一下"确认"—— 避免投递脏数据。
拆成 Braidrun 的步骤
fetch— code 步骤:用 Node 抓 RSS 解析成 JSON。classify— classifier 步骤:每条新闻打类别标签。summarize— single 步骤:只对 AI 类生成中文摘要。compile— code 步骤:拼成一整份 Markdown。review— manual_approval:发送前暂停让我看一眼。deliver— sub_workflow:复用内置 Slack 投递模块。
第 1 步 · 新建工作流
- 左侧主导航点「工作流」→ 右上角「新建工作流」。
- 名称:
daily-tech-digest,描述:每天早 8 点的科技新闻摘要。 - 点「创建」。立刻跳入编辑器。
编辑器打开后是两栏:左边是画布(DAG 可视化),右边是 YAML。任何一边的改动会实时同步到另一边。
第 2 步 · 声明 Agent
切到 YAML 栏,在顶部(variables 之后)加一段 agents:
agents:
writer:
preset: writer
overrides:
max_iterations: 3
provider: openai_api_key # 引用凭据中心里的 Key说明:writer preset 专为中文写作优化。overrides 把 max_iterations 限到 3,让 Agent 不会在一条摘要上反复思考太多(摘要本身很简单)。
preset 详解见 Agent 配置详解。
第 3 步 · fetch:code 步骤抓 RSS
继续在 YAML 里,在 steps 下加 fetch:
steps:
- id: fetch
type: code
language: node
idempotent: true
code: |
const parser = new (await import('rss-parser')).default();
const feeds = [
'https://hnrss.org/frontpage',
'https://36kr.com/feed'
];
const articles = [];
for (const url of feeds) {
const feed = await parser.parseURL(url);
feed.items.slice(0, 5).forEach(item => {
articles.push({ title: item.title, url: item.link });
});
}
return { articles };
extract:
articles: $.articles几个要点:
language: node— Node 20 是内置支持的运行时之一。extract— 从脚本 stdout JSON 里挑出 articles 字段,下游用 steps.fetch.data.articles 引用。idempotent: true— "同一天拉新闻大概率是同样的结果"—— 服务重启续跑时可跳过。
你的脚本会在一次性隔离沙箱里运行,默认有 512MB 内存、0.5 CPU 核、300 秒超时。网络只允许出站请求。你可以放心 fetch 外部数据,但不能监听端口;写磁盘只有 /tmp 可用。
第 4 步 · classify:给每条新闻打标签
- id: classify
type: classifier
agent: writer
task: |
给下面这条新闻标题分一个类别:
标题: {{steps.fetch.data.articles[0].title}}
categories:
- name: ai
description: AI / LLM / 机器学习相关
- name: hardware
description: 硬件 / 芯片 / 设备
- name: other
description: 其它科技话题
output_variable: categoryclassifier 的输出可通过 {{classifier.category}} 引用 —— 就是上一条新闻的分类结果。我们会在下一步用它做条件分支。
变量引用的完整语法见 变量与表达式。
第 5 步 · summarize:只对 AI 类写摘要
- id: summarize
type: single
agent: writer
idempotent: true
condition: "{{classifier.category}} == 'ai'"
task: |
用一段中文(不超过 60 字)概括下面这条 AI 新闻:
{{steps.fetch.data.articles[0].title}}说明:
condition让这一步仅在分类结果是 ai 时执行,其它类别被跳过(状态 SKIPPED,不算失败)。idempotent: true—— 纯摘要是幂等的,同样的输入 → 同样的输出。
第 6 步 · compile:拼成 Markdown 整份
- id: compile
type: code
language: node
depends_on: [summarize]
code: |
const summaries = JSON.parse(process.env.STEP_OUTPUT_SUMMARIZE || '[]');
const md = [
'# 📰 今日科技 AI 摘要',
'',
...summaries.map((s, i) => `${i + 1}. ${s}`)
].join('\n');
return { markdown: md };
extract:
markdown: $.markdown这一步把上游所有 summarize 结果聚合成一份可直接发送的 Markdown。extract 把它暴露为 steps.compile.data.markdown,下游引用更干净。
第 7 步 · review:发送前人工确认
- id: review
type: manual_approval
title: "请审阅今日 AI 摘要"
body: "{{steps.compile.data.markdown}}"
approvers: [] # 空 = 团队所有 Admin 都可审批
notify: [in_app] # 也可以 slack / email / feishu这是 manual_approval 的最简形式 —— 流程会在这里暂停,"审批管理"页会出现一条待审批。你点"批准"工作流才往下走;点"拒绝"则 execution 状态变成 FAILED,后续 step 全部取消。
更灵活的审批配置(approvers 指定、notify channels、expiration)见 人工审批。
第 8 步 · deliver:sub_workflow 调用平台内置模块
- id: deliver
type: sub_workflow
module: dingyue-module-slack-deliver
depends_on: [review]
inputs:
slack_webhook_url: "{{var:slack_webhook_url}}"
message_text: "{{steps.compile.data.markdown}}"说明:
dingyue-module-slack-deliver是平台自带的内置投递模块之一,封装了 "组装 payload → 发消息 → 重试 → 回写状态" 整个套路。slack_webhook_url引用变量里配置的 Slack Incoming Webhook URL —— 我们会在第 10 步配。
第 9 步 · 画布预览
切回左边画布栏,你会看到 6 个节点按 fetch → classify → summarize → compile → review → deliver 连成一条线。双击任一节点可以展开它的 YAML 片段;拖动节点可调整位置 —— 布局变化只是视觉,不影响 YAML。
第 10 步 · 补齐凭据
在跑这条工作流之前,凭据中心里需要有:
openai_api_key(Agent 用的 LLM Key)- 类型选 api_key,值粘贴你从 OpenAI 拿的 sk-...。
slack_webhook_url(投递用的 Slack Incoming Webhook URL)- 类型选 api_key 或 secret_text,值粘贴 Slack Incoming Webhook URL。
保存后,工作流里引用的凭据名字和这里一一对应,就能被运行时解析了。
第 11 步 · 验证 & dry-run
11.1 验证
顶部点「验证」。平台用完整 agent 解析器做一次 round-trip;有语法 / 引用错误会给出具体行号 + 原因。搞定所有红线再下一步。
11.2 dry-run
点「执行」,对话框里勾选 dry-run,点"开始执行"。这次:
- fetch 会真的跑 —— code 步骤的副作用判断难,我们默认让它真跑(但网络沙箱受限);
- classify / summarize / compile —— 因为是 LLM,dry-run 时返回模拟值;
- review —— dry-run 下自动批准(不会真弹审批);
- deliver —— 模块知道 dry-run 模式,不会真发 Slack 消息。
成功的话,所有 step 都会是绿色 COMPLETED 或灰色 SKIPPED。去右下区检视每步的"输入 / 输出 / 变量快照",确认变量都串对了。
第 12 步 · 真跑一次
- 点「执行」, 取消 勾选 dry-run,开始执行。
- 右侧实时事件流开始刷。看 fetch 跑完后有没有拉到 10 条;classify 每跑一条打一个标签;summarize 只对 AI 那几条执行。
- 跑到 review 时,整个执行会暂停,状态变成 PENDING_APPROVAL。切到「审批管理」,你会看到一条待审批,点「查看」读 Markdown 草稿。
- 满意就点「批准」。工作流自动继续到 deliver,Slack 消息就发出去了。
- 跑完回到工作流详情页看这次 execution 的 token / cost 统计。
大概耗时 1–3 分钟(取决于网络),token 消耗约 5000–15000(按 openai gpt-4.1-mini 约 $0.01–0.03)。低于这个范围可能是拉新闻失败了,高于这个范围可能是 summarize 反复思考。
第 13 步 · 挂 cron 让它每天自动跑
- 左侧「调度管理」→「新建调度」。
- 目标工作流:daily-tech-digest;类型:cron;表达式:
0 0 8 * * ?;时区:Asia/Shanghai。 - 「参数预设」可以空 —— 我们的 variables 都有合理默认值。
- 保存。「下次触发时间」会显示明天 8:00。
工作流 YAML 完整版
展开看完整 YAML
name: daily-tech-digest
version: "1.0.0"
description: 每天早 8 点的科技新闻摘要
recovery:
autoResumeOnRestart: true
policy: RESUME_FROM_LAST_INCOMPLETE
agents:
writer:
preset: writer
overrides:
max_iterations: 3
provider: openai_api_key
steps:
- id: fetch
type: code
language: node
idempotent: true
code: |
const parser = new (await import('rss-parser')).default();
const feeds = [
'https://hnrss.org/frontpage',
'https://36kr.com/feed'
];
const articles = [];
for (const url of feeds) {
const feed = await parser.parseURL(url);
feed.items.slice(0, 5).forEach(item => {
articles.push({ title: item.title, url: item.link });
});
}
return { articles };
extract:
articles: $.articles
- id: classify
type: classifier
agent: writer
task: |
给下面这条新闻标题分一个类别:
标题: {{steps.fetch.data.articles[0].title}}
categories:
- name: ai
- name: hardware
- name: other
output_variable: category
- id: summarize
type: single
agent: writer
idempotent: true
condition: "{{classifier.category}} == 'ai'"
task: |
用一段中文(不超过 60 字)概括下面这条 AI 新闻:
{{steps.fetch.data.articles[0].title}}
- id: compile
type: code
language: node
depends_on: [summarize]
code: |
const summaries = JSON.parse(process.env.STEP_OUTPUT_SUMMARIZE || '[]');
const md = [
'# 📰 今日科技 AI 摘要',
'',
...summaries.map((s, i) => `${i + 1}. ${s}`)
].join('\n');
return { markdown: md };
extract:
markdown: $.markdown
- id: review
type: manual_approval
title: "请审阅今日 AI 摘要"
body: "{{steps.compile.data.markdown}}"
notify: [in_app]
- id: deliver
type: sub_workflow
module: dingyue-module-slack-deliver
depends_on: [review]
inputs:
slack_webhook_url: "{{var:slack_webhook_url}}"
message_text: "{{steps.compile.data.markdown}}"做完之后你已经会的
- 用 YAML + 画布双栏编辑一条完整工作流
- 代码步骤、classifier、single、manual_approval、sub_workflow 五种主模式的实际写法
- 变量引用(var / steps / classifier / credentials)的不同用途
- validate / dry-run / 真跑 / 挂 cron 的完整上线循环
- 凭据中心与工作流 YAML 之间怎么配合
接下来
- 读完 8 种步骤类型 — 每种的用法、字段、组合约束
- 学会断点调试 — 工作流变复杂时省 80% 的排错时间
- 模块化与复用 — 有了第二条第三条工作流时,把公共逻辑抽成 sub_workflow