Routing(路由与分支)学习笔记

一、核心背景与痛点

1. 万能 Prompt 的局限

当用户发送不同意图的请求(如 "我要退钱""你们态度太差了我要投诉")时,仅用一个万能 Prompt 会导致模型 "自信地出错":该共情时询问订单号,该查物流时解释退款政策,无法精准匹配不同场景的需求。

2. 单层路由的短板

实现的 "最小路由"(意图识别 + 阈值 + 兜底)在实际应用中存在三大问题:

  • 意图类别增多时,关键词表维护爆炸,易出现互相污染;
  • 多意图冲突频发(如 "我要退货且投诉快递员"),置信度常不足;
  • 查询物流、退款进度等场景需调用工具查库,仅靠文本回复无法满足需求。

二、核心概念解析

1. 四大基础概念

概念 定义 核心作用
意图(Intent) 用户请求的业务需求类型标签(如退款、投诉、查询物流等) 区分不同处理逻辑,匹配对应语气、流程和工具
链路(Chain) 由 "LLM+Prompt + 输出格式" 组成的处理流程 为每个意图提供专业的标准化处理步骤
阈值(Threshold) 分类器对意图识别的置信度临界值 防止误判,低于阈值则进入兜底流程
兜底(Fallback) 系统不确定时的安全链路 将模糊请求转化为澄清性问题,避免出错

2. 路由核心原则

  • 先决策后生成:先通过分类器判断意图,再分发到对应链路处理;
  • 不确定不装懂:置信度不足时,通过兜底流程澄清需求,而非强行回应;
  • 分层处理:复杂场景采用两级路由,平衡覆盖度与精准度。

三、基础路由(单层路由)实现

1. 核心流程

  1. 输入规范化(Normalize):去噪、切分、大小写统一;
  2. 意图分类(Classify):基于关键词表识别意图,计算置信度;
  3. 链路分发:根据置信度与阈值判断,分发到对应业务链或兜底链。

2. 代码示例(基础版)

复制代码
import re

# 1. 输入规范化:去噪、切分、小写转换
def normalize_input(text: str) -> str:
    # 去除多余空格和特殊符号(保留关键标点)
    text = re.sub(r'\s+', ' ', text.strip())
    # 仅保留中文、英文、数字和核心标点
    text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9\s,。!?、]', '', text)
    return text.lower()

# 2. 关键词表定义
INTENT_KEYWORDS = {
    "refund": ["退款", "退钱", "退货", "取消订单", "不想要了"],
    "complaint": ["投诉", "差评", "态度差", "欺骗", "举报"],
    "query": ["查询", "物流", "进度", "订单号", "在哪里"]
}

# 3. 意图分类与置信度计算
def classify_intent(text: str) -> tuple[str, float, str]:
    normalized_text = normalize_input(text)
    score_dict = {intent: 0 for intent in INTENT_KEYWORDS}
    
    # 关键词匹配计分
    for intent, keywords in INTENT_KEYWORDS.items():
        for keyword in keywords:
            if keyword in normalized_text:
                score_dict[intent] += 1
    
    total_score = sum(score_dict.values())
    if total_score == 0:
        return "fallback", 0.0, "未命中任何关键词"
    
    # 计算置信度(最高得分/总得分)
    best_intent = max(score_dict, key=score_dict.get)
    confidence = score_dict[best_intent] / total_score
    reason = f"命中关键词:{[k for k in INTENT_KEYWORDS[best_intent] if k in normalized_text]}"
    
    return best_intent, confidence, reason

# 4. 业务链路定义
def refund_chain() -> str:
    return "🔹 退款处理流程:\n1. 请提供订单号和支付方式;\n2. 为您核实退款资格;\n3. 告知手续费及到账时效"

def complaint_chain() -> str:
    return "🔹 投诉处理流程:\n1. 非常抱歉给您带来不佳体验!\n2. 请描述具体问题和时间线;\n3. 我们将升级处理并尽快反馈"

def query_chain() -> str:
    return "🔹 查询处理流程:\n1. 请提供订单号;\n2. 为您查询相关信息;\n3. 同步最新进度"

