第 11 章 路由 Routing

第 11 章 路由 Routing

本章要解决的问题

一个入口要处理多种类型的请求------让模型决定走哪条处理链路,判断错了怎么办?

章节大纲

  • 11.1 按输入特点分发到不同处理分支
  • 11.2 路由决策:规则 vs 模型分类
  • 11.3 多路由 Agent 的实现
  • 11.4 主流框架对照与变体
  • 🛠 解决方案:路由误判的兜底与回退设计

11.1 模式原理:按输入特点分发到不同处理分支

11.1.1 一句话定义

路由(Routing)是在系统入口处,根据输入的特点(意图、语言、复杂度、敏感度、用户身份......)把请求分发到不同的处理分支,让每个分支用最适合自己的提示词、模型甚至整条流水线来处理。

如果说提示链解决的是"一个任务怎么拆",路由解决的是"多个任务怎么分流"。它们是第 IV 部分里最常被一起使用的两个模式------第 10 章 10.5.1 的条件链,本质就是路由 + 提示链的组合。

11.1.2 为什么需要路由:一个万能提示词撑不住的场景

看一个典型场景:一个客服 Agent 入口,要同时处理:

text 复制代码
1. "我买的手机壳什么时候发货?"      → 查物流(工具调用 + 短链)
2. "你们家的政策太烂了,我要投诉!"   → 投诉工单(情绪处理链)
3. "你好"                            → 闲聊寒暄(简单回复,别烧钱)
4. "帮我算一下退货运费是多少?"      → 计算(工具调用)
5. "退款到账了吗?"                  → 查订单状态(工具 + 长上下文)

如果用一个万能提示词硬接这五类请求,会出两个问题:

  1. 每类请求的最优提示词不同:查物流需要"订单号抽取 + 工具调用",投诉需要"情绪安抚 + 升级策略"。混在一个提示词里,提示词会膨胀到无法维护,且互相干扰。
  2. 成本浪费 :闲聊请求不需要挂载 5 个工具定义 + 长系统提示词,但万能提示词对每个请求都背着全部负担------token 成本被最低价值请求拉高

路由的价值正是:在入口花很小的代价(一次分类),让后续每个分支都用最小、最合适的上下文处理,质量、成本、延迟三者同时优化。

11.1.3 路由的分层:从粗到细

路由层次 判断依据 例子
入口路由 意图/任务类型 查物流 vs 投诉 vs 闲聊
语言路由 语言 中文走中文链、英文走英文链
模型路由 复杂度/预算 简单→小模型(便宜快)、复杂→大模型
敏感路由 数据敏感度 含 PII → 脱敏链 + 私有模型
用户路由 身份/等级 VIP → 优先队列 + 专属策略
异常路由 失败兜底 主链路失败 → 降级分支

图 1:路由分层

本书聚焦前三层(意图/语言/模型),敏感路由详见第 22 章安全,异常路由见本章 11.4 兜底设计。

11.2 路由决策:规则 vs 模型分类

路由的核心是决策器:怎么判断"这个请求该走哪条路"?两种主流做法,各有适用边界。

图 2:规则 vs 模型路由

11.2.1 规则路由(Rule-based Routing)

用关键词、正则、枚举匹配直接决定去向,零 LLM 调用、零延迟、零成本:

python 复制代码
def rule_router(text: str) -> str:
    t = text.lower()
    if any(k in t for k in ["发货", "物流", "快递", "到哪了"]):
        return "logistics"
    if any(k in t for k in ["投诉", "举报", "太差", "再也不"]):
        return "complaint"
    if any(k in t for k in ["你好", "hi", "hello", "在吗"]):
        return "chitchat"
    return "fallback"

优点 :快、准、可解释、免费。 缺点:只能覆盖"说得出口"的模式------用户换个说法("我的包裹飞哪去了")就漏了;表达天然多样,规则很难覆盖所有情况。

适用:意图集合固定且用词收敛的场景(比如"查物流/查订单"这种动词明确的服务入口)。

11.2.2 模型路由(Model-based Routing)

让 LLM 做一次"分类 + 抽取",一次调用完成意图判断,甚至直接输出要走的路径:

python 复制代码
ROUTE_PROMPT = """你是请求分类器。将用户请求分到以下类别之一,只输出 JSON:
{"route": "logistics|complaint|chitchat|order|billing", "reason": "一句话理由"}

用户请求:{query}"""

def model_router(query: str) -> str:
    resp = client.chat.completions.create(
        model="deepseek-chat",
        messages=[{"role": "user",
                   "content": ROUTE_PROMPT.format(query=query)}],
        temperature=0.1,          # 分类任务用低温度(第 24 章表二)
        response_format={"type": "json_object"},
    )
    try:
        return json.loads(resp.choices[0].message.content)["route"]
    except (json.JSONDecodeError, KeyError):
        return "chitchat"  # 解析失败时降级到安全路由

