Dify 中级实验(15):条件分支高阶策略——多条件路由如何避免分支爆炸?
1. 业务场景
先讲一个我们实际遇到的场景。
一家电商平台的客服系统每天收到几千条用户问题,需要自动分流:VIP 客户的紧急售后要立刻转人工,普通用户的技术咨询走自助服务,新用户的简单问题走标准流程。分流依据不是单一条件——客户等级、问题紧急度、问题类型、历史交互次数四个维度叠加判断。他们一开始用「if-else 二选一」的思路搭:一个分支判断是不是 VIP,另一个分支判断是不是紧急……结果分支越叠越多,画布上一团乱麻,改一个规则要顺着十几条线找半天,还老是漏掉某些输入组合——那些用户请求没有匹配到任何分支,直接卡死在流程里。
我们第一次接这类需求时,第一反应也是「多画几个 if-else 分支不就行了」。真正画到第三个维度才发现——分支会爆炸:组合数量是各维度取值的乘积,画布根本放不下,改一个规则要顺着十几条线找半天。
这不是个例。任何「多个维度叠加、需要按综合情况路由」的场景都是这个模式:售后工单按紧急度+客户价值分流、营销活动按用户画像+行为分流、审批流按金额+角色分流——「二选一」的条件分支根本写不下多维判断,分支会爆炸。
2. 场景痛点
这个流程的痛点,在客服分流系统的迭代过程中体现得最直接:
- if-else 写不下多维判断:4 个维度、每个维度 3-4 个取值,组合起来几十种情况,全用条件分支表达,画布根本放不下,配置工作量失控。
- 分支爆炸、改不动:规则散落在几十条分支里,改一个判定标准要动十几处,牵一发动全身,业务方提需求没人敢接。
- 漏掉组合就死路:总有一些输入组合没被任何分支覆盖,请求静默卡死——用户发完问题毫无响应,客诉直接升级。
- 决策逻辑和路由逻辑混在一起:算分、比较、兜底全埋在分支条件里,可读性差,出问题不知道从哪查起。
本质上,问题不在「要不要分支」,而在「决策和路由该不该混在一起」——复杂决策应该收敛到代码里,分支节点只做纯粹的路由分发。
3. 方案:为什么是评分制路由 + 条件分支
Dify 工作流里解决多维路由的标准姿势是「代码节点做决策(算分),条件分支只做路由(按分数分发)」——本实验用「评分 + 条件分支」的组合搭一个智能客户分流系统。
选它的理由:
- 决策逻辑收敛:四个维度按权重打分全部写在一个代码节点里,改规则只改一处,分支保持纯粹;
- 分支数量可控:分数算出来只有 4 个档位(转人工/优先队列/自助服务/标准流程),分支从「几十条组合」降为「4 个 case」;
- 默认回退兜底:评分代码里留 else 分支,任何输入组合都有去处,杜绝「请求卡死无输出」。
这篇文章我们就用它搭一个「智能客户分流系统」:每个客户请求按客户等级、问题紧急度、问题类型、历史交互次数四个维度打分,路由到转人工/优先队列/自助服务/标准流程四个分支之一。
4. 整体架构
链路很清晰:入口收 4 个维度变量 → 代码节点加权算分 → IF-ELSE 按分数四路分发 → 各分支独立处理收尾。整条链路 14 个节点、13 条边,四个分支互不干扰、各自独立收尾——关键设计就是「分数收敛、路由纯粹」。
5. 模块设计
5.1 开始节点
interaction_count 必须用 number
类型——如果图省事用 text-input,传进来是字符串,评分代码里
"12" > 10 直接抛 TypeError:
- label: 客户等级
options: [vip, normal, new]
required: true
type: select
variable: customer_level
- label: 历史交互次数
required: true
type: number
variable: interaction_count5.2 评分路由节点(Code,核心)
四个维度按权重打分(等级最高 30、紧急度最高 40、类型
10-20、历史交互加减分),决策逻辑全部收敛在这个节点,下游分支只认
route 字符串:
def main(customer_level: str, urgency: str, issue_type: str, interaction_count: int) -> dict:
import json
try:
count = int(interaction_count or 0)
except Exception:
count = 0
score = 0
level_scores = {"vip": 30, "normal": 15, "new": 10}
score += level_scores.get(customer_level, 0)
urgency_scores = {"emergency": 40, "general": 20, "inquiry": 5}
score += urgency_scores.get(urgency, 0)
type_scores = {"tech": 10, "after_sale": 20, "business": 15}
score += type_scores.get(issue_type, 0)
if count > 10:
score += 10
elif count == 0:
score -= 5
elif count > 3 and issue_type == "after_sale":
score += 15
if score >= 60:
route, reason = "human_agent", "高优先级(总分超过阈值)"
elif score >= 40 or customer_level == "vip":
route, reason = "priority_queue", "中等优先级或 VIP 客户"
elif urgency == "inquiry":
route, reason = "self_service", "简单咨询,自助服务"
else:
route, reason = "normal_queue", "标准流程处理" # 兜底:任何组合都有去处
breakdown = {
"level_score": level_scores.get(customer_level, 0),
"urgency_score": urgency_scores.get(urgency, 0),
"type_score": type_scores.get(issue_type, 0),
"history_bonus": score - level_scores.get(customer_level, 0)
- urgency_scores.get(urgency, 0)
- type_scores.get(issue_type, 0)
}
return {"priority_score": score, "route": route, "reason": reason,
"max_score": 100, "breakdown_json": json.dumps(breakdown, ensure_ascii=False)}注意最后一行:评分明细用
breakdown_json(string)输出而不是 object——Dify 的
object 类型在节点间不可见,要留给后续节点查看就必须展平成 JSON
字符串。
5.3 路由分流节点(IF-ELSE)
四个 case 全部显式声明,每个 case 一条条件(字符串比较用
is):
cases:
- case_id: case_human
conditions:
- comparison_operator: is # ⚠️ 字符串比较必须用 is,不是 =
value: human_agent
variable_selector: [cd_route, route]
logical_operator: and
- case_id: case_priority
conditions:
- comparison_operator: is
value: priority_queue
variable_selector: [cd_route, route]
logical_operator: and
# Dify 中级实验(15):条件分支高阶策略——多条件路由如何避免分支爆炸?6. 运行验证
用实验文档的 5 组用例实测(点击「运行」,填 4 个维度变量):
| 输入组合 | 期望路由 | 实测结果 |
|---|---|---|
| VIP + 紧急 + 售后 | human_agent | 评分 30+40+20+15=105,转人工 ✓ |
| normal + 一般 + 技术 | normal_queue | 评分 15+20+10=45 <60 且非 VIP,标准流程 ✓ |
| normal + 一般 + 咨询 | self_service | 评分 40,非 VIP 且是咨询,自助服务 ✓ |
| new + 紧急 + 售后 | priority_queue | 评分 10+40+20-5=65,超过 60 转人工(看具体分数) |
| VIP + 咨询 + 首次 | priority_queue | 评分 30+5+10-5=40,VIP 直接进优先队列 ✓ |
日志里核对每个场景的
priority_score、route、reason,确认决策链路符合预期。
7. 实战坑
| 坑 | 现象 | 修复 |
|---|---|---|
| 多分支各自接 End 节点时 variable 重名 | 四个 End 输出都叫
reply,校验报变量重复 |
每个分支 End 的 variable 加后缀(reply_ha/reply_pq/reply_ss/reply_nq),跨节点唯一 |
字符串条件用 = 比较 |
Pydantic 校验报错「Input should be 'contains'…」 | 字符串用 is;数字比较才用
=,且 ≥/≤ 必须用 Unicode
符号(>= 会报错) |
| 交互次数变量用 text-input | 代码里 "12" > 10 报
TypeError: '>' not supported |
start 变量用 number + 代码内
int() 防御转换双保险 |
| 分支条件漏了兜底 case | 部分输入组合走到死路,无输出 | 四路 case 全量声明,最后一个 case 永远留给默认路径(评分制里就是 else 分支) |
| 评分明细用 object 输出 | 下游节点引用
breakdown.level_score 取不到值 |
object 展平为 breakdown_json
字符串,需要时 json.loads |
采坑点均来自本实验 DSL 生成与运行验证的真实记录(多 End 变量重复、Unicode 运算符、number 类型、object 展平)。
8. 实验文档及源码获取
- 实验文档(完整操作步骤):DIFY-16:条件分支高阶策略.md
- 源码(可直接导入):dify102_16_智能客户分流系统.yml
- DSL 目录:dify-102/dsl/
文章聚焦核心配置与采坑点;实验文档还包含多级嵌套分支、动态条件路由(规则表下放)、A/B 测试分流三个进阶实验的完整分步操作。
- Dify 中级实验(01):参数提取器实战——如何从自然语言中提取结构化数据?
- Dify 中级实验(02):问题分类器——智能路由引擎如何四路分发?
- Dify 中级实验(03):模板转换实战——如何用零 Token 完成文本加工?
- Dify 中级实验(04):迭代进阶——如何批量处理数据并守住性能边界?
- Dify 中级实验(05):并行执行——如何让多路任务同时跑?
- Dify 中级实验(06):变量聚合——如何确定性合并多路分支结果?
- Dify 中级实验(07):子工作流——如何把公共逻辑做成可复用积木?
- Dify 中级实验(08):代码节点进阶——如何用标准库处理文件与数据?
- Dify 中级实验(09):HTTP 节点进阶——如何搞定认证、分页与错误重试?
- Dify 中级实验(10):知识库深度调优——如何科学评估检索质量?
- Dify 中级实验(11):高级 RAG 流水线——如何搭建多路检索与精排?
- Dify 中级实验(12):Agent 深度配置——如何让智能体自主调用工具?
- Dify 中级实验(13):多 Agent 协作——如何编排多个智能体分工干活?
- Dify 中级实验(14):对话变量与状态管理——如何让工作流记住多轮对话的状态?
- Dify 中级实验(15):条件分支高阶策略——多条件路由如何避免分支爆炸?
- Dify 中级实验(16):错误处理与降级——工作流如何有尊严地失败?
- Dify 中级实验(17):调试监控与性能优化——响应慢和 Token 超支如何定位?
- Dify 中级实验(18):插件开发入门——如何把工作流变成 Agent 可调用的工具?
- Dify 中级实验(19):综合实战——如何把 19 个实验串成一条生产级流水线?
- Dify 中级实验(20):综合实战——自动化报告生成流水线如何从数据到周报一步到位?