🎯 RAG高效召回实战(一):6大策略解决"找不准"难题

83 阅读17分钟

在构建企业级 RAG 系统时,很多人把精力全押在“大模型选型”或“Prompt 工程”上,却忽略了最关键的一步:召回(Retrieval)。试想:

  • 用户问"如何优化 LLM 推理延迟?",系统却召回一堆"训练加速"文档;
  • 客服场景中,明明有解决方案,AI 却回答"暂无相关信息";
  • 甚至因召回偏差,导致生成内容出现事实性幻觉……

⚠️ 问题不在模型,而在"没找到对的文档"。

本文作为《RAG高效召回方法》系列的第一篇,将深入剖析6 大高阶召回策略

从基础的 Top-K 与混合检索,到重排序(Rerank)、查询改写(Query Rewriting),再到动态联网增强——每一种方法都经过工业级验证,可显著提升 Hit Rate 与 MRR。

更重要的是,后续要掌握的双向改写(Query2Doc/Doc2Query)、索引扩展(Small-to-Big、连续/离散索引)、知识库健康治理等能力深度融合,共同构建一个高精度、高鲁棒、可演进的 RAG 引擎

现在,让我们先打好地基——从这 6 个关键召回策略开始。

📊 1、合理设置Top-K

如果要召回更多的片段,如何设置?

docs = knowledgeBase.similarity_search(query, k=10)

🔍 2、改进检索算法

**🗺️ 知识图谱:**利用知识图谱中的语义信息和实体关系,增强对查询和文档的理解,提升召回的相关性。(后面章节介绍)

🔄 3、引入重排序(Reranking)

🔄 重排序模型:

对召回结果进行重排,提升问题和文档的相关性。常见的重排序模型有BGE-Rerank和Cohere Rerank。

🎬 场景:

用户查询"如何提高深度学习模型的训练效率?"

📄 召回结果:

初步召回10篇文档,其中包含与"深度学习"、"训练效率"相关的文章。

🔄 重排序:

BGE-Rerank对召回的10篇文档进行重新排序,将与"训练效率"最相关的文档(如"优化深度学习训练的技巧")排在最前面,而将相关性较低的文档(如"深度学习基础理论")排在后面。

🔀 混合检索:

结合向量检索和关键词检索的优势,通过重排序模型对结果进行归一化处理,提升召回质量。

💡 什么是重排序模型?

重排序Rerank主要用于优化初步检索结果的排序,提高最终输出的相关性或准确性。

BGE-Rerank和Cohere Rerank是两种广泛使用的重排序模型,它们在检索增强生成(RAG)系统、搜索引擎优化和问答系统中表现优异。

🔬 1、BGE-Rerank 由北京智源人工智能研究院(BAAI)开源发布,属于FlagEmbedding项目的一部分。

基于Transformer的Cross-Encoder结构,直接计算查询(Query)与文档(Document)的交互相关性得分。

📚 训练数据:

支持多语言(中、英等),训练数据包括T2Ranking、MSMARCO、NLI等数据集。

提供bge-reranker-base和bge-reranker-large两个版本,后者在精度上更优。

🚀 部署方式:

可本地部署

✨ 优势:

开源免费,适合本地化部署,保护数据隐私。

在中文任务中表现优秀,适用于垂直领域优化

💻 示例代码

#模型下载
from modelscope import snapshot_download
model_dir = snapshot_download('BAAI/bge-reranker-large', cache_dir='/root/autodl-tmp/models')
import torch
from transformers import AutoModelForSequenceClassification, AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained('/root/models/BAAI/bge-reranker-large')
model = AutoModelForSequenceClassification.from_pretrained('/root/models/BAAI/bge-reranker-large')
model.eval()
pairs = [['what is panda?', 'The giant panda is a bear species endemic to China.']]
inputs = tokenizer(pairs, padding=True, truncation=True, return_tensors='pt')
scores = model(**inputs).logits.view(-1).float()
print(scores)  # 输出相关性分数
# 输出相关性分数
pairs = [
    ['what is panda?', 'The giant panda is a bear species endemic to China.'],  # 高相关
    ['what is panda?', 'Pandas are cute.'],                                     # 中等相关
    ['what is panda?', 'The Eiffel Tower is in Paris.']                        # 不相关
]
inputs = tokenizer(pairs, padding=True, truncation=True, return_tensors='pt')
scores = model(**inputs).logits.view(-1).float()
print(scores)

