从输入到决策:意图识别在 AI 架构中的定位与应用 — 第六章《置信度决策路由》

本章定位

markdown 复制代码
用户原始输入
    │
    ▼
①~⑤ 预处理 → 护栏 → 分类 → 实体 → 多意图    ← 前五章
    │
    ▼
⑥ 置信度决策路由(本章)   ← 根据置信度决定:直接处理 / 确认 / 兜底
    │
    ▼
路由到对应业务处理节点

意图分类的结果不是 100% 可靠的。置信度决策路由就是根据"系统有多确定"来决定走哪条路------高置信直接处理、中置信先确认、低置信兜底转人工。


一、三级路由策略

1.1 核心架构

markdown 复制代码
意图分类结果(intent + confidence)
    │
    ├── confidence >= 高阈值 ──→ 直接路由到业务处理节点
    │
    ├── 低阈值 <= confidence < 高阈值 ──→ 确认式交互(向用户确认意图)
    │                                        │
    │                                        ├── 用户确认 → 执行
    │                                        └── 用户否认 → 提供选项 / 转人工
    │
    └── confidence < 低阈值 ──→ 兜底处理
                                  │
                                  ├── 提供选项让用户选
                                  ├── 引导用户重新描述
                                  └── 转人工客服

1.2 阈值不是全局统一的

不同意图、不同风险等级应该有不同的阈值:

python 复制代码
# 默认阈值
DEFAULT_THRESHOLDS = {
    "high": 0.80,   # 高置信线
    "low": 0.45,    # 低置信线
}

# 高风险操作的阈值更严格
HIGH_RISK_THRESHOLDS = {
    "high": 0.90,
    "low": 0.60,
}

# 从意图配置中读取风险等级,动态选择阈值
def get_thresholds(intent_l1: str, intent_l2: str, intent_schema: dict) -> dict:
    """
    根据意图的风险等级返回对应的阈值。
    高风险(退款、投诉等)→ 阈值更严格
    普通操作 → 默认阈值
    """
    intent_config = intent_schema["intent_map"].get(intent_l1, {}).get(intent_l2, {})
    risk_level = intent_config.get("risk_level", "normal")

    if risk_level == "high":
        return HIGH_RISK_THRESHOLDS
    return DEFAULT_THRESHOLDS

二、高置信路由:直接执行

2.1 做什么

系统对分类结果足够自信,直接路由到对应的业务处理节点。

2.2 实现

python 复制代码
from typing import Literal
from langgraph.graph import END


def route_high_confidence(state: dict) -> str:
    """
    高置信路由:根据 L1 意图路由到对应的业务处理节点。
    返回节点名称。
    """
    l1 = state["l1_intent"]

    route_map = {
        "售前咨询": "presale_handler",
        "售后服务": "aftersale_handler",
        "投诉建议": "complaint_handler",
        "账号问题": "account_handler",
        "闲聊": "chitchat_handler",
    }

    target = route_map.get(l1, "chitchat_handler")
    return target

2.3 但在路由之前,还要检查槽位

即使置信度高,如果必填槽位缺失,也不能直接进入业务处理:

python 复制代码
def pre_route_check(state: dict) -> str:
    """
    路由前置检查:
    1. 槽位齐全 → 进入业务处理
    2. 槽位缺失 → 进入追问节点
    """
    if state.get("slots_complete", False):
        return "route_to_business"
    else:
        return "ask_for_missing_slots"


def ask_for_missing_slots_node(state: dict) -> dict:
    """追问缺失槽位"""
    return {
        "response": state["follow_up_question"],
        "awaiting_slot_fill": True,
    }

三、中置信路由:确认式交互

3.1 做什么

系统不够确定,先向用户展示识别结果,请求确认后再处理。

3.2 确认话术生成

