Skip to content

如何提高意图识别的准确性

问题:如何提高意图识别的准确性?

意图识别是 NLU(自然语言理解)的核心环节,以下从数据层、模型层、工程层、交互层四个维度系统梳理提升准确性的方法。


1. 数据层:根基决定上限

模型的上限由数据决定,算法只是在逼近这个上限。数据质量是意图识别准确率的第一杠杆。

1.1 构建清晰的意图分类体系(Taxonomy)

意图定义不清是准确率低的头号元凶。常见问题:

问题类型错误示例正确做法
意图重叠"查询订单"和"订单状态"并存合并为一个意图,用槽位区分
粒度不一致一级"购物"和二级"退款"同级严格按层级拆分
边界模糊"投诉"和"建议"难以区分写明判定规则:带负面情绪→投诉

实践建议

  • 每个意图必须有3 要素:意图名称、自然语言定义(2~3 句话)、3+ 正例和 2+ 反例
  • 使用决策树辅助标注者判断边界 case
  • 定期计算 Cohen's Kappa 系数,保证标注一致性 ≥ 0.85
markdown
## 意图定义示例

### cancel_order(取消订单)
**定义**:用户明确表达要取消一个已经创建但未完成的订单。
**正例**
- "我不想要了,帮我取消"
- "刚才下错单了,撤销一下"
- "把那个订单退了吧"
**反例**
- "我想退货" → 属于 return_goods
- "订单怎么还没到" → 属于 query_logistics

1.2 数据增强:让每个意图覆盖更多表达

单一表达方式的训练数据会导致模型"见过才认识"。增强策略按效果排序:

① 同义改写(最基础)

python
# 原始: "帮我查一下天气"
# 改写:
"今天天气怎么样"
"看看外面天气如何"
"明天会下雨吗"
"查下北京的气温"

② 回译(Back Translation)

python
# 中文 → 英文 → 中文,产生自然的表达变体
原始: "我要退款"
中→英: "I want a refund"
英→中: "我想要退款" / "我需要申请退款"

③ LLM 批量生成变体(推荐)

python
prompt = """
为意图 "cancel_order"(取消订单)生成 20 条多样化的用户表达。
要求:
1. 涵盖口语/书面语/方言
2. 包含简短表达(<5字)和复杂表达(>15字)
3. 包含带情绪的表达(着急、生气)
4. 包含隐含意图(不直接说"取消")
"""

④ 实体替换

python
# 模板: "帮我订{日期}从{城市A}到{城市B}的机票"
# 生成:
"帮我订明天从北京到上海的机票"
"帮我订下周五从广州到成都的机票"

1.3 数据分布平衡

实际业务中意图分布极度不均(二八定律),如"闲聊"占 60%,"投诉"仅占 2%。

方法适用场景注意事项
过采样(Oversampling)少量样本的意图简单复制会过拟合,建议结合增强
欠采样(Undersampling)高频意图样本冗余可能丢失有用信息
SMOTE特征空间可插值文本领域效果一般,推荐在 embedding 空间做
类别权重调整训练时动态调权最简单有效,class_weight='balanced'
Focal Loss深度学习模型γ=2 是常用起点

经验值:每个意图至少 50~100 条高质量样本,理想状态 200~500 条

1.4 边界样本挖掘

最容易出错的地方是意图之间的"灰色地带"。

方法一:混淆矩阵驱动

1. 训练初版模型
2. 在验证集上生成混淆矩阵
3. 找出 Top-5 混淆意图对(如 query_price ↔ query_order)
4. 针对性补充边界样本
5. 重新训练 → 循环

方法二:主动学习(Active Learning)

1. 模型预测未标注数据
2. 筛选置信度在 0.4~0.6 之间的样本(最不确定)
3. 人工标注这些样本(投入产出比最高)
4. 加入训练集重新训练

1.5 数据飞轮(Data Flywheel)

线上用户输入 → 模型预测 → 记录日志

                     [低置信度/用户纠正/反馈]

                     标注队列(人工审核)

                     加入训练集 → 模型迭代

                     上线新模型 → 循环

关键指标监控

  • 每日各意图的识别量和置信度分布
  • Reject rate(兜底率)趋势
  • 用户纠正率(预测后用户否定的比例)

2. 模型层:选对架构和训练策略

2.1 分层意图体系(Hierarchical Classification)

