← 返回文章列表

Dify 中级实验(07):子工作流——如何把公共逻辑做成可复用积木?

1. 业务场景

先讲一个我们实际遇到的场景。

一家同时做客服、产品调研、舆情监控的公司:客服要实时看客户情绪,产品团队要分析用户反馈,舆情部门要盯社区言论。三拨人各自开发了一套「情绪分析」——三份重复代码、三套维护成本、三个不一致的口径:客服判「负面」的,舆情可能判「中性」。

我们第一次接这类需求时,第一反应也是「把情绪分析的代码复制三份,各改各的参数」。真正动手才发现——需求一样、代码三份,改一处要改三处,漏改一套口径就漂移。后来翻 Dify 的节点列表才发现:模块化设计有原生手段——子工作流(Sub-workflow):把「情绪分析」这类公共逻辑抽成独立工作流,一处定义、多处调用(1.16 的真实做法是发布为工具调用)。

这不是个例。任何「多个业务线共用同一能力」的业务场景都是这个模式:情绪分析、文本清洗、格式化输出……公共逻辑不做模块化,就是在重复造轮子,而且每个轮子还不太一样。

2. 场景痛点

这个流程的痛点,在研发/业务团队身上体现得最直接:

本质上,公共逻辑每多一个调用方,重复成本就翻一倍——模块化的价值不在「少写代码」,而在「一处定义、多处调用、接口即契约」

3. 方案:为什么是子工作流

选子工作流的理由,我们实际对比过:

这篇文章我们就用它搭「情绪分析引擎」子工作流 + 两个消费它的主工作流(客服情绪看板 / 批量反馈分析)。

4. 整体架构

graph TD subgraph sub_engine["子工作流:情绪分析引擎"] s_start["开始:text / language"] s_llm["情绪分析:LLM"] s_pe["提取结构化字段:PE"] s_end["结束"] end %% 输出:sentiment / score / confidence / keywords / brief / urgency subgraph main_board["主工作流 1:客服情绪看板"] m1_start["开始:customer_message / agent_name"] m1_tool["调用情绪分析引擎:Tool provider_type workflow"] m1_parse["解析情绪结果:Code 从 json 数组展平字段"] m1_rule{"紧急度分支:IF-ELSE"} m1_high["高优安抚回复:LLM"] m1_end_high["结束:高优"] m1_normal["常规回复:LLM"] m1_end_normal["结束:常规"] end subgraph main_batch["主工作流 2:批量反馈分析"] m2_start["开始:feedback_list JSON 数组"] m2_parse["解析反馈列表:Code"] m2_iter["逐条情绪分析:迭代 Tool + 解析 Code"] m2_summary["汇总情绪分布:Code"] m2_end["结束"] end s_start --> s_llm --> s_pe --> s_end m1_start --> m1_tool --> m1_parse --> m1_rule m1_rule -- "case_high" --> m1_high --> m1_end_high m1_rule -- "case_normal" --> m1_normal --> m1_end_normal m2_start --> m2_parse --> m2_iter --> m2_summary --> m2_end

链路很清晰:子工作流定义能力,主工作流消费能力——情绪分析引擎只做「分析并输出结构化字段」,两个主工作流各自编排自己的业务逻辑(看板按紧急度分流、批量分析走迭代)。

5. 模块设计

5.1 子工作流:枚举字段必须给显式规则

LLM 输出的 JSON 里 urgency 是枚举字段——只写 low/medium/high 不给规则,LLM 会随意输出(实测负面投诉返回 low,下游分支全走错):

prompt_template:

