如何提高 RAG 的检索质量?这才是真正的瓶颈所在

如何提高 RAG 的检索质量?这才是真正的瓶颈所在

有一句在 AI 工程圈流传的话:

"RAG 没问题,问题出在你的检索层。"

大多数开发者遇到 RAG 效果差时,第一反应是换更大的模型、调 temperature、改 prompt。折腾一圈发现没用------因为根本没对症。

真正的问题几乎总是出在 检索 这一层。


一、为什么 RAG 会失败?

先看一个典型的"标准"RAG 架构:

复制代码
用户 Query
  → OpenAI Embeddings 向量化
  → Pinecone 召回 200 条候选
  → Cohere Rerank 重排
  → 塞进 LLM(~50K tokens)
  → 输出答案

听起来还不错,但实际运行中这个 pipeline 有几个严重问题:

问题 1:向量相似度 ≠ 相关性

余弦相似度衡量的是"语义接近",不是"对这个问题有用"。

经典反例:用户问"如何处理 API 超时错误",系统会召回大量"讲超时是什么"的段落,却没有一条包含解决方案。语义上很近,但对用户没用。

问题 2:候选池太大,质量被稀释

召回 200 条候选,哪怕 rerank 做得再好,也是在一堆噪声里打捞。Rerank 是补丁,不是根治。

问题 3:50K tokens 的上下文是资源浪费

把 50K tokens 的"松散相关"内容喂给 LLM,不叫"提供上下文",叫"让模型在噪声里猜答案"。结果是答案质量差、token 成本高、延迟上升(常需要重试)。

问题 4:chunk 策略是拍脑袋定的

绝大多数项目用固定 512 token 切块,完全无视文档结构、段落边界和跨 chunk 的逻辑依赖。这是质量低下的根源之一,也是最容易被忽视的。


二、提升检索质量的五个核心方向

1. Hybrid Search:向量 + BM25 双引擎

单纯向量检索的盲区:精确名词、型号、代码、缩写。

比如用户问 "GPT-4o mini 的 context window 是多少",纯向量检索可能返回一堆泛泛讲 GPT 系列的段落,而 BM25 关键词检索能精确定位含"GPT-4o mini"的文本。

Hybrid Search 同时跑向量检索和 BM25,再用 RRF(Reciprocal Rank Fusion) 合并排序:

python 复制代码
# RRF 融合示例(k=60 是经验常量)
def rrf_score(rank, k=60):
    return 1 / (k + rank)

def merge_results(vector_results, bm25_results):
    scores = {}
    for rank, doc in enumerate(vector_results):
        scores[doc.id] = scores.get(doc.id, 0) + rrf_score(rank)
    for rank, doc in enumerate(bm25_results):
        scores[doc.id] = scores.get(doc.id, 0) + rrf_score(rank)
    return sorted(scores.items(), key=lambda x: -x[1])

实测效果:在精确名词检索场景,Hybrid Search 比纯向量检索的召回精度提升 20-40%。

支持 Hybrid Search 的工具:Elasticsearch、Weaviate、Qdrant(原生支持),或手动实现 RRF 融合。


2. 优化 Chunk 策略:Parent-Child Chunking

固定长度切块的问题:

  • 小块(128 tokens):检索精度高,但上下文不完整,LLM 看不懂
  • 大块(1024 tokens):上下文完整,但向量语义被稀释,检索精度差

Parent-Child Chunking 解决这个矛盾:

复制代码
原始文档
  ├── Parent Chunk(512-1024 tokens)← 存入索引,提供完整上下文
  │     └── Child Chunk 1(128 tokens)← 用于精确检索
  │     └── Child Chunk 2(128 tokens)← 用于精确检索
  │     └── Child Chunk 3(128 tokens)← 用于精确检索

检索时用小块,填充上下文用大块。

python 复制代码
# LlamaIndex 实现示例
from llama_index.node_parser import HierarchicalNodeParser

parser = HierarchicalNodeParser.from_defaults(
    chunk_sizes=[1024, 512, 128]  # 父→子层级
)
nodes = parser.get_nodes_from_documents(documents)

效果:在长文档问答场景,答案完整性显著提升,同时避免无关内容干扰。


3. Query 优化:让问题更适合检索

用户的原始问题往往不适合向量检索。"我的报表跑不出来"------这句话的语义向量几乎不会命中任何文档。

3.1 Query Rewriting

用 LLM 把用户问题改写为更适合检索的形式:

python 复制代码
rewrite_prompt = """
将以下用户问题改写为更适合文档检索的查询语句。
去除口语化表达,突出关键概念和实体。

用户问题:{query}
改写后的检索查询:
"""

3.2 HyDE(Hypothetical Document Embeddings)

不用原始 query 做检索,而是先让 LLM 生成一个"假设中的答案文档",再用这个假设文档的向量去检索真实文档:

python 复制代码
hyde_prompt = """
假设你是一个知识库文档,请生成一段可能回答以下问题的文档段落:

问题:{query}
文档段落:
"""

# 用假设文档的向量做检索
hypothetical_doc = llm.generate(hyde_prompt.format(query=user_query))
search_vector = embed(hypothetical_doc)  # 而不是 embed(user_query)
results = vector_store.search(search_vector, top_k=10)

HyDE 的逻辑:假设答案的语义空间比问题的语义空间更接近正确文档。在技术文档、知识库问答场景效果尤其明显。

3.3 Query Decomposition

复杂问题拆成多个子问题分别检索,再合并:

css 复制代码
原始问题:比较 A 产品和 B 产品的性价比
  → 子问题 1:A 产品的价格和功能是什么?
  → 子问题 2:B 产品的价格和功能是什么?
  → 分别检索 → 合并结果 → LLM 综合回答

4. 重新审视 Rerank 的位置

Rerank 不是万能的,但用对了位置很关键。

错误的用法:用 Rerank 弥补召回质量差的问题(从 200 条噪声里 rerank)。

正确的用法:先把候选集控制在 20-30 条高质量结果,再用 Rerank 做精排。

css 复制代码
向量检索 top-30(而不是 top-200)
  + BM25 top-20
  → RRF 合并 → 去重 → ~30 条候选
  → Rerank(Cohere / BGE / bge-reranker-v2)
  → top-5 to top-10
  → 填入 LLM(~5K tokens 而不是 50K)

这一步改动,token 消耗能降低 80-90%,同时答案质量反而更好------因为 LLM 看到的都是真正相关的内容。


5. 评估驱动优化:你没有 eval,就没有改进

所有优化都是盲目的,如果你没有一套评估体系。

最简单的起点:构建一个"问题-标准答案"测试集,用以下指标衡量:

指标 含义
Recall@K 正确文档是否出现在 top-K 结果中
MRR(Mean Reciprocal Rank) 正确文档排在第几位
Answer Faithfulness LLM 的回答是否忠实于检索结果
Answer Relevance 回答是否真正回答了用户问题

推荐工具:RAGAS (专为 RAG 评估设计)、LlamaIndex 的 Evaluation 模块

python 复制代码
from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy, context_recall

result = evaluate(
    dataset,
    metrics=[faithfulness, answer_relevancy, context_recall]
)

没有 eval,任何"优化"都只是感觉。


三、一个现实的优化路径

不建议同时上所有技术,认知负担太重,也不知道哪个有效。推荐按优先级迭代:

css 复制代码
第一步:先建评估集(20-50 个真实问题 + 标准答案)
  ↓
第二步:优化 Chunk 策略(Parent-Child + 按文档结构切)
  ↓
第三步:上 Hybrid Search(向量 + BM25 + RRF)
  ↓
第四步:控制候选数量(top-20~30)+ 轻量 Rerank
  ↓
第五步:Query Rewriting / HyDE(针对复杂问题场景)

每一步都跑一遍评估,看哪里有提升,哪里没有。不做评估就跳步,是工程上最大的资源浪费。


四、总结

常见误区 正确认知
RAG 效果差,换更大的模型 先查检索层,90% 概率问题在这里
Rerank 能解决一切 Rerank 是精排,不是救场
候选越多越好 候选质量 > 候选数量
固定 512 token 切块万能 Chunk 策略必须配合文档结构
向量相似度等于相关性 两者相关但不等价,需要混合检索

RAG 的本质是一个信息检索问题 ,不是一个LLM 问题

把精力放在让正确的文档出现在正确的位置上,LLM 自然能给出好答案。

检索质量决定答案质量。垃圾进,垃圾出------不管你用多好的模型。

相关推荐
AI攻城狮4 小时前
DeepSeek KV Cache 入门解读:98% 命中率背后的工程逻辑
云原生
菜鸟的日志8 小时前
【软件架构风格】面向服务架构(SOA)及其微服务演进
微服务·云原生·架构
PH = 78 小时前
K8S集群的搭建
云原生·容器·kubernetes
CS创新实验室9 小时前
CS实验室行业报告:云计算与云原生行业分析报告
云原生·云计算
AI攻城狮9 小时前
如何维护公司级别的 CLAUDE.md 文件?
云原生
AIMath~1 天前
雪花算法+ZooKeeper解决方案+RPC是什么
分布式·zookeeper·云原生
gwjcloud1 天前
Kubernetes从入门到精通(进阶篇)03
云原生·容器·kubernetes
日取其半万世不竭1 天前
PeerTube 部署指南:自建视频托管平台
云原生·eureka·音视频
小义_1 天前
【Kubernetes】(十二)配置存储卷
云原生·容器·kubernetes