将意图组织成树状结构,逐层分类,每层候选空间更小、更易区分:

                    用户输入

            ┌─────────┼─────────┐
          音乐      出行       购物        ← 一级意图(领域)
          ↓         ↓         ↓
      ┌───┼───┐   ┌─┼─┐   ┌──┼──┐
     播放 收藏 搜索 订票 查价 下单 退款      ← 二级意图(动作)

为什么有效

  • 一级分类只需从 5~10 个领域中选 1 个,准确率可达 95%+
  • 二级分类在确定领域后只需从 3~5 个动作中选,准确率 90%+
  • 总体准确率 = 95% × 90% = 85.5%,远高于从 50 个意图里直接选的效果
  • 即使一级分错,错误被限制在领域内,不会出现"查天气→退款"这种离谱错误

实现方式

python
# 方式一:级联分类器
class HierarchicalClassifier:
    def __init__(self):
        self.domain_clf = BertClassifier(num_labels=10)    # 一级
        self.action_clfs = {                                # 二级(每个领域一个)
            "music": BertClassifier(num_labels=4),
            "travel": BertClassifier(num_labels=3),
            "shopping": BertClassifier(num_labels=5),
        }

    def predict(self, text):
        domain = self.domain_clf.predict(text)
        action = self.action_clfs[domain].predict(text)
        return f"{domain}.{action}"

# 方式二:LLM 两步推理
step1_prompt = "判断用户意图属于哪个领域:音乐/出行/购物/..."
step2_prompt = f"在 {domain} 领域下,具体意图是:{action_list}"

2.2 模型选型详解

① BERT 微调(经典方案,延迟 < 50ms)

python
from transformers import BertForSequenceClassification, Trainer

model = BertForSequenceClassification.from_pretrained(
    "bert-base-chinese",
    num_labels=len(intent_labels)
)

# 训练配置要点
training_args = TrainingArguments(
    learning_rate=2e-5,       # BERT 微调黄金学习率
    num_train_epochs=5,       # 意图分类通常 3~5 轮足够
    per_device_train_batch_size=32,
    warmup_ratio=0.1,         # 前 10% 步骤 warmup
    weight_decay=0.01,
)

适用场景:意图数 < 50、数据量 > 1000 条/意图、对延迟要求高(< 50ms)

② Sentence-BERT + 相似度匹配(灵活方案)

不需要重新训练,适合意图频繁变动的场景:

python
from sentence_transformers import SentenceTransformer
import numpy as np

model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')

# 每个意图准备 5~10 条代表性表达
intent_examples = {
    "cancel_order": ["取消订单", "不想要了", "帮我撤销"],
    "check_weather": ["今天天气", "会下雨吗", "气温多少"],
}

# 预计算意图中心向量
intent_centers = {}
for intent, examples in intent_examples.items():
    embeddings = model.encode(examples)
    intent_centers[intent] = np.mean(embeddings, axis=0)

# 在线推理:计算用户输入与各意图中心的余弦相似度
def predict(text):
    emb = model.encode(text)
    scores = {k: cosine_sim(emb, v) for k, v in intent_centers.items()}
    best = max(scores, key=scores.get)
    return best, scores[best]

优势:新增意图只需添加几条样本,无需重训模型。 劣势:对表达多样性高的意图效果差,需要配合阈值做 reject。

③ LLM 方案(见第 5 章详解)

适合复杂业务、快速迭代、意图定义不稳定的场景。

2.3 训练技巧详解

对比学习(Contrastive Learning)

核心思想:让同意图的文本在向量空间中靠近,不同意图的远离。

python
# SimCSE / Supervised Contrastive Loss
# 构造训练对:
# 正例对:(text_a, text_b) 属于同一意图
# 负例对:(text_a, text_c) 属于不同意图

from pytorch_metric_learning import losses

loss_fn = losses.SupConLoss(temperature=0.07)

# 效果:embedding 空间中意图聚类更清晰
# 下游分类器(哪怕是简单的 KNN)准确率都会显著提升

Focal Loss(解决类别不平衡)

python
import torch.nn.functional as F

def focal_loss(logits, targets, gamma=2.0, alpha=None):
    """
    gamma: 聚焦参数,越大越关注难分样本
    alpha: 各类别权重,补偿样本不平衡
    """
    ce_loss = F.cross_entropy(logits, targets, reduction='none')
    pt = torch.exp(-ce_loss)  # 预测正确的概率
    focal_weight = (1 - pt) ** gamma
    loss = focal_weight * ce_loss
    return loss.mean()

