RAG 查询路由实战:3 种方案对比 + 完整代码

发布时间:2026/8/6 23:21:20
RAG 查询路由实战:3 种方案对比 + 完整代码
有读者问了一个好问题决策树本身用什么实现也是 LLM 吗这个问题直接命中了那篇文章没讲清楚的一个漏洞。我的回答是「能用 if-else 写的判断从来就不是路由的难点。难的那部分确实得靠模型——但不是你想的那种用法。」先把问题说清楚决策树 ≠ 路由实现上一篇的决策树是策略不是代码。它告诉你什么情况该走哪条路但没说运行时怎么判断一个 query 属于什么情况。这两件事完全不同层是什么难度策略层决策树设计图人来画设计决策一次性路由层运行时把真实 query 归类到对应分支这才是工程难点策略层画好之后你需要一个机制在运行时把每个 query 自动分到正确的分支。这就是**查询路由Query Routing**要解决的问题。为什么 if-else 解决不了先把决策树的每个判断条件按「能不能写死」拆开判断条件能不能纯代码原因Query 长度 10 字✅ 能len(query) 10含精确数字、日期、型号✅ 基本能正则可以覆盖大部分Query 质量差口语化、缩写、错别字❌ 不行质量差是语义判断规则只能抓错别字词典问题过于具体、缺背景知识❌ 不行纯语义没有可提取的客观特征问题多义 / 歧义 / 多跳❌ 不行最难完全依赖意图理解问题在这里真正决定该走哪条路的判断恰恰全部落在 ❌ 那一行。正则和len()只能处理两个 trivial 分支剩下的全靠语义。你写 10,000 行 if-else能覆盖的依然只是表面——同一个意图换个说法就失效了。三种路由方案从低成本到高准确方案一规则路由Rule-Based适合条件有明确客观特征的分支或系统要求延迟极低。import redef rule_router(query: str) - str: # 客观特征可以写死 if len(query) 8: return hyde # 短 query用 HyDE 扩展假设文档 if re.search(r\d{4}[-/年]\d{1,2}|\b[A-Z]{2,}\d\b|[\d.]%, query): return direct # 含精确数字/型号直接检索别乱改 # 主观特征规则失效返回 None 告知上游需要更强判断 return None这段代码能做什么接住短 query 走 HyDE和含精确数值直接检索这两个分支。不能做什么判断一个 query 是否歧义、是否质量差、是否多跳。结论规则路由只是第一道筛子不是完整方案。方案二LLM 语义路由Logical Routing让小模型做分类。核心是不要让 LLM 自由发挥用**结构化输出Structured Output**锁死它的返回格式。from openai import OpenAIfrom pydantic import BaseModelfrom typing import Literalclient OpenAI()class QueryRoute(BaseModel): 把 query 路由到最合适的改写策略 strategy: Literal[direct, rewrite, hyde, step_back, multi_query] confidence: float # 0.0–1.0低置信度时考虑并行多策略 reasoning: str # 一句话说明理由方便 debugROUTER_PROMPT 你是一个 RAG 查询分析器。根据用户查询的特征选择最合适的处理策略- direct查询清晰含精确标识符订单号、型号、日期直接检索即可- rewrite查询口语化、有错别字、缩写需要规范化改写- hyde查询很短或缺乏上下文需要生成假设性答案文档扩展语义- step_back查询过于具体需要先回到更高层级的背景知识- multi_query查询存在歧义或多个子问题需要多角度检索只返回 JSON。def llm_router(query: str) - QueryRoute: response client.beta.chat.completions.parse( modelgpt-4o-mini, # 路由不需要大模型 messages[ {role: system, content: ROUTER_PROMPT}, {role: user, content: f分析并路由{query}} ], response_formatQueryRoute, temperature0, # 路由必须确定性输出temperature0 ) return response.choices[0].message.parsed几个实际效果# 测试queries [ 跟上周那个一样的问题, # 歧义应走 multi_query transformer的注意力是啥, # 口语化应走 rewrite 或 hyde 2024年Q3财报第17页的净利润数字, # 精确应走 direct 为什么我的代码会OOM, # 过于具体应走 step_back]for q in queries: result llm_router(q) print(fQuery: {q}) print(f 策略: {result.strategy} | 置信度: {result.confidence}) print(f 理由: {result.reasoning}\n)# 输出示例# Query: 跟上周那个一样的问题# 策略: multi_query | 置信度: 0.85# 理由: 查询含歧义指代那个需多角度检索## Query: 2024年Q3财报第17页的净利润数字# 策略: direct | 置信度: 0.95# 理由: 含精确时间和定位标识直接检索即可这套方案的真正优点不是准确率而是可维护性。你不用维护一堆关键词列表和正则只需要改ROUTER_PROMPT里的策略描述模型会自己适配。方案三路由 改写合并为一次调用上面的路由是独立的一次 LLM 调用加在改写之前。但路由结果选什么策略和改写结果改写后的 query本来就可以在一次调用里同时产出不额外增加延迟。from pydantic import BaseModel, Fieldfrom typing import Literal, Optionalclass QueryAnalysis(BaseModel): 一次调用同时出路由决策 改写结果 strategy: Literal[direct, rewrite, hyde, step_back, multi_query] confidence: float # 根据策略填充对应字段不适用的字段为 None rewritten_query: Optional[str] Field( None, descriptionrewrite 策略规范化后的查询 ) hyde_document: Optional[str] Field( None, descriptionhyde 策略生成的假设性答案文档200 字以内 ) step_back_query: Optional[str] Field( None, descriptionstep_back 策略退一步的上位问题 ) multi_queries: Optional[list[str]] Field( None, descriptionmulti_query 策略3 个不同角度的子查询 )COMBINED_PROMPT 你是一个 RAG 查询处理器。分析用户查询选择最优策略并直接输出处理结果策略选择规则- direct含精确标识符直接检索无需改写- rewrite口语化/错别字/缩写输出规范化后的 rewritten_query- hyde查询太短或缺上下文生成 200 字以内的假设性答案文档 (hyde_document)- step_back问题太具体输出退一步的上位问题 (step_back_query)- multi_query存在歧义输出 3 个不同角度的子查询 (multi_queries)只填写与所选策略对应的字段。def route_and_rewrite(query: str) - QueryAnalysis: response client.beta.chat.completions.parse( modelgpt-4o-mini, messages[ {role: system, content: COMBINED_PROMPT}, {role: user, content: query} ], response_formatQueryAnalysis, temperature0, ) return response.choices[0].message.parsed# 使用方式result route_and_rewrite(transformer的注意力咋整的)# strategy: rewrite# rewritten_query: Transformer 中 Attention 机制的工作原理是什么 这段代码建议收藏——一次 LLM 调用同时完成路由判断 改写执行是生产环境最实用的写法。一次调用 vs 两次调用两次调用路由 → 改写合并调用延迟~2×1×Token 消耗稍多正常代码复杂度略高简单推荐需要单独监控路由准确率时生产首选还有一条路干脆不判断广播 融合上面三个方案的前提都是先判断再走对应的路。但还有第四条路不判断——并行跑多个策略然后融合结果。import asynciofrom retriever import retrieve # 你的检索函数async def parallel_retrieve(query: str, top_k: int 5) - list: 不路由并行跑 3 条路用 RRF 融合 # 同时跑 3 个改写策略 rewrite_task asyncio.create_task( retrieve(rewrite_query(query), top_k) ) hyde_task asyncio.create_task( retrieve(generate_hyde(query), top_k) ) step_back_task asyncio.create_task( retrieve(step_back(query), top_k) ) results await asyncio.gather(rewrite_task, hyde_task, step_back_task) return reciprocal_rank_fusion(results) # RRF 融合天然吸收噪声这条路的核心逻辑• 路由判断本身是不确定的模型也会判错判错就走错了整条路• RRF 融合能吸收噪声——某个策略产出的垃圾结果在融合里权重自然低• 代价是多几路检索延迟在延迟敏感度不高的场景完全 OK什么时候用哪条路场景推荐方案延迟 200ms查询分布固定规则路由查询多样需要语义判断LLM 语义路由合并调用版召回率优先延迟可接受并行广播 RRF 融合不知道哪种适合先上并行融合跑 A/B 之后再决定是否加路由一个容易踩的坑路由的 temperature 必须是 0LLM 路由跟普通生成任务不同它是一个分类任务不是创作任务。如果temperature 0同一个 query 多次调用可能路由到不同分支系统行为变得不可复现debug 会很痛苦。# ❌ 错的response client.chat.completions.create( modelgpt-4o-mini, temperature0.7, # 路由绝对不能这样 ...)# ✅ 对的response client.beta.chat.completions.parse( modelgpt-4o-mini, temperature0, # 路由必须确定性 response_formatQueryRoute, ...)同样原因路由用结构化输出Structured Output而不是自由文本杜绝模型返回我觉得应该用 rewrite 策略这类无法解析的字符串。小结回到最开始的问题决策树的路由用 LLM 做吗•客观分支长度、数字→ 规则不需要 LLM•语义分支歧义、口语化、过具体→ 必须靠模型规则填不满•生产首选→ 合并调用路由 改写一次出结果小模型gpt-4o-mini 足够 temperature0 结构化输出•更激进的方案→ 不路由并行广播 RRF 融合把判断难题直接绕过去学AI大模型的正确顺序千万不要搞错了2026年AI风口已来各行各业的AI渗透肉眼可见超多公司要么转型做AI相关产品要么高薪挖AI技术人才机遇直接摆在眼前有往AI方向发展或者本身有后端编程基础的朋友直接冲AI大模型应用开发转岗超合适就算暂时不打算转岗了解大模型、RAG、Prompt、Agent这些热门概念能上手做简单项目也绝对是求职加分王给大家整理了超全最新的AI大模型应用开发学习清单和资料手把手帮你快速入门学习路线:✅大模型基础认知—大模型核心原理、发展历程、主流模型GPT、文心一言等特点解析✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑✅开发基础能力—Python进阶、API接口调用、大模型开发框架LangChain等实操✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经以上6大模块看似清晰好上手实则每个部分都有扎实的核心内容需要吃透我把大模型的学习全流程已经整理好了抓住AI时代风口轻松解锁职业新可能希望大家都能把握机遇实现薪资/职业跃迁这份完整版的大模型 AI 学习资料已经上传CSDN朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】