def fallback_chain() -> str:
    return "🔍 为了更好地帮助您,请选择需求类型:\n1. 退款 2. 投诉 3. 查询(物流/订单等)"

# 5. 路由主函数
def basic_router(text: str, threshold: float = 0.6) -> str:
    intent, confidence, reason = classify_intent(text)
    print(f"意图:{intent} | 置信度:{confidence:.2f} | 理由:{reason}")
    
    if confidence >= threshold:
        if intent == "refund":
            return refund_chain()
        elif intent == "complaint":
            return complaint_chain()
        elif intent == "query":
            return query_chain()
    return fallback_chain()

# 测试
if __name__ == "__main__":
    test_cases = [
        "我要退款,昨天买的东西不想要了",
        "你们客服态度太差了,我要投诉",
        "我的快递到哪了?查一下物流",
        "货不对版,差评!还要退货"
    ]
    for case in test_cases:
        print(f"\n用户输入:{case}")
        print(f"机器人回复:{basic_router(case)}")

3. 常见错误演示

  • 未设阈值:直接分发链路,导致低置信度请求误判(如 "货不对版" 可能被误判为查询);
  • 去噪过度:删除所有标点符号,丢失 "!""?" 等情绪信号;
  • 无兜底链:未命中关键词时直接返回 "无法理解",用户体验差。

四、进阶路由(两级路由)实现

1. 核心优化目标

解决单层路由的三大痛点,实现:

  • 类别扩容:支持更多意图类别,避免关键词表污染;
  • 多意图处理:通过澄清、拆分、主次策略解决冲突;
  • 工具调用:识别需查库的场景,标记need_tool

2. 两级路由逻辑

  1. 一级路由(Domain):粗分类(如售后、投诉、查询、VIP 服务);
  2. 二级路由(Intent):细分类(如售后下的退款、退货、取消订单);
  3. 策略层:判断多意图优先级、是否调用工具。

3. 代码示例(进阶版)

复制代码
import re

# 1. 两级关键词表(Domain→Intent)
DOMAIN_INTENT_MAP = {
    "after_sales": {  # 售后域
        "refund": ["退款", "退钱", "不想要了"],
        "return_goods": ["退货", "寄回", "换货"],
        "cancel_order": ["取消订单", "订单作废"]
    },
    "complaint": {  # 投诉域
        "service_complaint": ["态度差", "服务烂", "客服不专业"],
        "product_complaint": ["质量差", "货不对版", "瑕疵"]
    },
    "query": {  # 查询域
        "logistics": ["物流", "快递", "运输", "送达时间"],
        "refund_progress": ["退款进度", "到账时间", "退款状态"],
        "order_info": ["订单号", "订单详情", "购买记录"]
    }
}

# 风险优先级(高→低)
RISK_PRIORITY = ["complaint", "after_sales", "query"]

# 2. 一级路由:Domain分类
def classify_domain(text: str) -> tuple[list[str], list[float]]:
    normalized_text = normalize_input(text)  # 复用基础版的normalize_input函数
    domain_scores = {domain: 0 for domain in DOMAIN_INTENT_MAP}
    
    # 计算各域得分
    for domain, intents in DOMAIN_INTENT_MAP.items():
        for keywords in intents.values():
            for keyword in keywords:
                if keyword in normalized_text:
                    domain_scores[domain] += 1
    
    # 筛选得分>0的域,按风险优先级排序
    valid_domains = [d for d in RISK_PRIORITY if domain_scores[d] > 0]
    valid_scores = [domain_scores[d]/sum(domain_scores.values()) for d in valid_domains]
    
    return valid_domains, valid_scores

# 3. 二级路由:Intent分类
def classify_intent_advanced(domain: str, text: str) -> tuple[str, float]:
    normalized_text = normalize_input(text)
    intents = DOMAIN_INTENT_MAP[domain]
    intent_scores = {intent: 0 for intent in intents}
    
    for intent, keywords in intents.items():
        for keyword in keywords:
            if keyword in normalized_text:
                intent_scores[intent] += 1
    
    total_score = sum(intent_scores.values())
    best_intent = max(intent_scores, key=intent_scores.get)
    confidence = intent_scores[best_intent] / total_score if total_score > 0 else 0.0
    return best_intent, confidence