在BGE-Rerank模型中,相关性分数scores是一个未归一化的对数几率(logits)值,范围没有固定的上限或下限(不像某些模型限制在0-1)。不过BGE-Rerank的分数通常落在以下范围:

  • 高相关性: 3.0~10.0

  • ⚠️ 中等相关性: 0.0~3.0

  • 低相关性/不相关: 负数(如-5.0以下)

☁️ 2、Cohere Reank 由Cohere公司提供的商业API服务。基于专有的深度学习模型,支持多语言(如rerank-multilingual-v3.0)。

📚 训练数据:

优化了语义匹配,特别适用于混合检索(如结合BM25和向量检索)后的结果优化。

🔧 使用方式:

通过API调用,集成到LangChain、LlamaIndex等框架中。

✨ 优势:

• 简单易用,适合快速集成到现有系统。

• 在英文和多语言任务中表现优异,如提升Hit Rate(命中率)和MRR(平均倒数排名)。

import cohere
co = cohere.Client(api_key="YOUR_API_KEY")
query = "What is the capital of France?"
docs = ["Paris is the capital of France.", "Berlin is the capital of Germany."]
results = co.rerank(query=query, documents=docs, top_n=2, model='rerank-multilingual-v3.0')
print(results) #Cohere Rerank的API返回的是归一化后的相关性分数(如0-1),更易解释

📊 对比

特性BGE-RerankCohere Rerank
开源/商业开源商业API
部署方式本地部署云端调用
多语言支持中英优化多语言
适用场景数据敏感,垂直领域快速集成,多语言优化

🔄 4、优化查询扩展

💬 相似语义改写:

使用大模型将用户查询改写成多个语义相近的查询,提升召回多样性。

  • 例如,LangChain的 MultiQueryRetriever 支持多查询召回,再进行回答问题。
# 加载向量数据库,添加allow_dangerous_deserialization=True参数以允许反序列化
vectorstore = FAISS.load_local("./faiss-1", embeddings, allow_dangerous_deserialization=True)
# 创建MultiQueryRetriever
retriever = MultiQueryRetriever.from_llm(
retriever=vectorstore.as_retriever(),
llm=llm
)
query = "客户经理的考核标准是什么?"
# 执行查询
results = retriever.get_relevant_documents(query)

✏️ 5、查询改写

❓ 为什么需要查询改写?

RAG 的核心在于"检索-生成"。如果第一步"检索"就走偏了,那么后续的"生成"质量也会降低。

用户提出的问题往往是口语化的、承接上下文的、模糊的,甚至是包含了情绪的。

而知识库里的文本(切片/Chunks)通常是陈述性的、客观的。=> 需要一个翻译官的角色,将用户的"口语化查询"转换成"书面化、精确的检索语句"

💡 如何针对不同类型的Query进行改写?

通过精心设计的 Prompt 来引导 LLM完成这项任务。

🔗 1、上下文依赖型

📋 特征:

当前问题无法独立理解,必须结合前一轮或多轮对话历史才能明确意图。

🎬 典型场景:

"它支持哪些格式?"

"这个方法有效吗?"

❌ 问题:

"它""这个"等代词指代不明,直接检索会失败。

✅ 改写策略: 结合对话上下文,将代词替换为具体实体或动作。

📝 改写示例:

"""上下文依赖型Query改写"""
instruction = """
你是一个智能的查询优化助手。请分析用户的当前问题以及前序对话历史,判断当前问题是否依赖于上下文。
如果依赖,请将当前问题改写成一个独立的、包含所有必要上下文信息的完整问题。
如果不依赖,直接返回原问题。
"""
prompt = f"""
### 指令 ###
{instruction}
### 对话历史 ###
{conversation_history}
### 当前问题 ###
{current_query}
### 改写后的问题 ###

💬 对话历史:

👤 用户**:**"我想了解一下上海迪士尼乐园的最新项目。"

🤖 AI:"上海迪士尼乐园最新推出了'疯狂动物城'主题园区,这里有朱迪警 官和尼克狐的互动体验。"

👤 用户:"这个园区有什么游乐设施?"

🤖 AI:"'疯狂动物城'园区目前有疯狂动物城警察局、朱迪警官训练营和尼 克狐的冰淇淋店等设施。"

**❓ 当前查询:**还有其他设施吗?

**✅ 改写结果:**除了疯狂动物城警察局、朱迪警官训练营和尼克狐的冰淇淋 店之外,'疯狂动物城'园区还有其他设施吗?

⚖️ 2、对比型

📋 特征:

用户希望比较两个或多个对象、方法、模型的异同或优劣。

🎬 典型场景: "RAG 和微调哪个更好?"

"BGE-Rerank 和 Cohere Rerank 有什么区别?"

❌ 问题: 知识库中文档通常单独描述某个技术,很少直接包含"A vs B"的对比内容。若按原句检索,可能只召回其中一方的信息。

✅ 改写策略: 拆解为多个独立查询,或生成涵盖双方的中性检索语句。

📝 改写示例:

"""对比型Query改写"""
instruction = """
你是一个查询分析专家。请分析用户的输入和相关的对话上下文,识别出问题中需要进行比较的多个对象。
然后,将原始问题改写成一个更明确、更适合在知识库中检索的对比性查询。
"""
prompt = f"""
### 指令 ###
{instruction}
### 对话历史/上下文信息 ###
{context_info}
### 原始问题 ###
{query}
### 改写后的查询 ###