# gamma=0 退化为普通交叉熵
# gamma=2 是论文推荐值,实践中 1~3 都可以尝试

联合训练(Joint Intent + Slot)

意图识别和槽位填充共享底层表示,互相增强:

输入: "帮我订明天从北京到上海的机票"

        [CLS] 帮 我 订 明天 从 北京 到 上海 的 机票
          ↓    ↓  ↓  ↓  ↓   ↓  ↓   ↓  ↓   ↓  ↓
        BERT Encoder(共享)
          ↓    ↓  ↓  ↓  ↓   ↓  ↓   ↓  ↓   ↓  ↓
       意图头  ────────── 槽位序列标注头 ──────────
         ↓              ↓
    book_flight    O O O B-date O B-from O B-to O O

Total Loss = α × Intent_Loss + (1-α) × Slot_Loss

为什么有效:识别出 "北京""上海" 等槽位,反过来帮助确认这是 "订票" 而非 "查价"。


3. 工程层:系统级优化

3.1 上下文感知

单轮理解的局限性:

用户: "帮我取消"
系统: 取消什么?→ 无法判断意图

加入上下文后:

用户 (turn 1): "我刚在你们平台上下了一单"
用户 (turn 2): "帮我取消"
系统: 结合上文 → cancel_order ✓

实现方案

python
# 方案一:拼接历史对话(简单有效)
def build_input(history: list[dict], current: str, max_turns=3):
    """将最近 N 轮历史拼接为模型输入"""
    context = ""
    for turn in history[-max_turns:]:
        context += f"[用户] {turn['user']}\n[助手] {turn['assistant']}\n"
    context += f"[用户] {current}"
    return context

# 方案二:对话状态追踪(Dialog State Tracking)
dialog_state = {
    "current_domain": "shopping",     # 已确定领域
    "active_entities": ["订单#12345"], # 已提及实体
    "pending_slots": ["reason"],      # 待填充槽位
    "candidate_intents": ["cancel_order", "modify_order"]  # 缩小候选
}
# 有了状态,意图只需从 2 个候选中选,而非 50 个

经验总结

  • 历史轮数建议 2~5 轮,太长反而引入噪声
  • 对于 LLM,把历史放在 user message 中效果最好
  • 对于 BERT,建议用 [SEP] 分隔各轮

3.2 规则 + 模型混合架构

生产环境中最稳定的方案是三层漏斗架构

                    用户输入

        ┌─────────────────────────────┐
        │  第一层:精确规则匹配         │
        │  关键词/正则/实体词典         │
        │  命中 → 直接返回(0ms延迟)   │
        └──────────┬──────────────────┘
                   ↓ 未命中
        ┌─────────────────────────────┐
        │  第二层:ML 模型预测          │
        │  BERT/向量匹配              │
        │  置信度 > 阈值 → 返回        │
        └──────────┬──────────────────┘
                   ↓ 低置信度
        ┌─────────────────────────────┐
        │  第三层:LLM 兜底            │
        │  复杂/模糊表达的最后防线      │
        │  仍不确定 → 触发反问          │
        └─────────────────────────────┘

规则层实现示例

python
import re

INTENT_RULES = [
    # (正则模式, 意图, 置信度)
    (r"退[款钱货]|申请退|不想要了", "refund", 0.95),
    (r"密码.*[忘改换]|[忘改换].*密码", "reset_password", 0.95),
    (r"投诉|举报|曝光", "complaint", 0.90),
    (r"^(你好|hi|hello|)$", "greeting", 0.99),
]

def rule_match(text: str) -> tuple[str, float] | None:
    for pattern, intent, confidence in INTENT_RULES:
        if re.search(pattern, text):
            return intent, confidence
    return None

何时用规则 vs 模型

场景用规则用模型
"退款" → refund✅ 100% 确定不需要
"这个东西质量太差了想退"❌ 关键词不明显✅ 理解语义
"帮我看看订单"❌ 可能是查询/修改✅ 结合上下文

3.3 置信度管控 + Reject Option

核心原则:宁可不答,不可答错。