python 复制代码
def generate_confirmation(state: dict) -> dict:
    """
    生成确认话术。
    根据意图类型生成不同风格的确认问题。
    """
    l1 = state["l1_intent"]
    l2 = state["l2_intent"]
    confidence = state["confidence"]
    entities = state.get("filled_slots", {})

    # 构建确认消息
    intent_label = f"{l1} - {l2}"

    # 如果有提取到的实体,附带在确认中
    entity_parts = []
    if entities.get("order_id"):
        entity_parts.append(f"订单 {entities['order_id']}")
    if entities.get("product"):
        entity_parts.append(f"产品「{entities['product']}」")

    entity_text = f"(涉及{','.join(entity_parts)})" if entity_parts else ""

    confirmation = f"请确认:您是想要「{l2}」{entity_text}吗?"

    # 提供明确的操作选项
    options = f"请回复:\n1. 是的\n2. 不是,我想要的是..."

    return {
        "response": f"{confirmation}\n{options}",
        "awaiting_confirmation": True,
        "pending_intent": {"l1": l1, "l2": l2},
    }

3.3 处理用户的确认回复

python 复制代码
def handle_confirmation_response(state: dict) -> dict:
    """
    处理用户对意图确认的回复。
    """
    user_reply = state["processed_input"].strip()
    pending = state.get("pending_intent", {})

    # 注意:必须先检查否定再检查肯定!
    # 错误做法:any("是" in "不是") → True,把"不是"误判为肯定
    # 正确做法:否定词排前面,用 startswith / == 代替 in 子串匹配

    # 否定信号(长词优先,避免"不"误匹配"不错")
    negative_signals = ["不是", "不对", "错了", "换一个", "不", "2"]
    # 肯定信号
    positive_signals = ["是的", "没错", "确认", "对的", "好的", "是", "对", "嗯", "1"]

    is_denied = any(user_reply == s or user_reply.startswith(s) for s in negative_signals)
    is_confirmed = (not is_denied) and any(
        user_reply == s or user_reply.startswith(s) for s in positive_signals
    )

    if is_confirmed:
        return {
            "l1_intent": pending["l1"],
            "l2_intent": pending["l2"],
            "confidence": 0.95,  # 用户确认后提升置信度
            "awaiting_confirmation": False,
            "confirmation_status": "confirmed",
        }

    if is_denied:
        return {
            "awaiting_confirmation": False,
            "confirmation_status": "denied",
            # 否认后进入兜底流程
        }

    # 用户回复模糊,再问一次或直接转兜底
    return {
        "awaiting_confirmation": False,
        "confirmation_status": "unclear",
    }

3.4 确认不超过 2 轮

python 复制代码
MAX_CONFIRMATION_ROUNDS = 2

def should_escalate_confirmation(state: dict) -> bool:
    """如果确认超过 2 轮用户还是不明确,放弃确认,转兜底"""
    rounds = state.get("confirmation_rounds", 0)
    return rounds >= MAX_CONFIRMATION_ROUNDS

四、低置信路由:兜底处理

4.1 三种兜底策略

python 复制代码
def fallback_handler(state: dict) -> dict:
    """
    低置信兜底处理。
    根据场景选择不同的兜底策略。
    """
    confidence = state["confidence"]
    l1 = state.get("l1_intent", "")

    # --- 策略 1:提供选项让用户选(优先) ---
    if confidence >= 0.2:
        # 还有一定线索,提供 top-3 可能的意图供选择
        top_intents = state.get("top3_intents", [])
        if top_intents:
            options = "\n".join(
                f"{i+1}. {t['l1']} - {t['l2']}"
                for i, t in enumerate(top_intents[:3])
            )
            return {
                "response": f"抱歉,我不太确定您的需求。请问您想要的是:\n{options}\n\n请回复序号,或者重新描述您的问题。",
                "fallback_type": "provide_options",
            }

    # --- 策略 2:引导用户重新描述 ---
    if confidence >= 0.1:
        return {
            "response": "抱歉,我没有完全理解您的问题。能再详细描述一下吗?比如:\n- 您遇到了什么问题?\n- 涉及哪个订单或产品?",
            "fallback_type": "ask_rephrase",
        }

    # --- 策略 3:转人工(最后手段) ---
    return {
        "response": "抱歉,我暂时无法理解您的需求。正在为您转接人工客服,请稍候...",
        "fallback_type": "transfer_human",
        "transfer_to_human": True,
    }

