跳转到主要内容
文档入门实战:第一条工作流
实战 · 15 分钟

实战:从零写一条每日摘要工作流

跟着一个具体业务场景把所有核心概念串起来:拉数据 → 分类 → 汇总 → 投递。

这一篇把一个具体的业务场景从头到尾走一遍,让前面学过的所有概念(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 的步骤

  1. fetchcode 步骤:用 Node 抓 RSS 解析成 JSON。
  2. classifyclassifier 步骤:每条新闻打类别标签。
  3. summarizesingle 步骤:只对 AI 类生成中文摘要。
  4. compilecode 步骤:拼成一整份 Markdown。
  5. reviewmanual_approval:发送前暂停让我看一眼。
  6. deliversub_workflow:复用内置 Slack 投递模块。

第 1 步 · 新建工作流

  1. 左侧主导航点「工作流」→ 右上角「新建工作流」。
  2. 名称: daily-tech-digest,描述: 每天早 8 点的科技新闻摘要
  3. 点「创建」。立刻跳入编辑器。

编辑器打开后是两栏:左边是画布(DAG 可视化),右边是 YAML。任何一边的改动会实时同步到另一边。

第 2 步 · 声明 Agent

切到 YAML 栏,在顶部(variables 之后)加一段 agents:

yaml
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:

yaml
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 — "同一天拉新闻大概率是同样的结果"—— 服务重启续跑时可跳过。
code 步骤的沙箱

你的脚本会在一次性隔离沙箱里运行,默认有 512MB 内存、0.5 CPU 核、300 秒超时。网络只允许出站请求。你可以放心 fetch 外部数据,但不能监听端口;写磁盘只有 /tmp 可用。

第 4 步 · classify:给每条新闻打标签

yaml
  - 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: category

classifier 的输出可通过 {{classifier.category}} 引用 —— 就是上一条新闻的分类结果。我们会在下一步用它做条件分支。

变量引用的完整语法见 变量与表达式

第 5 步 · summarize:只对 AI 类写摘要

yaml
  - 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 整份

yaml
  - 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:发送前人工确认

yaml
  - 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 调用平台内置模块

yaml
  - 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 步 · 补齐凭据

在跑这条工作流之前,凭据中心里需要有:

  1. openai_api_key (Agent 用的 LLM Key)
    • 类型选 api_key,值粘贴你从 OpenAI 拿的 sk-...。
  2. 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 步 · 真跑一次

  1. 点「执行」, 取消 勾选 dry-run,开始执行。
  2. 右侧实时事件流开始刷。看 fetch 跑完后有没有拉到 10 条;classify 每跑一条打一个标签;summarize 只对 AI 那几条执行。
  3. 跑到 review 时,整个执行会暂停,状态变成 PENDING_APPROVAL。切到「审批管理」,你会看到一条待审批,点「查看」读 Markdown 草稿。
  4. 满意就点「批准」。工作流自动继续到 deliver,Slack 消息就发出去了。
  5. 跑完回到工作流详情页看这次 execution 的 token / cost 统计。
第一次真跑的期望

大概耗时 1–3 分钟(取决于网络),token 消耗约 5000–15000(按 openai gpt-4.1-mini 约 $0.01–0.03)。低于这个范围可能是拉新闻失败了,高于这个范围可能是 summarize 反复思考。

第 13 步 · 挂 cron 让它每天自动跑

  1. 左侧「调度管理」→「新建调度」。
  2. 目标工作流:daily-tech-digest;类型:cron;表达式: 0 0 8 * * ?;时区:Asia/Shanghai。
  3. 「参数预设」可以空 —— 我们的 variables 都有合理默认值。
  4. 保存。「下次触发时间」会显示明天 8:00。

工作流 YAML 完整版

展开看完整 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 之间怎么配合

接下来