python
def classify_with_reject(text: str, threshold=0.7, margin=0.15):
    """
    置信度管控策略:
    1. 最高分 < threshold → reject(太不确定)
    2. top1 - top2 < margin → reject(两个意图太接近)
    """
    scores = model.predict_proba(text)  # {intent: score}
    sorted_scores = sorted(scores.items(), key=lambda x: -x[1])

    top1_intent, top1_score = sorted_scores[0]
    top2_intent, top2_score = sorted_scores[1]

    # 绝对置信度检查
    if top1_score < threshold:
        return "unknown", top1_score, "置信度不足"

    # 相对置信度检查(两个意图太接近)
    if top1_score - top2_score < margin:
        return "ambiguous", top1_score, f"在 {top1_intent}{top2_intent} 之间犹豫"

    return top1_intent, top1_score, "confident"

阈值调优方法

  • 用验证集画 Precision-Recall 曲线
  • 业务优先精度 → 阈值调高(如 0.85)
  • 业务优先召回 → 阈值调低(如 0.6)
  • 不同意图可以设不同阈值(高风险操作如退款用高阈值)

3.4 意图纠错/确认机制

根据操作风险等级,采取不同确认策略:

风险等级策略示例
低风险(查询类)直接执行"查天气" → 直接返回天气
中风险(修改类)隐式确认"已帮您修改地址为XX,如有误请说'撤销'"
高风险(不可逆)显式确认"您确认要取消订单并退款吗?"
python
RISK_LEVELS = {
    "query_order": "low",
    "modify_address": "medium",
    "cancel_order": "high",
    "delete_account": "critical",
}

def handle_intent(intent, confidence):
    risk = RISK_LEVELS.get(intent, "medium")

    if risk == "low":
        return execute(intent)                    # 直接执行
    elif risk == "medium":
        result = execute(intent)
        return f"{result}\n如有误请说'撤销'"      # 隐式确认
    elif risk in ("high", "critical"):
        return f"您确认要{intent_desc}吗?请回复'确认'" # 显式确认

4. 交互层:引导用户降低歧义

最好的意图识别不是"猜得准",而是"不用猜"。

4.1 快捷入口 / 按钮引导

通过 UI 设计直接消除歧义:

Bot: "您好,请问需要什么帮助?"
     [查询订单]  [申请退款]  [修改地址]  [联系人工]

用户点击按钮 → 意图 100% 确定,零识别成本。

4.2 引导式提问(Clarification)

当意图模糊时主动缩小范围:

用户: "我的订单有问题"
Bot:  "请问您遇到的是哪种问题?"
      1. 订单状态查询
      2. 商品质量问题
      3. 配送/物流问题
      4. 申请退款退货

触发条件

  • 置信度 < 阈值
  • top1 和 top2 分差 < margin
  • 用户输入太短(< 3 字)或太长(> 50 字,可能包含多个意图)

4.3 槽位追问(Slot Elicitation)

意图确定但信息不完整时,逐步收集:

用户: "帮我订机票"          → 意图确定: book_flight
Bot:  "请问出发城市是?"     → 收集 slot: departure
用户: "北京"
Bot:  "目的地是?"          → 收集 slot: destination
用户: "上海"
Bot:  "出发日期是?"        → 收集 slot: date

4.4 输入联想 / 自动补全

在用户输入时实时提供候选,收敛表达空间:

用户输入: "退..."
联想列表: "退款申请" | "退货流程" | "退换货政策"

用户选择联想项后,意图直接确定。


5. 用 LLM 做意图识别(当前主流趋势)

5.1 基础方案:结构化 Prompt

python
SYSTEM_PROMPT = """
你是一个意图分类器。根据用户输入,从以下意图中选择最匹配的一个。

## 意图列表(每个意图附带定义和示例)

### 1. book_flight
- 定义:用户想预订机票或航班
- 示例:"帮我订明天去上海的机票"、"查一下飞北京的航班"
- 注意:仅查价格不订不算,归入 query_price

### 2. cancel_order
- 定义:用户要取消已下的订单
- 示例:"取消订单"、"不想要了"、"帮我撤销刚才那单"
- 注意:"退货"归入 return_goods,不是 cancel

### 3. check_weather
- 定义:查询天气信息
- 示例:"今天天气怎么样"、"明天会下雨吗"

### 4. chitchat
- 定义:闲聊,不包含任何业务意图
- 示例:"你叫什么名字"、"讲个笑话"

## 输出格式(严格 JSON)
{"intent": "意图ID", "confidence": 0.0~1.0, "reasoning": "判断依据"}

## 规则
- 如果无法确定,返回 {"intent": "unknown", "confidence": 0.0, "reasoning": "原因"}
- 不要编造列表外的意图
- confidence 请根据你的确定程度如实填写
"""

