目录

前言:

1、Embedding 与向量检索:决定检索质量

1.1 Embedding 模型选择:最容易被低估的决策

1.1.1 选型维度

1.1.2 中文场景的推荐选择

1.2 向量相似度:不只是「余弦」那么简单

1.3 向量数据库:选型没有银弹

1.3.1 本地开发 vs 生产级

1.4 索引与检索优化:性能的关键

1.4.1 三大索引算法

1.4.2 关键调优参数

1.4.3 规模化:分片、副本、负载均衡

1.5 混合检索:单靠向量是不够的

1.5.1 为什么需要混合

1.5.2 融合排序策略

1.6 过滤检索:别忘了元数据的价值

过滤的三个维度

2、Query 处理:用户问题不能直接拿去检索

2.1 查询改写:让问题变得「可检索」

2.2 查询扩展:一个角度不够,就多试几个

2.3 HyDE:假设性文档嵌入

2.4 多查询检索

2.5 意图识别:不是所有问题都需要检索

2.6 多轮对话改写:最容易被忽视的难题

写在最后


 

前言:

如果你做过 RAG 系统,一定有过这样的体验:明明 LLM 够聪明,向量库也跑了,可检索出来的结果就是「答非所问」。问题出在哪?绝大多数时候,不是模型不行,而是你的检索管道太粗糙。

这篇文章,我想把 RAG 中最关键、也最容易被轻视的两个模块讲透:Embedding 与向量检索,以及 Query 处理。前者决定了你「能不能找到」,后者决定了你「找得准不准」。作为一个在生产环境踩过无数坑的 Agent 开发者,我会尽量把每个决策点背后的思考讲清楚——不是告诉你「该怎么做」,而是告诉你「为什么该这么做」。

核心观点:RAG 的天花板不在大模型,而在检索。检索质量每提升 10%,最终回答质量可能提升 30%。把精力花在检索管道上,性价比远超换一个更大的模型。

1、Embedding 与向量检索:决定检索质量

上一篇我们讲了数据处理,这一篇聚焦检索本身检索质量 = Embedding 模型 × 相似度算法 × 向量库 × 索引策略 × 检索方式,任何一个环节选错或调差,都会让最终答案大打折扣。

核心观点:检索质量不是由某一个"最好的组件"决定的,而是由整条链路的 短板决定的。Embedding 模型再好,向量库索引配错,检索照样不准;混合检索做得很完善,但 Query 没改写,原始口语化的问题根本检索不到相关文档。

Embedding 是 RAG 的地基。它的工作很简单——把文本变成一组数字(向量),让语义相近的文本在向量空间中彼此靠近。但「简单」两个字背后,藏着大量影响最终效果的决策。

1.1 Embedding 模型选择:最容易被低估的决策

Embedding 模型是检索链路的第一环,也是影响最大的单一组件。它决定了文本被编码成什么样的向量,而向量空间的质量直接决定了"相似"的含义是否被正确建模。

1.1.1 选型维度
维度 说明 关键考量
语言适配 中文优化 / 多语言 / 英文为主 中文场景下通用英文模型效果会明显下降;多语言模型在中文专项上不如中文专用模型
向量维度 768 / 1024 / 1536 / 3072 维度越高表达能力越强,但存储和计算成本越高
最大输入长度 512 / 2048 / 8192 tokens 决定单个 chunk 能多大;短输入模型需要更细的分块
成本 开源(免费) / API 调用(按量计费) 大规模数据需要批量 embedding,API 成本会累积
延迟 本地推理 10-50ms / API 调用 100-300ms 实时检索场景中 Query embedding 的延迟直接影响响应速度
1.1.2 中文场景的推荐选择

选型误区

不要只看 MTEB 排行榜。排行榜用的是公开数据集,你的业务数据分布完全不同。正确做法是:从排行榜选 3-5 个候选模型,用你的真实文档和真实 Query 构建评估集,做 A/B 对比。一个在自己数据上表现好但排行榜一般的模型,远比排行榜第一但没验证的模型可靠。

图 1:Embedding 与向量检索基础流程——文档和 Query 必须使用同一模型编码

