RAG 的查询理解:用户意图识别与查询扩展的研究与实践

意图不是分类题,是检索策略的选择题

查询改写解决的是"把写得差的查询变好",查询路由解决的是"查询发给哪个后端"。这一篇讨论中间那一层:理解查询想要什么,据此选择检索与组装策略。很多系统把这层做成了"意图分类打标签",标签打完却没有下游消费——这是把意图理解做成了展示品。正确的框架是:每个意图类别对应一条明确的检索策略差异,意图分类的价值完全由它触发的策略分叉决定,没有策略分叉的类别就不该存在。

信息需求的类型学:五类够用

从检索策略差异出发,企业场景的查询类型收敛成五类最实用:事实型("A 产品的接口超时是多少毫秒")——答案在单一段落里,策略是精排 top-1 加强引用;流程型("怎么申请数据导出权限")——答案需要保持步骤顺序,策略是优先命中操作手册并保序注入,绝不能按相似度打乱步骤;对比型("A 方案和 B 方案的差别")——需要多个对象各自的段落,策略是多路定向检索加表格化组装;定位型("报销制度里关于发票的那一节在哪")——用户要的是入口不是答案,策略是命中后直接给文档路径与章节锚点,甚至跳过生成;探索型("我们内部有哪些工具可以做数据脱敏")——需要覆盖面而不是单点精度,策略是高召回候选加多样性重排(MMR 类算法)防同质化。

这五类的设计含义是:分类标签背后各自挂一条从检索参数到组装模板的完整策略链。事实型与探索型的差异贯穿检索深度(精度优先还是召回优先)、重排目标(相关性还是多样性)、生成提示词(简洁引用还是结构化罗列)。标签体系超过七八类的经验后果是:每类分到的样本太少、分类边界开始模糊、下游策略差异不足以支撑类别存在——类型学要服务于策略分叉,而不是服务于分类报告的美观。

混合意图:一问多事怎么办

真实查询常携带混合意图:"A 和 B 哪个便宜、各自适合什么场景"同时是对比型与事实型。处理原则是拆解优先于择主:查询理解层把混合查询拆成子查询(每个携带单一意图与独立策略),检索层并行执行、生成层合并答案;拆解由规则触发(并列连词、多重疑问词)或 LLM 完成。拆解的收益是每条子检索的策略都是纯的,避免"对比策略迁就事实子问"这类两头不讨好的降级。拆不动时(子问纠缠太深)才退回主导意图策略——按信息需求的紧迫度选主,并在生成提示里声明覆盖范围("这里先对比价格维度"),让用户的预期与答案的边界对齐。混合意图的占比用日志抽样测一下再决定投入:多数业务库在 5% 到 15% 区间,值得做拆解但不值得做成主工程。

分类器的成本档位与便宜替代

意图分类的实现按延迟与成本分三档。第一档是规则先行:疑问词、句式模板、关键词信号能判掉三到五成流量("怎么/如何/流程"→流程型,"区别/对比/vs"→对比型),微秒级、零成本、行为完全可解释。第二档是小模型分类器:用会话日志标注几百条样本微调一个 bert 量级的小模型,延迟几毫秒,覆盖规则漏掉的部分;标注数据正好来自反馈循环里沉淀的查询集(与本系列反馈那篇的数据底座衔接)。第三档才是 LLM few-shot 判别:留给前两档都判不准的长尾,并且应该和查询改写合并成同一次调用省成本。绝大多数系统的错误是把第三档当第一档用——每条查询都过一遍 LLM 意图分析,延迟和成本翻倍,而规则本可以白拿五成流量。

查询扩展的研究脉络:从 Rocchio 到 HyDE

经典 PRF:为什么向量检索时代还值得学