5.2 进阶:Few-shot + Chain-of-Thought

python
FEW_SHOT_PROMPT = """
## 示例(注意边界 case 的处理)

用户: "这个东西太贵了不买了"
思考: 用户说"不买了",但还没有下单,这不是取消已有订单。没有明确的业务意图。
输出: {"intent": "chitchat", "confidence": 0.75, "reasoning": "表达不满但无具体业务请求"}

用户: "算了不要了帮我退掉"
思考: "不要了"+"退掉"表明用户想取消一个已存在的东西,结合语境是取消订单。
输出: {"intent": "cancel_order", "confidence": 0.90, "reasoning": "明确表达放弃并要求撤销"}

用户: "你们家的退款政策是什么"
思考: 用户在询问政策信息,并非在执行退款操作。这是一个FAQ/咨询类问题。
输出: {"intent": "faq_policy", "confidence": 0.85, "reasoning": "询问政策规则而非执行退款"}

## 现在请分析以下输入:
用户: "{user_input}"
思考:
"""

5.3 进阶:Function Calling 约束输出

python
# 使用 OpenAI Function Calling 确保输出格式
tools = [{
    "type": "function",
    "function": {
        "name": "classify_intent",
        "description": "对用户输入进行意图分类",
        "parameters": {
            "type": "object",
            "properties": {
                "intent": {
                    "type": "string",
                    "enum": ["book_flight", "cancel_order",
                             "check_weather", "chitchat", "unknown"],
                    "description": "识别到的意图"
                },
                "confidence": {
                    "type": "number",
                    "minimum": 0, "maximum": 1
                },
                "reasoning": {
                    "type": "string",
                    "description": "判断依据"
                }
            },
            "required": ["intent", "confidence", "reasoning"]
        }
    }
}]

# 输出被约束为合法 JSON,不可能出现格式错误或编造意图

5.4 进阶:Two-pass 精排策略

第一轮(快速粗筛):
  Prompt: "从 50 个意图中选出最可能的 Top-3"
  模型: GPT-4o-mini(快、便宜)
  输出: [cancel_order, return_goods, complaint]

第二轮(精确决策):
  Prompt: "仅从以下 3 个意图中选择,并详细说明理由"
  模型: GPT-4o(准)
  输出: cancel_order, confidence=0.92

优势

  • 第一轮缩小范围,降低第二轮的难度
  • 总成本比直接用 GPT-4o 处理 50 个意图更低
  • 准确率接近但延迟和成本下降 40~60%

5.5 LLM vs 传统模型 对比

维度BERT 微调LLM Prompt
准确率(充足数据)⭐⭐⭐⭐⭐⭐⭐⭐⭐
准确率(冷启动/少样本)⭐⭐⭐⭐⭐⭐⭐
延迟< 50ms500ms~2s
成本自部署,接近 0按 token 计费
灵活性(新增意图)需重训改 Prompt 即可
可解释性高(CoT 输出理由)
适合阶段业务稳定期探索期/快速迭代

6. 总结

6.1 优先级排序(ROI 从高到低)

优先级策略预期提升投入成本
🥇数据质量 + 意图体系+15~25%中(人工标注)
🥈规则 + 模型混合+5~10%低(工程实现)
🥉Reject + 反问机制体验提升显著
4模型/Prompt 优化+3~8%
5上下文感知+5~10%(多轮场景)
6数据飞轮持续提升高(需要基建)
7交互引导减少歧义输入 30%+低(UI 改造)

6.2 不同场景的优化侧重

场景首要策略次要策略推荐模型
客服对话机器人上下文感知 + 反问规则兜底BERT + 规则
工单自动分派数据质量 + 分层体系Focal LossBERT 微调
Agent 工具路由LLM Prompt + 置信度Function CallingGPT-4o
搜索意图理解向量匹配 + 改写用户行为反馈Sentence-BERT
语音助手ASR 纠错 + 规则层多轮状态混合架构

坚持是一种品格