ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

14 所有问题都丢给 RAG?面试官想听的是「Query 路由」

14 所有问题都丢给 RAG?面试官想听的是「Query 路由」 面试官你们那个知识库问答用户每问一句你都去向量库捞一遍候选人对啊先检索 top-k把资料塞进 Prompt再让模型答。面试官那用户说你好、说你会说英语吗、说3 加 5 等于几你也去捞一遍候选人……也能捞就是浪费点。面试官那用户问今天上海天气怎么样你库里压根没这条模型给你编一个用户信了算谁头上候选人……这道题的胜负手就四个字Query 路由。大多数同学做 RAG脑子里只有一条线来一个问题 → 检索 → 生成。但真实的生产系统里用户发来的每一句话并不都该走检索这条道。有的该直接答、有的该调工具、有的该干脆拒答。这一句该怎么处理就是 Query 路由干的活。这篇把它讲透为什么要路由、路由分几条道、三档实现方案怎么选、Java 里怎么写、路由错了怎么兜。一、为什么每句话都检索是个坑先把代价说清楚不然你会觉得路由是过度设计。第一烧钱。检索不是免费的。用户问题要先过一遍 embedding 模型一次 API 调用或一次 GPU 推理再去向量库做相似度搜索。这两步都有延迟、都有账单。用户只是想打个招呼你却在背后默默算了一次向量。第二添乱。检索出来的 top-k 文档是向量最相似的不是真正相关的。用户问你们公司几点上班向量库可能捞出一堆语义相近但毫不相干的段落全塞进上下文反而成了噪声把模型带偏。上下文里垃圾越多答错概率越大。第三答不了。有些问题压根不该查库。今天是几号、现在几点、这个订单发到哪了、这台机器负载多少——这些是实时、外部的信息知识库里没有。硬走 RAG模型只能编。幻觉的一大来源就是让模型去回答它根本拿不到的东西。一句话RAG 是给有据可查的问题准备的不是万能钥匙。二、Query 路由把一句话分到四条道上路由的本质是先判意图再选链路。一条典型的四岔路1.直接回答Direct寒暄、闲聊、通用常识、礼貌用语。不查库直接让模型答命中缓存就更快。2.检索增强RAG关于知识库、内部文档、业务资料的问题。走检索 → 拼上下文 → 生成。3.工具调用Tool实时数据、精确计算、查订单、发消息。走 Function Calling。4.拒答兜底Refuse越界、敏感、拿不准。明确说这个我答不了而不是硬编。面试官问你们怎么做路由其实是在问你有没有意识到检索只是众多处理链路里的一条。三、三档实现方案按成本排方案一规则 关键词零成本覆盖 60%最简单也最稳。用关键词、正则、长度、疑问词来粗筛。public class RuleRouter { private static final SetString GREETINGS Set.of(你好, 在吗, hi, hello, 嗨); private static final Pattern ARITHMETIC Pattern.compile(^\\s*\\d\\s*[\\-*/]\\s*\\d.*); public Route route(String question) { String q question.trim().toLowerCase(); // 寒暄别去查库浪费 embedding if (GREETINGS.contains(q)) return Route.DIRECT; // 纯算术交给计算器工具模型算数不靠谱 if (ARITHMETIC.matcher(q).matches()) return Route.TOOL; // 实时数据关键词走工具接口 if (q.contains(订单) || q.contains(物流) || q.contains(余额)) { return Route.TOOL; } // 默认走检索 return Route.RAG; } }规则路由的好处是快、便宜、可解释错了能一眼看出是哪条规则的问题。坏处是死用户换种说法就绕过去了。方案二小模型分类器低成本覆盖 85%把意图识别当成一个短文本分类任务。两条常见路子标几百条样本训个小分类器或者更省事——用 embedding 拿用户问题去和每个意图的锚点句算相似度取最近的意图。public class EmbeddingRouter { private final EmbeddingModel embeddingModel; // 每个意图准备几句锚点句 private final MapRoute, ListString anchors; // 低于这个相似度就当拿不准 private static final double THRESHOLD 0.35; public Route route(String question) { float[] qv embeddingModel.embed(question); Route best Route.RAG; double bestScore -1.0; for (Map.EntryRoute, ListString entry : anchors.entrySet()) { for (String anchor : entry.getValue()) { double score cosine(qv, embeddingModel.embed(anchor)); if (score bestScore) { bestScore score; best entry.getKey(); } } } // 都不够像 → 退回最稳的 RAG别乱猜 return bestScore THRESHOLD ? Route.RAG : best; } private double cosine(float[] a, float[] b) { double dot 0, na 0, nb 0; for (int i 0; i a.length; i) { dot a[i] * b[i]; na a[i] * a[i]; nb b[i] * b[i]; } return dot / (Math.sqrt(na) * Math.sqrt(nb) 1e-8); } }这一档性价比最高延迟只有一次 embedding、成本低、对多数业务准确率够用。锚点句要覆盖真实用户的口语别拿标准书面语去测。方案三让大模型自己判灵活最贵给模型一段路由提示用结构化输出见第 20 篇保证它稳定吐标签。private static final String ROUTER_PROMPT 你是意图分类器只输出一个标签不要任何解释。 direct 闲聊 / 寒暄 / 通用常识 rag 需要查询内部知识库 tool 需要实时数据或精确计算 refuse 越界或不该回答 用户问题%s 标签;代价是每次路由都多烧一次模型调用延迟和成本双涨。所以大模型路由一般只用来兜底规则和小模型拿不准的才交给它。生产的答案通常是混合规则先筛快、覆盖大头→ 小模型补位 → 边缘问题交大模型 → 结果进缓存同一句话下次直接命中。四、路由错了怎么办兜底比准确更重要路由不是对/错的判断题是概率判断。再好的分类器也会判错。所以设计路由时重点不是怎么不失手而是失手了会怎样。几条铁律-检索为空要明说。向量库没捞到足够相关的文档别把空上下文丢给模型硬编。直接回知识库里暂时没有相关资料或者转人工。-路由失败默认检索。分类器报错、超时、返回未知标签时落到最稳的那条道通常是 RAG。宁可多查一次也别乱答。-工具调用要有超时和降级。外部接口会超时、会报错要设超时、要降级暂时查不到请稍后再试别让一条慢调用把整条链路拖死。-拒答要具体。不是冷冰冰的无法回答而是这个问题超出我的知识范围你可以换个说法或联系人工。还有一点容易被忽略路由要可观测。上线后要记录每一句走到了哪条道、命中率多少、兜底率多少。否则你根本不知道路由是好是坏。五、别把路由和查询改写搞混这一点面试里常被追问。查询改写Query Rewrite问题还是要走检索只是先把口语化、指代不清的问法改写成更适合检索的形式。比如把它怎么用改成XX 功能怎么使用。它优化的是检索质量。Query 路由Routing决定走不走检索这条岔路。它优化的是链路选择。一个守在岔路口一个站在车道里。两者是配合关系路由决定走检索了才轮到改写去打磨检索词。 面试官视角的标准回答被问你们怎么做 Query 路由按这个顺序答稳第一句先给结论——我们的原则是不是每个问题都走检索。先判意图再选链路。第二句讲四条道——兜底是直接回答比如寒暄和常识知识类问题走 RAG 检索实时数据和精确计算走 Function Calling越界问题直接拒答。第三句讲实现——规则筛一遍打头阵成本为零规则拿不准的走 embedding 分类器一次向量调用就能判特别边缘的才让大模型兜底因为它最贵。结果统一进缓存。第四句讲兜底——路由是概率判断所以关键在设计兜底检索为空要明说、路由失败默认走检索、工具调用必须有超时降级、拒答要具体。同时全程记日志保证线上可观测。最后补一句区别——注意路由和查询改写不是一回事改写是决定怎么查路由是决定要不要查。这五句说下来面试官就知道你不是调了个 RAG 就完事的水平而是想过链路编排。踩坑提示- 别把路由逻辑写死在 Controller 里抽成QueryRouter接口方便换实现、方便测试。- 规则路由的关键词表一定要拿真实用户日志去迭代别拍脑袋。- 缓存 key 别只用原始问题同一句在不同用户、不同权限下答案可能不同要带上身份维度。- 路由判错时日志里要留原始问题和判定结果否则线上出问题你无从复盘。下篇预告既然检索不是万能的那把知识灌进模型这件事到底该靠 RAG 还是靠微调第 15 篇聊这个绕不开的选择题。
返回列表