# 4. 工具路由判断
def need_tool(intent: str) -> tuple[bool, str]:
    tool_intents = {
        "logistics": ("物流查询接口", "需订单号"),
        "refund_progress": ("退款系统接口", "需订单号+支付方式"),
        "order_info": ("订单查询接口", "需订单号")
    }
    return tool_intents.get(intent, (False, ""))

# 5. 进阶路由主函数
def advanced_router(text: str, threshold: float = 0.5) -> str:
    # 一级Domain分类
    domains, domain_scores = classify_domain(text)
    if not domains:
        return fallback_chain()  # 复用基础版的fallback_chain函数
    
    # 处理多域冲突(取优先级最高且置信度达标者)
    main_domain = None
    main_domain_confidence = 0.0
    for domain, score in zip(domains, domain_scores):
        if score >= threshold:
            main_domain = domain
            main_domain_confidence = score
            break
    
    if not main_domain:
        # 多域置信度均不达标,澄清优先级最高的域
        return f"🔍 您的需求涉及{domains[0]}相关,是否需要先处理{domains[0]}业务?(如退款/投诉/查询)"
    
    # 二级Intent分类
    intent, intent_confidence = classify_intent_advanced(main_domain, text)
    need_tool_flag, tool_info = need_tool(intent)
    
    # 生成回复
    response = f"📌 业务类型:{main_domain}→{intent}\n"
    if need_tool_flag:
        response += f"🔧 需调用工具:{tool_info}\n"
    
    # 业务逻辑补充
    if main_domain == "after_sales" and intent == "refund":
        response += "流程:提供订单号→核实资格→告知到账时效"
    elif main_domain == "complaint" and intent == "service_complaint":
        response += "流程:道歉→记录问题→升级处理(24小时内反馈)"
    elif main_domain == "query" and intent == "logistics":
        response += "流程:提供订单号→调用物流接口→返回实时进度"
    
    return response

# 测试
if __name__ == "__main__":
    test_cases = [
        "我要退货,而且你们客服态度特别差",
        "查询我的退款进度,订单号123456",
        "货不对版,要求换货并投诉质量问题"
    ]
    for case in test_cases:
        print(f"\n用户输入:{case}")
        print(f"机器人回复:{advanced_router(case)}")

五、关键注意事项

  1. 阈值调整:需平衡精准度与覆盖率,过高易频繁兜底,过低易误判;
  2. 链路设计:每个链路需明确流程步骤,避免冗余,同时保留灵活性;
  3. 可观测性:输出意图、置信度、理由等日志,便于调试链路错误;
  4. 兜底设计:兜底不是 "无法理解",而是通过选择题、补充信息等方式引导用户明确需求。
相关推荐
A9better2 小时前
嵌入式开发学习日志50——任务调度与状态
stm32·嵌入式硬件·学习
四维碎片2 小时前
QSettings + INI 笔记
笔记·qt·算法
非凡ghost2 小时前
ESET NupDown Tools 数据库下载工具
学习·软件需求
zzcufo3 小时前
多邻国第5阶段17-18学习笔记
笔记·学习
BlackWolfSky3 小时前
鸿蒙中级课程笔记4—应用程序框架进阶1—Stage模型应用组成结构、UIAbility启动模式、启动应用内UIAbility
笔记·华为·harmonyos
中屹指纹浏览器3 小时前
指纹浏览器性能优化实操——多实例并发与资源占用管控
经验分享·笔记
brave and determined4 小时前
工程设计类学习(DAY9):印刷电路板(PCB)材料选择、工艺特性与制造技术综合详解
学习·制造·pcb·smt·工程设计·fr-4·pcb钻孔
了一梨4 小时前
SQLite3学习笔记5:INSERT(写)+ SELECT(读)数据(C API)
笔记·学习·sqlite
-To be number.wan5 小时前
算法学习日记 | 枚举
c++·学习·算法
jrlong5 小时前
DataWhale大模型基础与量化微调task5学习笔记(第 3 章:大模型训练与量化_模型量化实战)
笔记·学习