跳转到主要内容
文档构建工作流模块化与复用

模块化与复用

sub_workflow、把一段逻辑封装成模块、Promote / Demote 一键重构 —— 让工作流不再一条条复制粘贴。

当你做到第二、第三条工作流时,会发现某段逻辑反复出现 —— 拉 ASA 数据、发 Slack 消息、生成 Excel 报表。再复制粘贴下去,改一处要改三处、测试重复、版本失控。这时候就该做模块化了。

模板 vs 模块 再强调一次

模板 = 起始文档的快照,克隆后独立演化;模块 = 可被引用的子流程,升级后调用方自动受益。两者不是替代关系,是互补的。本页讲模块。

什么是「模块」

一个"模块"本质上就是一个声明了 WorkflowModuleContract 顶层字段的 workflow。它额外公开了:

  • inputs — 调用方必须传入的参数(带名称、类型、必填、默认值、描述)
  • outputs — 执行完产生的结构化结果(调用方可直接引用)
  • version — 语义化版本号,便于调用方锁定
yaml
name: my-team-slack-notifier
version: "1.0.0"

module:
  enabled: true
  inputs:
    webhook_url:
      type: string
      required: true
      description: Slack incoming webhook URL
    channel:
      type: string
      required: false
      default: "#general"
    message:
      type: string
      required: true
    mentions:
      type: array
      items: { type: string }
      required: false
  outputs:
    message_ts:
      type: string
      description: Slack 返回的 message timestamp,可用于后续更新消息
    status:
      type: string
      enum: [sent, failed]

# 模块自己的步骤(内部细节,对调用方黑盒)
steps:
  - id: format_text
    type: code
    language: node
    code: |
      const msg = process.env.WF_VAR_MESSAGE;
      const mentions = JSON.parse(process.env.WF_VAR_MENTIONS || '[]');
      return { text: mentions.map(u => `<@${u}>`).join(' ') + ' ' + msg };

  - id: post
    type: code
    depends_on: [format_text]
    credentials: [slack_oauth]
    code: |
      //  Slack API,返回 message_ts / status 

有了契约之后,调用方(父 workflow)的 sub_workflow step 会:

  • 在保存时静态校验 —— 必填 inputs 缺一项就不让保存;
  • 在运行时校验 —— 类型不符 / 枚举不在列表里,直接报错;
  • 在编辑器里自动补全 —— 你写 inputs: 时能下拉看到模块公开了哪些字段。

父工作流如何调用模块

yaml
steps:
  - id: build_report
    type: code
    # ...

  - id: notify
    type: sub_workflow
    module: my-team-slack-notifier    # 引用我们上面定义的模块
    version: "^1.0.0"                  # 锁主版本
    inputs:
      webhook_url: "{{credentials.slack_webhook}}"
      channel: "#ops-alerts"
      message: "日报已生成:{{steps.build_report.data.summary}}"
      mentions: ["U1234", "U5678"]

  - id: record
    type: code
    depends_on: [notify]
    code: |
      const ts = process.env.STEP_OUTPUT_NOTIFY_MESSAGE_TS;
      // 把 ts 存到你自己的数据库,方便后续更新那条消息 ...

要点:

  • module: dingyue-module-slack-deliver 是模块 id,平台会按名字 + version 解析。
  • inputs: 和模块契约里的 inputs 一一对应。
  • 调用完成后,可通过 {{steps.deliver.outputs.message_id}} 读模块输出。

让已有工作流变成模块:Promote to module

大部分时候你不会从零写一个模块,而是从现有工作流里抽。流程:

  1. 打开一个你觉得"这段逻辑值得复用"的工作流。
  2. 在画布里用鼠标框选或 Shift+点击选中若干连续 step。
  3. 右键菜单 →「Promote to module」,或顶部工具栏同名按钮。
  4. 弹窗里:
    • 起一个有意义的模块名字(全局唯一,建议前缀你团队名,例:my-team-slack-notifier);
    • 平台会自动根据选中片段推断 inputs / outputs 初稿,你可以编辑;
    • 确认后点「创建模块」。
  5. 原工作流里这几步会被替换成一个 sub_workflow 节点 —— 参数和连线自动对齐。
Promote 的心智模型

就像 IDE 里的"提取函数"重构。你选一段代码,IDE 帮你抽成一个带参数和返回值的函数,并把原位置替换成调用。Braidrun 做的是完全一样的事 —— 只不过对象是 workflow step。

模块升级与版本兼容

每次保存一个模块都会创建一个新版本。父 workflow 在 sub_workflow step 里可以选择:

  • version: latest — 自动用最新版(适合你自己维护的模块)
  • version: "1.2.0" — 锁定到具体版本(适合引用别人的模块、怕升级破坏兼容)
  • version: "^1.2.0" — 允许小版本升级,禁止跨大版本

什么是"兼容的变更"?

  • ✓ 加一个 optional input(必填 / 默认值都不算破坏)
  • ✓ 加一个 output 字段
  • ✗ 删 / 改名 input
  • ✗ 删 / 改名 output
  • ✗ 改 input 类型

做破坏兼容的改动时,语义版本应该升到下一个 MAJOR。平台不会强制,但调用方锁 ^X.Y.Z 时会受保护。

环路检测

模块 A 不能间接引用自己。平台在两层做了检测:

  1. 发布时静态检测 — 保存模块时,DFS 扫所有它引用的其它模块的依赖图,有环则拒绝保存。
  2. 运行时动态检测 — 执行期间如果出现 runtime 解析到的环(譬如条件分支后才被引用),会立刻抛错。

最大递归深度默认 8 层。这是"父调子调孙"的深度,不是单次调用的 fanout。

变量隔离

模块跑起来是一个"子 WorkflowExecutionContext",和父上下文完全隔离:

  • 父的 variables / steps outputs / agents 默认不继承给子;
  • 子的内部 step 输出也不会溢出到父 —— 父只能看到契约里声明的 outputs;
  • 跨父子传数据的唯一方式是 inputs / outputs(显式契约)。

这种隔离是模块化可靠性的基石 —— "黑盒"不是比喻,是字面意义上的。

Demote from module:把模块还原为内联 step

如果你改着改着发现某段逻辑其实只有一处在用,做成模块反而增加了理解成本,可以反向 Demote:

  1. 在父 workflow 里选中那个 sub_workflow 节点;
  2. 右键 →「Demote inline」;
  3. 平台把模块的 step 展开回父 workflow,sub_workflow 节点消失。

原模块本身不会被删除 —— 如果还有其它工作流引用它,那些仍然有效。

在哪里管理所有模块

左侧主导航「模块库」是你的总管视图。这里可以:

  • 看所有模块列表(自带的 12 个 + 你团队创建的)
  • 按 "谁在引用我" 反向查询 —— 改模块前先看谁会受影响
  • 看模块的版本历史 / diff
  • 发布 / 归档 / 转移所有权

什么时候该做模块

  • ✓ 同一段逻辑在 2 个以上 workflow 里出现过
  • ✓ 一段逻辑稳定且对外有清晰契约(典型的 "发消息 / 查报表 / 生成 Markdown")
  • ✓ 一个单 workflow 已经超过 15 个 step,可读性下降
  • ✗ 一段逻辑只用一次且未来不会复用 —— 直接内联
  • ✗ 一段 2–3 行的小工具函数 —— 用 code step 的代码复用比模块更自然

下一步