Embedding 模型对检索质量的影响,远比你想的大。它本质上决定了「你觉得语义相近的两段文本,在向量空间里是不是真的相近」。一个对中文理解不够好的模型,会把「请假流程」和「年假申请」推到很远的地方——明明在中文语境下,这两者几乎是同义词。

选型建议(个人经验,供参考):

纯中文场景,优先考虑 bge-m3bge-large-zhgte-Qwen2 这类中文优化模型。它们在中文语义理解上通常比通用英文模型好一个档次。多语言混合场景,bge-m3 是目前综合表现最稳的选择——它同时支持中英文,且提供了稠密、稀疏、多向量三种检索模式。

选型时重点看四个维度:模型维度(影响存储和检索速度)、最大输入长度(决定你能塞多大的 chunk)、MTEB/C-MTEB 排名(衡量检索质量的基准测试)、以及推理成本与延迟(生产环境必须考虑)。

一个常见的误区是:只看 benchmark 分数,不看实际业务数据。我的建议是,拿你自己的业务数据做一个小规模评测。跑 100-200 条 query,看不同模型在你的场景下的 recall@10 和 MRR。这比看任何排行榜都靠谱。

1.2 向量相似度:不只是「余弦」那么简单

把文本变成向量后,怎么判断两个向量"有多相似"?这就是相似度计算的问题。看似简单,但不同计算方式在不同场景下效果差异不小。

余弦相似度是最常用的。它只关心两个向量的「方向」是否一致,不关心「长度」。好处是对向量模长不敏感,坏处是丢失了模长中可能携带的信息。大多数现代 Embedding 模型会对输出做归一化,归一化后余弦相似度和点积是等价的——这也是为什么很多向量库默认用余弦。

点积同时考虑方向和长度。如果你的 Embedding 没有做归一化,点积可能更合适。某些模型(如 ColBERT)的 late interaction 机制天然就是基于点积的。

欧氏距离直观但用得少。它在图像检索中比较常见,文本场景下通常不如余弦。

实用建议

文本检索默认用余弦相似度。原因:Embedding 模型训练时通常以余弦相似度作为优化目标,推理阶段保持一致效果最好。注意:不同向量库默认的相似度算法可能不同——Milvus 默认用 IP(内积),Qdrant 默认用 Cosine,如果不确认,一定要显式指定。

归一化陷阱

如果用点积(IP)做相似度,但 Embedding 模型输出的向量没有归一化,那么长文本(模长大)会在检索中天然获得更高分数,导致检索偏向长文档。解决方案:在写入向量库之前统一做 L2 归一化(vector / np.linalg.norm(vector)),归一化后点积等价于余弦相似度。

1.3 向量数据库:选型没有银弹

向量数据库的选型,本质上是一个在「开发效率」、「查询性能」、「运维成本」和「功能丰富度」之间做权衡的过程。

1.3.1 本地开发 vs 生产级
类别 方案 特点 适用阶段
本地开发 Chroma Python 原生,API 简洁,开箱即用 原型验证、小规模实验
FAISS Meta 开源,纯库无服务,极致性能 单机大规模检索、嵌入式场景
LanceDB 列式存储,支持多模态,Serverless 中小规模、多模态检索
生产级 Milvus 分布式架构,百亿级向量,社区活跃 大规模企业场景
Qdrant Rust 编写,高性能,过滤能力强 中大规模、元数据过滤密集
Weaviate 内置模块化检索,支持混合检索 需要混合检索开箱即用
Elasticsearch 成熟生态,BM25+向量双引擎 已有 ES 基础设施的团队
OpenSearch ES 分叉,开源许可更友好 需要开源许可合规的团队
Pgvector PostgreSQL 扩展,SQL 原生 中小规模、不想引入新组件

选型建议

如果你是从零开始,Qdrant 是目前最推荐的"开发到生产平滑过渡"方案:本地单机模式开发,上线后切换到分布式模式,代码零改动。如果你已有 PostgreSQL 基础设施且数据量在百万级以内,pgvector 是最低成本选择——不用引入新组件,一条 SQL 就能做向量检索。

1.4 索引与检索优化:性能的关键

当向量数据量从几千涨到百万、千万甚至亿级时,暴力检索(逐一计算相似度排序)完全不可行。这时就需要近似最近邻(ANN)索引算法来加速检索。

图 2:暴力检索 vs ANN 索引检索——数据量一大,索引是唯一出路

1.4.1 三大索引算法
算法 原理 检索精度 检索速度 构建速度 内存占用
HNSW 分层小世界图,从顶层粗导航逐层细化 高(最接近暴力检索结果) 极快 慢(需构建多层图) 大(需存储图结构)
IVF K-Means 聚类分桶,只检索最近的几个桶 中(取决于探针数 nprobe)
量化压缩 PQ/SQ 将浮点向量压缩为整型编码 低-中(有精度损失) 极快 极小(压缩 8-32 倍)

当你的向量库里有几百万、几千万条数据时,暴力搜索(逐一计算相似度)就不现实了。这时候你需要 ANN(Approximate Nearest Neighbors,近似最近邻)索引。

ANN 的核心思想是:牺牲一点点精度,换取几个数量级的速度提升。它通过精巧的数据结构,把搜索范围从「全量」缩小到「一小部分最有可能的候选」。

目前主流的 ANN 算法有两种:

HNSW(Hierarchical Navigable Small World)是当前的王者。它构建了一个多层图结构,上层稀疏用于快速跳跃,下层密集用于精细搜索。优点是查询速度快、召回率高;缺点是内存占用大(需要把图和向量都放在内存里)。大多数场景下,HNSW 是默认选择。

IVF(Inverted File Index)先用聚类把向量空间分成若干区域,查询时只在最近的几个区域里搜索。优点是内存友好(可以配合量化压缩);缺点是需要调参(nlist、nprobe),且在数据分布不均匀时效果会下降。

量化压缩是另一个重要的优化手段。PQ(Product Quantization)把高维向量压缩成短的编码,可以把内存占用降低 8-16 倍,代价是召回率下降 5-15%。SQ(Scalar Quantization)更温和一些,把 float32 压成 int8,内存减半,精度损失很小。在内存紧张的生产环境,量化几乎是必选项。

工程实践

生产中最常用的组合是 HNSW + 量化压缩:用 HNSW 保证检索精度,用量化压缩降低内存占用。Milvus 和 Qdrant 都原生支持这种组合。

IVF 适合你的场景如果:数据量极大(亿级以上)、对检索精度要求不是特别高、或者需要频繁新增数据(IVF 增量构建比 HNSW 快得多)。

1.4.2 关键调优参数

1.4.3 规模化:分片、副本、负载均衡

百万级向量单机就够了,但到了千万级甚至亿级,就需要分布式架构:

  • 分片(Sharding):将向量按哈希或范围分散到多个节点。检索时并行查询所有分片,合并 Top-K 结果。这是横向扩展的基础。
  • 副本(Replica):每个分片维护 1-2 个副本,保证高可用。单节点宕机不影响检索服务。
  • 负载均衡:请求均匀分配到各节点,避免热点。Milvus 的 QueryNode 和 Qdrant 的 Sharding Replica 都内置了负载均衡机制。

1.5 混合检索:单靠向量是不够的

纯向量检索有一个结构性弱点:它擅长语义相似,但不擅长精确匹配。比如用户搜"ErrorCode: E4042",向量检索可能返回"错误码 E4031"之类语义相近但实际不对的结果。

这就是为什么你需要混合检索:把向量检索(擅长语义匹配)和 BM25(擅长关键词精确匹配)结合起来。

1.5.1 为什么需要混合
检索方式 擅长 不擅长
稠密向量检索 语义相似、同义表达、模糊匹配 精确关键词、专有名词、代码标识符
稀疏检索(BM25) 精确匹配、关键词命中、专有名词 同义表达、语义理解、跨语言
混合检索 两者优势叠加,兼顾语义和精确 链路更长,需要调融合参数

图 3:混合检索架构——向量检索 + BM25 双路召回,融合后重排序

1.5.2 融合排序策略

两路检索各返回 Top-K 结果后,怎么合并成一个统一排序?两种主流方案:

融合策略最常见的是 RRF(Reciprocal Rank Fusion):对每条文档,取它在两路检索结果中的排名,算一个倒数之和作为最终分数。公式很简单:score = 1/(k+rank_dense) + 1/(k+rank_sparse),其中 k 通常取 60。RRF 的好处是不受原始分数量程影响,鲁棒性强。

另一种是加权融合:给两路的分数分别乘以权重(比如 0.7:0.3),然后求和。好处是可以调节,坏处是两路分数的量程可能不同,需要归一化。

实战数据:在我做过的几个企业知识库项目中,混合检索相比纯向量检索,Top-5 召回率平均提升了 15-25%。提升最明显的 case 恰好是那些包含专有名词、产品编号、人名等「不可语义化」信息的查询。如果你的知识库里有大量这类内容,混合检索不是可选项,而是必选项。

实战推荐

如果你不知道该选哪个融合策略,先用 RRF。它是我在项目中用得最多的方案——不需要调参、不依赖分数可比性、对两路检索质量差异不敏感。Qdrant 和 Weaviate 都内置了 RRF 支持。

1.6 过滤检索:别忘了元数据的价值

在真实业务中,几乎不存在"在所有文档中无差别检索"的需求。用户往往只关心特定范围:"这个季度的产品文档""我所在部门的制度""公开级别的知识"。这就是过滤检索的价值。

过滤的三个维度
  1. 按元数据过滤:文档类型、部门、标签、来源等。比如只检索"产品规格"类文档,排除"会议纪要"。
  2. 按权限过滤:根据当前用户的角色和权限,只检索其有权访问的文档。这是企业 RAG 的硬性要求。
  3. 按时间/业务线过滤:只检索 2024 年 Q3 之后的文档、只检索某条业务线的产品手册。

过滤的实现方式很关键

有两种实现方式:
· 先过滤后检索(Pre-filter):先按元数据缩小范围,再在子集中做向量检索。精度高,但如果过滤后子集太小可能召回不足。
· 检索后过滤(Post-filter):先做全量向量检索拿到 Top-K,再按元数据过滤。速度快,但可能过滤后剩余结果很少。

生产中推荐用向量库原生支持的过滤检索(如 Qdrant 的 Filter、Milvus 的 Attribute Filter),它们在索引层面做了优化,兼顾过滤精度和检索效率。

# Qdrant 过滤检索示例
client.search(
    collection_name="rag_docs",
    query_vector=query_embedding,
    query_filter=Filter(
        must=[
            FieldCondition(key="department", match=MatchValue(value="product")),
            FieldCondition(key="date", range=Range(gte="2024-01-01")),
            FieldCondition(key="permission", match=MatchAny(any=["public", "internal"])),
        ]
    ),
    limit=10
)

2、Query 处理:用户问题不能直接拿去检索

前面讲的是"检索引擎"本身的能力。但还有一个经常被忽略的环节:用户的问题本身。

用户的原始 Query 有多"烂"?举个例子:用户在多轮对话中说"那它跟 V2 版本有什么区别?"。这个 Query 直接做 embedding 检索,检索到的内容大概率是垃圾——因为"它"指代不明,"V2 版本"是哪个产品?没有上下文,向量模型也猜不到。

一个残酷的事实是:用户输入的问题,绝大多数时候都不适合直接拿去检索。它可能是口语化的(「那个啥啥怎么弄来着」),可能是模糊的(「帮我看看那个政策」),可能是缺上下文的(多轮对话中的追问)。直接拿这种 query 去检索,就像拿着一团模糊的毛线去找另一团毛线——能找对才怪。

核心问题

用户原始问题往往口语化、模糊、缺上下文。直接拿去 embedding,效果会很差。Query 处理的目标是:在检索之前,把"烂问题"变成"好问题"——让检索引擎能够理解用户真正想找什么。

2.1 查询改写:让问题变得「可检索」

查询改写的目标很明确:把用户的原始输入,变成一个更适合检索的形式。

这包括几个子任务:纠错(用户打错了字,「人工智障」→「人工智能」)、补全(用户说得太简略,「请假」→「员工请假审批流程」)、去口语化(「那个啥啥报销怎么弄来着」→「费用报销流程」)、提取关键词(从长句中提取核心检索词)。

最直接的实现方式是用 LLM 做改写。给它一个 prompt:「请将以下用户问题改写为更适合知识库检索的形式,保持语义不变,使其更加精确和完整」。成本低、效果好,是目前最实用的方案。

一个容易踩的坑:查询改写不是越详细越好。过度改写会引入噪声,把检索带偏。比如用户问「K8s 怎么扩容」,你改写成「Kubernetes 集群如何在生产环境中进行水平节点扩展和垂直资源调整」——看起来更专业了,但「垂直资源调整」是你脑补的,可能把不相关的文档拉进来。改写应该保持克制,目标是「精确化」而不是「膨胀化」

改写类型 原始 Query 改写后 目的
纠错 "embeding 模型选哪个" "embedding 模型选哪个" 修复拼写错误,避免检索偏移
补全 "向量库" "向量数据库推荐" 补全为完整的检索意图
去口语化 "那个检索效果不好咋办" "RAG 检索效果优化方法" 将口语表达转为检索友好的关键词
提取关键词 "我想了解一下 Milvus 和 Qdrant 哪个更适合中小团队" "Milvus vs Qdrant 中小团队 选型" 去掉冗余词,保留检索核心词
# 查询改写 prompt 示例
rewrite_prompt = """你是一个查询改写助手。
将用户的原始问题改写为适合向量检索的形式:
1. 修正拼写错误
2. 补全不完整的表达
3. 将口语化表达转为书面关键词
4. 保留原始意图,不增加或改变语义

用户问题:{user_query}
改写后:"""

2.2 查询扩展:一个角度不够,就多试几个

查询扩展的思路是:用户的一个问题,可能只触及了答案的一个角度。如果你能从多个角度去检索,召回正确答案的概率就更大。

同义词扩展是最基础的:把「辞职」扩展为「辞职 / 离职 / 解除劳动合同」。在中文场景下,这尤其重要,因为同一个概念的表达方式太多了。

多角度改写更进一步:把「怎么优化网页加载速度」改写成多个版本——「网页性能优化方法」、「前端加载速度提升技巧」、「Web 页面响应时间优化」。每个版本侧重不同,检索结果也会有差异。

多查询检索(Multi-Query Retrieval)是 LangChain 里一个很好用的技术:用 LLM 把原始问题生成 3-5 个不同角度的查询,分别检索,然后合并去重。这相当于用多次检索的开销,换取更高的召回率。在很多场景下,这个 trade-off 是值得的。

图 4:多查询扩展检索——一个 Query 生成多个改写版本,分别检索后融合

成本与收益的平衡

多查询扩展的代价是 N 倍的检索延迟和 API 调用(LLM 生成改写 + N 次向量检索)。建议 N=3-5,超过 5 个收益递减明显。如果延迟敏感,可以让多个检索并行执行,总延迟约等于单次检索。

2.3 HyDE:假设性文档嵌入

HyDE(Hypothetical Document Embeddings)是一个反直觉但非常有效的技巧。核心思路是:先让 LLM 生成一个假设性答案,再用这个答案去检索,而不是用原始问题去检索。

为什么有效?因为问题和答案在向量空间中的分布不同。答案文本通常包含更多与文档相似的特征词,而问题往往很短、很抽象。用"假答案"检索,比用"真问题"检索更容易命中正确文档。

图 5:HyDE 工作流——先用 LLM 生成假设答案,再用答案检索真实文档

为什么 HyDE 有效?因为「问题」和「答案」在语言上的差距,往往比「问题」和「问题」的差距小。用户问「年假有多少天」,文档写的是「员工年度带薪休假天数为 5 天」——这两者在词汇上几乎没有重叠,但语义是相通的。而 LLM 生成的假设答案,恰好充当了从「问题语言」到「文档语言」的桥梁。

当然,HyDE 也有局限:它依赖 LLM 生成的假设答案不能太离谱。如果 LLM 的假设答案方向就错了,那检索结果也会偏。另外,每次查询多了一次 LLM 调用,延迟和成本都会增加。我的建议是:在事实性问答场景优先尝试 HyDE,在开放性问答或闲聊场景则不必。

HyDE 的适用边界

HyDE 不是万能的。它适合问题简短抽象、文档详细具体的场景。如果问题本身已经很长很具体,HyDE 生成的假答案可能反而引入噪声。另外,HyDE 增加了一次 LLM 调用的延迟,对延迟敏感的场景需要权衡。

2.4 多查询检索

多查询检索和查询扩展有相似之处,但更侧重于拆解而非改写。把一个复杂问题拆成多个子问题,分别检索,最后合并。

比如用户问:"对比 Milvus 和 Qdrant 在过滤检索、分布式扩展和运维成本三方面的区别"。一个问题做检索很难同时命中三方面的内容。拆成三个子问题:

  • "Milvus vs Qdrant 过滤检索能力对比"
  • "Milvus vs Qdrant 分布式扩展架构"
  • "Milvus Qdrant 运维成本对比"

每个子问题独立检索,各拿到 Top-K,最后合并去重。这样每个维度都有专门的检索结果,综合答案更完整。

和查询扩展的区别

查询扩展是同一意图的多表达(换个说法),多查询检索是同一问题的多子任务(拆成子问题)。前者解决"表达不对"问题,后者解决"问题太复杂"问题。两者可以叠加使用。

2.5 意图识别:不是所有问题都需要检索

不是所有用户输入都需要检索。意图识别是 Query 处理的"前置路由器"——在决定怎么检索之前,先判断要不要检索、检索什么。

这是一个看似简单、实则极其重要的能力:判断用户的问题到底需不需要检索。

想想看,如果用户问「你好」或者「1+1等于几」,你还要去知识库里检索一番,这不是浪费资源吗?更糟糕的是,检索可能返回不相关的内容,反而干扰了 LLM 的回答。

意图识别要做的事情包括:

第一,判断是否需要检索。闲聊、常识问答、数学计算这些,直接让 LLM 回答就好,不需要检索。只有当问题涉及到特定领域的知识、可能随时间变化的信息、或者你的私有数据时,才需要检索。

第二,判断问题属于哪个知识域。如果你的系统接入了多个知识库(比如 HR 制度库、技术文档库、产品手册库),先判断问题属于哪个域,再去对应的库里检索,效果和效率都会好很多。

第三,判断问题类型。事实问答(「年假几天」)、总结(「总结一下这个项目的进展」)、对比(「A 方案和 B 方案有什么区别」)、推理(「如果销量增长 20%,产能是否跟得上」)——不同类型的问题,检索策略和 prompt 构造方式应该不同。

# 意图识别 prompt 示例
intent_prompt = """分析用户问题的意图,返回 JSON:
{{
  "need_retrieval": true/false,
  "domain": "product" | "policy" | "tech" | "general",
  "type": "fact" | "summary" | "comparison" | "reasoning" | "chitchat"
}}

用户问题:{user_query}
"""

工程实现建议

意图识别不需要一个很大的模型。用一个微调过的小模型(甚至规则 + 关键词匹配)就能覆盖 80% 的场景。关键是把意图分类的体系设计好,并且和后续的检索策略、prompt 模板联动起来。我见过一些团队用 GPT-4 做意图识别——杀鸡用牛刀了。

关键是保证分类速度快、准确率高——如果意图判断错误,后面的检索和生成全白做。

2.6 多轮对话改写:最容易被忽视的难题

如果你做过对话式 RAG(ChatBot),你一定遇到过这个问题:

用户第一轮问:「公司的补充医疗保险怎么报销?」
AI 回答:「根据 XX 制度,补充医疗保险的报销流程是……」
用户第二轮问:「那牙科呢?」

这个「那牙科呢?」直接拿去检索,几乎不可能找到正确答案——因为它缺了关键上下文:「牙科」是「补充医疗保险的牙科项目报销」。这就是多轮对话中的指代消解省略补全问题。