4.2 用户选择选项后的处理

python 复制代码
def handle_option_selection(state: dict) -> dict:
    """
    处理用户选择的选项。
    用户回复"1"/"2"/"3" 或意图关键词。
    """
    user_reply = state["processed_input"].strip()
    top_intents = state.get("top3_intents", [])

    # 尝试解析数字选项
    if user_reply in ["1", "2", "3"]:
        idx = int(user_reply) - 1
        if idx < len(top_intents):
            selected = top_intents[idx]
            return {
                "l1_intent": selected["l1"],
                "l2_intent": selected["l2"],
                "confidence": 0.90,  # 用户选择后置信度拉高
                "fallback_resolved": True,
            }

    # 用户可能用文字描述了意图,重新走分类
    return {
        "needs_reclassification": True,
        "fallback_resolved": False,
    }

五、完整的 LangGraph 路由节点

5.1 路由函数

python 复制代码
def confidence_router(state: dict) -> str:
    """
    置信度路由函数。
    作为 add_conditional_edges 的路由函数使用。
    """
    confidence = state["confidence"]
    l1 = state["l1_intent"]
    l2 = state["l2_intent"]
    intent_schema = state.get("_intent_schema", intent_schema_global)

    # 获取该意图的阈值
    thresholds = get_thresholds(l1, l2, intent_schema)

    # 高置信
    if confidence >= thresholds["high"]:
        # 还需要检查槽位
        if state.get("slots_complete", True):
            return "high_confidence_route"
        else:
            return "ask_missing_slots"

    # 中置信
    if confidence >= thresholds["low"]:
        return "confirm_intent"

    # 低置信
    return "fallback"

5.2 完整图结构

python 复制代码
from langgraph.graph import StateGraph, START, END


def build_routing_graph():
    """
    构建完整的置信度决策路由图。

    图结构:
                                    ┌→ presale_handler ──┐
        START                       ├→ aftersale_handler ─┤
          │                         ├→ complaint_handler ─┤
          ▼                         ├→ account_handler ───┤
    confidence_check                └→ chitchat_handler ──┤
          │                                               │
          ├─(高置信+槽位全)─→ business_router ────────────┘
          │                                               │
          ├─(高置信+槽位缺)─→ ask_missing_slots ──→ ...   │
          │                                               │
          ├─(中置信)────────→ confirm_intent               │
          │                      │                        │
          │                      ├─(确认)→ business_router│
          │                      ├─(否认)→ fallback       │
          │                      └─(超限)→ fallback       │
          │                                               │
          └─(低置信)────────→ fallback                     │
                                  │                       │
                                  ├─(选项)→ 重新路由      │
                                  ├─(重述)→ 重新分类      │
                                  └─(转人工)→ END ────────→ END
    """
    graph = StateGraph(RoutingState)

    # 节点
    graph.add_node("confidence_check", lambda s: s)  # 透传,路由在边上做
    graph.add_node("business_router", business_router_node)
    graph.add_node("ask_missing_slots", ask_for_missing_slots_node)
    graph.add_node("confirm_intent", generate_confirmation)
    graph.add_node("handle_confirmation", handle_confirmation_response)
    graph.add_node("fallback", fallback_handler)

    # 业务处理节点
    graph.add_node("presale_handler", presale_node)
    graph.add_node("aftersale_handler", aftersale_node)
    graph.add_node("complaint_handler", complaint_node)
    graph.add_node("account_handler", account_node)
    graph.add_node("chitchat_handler", chitchat_node)

    graph.add_node("format_response", format_response_node)

    # 入口
    graph.add_edge(START, "confidence_check")

    # 置信度路由
    graph.add_conditional_edges("confidence_check", confidence_router, {
        "high_confidence_route": "business_router",
        "ask_missing_slots": "ask_missing_slots",
        "confirm_intent": "confirm_intent",
        "fallback": "fallback",
    })

    # 业务路由
    graph.add_conditional_edges("business_router", route_high_confidence, {
        "presale_handler": "presale_handler",
        "aftersale_handler": "aftersale_handler",
        "complaint_handler": "complaint_handler",
        "account_handler": "account_handler",
        "chitchat_handler": "chitchat_handler",
    })

    # 所有业务节点 → 格式化输出
    for handler in ["presale_handler", "aftersale_handler", "complaint_handler",
                     "account_handler", "chitchat_handler"]:
        graph.add_edge(handler, "format_response")

    graph.add_edge("format_response", END)
    graph.add_edge("ask_missing_slots", END)  # 追问后等待用户下一轮输入
    graph.add_edge("confirm_intent", END)      # 确认后等待用户下一轮输入
    graph.add_edge("fallback", END)            # 兜底后等待用户下一轮输入

    return graph.compile()