💬 对话历史:

👤 用户: "我想了解一下上海迪士尼乐园的最新项目。"

🤖 AI: "上海迪士尼乐园最新推出了疯狂动物城主题园区,还有蜘蛛侠主 题园区"

❓ 当前查询: 哪个游玩的时间比较长,比较有趣

✅ 改写结果: 哪个游玩时间更长、更有趣:上海迪士尼乐园的疯狂动物城 主题园区和蜘蛛侠主题园区?

🔤 3、模糊指代型

📋 特征:

用户用"你""你们""你的模型"等指代当前 AI 系统本身。

🎬 典型场景:

"你支持多少种语言?"

"你的训练数据截止到什么时候?"

❌ 问题:

直接根据这些代词去知识库中查询,是查找不到结果的。

✅ 改写策略:

将这些指代词替换为明确的对象名称。

📝 改写示例:

"""模糊指代型Query改写"""
instruction = """
你是一个消除语言歧义的专家。请分析用户的当前问题和对话历史,找出问题中 "都"、"它"、"这个" 等模糊指代词具体指向的对象。
然后,将这些指代词替换为明确的对象名称,生成一个清晰、无歧义的新问题。
"""
prompt = f"""
### 指令 ###
{instruction}
### 对话历史 ###
{conversation_history}
### 当前问题 ###
{current_query}
### 改写后的问题 ###

💬 对话历史:

👤 用户: "我想了解一下上海迪士尼乐园和香港迪士尼乐园的烟花表演。"

🤖 AI: "好的,上海迪士尼乐园和香港迪士尼乐园都有精彩的烟花表演。"

**❓ 当前查询:**都什么时候开始?

**✅ 改写结果:**上海迪士尼乐园和香港迪士尼乐园的烟花表演都什么时候开始?

🎯 4、多意图型

📋 特征:

一个问题中隐含多个独立子问题或目标。

🎬 场景:

"怎么安装 CUDA 并配置 PyTorch 环境?"

"RAG 的召回率低怎么办?有哪些优化方法?"

❌ 问题:

单一检索可能只覆盖部分意图,导致答案不完整。

✅ 改写策略:

使用 LLM 将复合问题拆分为多个原子查询,分别检索后再融合。

📝 改写示例:

"""多意图型Query改写 - 分解查询"""
instruction = """
你是一个任务分解机器人。请将用户的复杂问题分解成多个独立的、可以单独回答的简单问题。以JSON数组格式输出。
"""
prompt = f"""
### 指令 ###
{instruction}
### 原始问题 ###
{query}
### 分解后的问题列表 ###
请以JSON数组格式输出,例如:["问题1", "问题2", "问题3"]
"""
response = get_completion(prompt, self.model)
try:
    return json.loads(response)
except:
    return [response]

❓ 原始查询: 门票多少钱?需要提前预约吗?停车费怎么收?

✅ 分解结果: ['门票多少钱?', '需要提前预约吗?', '停车费怎么收?']

5、反问型

📋 特征:

以否定、质疑或反问形式表达真实需求。

🎬 场景:

"难道就没有办法解决 RAG 召回不准的问题吗?"

"是不是所有大模型都不能联网?"

❌ 问题:

表面是否定句,实际是寻求肯定解决方案。若按字面检索"没有""不能",会召回错误或无关内容。

✅ 改写策略:

去除情绪化/修辞成分,提取核心正向意图。

📝 改写示例:

"""反问型Query改写"""
instruction = """
你是一个沟通理解大师。请分析用户的反问或带有情绪的陈述,识别其背后真实的意图和问题。
然后,将这个反问改写成一个中立、客观、可以直接用于知识库检索的问题。
"""
prompt = f"""
### 指令 ###
{instruction}
### 对话历史 ###
{conversation_history}
### 当前问题 ###
{current_query}
### 改写后的问题 ###
"""
return get_completion(prompt, self.model)

💬 对话历史:

👤 用户: "你好,我想预订下周六上海迪士尼乐园的门票。"

🤖 AI: "正在为您查询... 查询到下周六的门票已经售罄。"

👤 用户: "售罄是什么意思?我朋友上周去还能买到当天的票。"

❓ 当前查询: 这不会也要提前一个月预订吧?

✅ 改写结果: 迪士尼乐园门票是否需要提前一个月预订?

💻 基于Prompt生成文本

def get_completion(prompt, model="qwen-turbo-latest"):
    messages = [{"role": "user", "content": prompt}]
    response = dashscope.Generation.call(
        model=model,
        messages=messages,
        result_format='message',
        temperature=0,
    )
    return response.output.choices[0].message.content

🤖 6、自动识别Query类型进行改写

自动识别Query类型并进行改写

"""自动识别Query类型并进行改写"""
instruction = """
你是一个智能的查询分析专家。请分析用户的查询,识别其属于以下哪种类型:
1. 上下文依赖型 - 包含"还有"、"其他"等需要上下文理解的词汇
2. 对比型 - 包含"哪个"、"比较"、"更"、"哪个更好"、"哪个更"等比较词汇
3. 模糊指代型 - 包含"它"、"他们"、"都"、"这个"等指代词
4. 多意图型 - 包含多个独立问题,用"、"或"?"分隔
5. 反问型 - 包含"不会"、"难道"等反问语气
说明:如果同时存在多意图型、模糊指代型,优先级为多意图型>模糊指代型
请返回JSON格式的结果:
{
    "query_type": "查询类型",
    "rewritten_query": "改写后的查询",
    "confidence": "置信度(0-1)"
}
"""
prompt = f"""
### 指令 ###
{instruction}
### 对话历史 ###
{conversation_history}
### 上下文信息 ###
{context_info}
### 原始查询 ###
{query}
### 分析结果 ###
"""
response = get_completion(prompt, self.model)
try:
	return json.loads(response)
except:
	return {
		"query_type": "未知类型",
		"rewritten_query": query,
		"confidence": 0.5
	}

🌐 6、查询+联网搜索

🤔 你有没有遇到过这样的情况?

❓ 问AI:"今天北京天气怎么样?"

它却回答:"北京四季分明,春暖花开……"(但今天明明在下暴雨!)

❓ 问AI:"iPhone 17什么时候发布?"

它一本正经地告诉你:"苹果最新款是iPhone 15。"(可实际上,发布会就在下周!)

❓ 问题出在哪儿?

因为绝大多数大模型(比如GPT、通义千问、DeepSeek等)本质上是一个"静态知识库"——它们的知识截止于训练数据的时间点。比如,如果一个模型训练数据只更新到2024年7月,那么它对2025年发生的事就一无所知,只能靠"猜"或者"编",这就是所谓的 "幻觉"。

而联网搜索,就是给大模型装上一双"实时看世界的眼睛"。

💡 那么,哪些问题必须联网才能答对?

1. 🔍 识别查询是否需要联网搜索

identify_web_search_needs

📥 输入:

  • query:用户查询
  • conversation_history:对话历史上下文

📤 输出:

  • need_web_search: 是否需要联网搜索
  • search_reason: 搜索原因
  • confidence: 置信度(0-1)
instruction = """
你是一个智能的查询分析专家。请分析用户的查询,判断是否需要联网搜索来获取最新、最准确的信息。
需要联网搜索的情况包括:
1. 时效性信息 - 包含"最新"、"今天"、"现在"、"实时"、"当前"等时间相关词汇
2. 价格信息 - 包含"多少钱"、"价格"、"费用"、"票价"等价格相关词汇
3. 营业信息 - 包含"营业时间"、"开放时间"、"闭园时间"、"是否开放"等营业状态
4. 活动信息 - 包含"活动"、"表演"、"演出"、"节日"、"庆典"等动态信息
5. 天气信息 - 包含"天气"、"下雨"、"温度"等天气相关
6. 交通信息 - 包含"怎么去"、"交通"、"地铁"、"公交"等交通方式
7. 预订信息 - 包含"预订"、"预约"、"购票"、"订票"等预订相关
8. 实时状态 - 包含"排队"、"拥挤"、"人流量"等实时状态
请返回JSON格式:
{
    "need_web_search": true/false,
    "search_reason": "需要搜索的原因",
    "confidence": "置信度(0-1)"
}
"""
prompt = f"""
### 指令 ###
{instruction}
### 对话历史 ###
{conversation_history}
### 用户查询 ###
{query}
### 分析结果 ###
"""

2. ✏️ 为联网搜索改写查询

rewrite_for_web_search

📥 输入:

  • query:原始查询
  • search_type:搜索类型

📤 输出:

  • rewritten_query: 改写后的查询
  • search_keywords: 搜索关键字列表
  • search_intent: 搜索意图
  • suggested_sources: 建议搜索来源
instruction = """
你是一个专业的搜索查询优化专家。请将用户的查询改写为更适合搜索引擎检索的形式。
改写技巧:
1. 添加具体地点 - 如"上海迪士尼乐园"、"香港迪士尼乐园"
2. 添加时间范围 - 如"2024年"、"今天"、"本周"
3. 使用关键词组合 - 将长句拆分为关键词
4. 添加搜索意图 - 明确搜索目的
5. 去除口语化表达 - 转换为标准搜索词
6. 添加相关词汇 - 增加同义词或相关词
请返回JSON格式:
{
    "rewritten_query": "改写后的搜索查询",
    "search_keywords": ["关键词1", "关键词2", "关键词3"],
    "search_intent": "搜索意图",
    "suggested_sources": ["建议搜索的网站类型"]
}
"""
prompt = f"""
### 指令 ###
{instruction}
### 原始查询 ###
{query}
### 搜索类型 ###
{search_type}
### 改写结果 ###
"""

3. 🎯 生成搜索策略

generate_search_strategy

📥 输入:

  • query:原始查询
  • search_type:搜索类型

📤 输出:

  • primary_keywords: 主要关键字
  • extended_keywords: 扩展关键字
  • search_platforms: 搜索平台
  • search_tips: 搜索技巧
instruction = f"""
你是一个搜索策略专家。请为用户的查询制定详细的搜索策略。
当前日期:{current_date}
搜索策略包括:
1. 主要搜索词 - 核心关键词
2. 扩展搜索词 - 相关词汇和同义词
3. 搜索网站 - 推荐的搜索平台
4. 时间范围 - 具体的搜索时间范围
请返回JSON格式:
{{
    "primary_keywords": ["主要关键词"],
    "extended_keywords": ["扩展关键词"],
    "search_platforms": ["搜索平台"],
    "time_range": "具体的时间范围"
}}
"""
prompt = f"""
### 指令 ###
{instruction}
### 用户查询 ###
{query}
### 搜索类型 ###
{search_type}
### 搜索策略 ###
"""

📝 示例

💬 对话历史:

👤 用户:"我想去上海迪士尼乐园玩"

🤖 AI:"上海迪士尼乐园是一个很棒的选择!"

❓ 当前查询: 下周六的门票多少钱?需要提前多久预订?

需要联网搜索

🔍 搜索原因: 查询下周六的门票价格和预订时间,涉及价格信息和预订信息,需要联网获取最新、最准确的数据。

📊 置信度: 0.98

✏️ 改写查询: 下周六上海迪士尼乐园门票价格及预订时间要求

🔑 搜索关键词: ['下周六', '上海迪士尼乐园', '门票价格', '预订时间', '提前多久预订']

🎯 搜索意图: 获取特定日期的门票价格和预订政策信息

📚 建议来源: ['官方网站', '旅游预订平台(如携程、飞猪)', '景点官方社交媒体账号']

🎯 搜索策略:

  • 主要关键词: ['下周六的门票多少钱?需要提前多久预订?']

  • 扩展关键词: []

  • 搜索平台: ['百度', '谷歌']

  • 时间范围: 最近一周

📝 4.总结

大模型再聪明,也活在"过去"。

要让它回答"现在"和"未来"的事,必须给它插上联网的翅膀。

🎉 END

如果你觉得本文有帮助,欢迎点赞👍、在看👀、转发📤,也欢迎留言💬分享你的经验!

📚 往期文章回顾: