Agentic RAG 实战剖析(一):Query Processing 问题预处理 一、向量检索的原理用一个 embedding 模型,把文档的每段文本变成一串数字(向量),语义越接近的文本,向量在空间里离得越近。查询时把用户问题用同一个模型转成向量。然后在库里找离这个查询向量距离最近的 top-K 个向量,对应的原文就是检索结果——找意思相近就这样变成了找空间里距离最近的点。很多人做 RAG把全部精力花在 embedding 模型选型、chunk 大小、rerank 上却默认用户问什么就拿什么去检索。但真实场景里用户的原始 query 几乎从来都不是为检索优化的口语化、有错别字、指代不清、过于宽泛、术语和文档对不上。检索是垃圾进垃圾出的。一个没处理好的 query后面再强的检索和 rerank 也救不回来。所以在 Agentic RAG 里query 进库之前的这一层处理是性价比最高的优化点之一。本文并不罗列概念、名词而是会通过一次实战流程剖析问题预处理这一步到底在做什么。二、从一个真实的烂 query 说起举一个例子小E麻烦给我讲一下咱系统是怎么注册藏号的呀人一眼就知道这个问题的主干。但如果直接原封不动喂给 embedding 模型向量化以后全是噪音——称呼、礼貌词、语气词、错别字混在一起真正承载意图的系统、注册、账号反而被稀释了。这句话经过处理后应该变成系统怎么注册账号本文把处理步骤拆解为三步规范化、降噪、判定问题是否能直接进入检索。三、预处理第一步规范化规范化只改形式、不改意图分为三个方面纠正错别字、明显的同音 / 形近字错误。统一全半角、大小写。展开毫无歧义的常见技术缩写如K8s→Kubernetes。对应上面的例子这一步把藏号修正为账号。需要强调的是不确定的专有名词要保留原词避免把模型没见过的术语纠正成一个它认识但错误的词。第二步降噪用户在提问时往往会附带语气词、礼貌词整体偏口语化。这些词如果不去除embedding 之后会产生噪声导致检索效果下降。比如小E我想请问一下MySQL 有哪些锁啊降噪完应该是MySQL 有哪些锁去除了无信息量的干扰性词汇称呼小E、请求框架我想请问一下、语气词啊。但降噪有一条底线只删与检索无关的冗余措辞严禁删除任何技术限定词、修饰语、实体、版本号或承载意图的词。判据很简单——一个词删掉后若会改变检索命中就必须保留。反例「MySQL 聚簇索引和二级索引的区别」不可降成「MySQL 索引」删掉聚簇二级区别会直接毁掉用户的意图。第三步判定问题是否能直接进入检索这一步也是最复杂的一步。用户提出的问题千奇百怪如果只是单纯规范化、降噪完就走统一的检索策略去搜索答案是很难给出合格答案的。举个例子详细讲述一下产品 A可能整个文档都在讲产品 A。如果只拿这句话去向量检索产品 A召回的向量经过 rerank 也许都符合但结果一定是不全且片面的。所以这一步要做的是判断 query 是否能直接进入检索如果能再进一步区分出不能的原因交给后续不同的处理流程。不能的原因可以分为三类1.信息不足。比如“这个产品是怎么登陆的” 这个产品是什么明显缺少信息需要问题澄清。2.角度不定。评判维度/视角缺失比如“我需要用OpenClaw嘛”3.需要拆分。问题包含多个实体或话题覆盖范围大需要拆分子项来检索。比如“讲述一下产品 A”四、设计预处理 LLM 的提示词按上面三步就可以设计出一个交给预处理 LLM 的提示词你是检索 query 处理器对下面的 query 依次做三步先规范化再降噪再判定该 query 能否直接进入检索。 要求如果问题已经足够清晰适合检索,以下三步皆可不对问题修改,不要强行改写 第一步 规范化始终执行只改形式不改意图 - 纠正错别字、明显的同音/形近字错误如装饰起→装饰器不确定时保留原词。 - 统一全半角、大小写。 - 仅展开毫无歧义的常见技术缩写如 K8s→Kubernetes。 规范化只修形式严禁改变用户意图或新增用户没提到的话题。 第二步 降噪去除口语化、礼貌性、请求词、无信息量的词保留关键词语实体技术名词 如原始问题:小E,我想请问一下,MySQL有哪些锁啊? 改写后的查询:MySQL有哪些锁 降噪只删与检索无关的冗余措辞严禁删除任何技术限定词、修饰语、实体、版本号或承载意图的词如聚簇行级全文有哪些区别第3章。判据一个词删掉后若会改变检索命中就必须保留。 反例「MySQL聚簇索引和二级索引的区别」不可降成「MySQL 索引」——删掉聚簇二级区别会毁掉意图。 第三步 判定该 query 能否直接进入检索基于规范化和降噪后的 query 【可以】可确定指向具体的技术概念/章节/问题能检索到精准、集中的内容。 特征仅检索问题即可 返回 {category:retrievable,rewritten_query: 处理后的 query} 【不可以】归入以下四类之一 - missing_info信息不足缺了检索必需的关键限定根本无法检索多为指代不明。 如「这个索引的应用场景是什么」——这个索引指代不明全文索引B树索引其他 返回 {category:missing_info,rewritten_query: 处理后的 query,clarify_reason: 需澄清的原因如这个索引指代不明} - ambiguous角度不定话题已具体、能集中命中但用户想要的维度/立场未给有多个合理答法不知道选哪个。 特征答案就一个主题但有几种角度/立场可选。 如「Vue和React哪个好」(缺选型维度)「Redis做缓存好吗」(缺评判角度) 返回 {category:ambiguous,rewritten_query: 处理后的 query} - pending_split 需要拆分问题显式包括多个实体。或话题大到要覆盖文档一整片内容检索会命中大量分散结果。 特征答案需要罗列并列子项才完整。 如「讲讲MySQL」「讲讲功能A和功能B」 返回 {category:pending_split,rewritten_query: 处理后的 query} - other其他无法直接检索的情况以上三类均不符合 返回 {category:other,rewritten_query: 处理后的 query} 【不可以】归类的优先级先判断信息是否不足再判断问题是否角度不定再判断问题是否需要拆分均不符合则为other 对照 「怎么优化MySQL」→ pending_split优化是一整片索引/查询/配置/架构 「MySQL大表查询慢怎么优化」→ ambiguous场景已具体仍有索引/分区/改SQL几个角度 「Vue和React哪个好」→ ambiguous缺好的维度虽然两个实体但仍为ambiguous 「Vue和React的区别」→ pending_split不缺维度需要拆分 category 仅为[retrievable|pending_split|missing_info|ambiguous|other]不允许有其他词rewritten_query 始终返回处理后的 query结果只返回 JSON不要其他任何内容。 query{query}这里定义了 LLM 的 JSON 返回格式用category区分出问题是否适合直接 向量检索如果不适合再区分出不适合的类型。在后续代码中就可以针对不同的category对问题进行拆解子问题并行检索、或者进行反问澄清等不同的处理流程线。五、为什么要把问题预处理作为显式的一步Agentic RAG 对比传统 workflow 的一大特点就是自主性通过 system promptagent 自身就可以对用户的问题进行预处理并调用 RAG 工具查询。那么为什么还要把 Query Processing 作为单独的步骤拿出来原因有三。它机械且高杠杆。这些步骤的最优路径基本是已知的而且错在这里会一路向下游传播。把它交给 ReAct 循环自主决定结果是模型时而改写得很好、时而干脆跳过、时而过度拆解——这种不一致性对一个本该稳定的前置环节是净亏损。它便宜到不值得让模型决策。主 Agent 自己判断要不要预处理这件事本身就是一次 LLM round-triptoken 延迟。而很多预处理你直接在代码里无脑做掉就行根本不需要先问模型我现在该不该改写。自主性是有税的预处理这里交的税往往换不来对应的灵活性。它关系到可评估性。固定 pipeline 能精确定位是 rewrite 错了还是 rerank 错了纯自主 agent 你很难复现它这次为什么没检索。生产系统要可调试、可回归这一条几乎是决定性的。六、小结Query Processing 看起来只是把问题洗干净,但它是 RAG 里典型的垃圾进垃圾出卡点——前面错一点,后面再强的检索和 rerank 都救不回来。本文把这一步拆成三段:规范化负责修形式(错别字、全半角、缩写),降噪负责删冗余(语气词、礼貌词)但死守会改变检索命中的词不删的底线,判定能否直接检索则负责把问题分流到retrievable / missing_info / ambiguous / pending_split / other,交给下游做直接检索、反问澄清或拆分并行检索。而把这一步做成显式 pipeline 而非交给 agent 自主,是本文真正想强调的工程判断:它机械、高杠杆、便宜,且关乎可评估性。自主性是有税的,在预处理这一环交的税往往换不来对应的灵活性。而Query Processing只是RAG pipeline的第一步搭配后续合适的处理才是完整的RAG流水线。