六、阈值调优

6.1 上线初期的保守策略

arduino 复制代码
刚上线(数据少,不确定分类质量):
  高阈值 = 0.85(宁可多确认)
  低阈值 = 0.55(宁可多兜底)

目标:减少"答非所问"的灾难性体验
代价:多一些确认交互,用户会多回复一轮

6.2 基于数据的阈值校准

上线一段时间后,用积累的数据来找最优阈值:

python 复制代码
import numpy as np

def calibrate_thresholds(
    classification_logs: list[dict],
    target_precision: float = 0.95,
) -> dict:
    """
    基于历史分类日志校准阈值。

    输入:分类日志列表,每条包含:
      - confidence: 分类置信度
      - is_correct: 是否正确(人工标注)

    方法:找到使正确率 >= target_precision 的最低置信度作为高阈值。

    返回:{"high": 0.82, "low": 0.43}
    """
    # 按置信度排序
    logs = sorted(classification_logs, key=lambda x: x["confidence"])

    confidences = np.array([log["confidence"] for log in logs])
    correctness = np.array([log["is_correct"] for log in logs])

    # 从高到低扫描,找到正确率首次低于目标的点
    best_high = 0.80  # 默认
    for threshold in np.arange(0.95, 0.30, -0.01):
        mask = confidences >= threshold
        if mask.sum() < 10:  # 样本太少,跳过
            continue
        precision = correctness[mask].mean()
        if precision >= target_precision:
            best_high = float(threshold)

    # 低阈值:正确率 < 70% 的区域
    best_low = best_high * 0.55  # 简单经验公式
    for threshold in np.arange(best_high - 0.01, 0.10, -0.01):
        mask = confidences >= threshold
        if mask.sum() < 10:
            continue
        precision = correctness[mask].mean()
        if precision < 0.70:
            best_low = float(threshold + 0.01)
            break

    return {"high": round(best_high, 2), "low": round(best_low, 2)}

6.3 持续迭代流程

markdown 复制代码
每 1~2 周执行一次阈值校准:

1. 导出最近的分类日志(input, confidence, l1, l2)
2. 抽样 200~500 条,人工标注"分类是否正确"
3. 运行 calibrate_thresholds 计算新阈值
4. 对比新旧阈值,分析变化原因
5. 灰度上线新阈值(10% 流量验证)
6. 全量切换

七、监控与告警

7.1 关键指标