- id: p_sentiment

  role: system

  text: |

    你是一个专业情绪分析师。分析以下文本的情感,输出 JSON 格式(不要 Markdown):

    文本:{{#start.text#}}

    {

      "sentiment": "positive/negative/neutral/mixed",

      "score": 0.0 到 1.0 之间的浮点数,

      "confidence": 0.0 到 1.0,

      "keywords": ["关键词1", "关键词2", ...],

      "brief": "一句话情感总结",

      "urgency": "low/medium/high"

    }

    紧急度规则:负面情绪/投诉/损坏/退款/愤怒类内容 → "high";中性咨询类 → "medium";正面/普通内容 → "low"

reasoning_format: separated

参数提取器 6 个参数(sentiment string / score number / confidence number / keywords array[string] / brief string / urgency string),reasoning_mode: function_call

5.2 主工作流:发布子工作流为工具(核心)

Dify 1.16.1 没有 sub-workflow 节点类型(运行时报 No class mapping found for node type: sub-workflow),必须「发布为工具」后用 tool 节点调用:

- data:

    provider_type: workflow

    provider_name: dify102_08_01_情绪分析引擎

    provider_id: 11e9699e-ffa8-448f-8278-e16799e5912a   # 发布时的注册 ID,重发会变!

    tool_name: dify102_08_01

    tool_description: 情绪分析引擎(子工作流)——输入文本,输出结构化情绪字段

    type: tool

    title: 调用情绪分析引擎

    tool_configurations:          # ⚠️ 与 tool_parameters 双写同一份值(UI 权威格式)

      text: {type: mixed, value: '{{#start.customer_message#}}'}

      language: {type: mixed, value: 中文}

    tool_parameters:

      text: {type: mixed, value: '{{#start.customer_message#}}'}

      language: {type: mixed, value: 中文}

    paramSchemas:

    - name: text

      default: 示例:太棒了,五星好评    # default 决定 UI 面板显示值

      required: true

      type: string

  id: tool_sentiment

5.3 输出不透传:必须解析展平

工具输出固定三件套 text(string)/files(array[file])/json(array[object])——子工作流 end 的自定义字段名不透传,下游直接引用 {{#tool.sentiment#}} 取不到。必须加代码节点从 json 数组提取:

def main(sent_json: list) -> dict:

    import json

    data = {}

    if isinstance(sent_json, list):

        for item in sent_json:

            if isinstance(item, dict):

                data.update(item)

    return {

        "sentiment": str(data.get("sentiment", "未知")),

        "brief": str(data.get("brief", "无摘要")),

        "urgency": str(data.get("urgency", "low")),

        "score": str(data.get("score", "")),

        "sentiment_json": json.dumps(data, ensure_ascii=False),

    }

紧急度分支用字符串比较(is / is not):

cases:

- case_id: case_high

  conditions:

  - comparison_operator: is

    value: high

    variable_selector: [cd_parse_sent, urgency]

    varType: string

  logical_operator: and

- case_id: case_normal

  conditions:

  - comparison_operator: is not

    value: high

    variable_selector: [cd_parse_sent, urgency]

    varType: string

  logical_operator: and

6. 运行验证

输入 预期 实测
正面:太棒了!客服很贴心,五星好评! sentiment=positive,urgency=low → 常规回复 与预期一致
负面:东西收到就坏了,联系客服三天没人理,太失望了! sentiment=negative,urgency=high → 安抚回复 与预期一致
中性:周二下午三点可以安排配送吗? sentiment=neutral,urgency=medium → 常规回复 与预期一致
批量:3 条反馈 JSON 迭代逐条分析,汇总情绪分布 与预期一致

7. 实战坑

现象 修复
用 sub-workflow 节点类型 运行报 No class mapping found for node type: sub-workflow 1.16 必须「发布为工具」:tool 节点 + provider_type: workflow
子工作流重导后 provider_id 失效 主工作流调用报 workflow provider not found 重发后从 tool-providers 动态查最新 provider_id 同步(实测 2a6187e8 → 11e9699e)
下游直接引用工具的自定义字段 {{#tool.sentiment#}} 取不到值,分支全走默认 工具输出只有 text/files/json,必须 code 解析 json 展平(见 5.3)
tool 参数只写 tool_parameters UI 配置面板显示参数为空,手动调试报「要分析的文本不能为空」 tool_parameterstool_configurations 双写同一份值;paramSchemas[].default 填占位值让 UI 不空
枚举字段不给规则 负面投诉消息返回 urgency=low,下游分支全走错 prompt 给显式映射规则(负面/投诉/退款→high,咨询→medium,正面→low)
迭代内 item 是字符串却传 item.content 工具收到空文本,全部判默认值 字符串 item 直接传 {{#iter.item#}}

💡 什么时候该抽子工作流:同一逻辑出现在 ≥2 个流程、接口稳定、输入输出边界清晰。接口即契约——改子工作流输出结构,所有调用方都要跟着改,这是模块化的真实成本。

8. 实验文档及源码获取

文章聚焦核心配置与采坑点;实验的完整分步操作(节点搭建/参数表/调试指引)见实验文档原文。

本系列 · Dify 实验 · 中级
  1. Dify 中级实验(01):参数提取器实战——如何从自然语言中提取结构化数据?
  2. Dify 中级实验(02):问题分类器——智能路由引擎如何四路分发?
  3. Dify 中级实验(03):模板转换实战——如何用零 Token 完成文本加工?
  4. Dify 中级实验(04):迭代进阶——如何批量处理数据并守住性能边界?
  5. Dify 中级实验(05):并行执行——如何让多路任务同时跑?
  6. Dify 中级实验(06):变量聚合——如何确定性合并多路分支结果?
  7. Dify 中级实验(07):子工作流——如何把公共逻辑做成可复用积木?
  8. Dify 中级实验(08):代码节点进阶——如何用标准库处理文件与数据?
  9. Dify 中级实验(09):HTTP 节点进阶——如何搞定认证、分页与错误重试?
  10. Dify 中级实验(10):知识库深度调优——如何科学评估检索质量?
  11. Dify 中级实验(11):高级 RAG 流水线——如何搭建多路检索与精排?
  12. Dify 中级实验(12):Agent 深度配置——如何让智能体自主调用工具?
  13. Dify 中级实验(13):多 Agent 协作——如何编排多个智能体分工干活?
  14. Dify 中级实验(14):对话变量与状态管理——如何让工作流记住多轮对话的状态?
  15. Dify 中级实验(15):条件分支高阶策略——多条件路由如何避免分支爆炸?
  16. Dify 中级实验(16):错误处理与降级——工作流如何有尊严地失败?
  17. Dify 中级实验(17):调试监控与性能优化——响应慢和 Token 超支如何定位?
  18. Dify 中级实验(18):插件开发入门——如何把工作流变成 Agent 可调用的工具?
  19. Dify 中级实验(19):综合实战——如何把 19 个实验串成一条生产级流水线?
  20. Dify 中级实验(20):综合实战——自动化报告生成流水线如何从数据到周报一步到位?

联系我

15088711270

手机端点击号码可直接拨打 · 桌面端可复制

微信二维码

扫码加微信 · 备注「门户」更快通过