解决方案是:在每一轮对话时,把历史对话作为上下文,用 LLM 把当前问题改写成一个独立的、自包含的查询。Prompt 大致是这样的:「给定以下对话历史和用户最新问题,将最新问题改写为一个独立的完整问题,补全省略的信息,替换指代词。」

「那牙科呢?」+ 历史上下文 → 「公司补充医疗保险是否覆盖牙科项目的报销?」

一个进阶技巧:不要只改写最后一轮。有时候用户第三轮的问题,需要回溯到第一轮才能理解完整意图。建议保留 3-5 轮历史对话作为改写上下文,但不要把太多历史塞进去——噪声太多反而会干扰改写质量。

另一个注意点:改写后的 query 应该给用户看到(至少在调试模式下)。这样当改写出了问题(过度改写、改写方向错误),用户可以及时纠正。这在生产环境的 debug 中极其有用。

另:这是 Query 处理中最容易被遗漏的环节。在多轮对话中,用户的问题往往依赖前几轮的上下文:

对话轮次 用户原始问题 改写后(用于检索) 改写类型
Turn 1 "RAG 的分块策略有哪些?" "RAG 的分块策略有哪些?"(无需改写)
Turn 2 "哪个效果最好?" "RAG 分块策略中哪个效果最好" 补全省略信息
Turn 3 "它和语义分块比呢?" "父子分块策略和语义分块策略的效果对比" 指代消解
Turn 4 "那具体怎么实现?" "父子分块策略的具体实现方法" 上下文依赖

如果不做多轮改写,Turn 2-4 的 Query 直接检索,向量模型完全不知道"哪个""它""怎么实现"指的是什么,检索结果毫无意义。

图 6:多轮对话改写——将依赖上下文的省略问题补全为完整的独立 Query

实现要点

多轮改写需要将对话历史当前问题一起发给 LLM,让它理解上下文后生成一个完整的独立问题。关键细节:
· 只保留最近 3-5 轮对话,太长会超出 token 限制且引入噪声
· 改写后的问题应该是自包含的——不看对话历史也能理解
· 如果当前问题本身已经完整,直接返回不改写,避免过度处理

# 多轮对话改写 prompt
rewrite_prompt = """根据对话历史,将用户最后一条消息改写为自包含的独立问题。
规则:
1. 补全省略的主语和宾语
2. 解析代词指代(它、那个、这个)
3. 如果当前问题已自包含,直接返回
4. 不要改变原始意图

对话历史:
{chat_history}

当前问题:{current_query}

改写后的问题:"""

写在最后

检索质量是 RAG 的命脉。而检索质量不是一个单一指标,它是 Embedding 模型、向量库、索引策略、检索方式、Query 处理等多环节的综合结果。

本文覆盖了两个核心维度:

  • 检索引擎侧:Embedding 模型选型(中文优先考虑中文优化模型)、相似度算法(默认余弦)、向量库选型(开发用 Qdrant,大规模用 Milvus)、索引优化(HNSW + 量化压缩)、混合检索(向量 + BM25 + RRF 融合)、过滤检索(元数据预过滤)。
  • Query 处理侧:查询改写(纠错 + 去口语化)、查询扩展(多角度改写 + 多查询召回)、HyDE(假设答案检索)、多查询检索(拆子问题)、意图识别(路由前置判断)、多轮对话改写(指代消解 + 上下文补全)。

如果你在 RAG 项目中遇到"检索不准"的问题,排查路径建议:先看 Query 处理 → 再看 Embedding 模型 → 然后看分块策略 → 最后看检索方式和索引参数。从我经手的项目经验看,Query 处理和分块策略的问题占比最大。

记住:检索是 RAG 的地基。地基不打牢,上面的生成再华丽,也是空中楼阁。

 RAG文章:
RAG 深度解析:从原理到工程实践(一)
RAG 检索深度指南:从 Embedding 到 Query 处理(二)

RAG 进阶双引擎:重排序精准化与生成可控化的工程实践(三)

RAG 进阶之路Agent 自主决策与评估体系(四)

RAG 可观测性实战:上线后必须能定位"为什么答错"(五)

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