优点 :理解自然语言、泛化强、能处理未见过的表达。 缺点:多一次调用(延迟 + 成本)、可能误判(见 11.4 兜底)。

适用:意图多样、表达自由、规则覆盖不住的中长尾场景。

11.2.3 混合路由:规则兜底 + 模型兜底,两层漏斗

工程上最稳的做法是两层漏斗

图 3:两层漏斗路由

css 复制代码
输入 → [第一层:规则快速命中] ─命中→ 走分支
                │
                └─未命中→ [第二层:模型分类] ─→ 走分支
                                │
                                └─不确定→ 走 fallback 分支

规则能覆盖的高频表达(80% 流量)零成本秒判;模型只处理规则的漏网之鱼。规则层拦掉大部分流量,模型层只吃长尾,成本与泛化兼得。

顺带一提:这个"规则优先、模型兜底"的思路在第 22 章安全(关键词拦截 + 模型审核双层)和第 17 章评估自检(规则校验 + 模型自检)中会反复出现,是全书高频复用的工程心法。

11.3 完整示例:多分支客服路由

我们实现一个真实的多分支路由系统:入口路由 + 模型路由 + 兜底,接到第 10 章的提示链分支。

图 4:客服多分支路由

python 复制代码
import json
from openai import OpenAI

client = OpenAI(base_url="https://api.deepseek.com", api_key="<你的Key>")

BRANCHES = {
    # 每个分支 = (名称, 处理函数)。简单分支直接返回文案,复杂分支复用提示链
    "logistics": lambda q: run_logistics_chain(q),   # 第10章风格链
    "complaint": lambda q: run_complaint_chain(q),   # 情绪处理链
    "order":     lambda q: run_order_chain(q),
    "billing":   lambda q: "退款相关问题请提供订单号,我们将在 1 个工作日内处理。",
    "chitchat":  lambda q: "在的~有什么可以帮您?也可以直接告诉我订单号查询进度哦。",
}

FALLBACK = "这个问题我暂时无法判断,已转人工客服,请稍候。"

# 第一层:规则快速命中(覆盖高频)
def rule_router(q: str) -> str | None:
    t = q.lower()
    if any(k in t for k in ["发货", "物流", "快递", "到哪", "配送"]):
        return "logistics"
    if any(k in t for k in ["投诉", "举报", "差评", "再也不"]):
        return "complaint"
    if any(k in t for k in ["退款", "运费", "费用", "钱"]):
        return "billing"
    return None

# 第二层:模型分类(覆盖长尾)
def model_router(q: str) -> dict:
    resp = client.chat.completions.create(
        model="deepseek-chat",
        messages=[{"role": "user", "content": f"""你是客服请求分类器。
类别:logistics(查物流/配送) | complaint(投诉/不满) | order(查订单/下单)
| billing(退款/费用) | chitchat(闲聊)
只输出 JSON:{{"route": "类别名", "confidence": 0到1}}
请求:{q}"""}],
        temperature=0.1,
        response_format={"type": "json_object"},
    )
    try:
        return json.loads(resp.choices[0].message.content)
    except json.JSONDecodeError:
        return {"route": "chitchat", "confidence": 0.0}  # 解析失败时降级

def route_and_handle(q: str) -> str:
    # 第一层
    r = rule_router(q)
    # 第二层(带置信度,低置信度不硬猜)
    if r is None:
        m = model_router(q)
        if m["confidence"] >= 0.6:
            r = m["route"]
    # 兜底
    handler = BRANCHES.get(r, lambda _: FALLBACK)
    return handler(q)

# 测试
for q in ["我的手机壳什么时候发货?", "你们太坑了我要投诉!", "在吗",
          "那个退款还没到账", "今天天气怎么样"]:
    print(f"[{q}] → {route_and_handle(q)[:40]}")

这个示例有几个工程要点:

  1. 置信度门槛:模型分类结果 confidence < 0.6 时宁可不猜,走兜底------路由误判的成本往往高于"转人工"。
  2. 分支可插拔:BRANCHES 是字典,新增业务分支 = 新增一个 key,无需改路由逻辑。
  3. 规则层先跑:高频请求零 LLM 开销,只有长尾才花一次模型分类的 token。

11.4 主流框架对照与变体

11.4.1 框架对照

实现方式 特点 适用
自研(本章主线) 两层漏斗 + 字典分支,完全可控,几行代码 中小规模,分支 ≤10 个
LangGraph 条件边 add_conditional_edges 根据 state 跳转 路由结果还要接复杂图
LangChain RouterChain 框架内置路由链,开箱即用 已用 LangChain 生态
语义路由器(如 semantic-router 库) 用 embedding 相似度匹配预定义意图 需要"近似匹配"而非精确分类

LangGraph 的路由写法(条件边):