查询扩展(Query Expansion)的研究比大模型早半个世纪。Rocchio(1971)的向量反馈模型奠定了框架:用相关文档把查询向量往相关方向拉、用不相关文档往反方向推。伪相关反馈(Pseudo-Relevance Feedback,PRF)把它变成无监督可用:假设 top-k 初检结果相关,拿它们的高频判别词扩展查询。RM3(在 Lavrenko 与 Croft 2001 的相关性模型基础上加入 MMR 式词项选择,Abdul-Jaleel 等人 2004 系统化)是 PRF 的成熟形态:从 top 段落估计词项与查询的相关分布,选几十个扩展词、按权重混合进查询。这套技术在 BM25 检索上至今普遍涨点(TREC 系列任务上 NDCG 提升通常在 5% 到 15% 区间,视集合与参数而定),现代检索引擎的相关性调优里 PRF/RM3 仍是标配手段。

为什么在 dense 检索时代还要讲它:因为 PRF 的机制洞察没有过时——查询和答案的词汇分布本来就有落差(用户问"卡顿",文档写"性能劣化"),统计层面的扩展桥接这个落差,而 dense embedding 只能部分地隐性桥接。工程上两者是互补关系:BM25 通道配 RM3 扩展,向量通道保持原查询(embedding 已经做了语义泛化,再扩展反而引入噪声),两条通道在融合层汇合。

生成式扩展与 HyDE 的位置

大模型时代的扩展分两种形态。一种是词项级生成扩展:让 LLM 给查询补同义表述、上位下位、相关术语,产出仍然是"查询侧的文本",用法等同于传统扩展词。另一种是 HyDE(Gao 等人 2023,Precise Zero-Shot Dense Retrieval without Relevance Labels):让 LLM 先虚构一个假设答案,用假设答案的向量去检索——本质上是用生成的"答案形态文本"做查询表示,在零标注场景下效果超过原始查询向量。HyDE 的适用边界要划清楚:它适合冷启动与零标注场景(没有相关性信号时借模型的先验),在线上系统里它是可选通道而非默认——假设答案的生成引入一次 LLM 调用的延迟与成本,且在模型先验与领域事实有偏差时会系统性带偏检索方向。冷启动工程那篇会把 HyDE 放进冷启动工具箱的合适位置。

扩展的风险:主题漂移

所有扩展手段共享一个核心风险:主题漂移。PRF 在初检质量差的查询上会滚雪球——top-3 本来就不相关,从它们身上挖的扩展词把查询带得更偏;LLM 生成扩展在多义查询上会选错方向("苹果 发布会"扩展出水果相关词)。缓解手段按序是:扩展词要有原始查询词的支撑度约束(RM3 的权重混合天然做了这件事——扩展词永远不是查询的主体);初检置信度低时不扩展(与下一节的 QPP 衔接);扩展词进独立的通道而不是覆写原查询,融合层保留原查询通道的投票权。扩展的本质是赌初检结果能代表信息需求,赌输的保险就是永远不让原查询退场。

自适应:先预测查询难度,再决定投入

QPP:查询性能预测的便宜实现

查询性能预测(Query Performance Prediction,QPP)回答一个问题:这次检索的质量可不可信。经典方法里最便宜好用的是 Clarity Score(Cronen-Townsend 等人 2002):比较"初检结果集合的词项分布"与"全集词项分布"的 KL 散度——初检结果高度聚焦(分布尖峰)说明查询指向明确、检索大概率有效;初检结果分散(分布接近全集背景)说明查询模糊或库里没有好答案。NQC(Zamani 与 Momtaz 等人后续工作)等变体改进了稳定性,思想同源。实现上 Clarity 只需要初检结果的词频统计,毫秒级完成,不需要任何模型调用。

QPP 在查询理解管线里的角色是火力分配的开关:Clarity 高的查询(简单、明确),原始查询直接走标准通道,不做重写不做深度扩展,省下延迟与成本;Clarity 低的查询(模糊、疑难),才动重武器——LLM 改写、生成式扩展、多路并行检索、提高 top-k。这套自适应是查询理解模块从"每条查询同等对待"进化为"按需投入"的关键,在真实流量结构下(多数查询是简单查询)能砍掉大部分不必要的 LLM 调用。

