Dify 高级实验(03):智能客服——如何让 AI 自动应答并把疑难问题转人工?
1. 业务场景
先讲一个我们实际遇到的场景。
一家电商公司每天要接几千条客服咨询,其中一大半是重复问题:「这个手机多少钱?」「我的订单到哪了?」「怎么退货?」。客服团队 30 个人三班倒,还是经常排队,高峰期用户等十几分钟才被接起。公司想过用 AI 全自动应答,但又不敢全放开——真遇到投诉、纠纷,AI 答不好反而火上浇油。于是客服主管的诉求很明确:能自动处理的自动处理,处理不了的带着完整上下文转人工,别让用户重复描述第二遍。
我们第一次接这类需求时,第一反应也是「全自动 AI 客服嘛,套个知识库就完事了」。真正动手才发现——知识库只能覆盖「商品咨询」这一路,订单查询要对接系统、退换货要按规则判定、转人工要生成工单,自动应答和转人工兜底是两套工程,少了哪一套,这个客服系统都立不住。
这不是个例。任何「客服量大的企业」都是这个模式:银行网点问答、运营商话费查询、SaaS 厂商工单支持——常见问题重复回答是常态,但完全用 AI 替代人工又不放心,难点从来不是「要不要用 AI」,而是「AI 接得住多少、接不住时怎么优雅地交给人工」。
2. 场景痛点
这个流程的痛点,在客服团队身上体现得最直接:
- 重复咨询吃掉人力:一半以上的会话是同类问题,客服每天把同样的答案说几十遍——人力成本高,回答质量还随状态波动,累了就敷衍。
- 高峰期排队流失用户:大促、活动期间咨询量翻倍,用户排队等几分钟就关页面走人,可能直接去竞品下单——每一次排队都是一次流失风险。
- 转人工要用户重复描述:AI 答不了转人工时,用户得把问题从头再说一遍,体验极差,客服还要重新理解上下文——「我已经说过了」是客服场景最常见的抱怨。
- 疑难问题无兜底:投诉、纠纷这类问题没有可靠的升级通道,AI 硬答容易激化矛盾,漏接则直接变成客诉事件。
本质上,客服的困境不是「AI 能不能替代人」,而是「常见问题自动化 + 疑难问题兜底」这套分工体系没搭起来——该自动的没自动,该转的没转好。
3. 方案:为什么是这套智能客服架构
Dify 的 Chatflow(对话型应用)里用「问题分类 → 多分支路由 → 自动应答 → 转人工兜底」的架构,正好把「自动 + 兜底」两件事同时解决。
选它的理由,我们实际对比过:
- 问题分类器原生路由:不用手写意图识别,配置好商品咨询/订单查询/退换货/转人工四类示例,LLM 自动把用户问题分到对应分支;
- 确定性问题不用 LLM:订单查询用正则提取订单号、退换货按规则判断,代码节点搞定——确定性规则交给代码,省 token 且结果稳定;
- 转人工带上下文:对话变量
conversation_history自动累积,转人工时取最近 3 条随工单带走,人工接手不用用户重新描述。
这篇文章我们就用它搭一个「电商智能客服」:商品咨询走知识库、订单查询走模拟 API、退换货按规则自动判定,用户说「转人工」则生成工单并通知客服。
4. 整体架构
链路很清晰:入口收用户消息 → 分类路由 → 各分支自动应答 → 疑难问题转人工。分类器是这个架构的枢纽——分错类,后面所有分支的努力都白费;所以分类器必须写清示例、并强制「用户明确要求人工时一定归入转人工」。
5. 模块设计
5.1 问题分类器 qc_main
单标签分类,四类指令必须写清示例,并强制兜底转人工:
class_list:
- 商品咨询 product: 商品信息、规格、库存、价格(例:这个手机多少钱?)
- 订单查询 order: 订单状态、物流、配送(例:我的订单到哪了?)
- 退换货 return: 退货、换货、退款(例:我要退货)
- 转人工 human: 用户主动要求找人工客服(例:转人工、找客服)
instruction: 用户明确要求人工时一定要归入"转人工",避免漏接。5.2 商品咨询分支:知识库 + LLM
kb_product 用
retrieval_mode: single(单路检索)、top_k: 3、score_threshold: 0.0,必须绑定真实知识库(dataset_ids
填实际库 ID,query_variable_selector
绑用户查询);lm_product 用 context
模式引用检索结果(context: {enabled: true, variable_selector: [kb_product, result]}),prompt
里用 {{#context#}}
占位符——由平台把检索结果注入上下文(引用溯源/上下文管理交给平台),而不是手动把
{{#kb_product.result#}}
拼进提示词。系统提示词要求仅依据检索结果回答、不编造规格价格:
你是电商客服助手小 D,负责商品咨询。请根据知识库检索到的商品信息回答用户问题。
用户问题:{{#sys.query#}}
商品资料:
{{#context#}}
要求:
1. 仅根据检索到的商品资料回答,不要编造规格、价格和库存
2. 如果检索结果为空或不足以回答,诚实说明,并建议用户转人工客服
3. 回答简洁友好
5.3 订单查询分支:正则提取订单号
用户句子是「我的订单 OD202607002
到哪了?」,不能整句查表,必须先正则提取 OD
开头的订单号:
def main(order_id):
import re
m = re.search(r"OD\d{6,}", order_id or "")
oid = m.group(0) if m else ""
mock_orders = {
"OD202607002": {"status": "配送中", "logistics": "中通 ZT9876543210", "eta": "2026-07-23"},
"OD202607003": {"status": "已签收", "logistics": "圆通 YT5555555555", "eta": "2026-07-20"},
}
order = mock_orders.get(oid, {"status": "未找到", "logistics": "", "eta": ""})
return {"found": "true" if oid in mock_orders else "false",
"order_id": oid, "status": order["status"],
"logistics": order["logistics"], "eta": order["eta"]}5.4 退换货分支:规则判断 + 条件分支
确定性规则用代码节点(不用 LLM),boolean
展平为字符串;cond_return 的 case_auto 用 OR 组合:
cd_return 输出: can_return / can_exchange 均为 "true"|"false" 字符串
case_auto: cd_return.can_return is "true" OR cd_return.can_exchange is "true"
case_human: cd_return.can_return is "false" AND cd_return.can_exchange is "false"5.5 转人工分支:工单 + 通知 + 上下文
cd_ticket
生成工单号、按是否含「投诉」定优先级,并带上对话变量
conversation_history 最近 3
条作为上下文,让人工客服不用用户重复描述:
def main(user_query, conversation_history):
import time
history = conversation_history if isinstance(conversation_history, list) else []
ticket_id = "TK-{}".format(int(time.time()))
priority = "high" if "投诉" in (user_query or "") else "normal"
ctx = ";".join(history[-3:]) if history else "(无历史记录)"
return {"ticket_id": ticket_id, "priority": priority, "context": ctx,
"message": "已为您转接人工客服,工单号:{}。客服将在 5 分钟内联系您。".format(ticket_id)}6. 运行验证
| 输入 | 期望行为 | 实测 |
|---|---|---|
| 「这个手机多少钱?」 | 分类为 product,知识库检索后回答商品信息 | 与预期一致 |
| 「我的订单 OD202607002 到哪了?」 | 正则提取订单号,返回「配送中 + 物流单号」 | 与预期一致 |
| 「我要退货,刚收到 3 天」 | 规则判定可退货,走 case_auto 自动回复 | 与预期一致 |
| 「投诉!你们物流太慢了,转人工」 | 分类 human,工单优先级 high,通知客服 | 与预期一致,工单上下文含历史记录 |
7. 实战坑
| 坑 | 现象 | 修复 |
|---|---|---|
| 分类器漏「转人工」兜底 | 用户说「找客服」被分到商品咨询,AI 答非所问且无法转人工 | 指令明确「用户明确要求人工时一定要归入转人工」+ 给示例(dify103_03 实测) |
| 订单号整句查表 | 「我的订单 OD202607002 到哪了」查不到(含多余文字) | 代码节点先
re.search(r"OD\d{6,}") 提取订单号再查(dify103_03
实测) |
| 代码节点返回 boolean | can_return 在 if-else
变量选择器不可见,分支配不上 |
boolean 展平为
"true"/"false" 字符串,条件用 is
比较 |
| 知识库分段不自包含 | single 检索单段命中,LLM 拿到的片段缺上下文,答不全 | 入库分段时保证每段自含答案(自包含分段,检索 TopK 才有效) |
| 转人工不带上下文 | 人工客服看不到用户之前说了什么,用户重复描述 | 工单代码引用
conversation_history 最近 3 条拼进 context(dify103_03
实测) |
8. 实验文档及源码获取
- 实验文档(完整操作步骤):DIFY-103-03:智能客服系统.md
- 源码(可直接导入):dify103_03_智能客服系统.yml
文章聚焦核心配置与采坑点;实验的完整分步操作(节点搭建/参数表/调试指引)见实验文档原文。
- Dify 高级实验(01):智能文档处理——如何把非结构化文档变成结构化数据?
- Dify 高级实验(02):对话式 BI 分析——如何让业务人员用自然语言直接查数?
- Dify 高级实验(03):智能客服——如何让 AI 自动应答并把疑难问题转人工?
- Dify 高级实验(04):RAG 增强问答——如何让知识库问答更准还能多轮追问?
- Dify 高级实验(05):多步推理——如何把复杂问题拆解成子问题逐个击破?
- Dify 高级实验(06):自动化报告——如何让系统定时自动生成周报?
- Dify 高级实验(07):智能审批——如何让机器自动完成流程审批?
- Dify 高级实验(08):智能告警——如何用 AI 替代固定阈值监控?
- Dify 高级实验(09):测试用例生成——如何让 AI 自动产出测试用例?
- Dify 高级实验(10):综合实战——如何从零搭建一个企业级智能客服平台?