Dify 中级实验(07):子工作流——如何把公共逻辑做成可复用积木?
Dify 实验系列 · 中级 07/20 | 实验编号:DIFY-102-08
上篇:Dify 中级实验(06):变量聚合——如何确定性合并多路分支结果?
1. 实验目的
掌握 子工作流(Sub-workflow) 的模块化设计:把「情绪分析」这类公共逻辑抽成独立工作流,一处定义、多处调用。理解 Dify 1.16 的真实实现方式——发布子工作流为工具(Workflow as Tool),以及接口即契约、修改即时性、禁止循环调用等设计原则。
适合场景:多个业务线共用同一能力(情绪分析、文本清洗、格式化输出)、复杂系统的分层设计。
2. 场景设计
客服系统、产品调研、舆情监控三条业务线都需要「客户情绪分析」。把情绪分析做成子工作流,两个主工作流消费它:
- 子工作流「情绪分析引擎」:输入 text/language → LLM 分析 → 参数提取器输出结构化字段(sentiment/score/confidence/keywords/brief/urgency);
- 主工作流 1「客服情绪看板」:单条消息 → 调子工作流 → 按 urgency 分支(high 安抚 / 其他正常回复);
- 主工作流 2「批量反馈分析」:JSON 反馈列表 → 迭代内逐条调子工作流 → 汇总情绪分布。
3. 节点拓扑
子工作流:情绪分析引擎
开始(text / language)→ 情绪分析(LLM)→ 提取结构化字段(PE)→ 结束
(输出:sentiment / score / confidence / keywords / brief / urgency)
主工作流 1:客服情绪看板
开始(customer_message / agent_name)
→ 调用情绪分析引擎(Tool:provider_type workflow)
→ 解析情绪结果(Code:从 json 数组展平字段)
→ 紧急度分支(IF-ELSE)
├─ case_high → 高优安抚回复(LLM)→ 结束(高优)
└─ case_normal → 常规回复(LLM)→ 结束(常规)
主工作流 2:批量反馈分析
开始(feedback_list:JSON 数组)
→ 解析反馈列表(Code)
→ 逐条情绪分析(迭代:Tool + 解析 Code)
→ 汇总情绪分布(Code)→ 结束
4. 关键配置
4.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。
4.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
4.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
5. 运行验证
| 正面:太棒了!客服很贴心,五星好评! | sentiment=positive,urgency=low → 常规回复 | 与预期一致 |
| 负面:东西收到就坏了,联系客服三天没人理,太失望了! | sentiment=negative,urgency=high → 安抚回复 | 与预期一致 |
| 中性:周二下午三点可以安排配送吗? | sentiment=neutral,urgency=medium → 常规回复 | 与预期一致 |
| 批量:3 条反馈 JSON | 迭代逐条分析,汇总情绪分布 | 与预期一致 |
6. 采坑点
| 用 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 展平(见 4.3) |
| tool 参数只写 tool_parameters | UI 配置面板显示参数为空,手动调试报「要分析的文本不能为空」 | tool_parameters 与 tool_configurations 双写同一份值;paramSchemas[].default 填占位值让 UI 不空 |
| 枚举字段不给规则 | 负面投诉消息返回 urgency=low,下游分支全走错 | prompt 给显式映射规则(负面/投诉/退款→high,咨询→medium,正面→low) |
| 迭代内 item 是字符串却传 item.content | 工具收到空文本,全部判默认值 | 字符串 item 直接传 {{#iter.item#}} |
💡 什么时候该抽子工作流:同一逻辑出现在 ≥2 个流程、接口稳定、输入输出边界清晰。接口即契约——改子工作流输出结构,所有调用方都要跟着改,这是模块化的真实成本。
7. 实验文档及源码获取
- 实验文档(完整操作步骤):DIFY-08:子工作流——搭积木式模块化设计.md
- 源码(可直接导入,先导子工作流再导主工作流):
- dify102_08_01_情绪分析引擎.yml
- dify102_08_02_客服情绪看板.yml
- dify102_08_03_批量反馈分析.yml
文章聚焦核心配置与采坑点;实验的完整分步操作(节点搭建/参数表/调试指引)见实验文档原文。
下一篇:Dify 中级实验(08):代码节点进阶——如何用标准库处理文件与数据?




