Dial-In LLM 层次化客服意图聚类 - 无监督发现 Case 意图树
Skill-DialIn-LLM-Case-Intent-Clustering · 09-DataAgent-LLM
1. 解决的问题
WF-C 客服分诊的核心是"意图细分"——母婴 Case 复杂(退款/换货/咨询/投诉/物流/产品使用/安全升级),嵌入距离无法区分字面相似但意图截然不同的对话片段(如"宝宝用了这个奶粉一直哭" 可能是质量投诉或产品适配咨询). Dial-In LLM 用 LoRA 微调小型 LLM 作为聚类"工具人"(Qwen2.5-7B / ChatGLM3-6B):① 连贯性评估器 判断簇语义一致性 ② 意图命名器 生成"动作-目标"标签 ③ 迭代搜索自动发现最优簇数,无需预设 K.
2. 核心算法逻辑
WFC 客服分诊的核心是"意图细分"——母婴 Case 复杂(退款/换货/咨询/投诉/物流/产品使用/安全升级),嵌入距离无法区分字面相似但意图截然不同的对话片段(如"宝宝用了这个奶粉一直哭" 可能是质量投诉或产品适配咨询). DialIn LLM 用 LoRA 微调小型 LLM 作为聚类"工具人"(Qwen2.57B / ChatGLM36B):① 连贯性评估器 判断簇语义一致性 ② 意图命名器 生成"动作目标"标签 ③ 迭代搜索自动
3. 业务应用场景
- 业务问题:母婴客服 Case 字面高度相似但意图截然不同 - "宝宝用了这个奶粉一直哭" → 可能是 质量投诉 或 产品适配咨询 - "我要换一罐" → 可能是 申请-换货 或 咨询-产品型号
现有 BERT 嵌入分类器对二者 cosine similarity 极高,分类错误率 30%+,导致 Case 错路由,客服效率低 - 数据要求:历史 Case 对话文本 10k-50k 条(月度积累即可) - Dial-In 配置: - LoRA 微调 Qwen2.5-7B 作 connector / evaluator(用论文 prompts) - 候选簇数 $N = \{5, 10, 20, 50\}$,迭代 5 轮 - 自动发现 5 大类意图树:`refund / exchange / consultation / complaint / safety_alert` - 训练下游轻量
- 业务问题:母婴产品安全事件(过敏/呛奶/缺陷)处理延迟超过 1 小时可能引发舆情危机(Amazon listing 下架 + 监管投诉). 现有客服系统按 FIFO 处理,安全事件淹没在常规 Case 中 - 数据要求:历史 Case + 已知安全事件案例库 - Dial-In 配置: - Role Separation: 客户发言中含 "过敏 / 呼吸困难 / 急救 / 红疹" → 映射高优先级簇 - 意图标签前缀规则: `投诉-安全` / `询问-过敏` / `申请-急救` 自动路由 - 触发条件:任意 case 命中 urgency 簇 → 1 分钟内推送 senior 客服 + 同
4. 输入数据要求
请查看原始代码模板获取输入规格。
5. 输出结果
请查看原始代码模板获取输出规格。
6. 业务价值 / ROI
- 极易:GitHub 完整开源,中文原生数据集
- 易处:BGE-large-zh-v1.5 嵌入模型公开可用
- 难处:LoRA 微调 Qwen 2.5-7B 需 A100 × 1 训练 8-16 小时
- 难处:母婴垂类意图树需业务专家初始化(20-30 个簇)
7. 代码模板
代码块数量:1 · 路径:未检测到
"""
Dial-In LLM 母婴客服 Case 意图聚类骨架
论文 arXiv:2412.09049 (EMNLP 2025)
完整代码: github.com/mengze-hong/Dial-in-LLM
依赖: pip install sentence-transformers scikit-learn transformers
"""
from __future__ import annotations
from typing import Dict, List, Tuple
URGENCY_PREFIXES = ("投诉-安全", "询问-过敏", "申请-急救")
class DialInIntentClustering:
def __init__(self, n_cluster_candidates: List[int] = None):
self.n_candidates = n_cluster_candidates or [3, 5, 10, 20]
def _coherence_score(self, sentences: List[str]) -> bool:
"""LLM 评估器:判断簇是否语义连贯
生产: LoRA 微调 Qwen2.5-7B + prompt
Stub: 用关键词重叠率近似
"""
if len(sentences) < 2:
return True
words_per_sent = [set(s.split()) for s in sentences]
common = words_per_sent[0]
for ws in words_per_sent[1:]:
common = common & ws
return len(common) >= 1
def _name_intent(self, sentences: List[str]) -> str:
"""LLM 命名器:生成"动作-目标"格式意图标签
生产: ChatGLM3-6B + prompt
Stub: 关键词模式匹配
"""
joined = " ".join(sentences).lower()
if any(kw in joined for kw in ["过敏", "红疹", "急救", "呼吸"]):
return "投诉-安全"
if any(kw in joined for kw in ["退款", "退货", "退掉"]):
return "申请-退款"
if any(kw in joined for kw in ["换货", "换一罐", "型号"]):
return "申请-换货"
if any(kw in joined for kw in ["怎么", "如何", "什么"]):
return "咨询-使用"
if any(kw in joined for kw in ["物流", "签收", "派送"]):
return "询问-物流"
return "其他-未分类"
def _simple_cluster(self, sentences: List[str], n: int) -> Dict[int, List[str]]:
"""简化聚类(生产用 AgglomerativeClustering 或 KMeans)"""
clusters: Dict[int, List[str]] = {i: [] for i in range(n)}
for i, s in enumerate(sentences):
cid = i % n
clusters[cid].append(s)
return clusters
def _optimal_n(self, sentences: List[str]) -> Tuple[int, Dict[int, List[str]]]:
"""局部搜索最优簇数(论文核心公式)"""
best_n, best_ratio, best_clusters = 0, -1.0, {}
for n in self.n_candidates:
8. 论文来源
- 2412.09049