python 复制代码
g.add_conditional_edges(
    "router",
    lambda state: state["route"],   # 返回的 key 决定走向
    {"logistics": "logistics_node",
     "complaint": "complaint_node",
     "chitchat": "chitchat_node",
     "fallback": END},
)

11.4.2 变体一:模型路由(Model Routing)

按"任务复杂度 × 预算"把请求分到不同模型(小模型 vs 大模型),是路由模式在成本侧的重要应用(呼应第 23 章成本优化):

python 复制代码
def model_router(complexity: str) -> str:
    # simple → 便宜快的小模型;complex → 更强的模型
    return "qwen-turbo" if complexity == "simple" else "deepseek-chat"

典型收益:简单请求(翻译、格式整理)走小模型,成本可降 60%+,延迟减半。判断复杂度本身也是一次小模型调用,别用大模型判断------判断模型的成本应该低于它省下的钱。

11.4.3 变体二:意图感知路由(Intent-aware Routing)

给模型路由的提示词里加上少量示例(few-shot),显著提升边界意图的准确率(呼应第 4 章 4.2 节):

python 复制代码
few_shot = """
示例1 请求:"快递显示签收但我没收到" → logistics
示例2 请求:"我想退货"             → order
示例3 请求:"客服是机器人吗?"      → chitchat
"""

11.4.4 变体三:级联路由(Cascade Routing)

路由不再是一次性,而是逐级细化:先分大类(服务类/非服务类),服务类再细分(物流/订单/退款)。级联的好处是每级分类的候选集小,准确率高,且可以不同层级用不同策略(第一级规则、第二级模型)。

🛠 解决方案:路由误判的兜底与回退设计

常见问题

  1. "路由分错了,把投诉分成了闲聊":这是最高成本的误判。对策:① 模型路由加置信度门槛,低置信度转人工;② 敏感分支(投诉)设置"疑似即升级"规则------被分到闲聊但含负面词,强制走投诉链。
  2. "模型路由太贵了,每个请求都花一次调用":先上规则层拦高频(两层漏斗),模型只吃长尾;或改用 embedding 相似度路由,比 LLM 分类便宜一个量级。
  3. "新增一个分支要改一堆代码":用分支字典/注册表模式(11.3 的 BRANCHES),新增分支 = 加一个函数 + 一个 key。
  4. "路由提示词经常答非所问" :类别定义写"类别 + 典型例子 + 边界说明",并给 few-shot 示例;输出用 JSON + 枚举约束("route": "logistics|complaint|...")。
  5. "简单请求也走大模型分支":加复杂度前置判断(模型路由变体),简单请求走小模型,别让大模型做杀鸡的牛刀。

解决方案速查表

现象 根因 解决方案
高频误判 仅用规则层 两层漏斗 + 模型兜底
长尾漏判 规则覆盖不全 模型路由 + few-shot 示例
敏感请求分错 分类粒度不够 疑似即升级 + 负面词强制拦截
路由成本高 每请求都调模型 规则先行 + embedding 路由
分支难扩展 硬编码 if-else 分支字典注册表

实战提示

  1. 先埋点再优化:上线第一周记录"每个请求的实际路由 + 用户是否满意",用真实数据校准路由规则,别靠猜。
  2. 路由要可观测:路由决策本身要打日志(含 confidence),这是后续调优的第一手数据。
  3. 兜底必须存在:任何路由系统都必须有 fallback 分支,且 fallback 应该"保守"------宁可转人工,不可瞎猜。
  4. 评估路由准确率:单独抽 100 条请求人工标注正确路由,建立路由评测集(呼应第 20 章),每次改规则后回归。
相关推荐
beiju15 分钟前
AI 改稿不该直接覆盖:从 Notion suggest edits 设计可审阅的 Patch 协议
人工智能
Summer-Bright16 分钟前
深度 | Hot Chips 2026 英特尔三响炮:256 核 Xeon、480GB 推理 GPU,赌 agentic 让 CPU 回归
人工智能·数据挖掘·回归·intel·hotchip
乌拉布拉乌18 分钟前
用 agents-md-writer 优化你的 AGENTS.md
人工智能·agent
1878770860923 分钟前
妙响和Mureka怎么选,AI音乐工具真实使用对比
人工智能
Bode_200223 分钟前
制造业的知识因果推理网
人工智能·智能工厂
梦想的颜色24 分钟前
【AI科普】什么是计算机视觉:硬核科普,它和大 AI 大模型到底是什么关系
人工智能·深度学习·计算机视觉·多模态·aiagent·#vlm·ai工程实战
whitelbwwww29 分钟前
RKNN静态量化
人工智能·深度学习
Henry-SAP31 分钟前
AI标准落地加速 安全与应用双突破
人工智能·云原生·sap·erp
zzzll111135 分钟前
LangChain4j:Java 生态的 AI 应用开发利器
java·开发语言·人工智能
卷无止境39 分钟前
社区里最好用的 Deep Research 技能,到底藏在哪几个仓库里
人工智能