QPP 还有训练式路线:拿历史查询的检索质量标注(来自人工抽检与反馈归因)训练一个小回归模型,特征用查询长度、初检命中率、分数分布统计量,精度通常高于 Clarity 这类无监督方法,但需要标注积累。工程建议是冷启动期先用 Clarity 打底,反馈循环运转半年后切训练式——两者切换对下游完全透明(都归约为 difficulty 一个数),契约设计的红利就在这种替换时刻显现。

管线组装:查询理解模块的输出契约

把上面的组件拼成管线,关键是定义清晰的输出契约——查询理解的结果是一个结构化对象,下游每个通道各取所需:

@dataclass
class QueryUnderstanding:
    intent: str            # factoid | procedure | compare | locate | explore
    entities: list[dict]   # 术语检测产物:[{text, canonical_id, domain, confidence}]
    expanded_terms: list[tuple[str, float]]   # 扩展词与权重(RM3 或 LLM 生成)
    filters: dict          # 由槽位/术语域/版本 scope 推导的过滤条件
    difficulty: float      # Clarity score 归一化后的检索难度
    strategy: str          # standard | aggressive | clarify
    rewritten: str | None  # 仅 aggressive 策略下生成

def route_strategy(qu: QueryUnderstanding) -> QueryUnderstanding:
    if qu.entities and any(e["confidence"] < 0.5 and e["ambiguous"] for e in qu.entities):
        qu.strategy = "clarify"        # 歧义术语无法消解:先澄清
    elif qu.difficulty < 0.4:          # Clarity 低:疑难查询,上重武器
        qu.strategy = "aggressive"
    else:
        qu.strategy = "standard"       # 简单查询:原查询直走
    return qu

这个契约的价值在于把"理解"与"执行"解耦:策略选择是纯函数(可测试、可回放、可离线评估),检索执行是通用引擎(按 strategy 参数切换配置)。查询理解层的所有改动——分类器升级、扩展算法更换、QPP 阈值调整——都在契约内演进,不惊动下游。

整条查询理解管线的组件拓扑与决策流如下:

flowchart LR
    Q[原始查询] --> RU[规则判意图<br/>疑问词 / 句式模板]
    RU --> TD[术语检测<br/>Aho-Corasick 最长匹配]
    TD --> IC[小模型意图分类<br/>规则漏掉的部分]
    IC --> LX[LLM 判别 + 改写<br/>仅长尾流量]
    LX --> EX[扩展选择<br/>RM3 统计扩展 / 生成式扩展]
    EX --> QS[QPP 难度预测<br/>Clarity Score]
    QS --> ST{策略路由}
    ST -->|standard| S1[原查询 + 标准通道]
    ST -->|aggressive| S2[改写 + 深度扩展 + 多路检索]
    ST -->|clarify| S3[澄清提问]
    S1 & S2 & S3 --> OUT[输出契约 JSON<br/>intent / entities / filters / strategy]

小结

查询理解层的正确姿势可以概括为三句话。第一,意图分类的价值由策略分叉决定:五类信息需求(事实/流程/对比/定位/探索)各自挂一条从检索参数到组装模板的策略链,没有策略差异的类别不值得存在;分类器按规则→小模型→LLM 的成本档位逐级兜底,别把 LLM 当第一档用。第二,扩展手段有谱系也有风险:PRF/RM3 的统计扩展在 BM25 通道仍是涨点标配,HyDE 式生成扩展适合零标注场景但要警惕延迟与先验偏差,所有扩展都要防主题漂移——初检置信度低不扩展、扩展词不覆写原查询,给赌错留保险。第三,QPP 是火力分配的开关:Clarity Score 这类毫秒级方法区分疑难查询与简单查询,重武器只打疑难仗,多数简单流量走标准通道省下真金白银。整层封装成一个结构化输出契约,理解与执行解耦,每个组件都可以独立替换与评估。查询理解是 RAG 管线里投入产出比最容易被低估的一层——它花的是毫秒级的钱,省的是整条下游管线的错误成本。

← 返回资讯列表

读者留言

COMMENTS 暂无
仅本站原创文章开放留言 · 请勿留下手机号、邮箱等个人信息

还没有留言,来说第一句?