python 复制代码
def compute_routing_metrics(logs: list[dict]) -> dict:
    """计算路由相关的监控指标"""
    total = len(logs)
    if total == 0:
        return {}

    high_count = sum(1 for l in logs if l["route"] == "high_confidence")
    confirm_count = sum(1 for l in logs if l["route"] == "confirm_intent")
    fallback_count = sum(1 for l in logs if l["route"] == "fallback")
    transfer_count = sum(1 for l in logs if l.get("transfer_to_human"))

    # 确认后的转化率
    confirmed_logs = [l for l in logs if l.get("confirmation_status") == "confirmed"]
    denied_logs = [l for l in logs if l.get("confirmation_status") == "denied"]
    confirm_success_rate = (
        len(confirmed_logs) / (len(confirmed_logs) + len(denied_logs))
        if (len(confirmed_logs) + len(denied_logs)) > 0 else 0
    )

    return {
        "total_requests": total,
        "high_confidence_rate": high_count / total,       # 目标:60~80%
        "confirm_rate": confirm_count / total,             # 目标:10~25%
        "fallback_rate": fallback_count / total,           # 目标:< 15%
        "human_transfer_rate": transfer_count / total,     # 目标:< 5%
        "confirm_success_rate": confirm_success_rate,      # 目标:> 70%
    }

7.2 告警规则

指标 正常范围 告警条件 可能原因
高置信率 60~80% < 50% 分类模型质量下降 / 新增未覆盖的意图
兜底率 < 15% > 20% 意图体系覆盖不全 / 阈值设置过高
人工转接率 < 5% > 10% 系统整体能力不足,需要优化
确认成功率 > 70% < 50% 中置信区间的分类质量太差
确认否认后转人工率 --- > 30% 兜底选项设计不合理

八、异常处理与降级

路由层的降级原则:路由异常时走 fallback,确保请求不会卡死在中间。

python 复制代码
def confidence_router(state: dict) -> str:
    """置信度路由函数。异常时兜底。"""
    try:
        confidence = state.get("confidence", 0.0)
        l1 = state.get("l1_intent", "")
        l2 = state.get("l2_intent", "")
        thresholds = config.get_thresholds(l1, l2)

        if confidence >= thresholds.high:
            return "business_router" if state.get("slots_complete", True) else "ask_missing_slots"
        if confidence >= thresholds.low:
            return "confirm_intent"
        return "fallback"
    except Exception:
        # 路由异常 → 一定走 fallback,不能卡死
        return "fallback"

九、本章小结

环节 企业级做法
三级路由 高置信直接处理 → 中置信确认后处理 → 低置信兜底
动态阈值 不同意图不同阈值,高风险操作(退款、投诉)阈值更严格
确认交互 生成确认话术 + 处理用户回复,最多确认 2 轮后转兜底
兜底分级 提供选项(有线索时)→ 引导重述(模糊时)→ 转人工(最后手段)
槽位联动 即使高置信,槽位缺失也要先追问再处理
阈值调优 上线初期保守,积累数据后每 1~2 周校准一次
监控告警 跟踪高置信率、兜底率、人工转接率、确认成功率
相关推荐
dy_Alley1 小时前
从输入到决策:意图识别在 AI 架构中的定位与应用 — 第七章《集成与组装》
llm
codefan※1 小时前
干掉幻觉实战:如何构建企业级知识图谱增强 RAG
人工智能·大模型·llm·知识图谱·neo4j·rag·graphrag
摸鱼同学3 小时前
LLM 是什么?从 API 调用到 Token 机制
ai·大模型·llm·token·claudecode
小锋学长生活大爆炸5 小时前
【培训】Agent与OpenClaw
llm·agent·教程·科普·知识·培训·openclaw
Lkstar17 小时前
高级提示技巧:Few-shot、Chain-of-Thought、自一致性——让大模型推理能力翻倍
程序员·llm·ai编程
qq_白羊座18 小时前
DeepEval vs EvalScope 完整对比
llm
AlfredZhao18 小时前
AI编程系列01:裸 API 账单场景下,如何自建 LLM 用量可视化看板
llm·vibecoding·氛围编程
CoderJia程序员甲21 小时前
GitHub 热榜项目 - 周榜(2026-05-31)
ai·大模型·llm·github·ai教程
老A的AI实验室1 天前
Cyber Weekly #66
人工智能·ai·llm·agi·genai