【手摸手学 RAG】我的RAG机器人咋总答不对?三步升级你的检索系统
大家好,我是胡琦。
很多小伙伴在上手RAG(检索增强生成)之后,都会遇到一个灵魂拷问:“为什么我喂给RAG的文档明明有答案,但它就是答不对或者答不好?” 问题往往不出在LLM身上,而是出在“检索”这一环。基础的向量检索虽然好用,但面对真实、复杂的业务场景,就如同“新手村装备”,亟需升级。
别担心!本篇,我们就“手摸手”带你走一遍RAG检索系统的“装备升级”之路,通过混合检索、查询改写和结果重排三步,让你的RAG机器人战斗力瞬间爆表!
第一步:双剑合璧 —— 混合检索(Hybrid Search)
痛点:单纯的向量检索,只懂“语义”,不懂“字词”。当用户查询专有名词、代码函数或产品型号(比如 ModelArts
、昇思MindSpore
)时,它可能会返回一堆语义相关但并不精准的内容。
解决方案:让我们把“关键词检索”这位老将(如BM25)请回来,和“向量检索”这位新星联手,实现 1+1 > 2
的效果。
实战代码(以LangChain为例):
想象一下,我们已经有了一些文档 docs
。
# 导入我们需要的库
from langchain.retrievers import BM25Retriever, EnsembleRetriever
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings # 这里以OpenAI为例,也可以换成华为云的或其他
# 我们的文档
docs = [
"华为云ModelArts是面向AI开发者的平台。",
"昇思MindSpore是一个全场景AI框架。",
"ModelArts Pro是企业级AI应用开发套件。"
]
doc_texts = [d for d in docs]
# 1. 准备“向量检索”这位新星
embeddings = OpenAIEmbeddings()
vector_store = FAISS.from_texts(doc_texts, embeddings)
vector_retriever = vector_store.as_retriever(search_kwargs={"k": 2})
# 2. 准备“关键词检索”这位老将
bm25_retriever = BM25Retriever.from_texts(doc_texts)
bm25_retriever.k = 2
# 3. 双剑合璧!初始化EnsembleRetriever
# 我们可以通过 weights 参数调整两位的发言权重
ensemble_retriever = EnsembleRetriever(
retrievers=[bm25_retriever, vector_retriever],
weights=[0.5, 0.5]
)
# 试一试效果!
query = "ModelArts平台是做什么的?"
retrieved_docs = ensemble_retriever.invoke(query)
print(retrieved_docs)
效果分析:现在,当用户查询 ModelArts
时,bm25_retriever
能确保包含“ModelArts”关键词的文档被高优先级召回,而 vector_retriever
则能补充那些语义上描述“AI开发者平台”但可能没有直接出现关键词的文档。完美!
第二步:读懂人心 —— 查询改写(Query Rewriting)
痛点:在多轮对话中,用户经常会问“它怎么样?”或者“具体说说第二个”。这种依赖上下文的查询,如果直接扔给检索系统,它肯定一脸懵。
解决方案:在检索之前,先让LLM扮演一个“翻译官”的角色,把用户的口语化、有歧义的查询,改写成一个独立的、信息完整的查询语句。
实战代码(LLM来助阵):
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
# 1. 定义一个“翻译官”的指令(Prompt)
rewrite_prompt = ChatPromptTemplate.from_messages([
("system", "你是一个精通信息检索的助手。请根据对话历史,将用户的最新问题改写成一个独立的、对检索系统更友好的问题。"),
("user", "对话历史:\n{chat_history}\n\n最新问题: {question}")
])
# 2. 召唤LLM
model = ChatOpenAI()
rewriter = rewrite_prompt | model
# 3. 模拟一个对话场景
chat_history = "用户: 给我介绍下Text2SQL技术。\nAI: Text2SQL能将自然语言转化为SQL查询语句,非常智能。"
question = "它主要用在哪些场景?"
# 开始改写!
rewritten_question = rewriter.invoke({
"chat_history": chat_history,
"question": question
})
print(f"原始问题: {question}")
print(f"改写后问题: {rewritten_question.content}")
# 接下来,我们应该用 rewritten_question.content 去进行检索
# retrieved_docs = ensemble_retriever.invoke(rewritten_question.content)
输出可能为:
原始问题: 它主要用在哪些场景?
改写后问题: Text2SQL技术主要应用在哪些业务场景?
效果分析:看到了吗?通过简单一步预处理,我们的RAG系统现在能“读懂人心”了,再也不怕用户的“代词攻击”了。这个思想同样可以延伸到Text2SQL等查询构建场景,本质都是让LLM先把自然语言“翻译”成机器爱听的“话”。
第三步:精益求精 —— 结果重排(Re-ranking)
痛点:混合检索保证了召回结果的“广度”,但可能不够“精度”。返回的5个文档里,也许只有2个是真正高度相关的,如果这2个排在后面,就会影响LLM最终生成答案的质量。
解决方案:引入一个“精排裁判”(Reranker模型),对初步召回的结果进行二次打分和排序,把最相关的文档顶到最前面。
实战代码(引入Cross-encoder做精排):
# Cross-encoder通常需要更专门的模型,这里我们用LangChain的包装来演示概念
from langchain.retrievers.document_compressors import CrossEncoderReranker
from langchain.retrievers import ContextualCompressionRetriever
from sentence_transformers.cross_encoder import CrossEncoder # 需要 pip install sentence-transformers
# 1. 初始化一个“精排裁判”模型
# bge-reranker-large 是一个很不错的开源选择
cross_encoder_model = CrossEncoder('BAAI/bge-reranker-large')
# 2. 把裁判包装成LangChain认识的组件
compressor = CrossEncoderReranker(model=cross_encoder_model, top_n=3) # top_n=3表示我们精排后只取前3名
# 3. 创建一个“精排检索器”
# 它的作用是:先用我们的老检索器(第一步的混合检索器)去检索,然后结果交给compressor去精排
compression_retriever = ContextualCompressionRetriever(
base_compressor=compressor,
base_retriever=ensemble_retriever # 使用我们第一步创建的混合检索器
)
# 见证奇迹的时刻!
query = "介绍一下ModelArts Pro套件"
reranked_docs = compression_retriever.invoke(query)
print(reranked_docs)
效果分析:经过Reranker
的“火眼金睛”,返回的文档不仅相关,而且是最相关的文档排在最前面。这相当于给LLM递上了一份“标准答案”,它生成回答的质量自然就水涨船高了。
总结
怎么样,跟着我们走完这三步,是不是感觉自己的RAG系统战斗力瞬间提升了好几个档次?
- 混合检索:解决了“字词”与“语义”的平衡问题。
- 查询改写:让系统能理解复杂的对话上下文。
- 结果重排:在召回结果中优中选优,提升精准度。
这套“三连招”组合下来,基本上能解决大部分RAG应用中遇到的检索难题。当然,RAG的优化之路远不止于此,还有更精细化的分块策略(Chunking)、更高级的**查询转换(Query Transformation)**等技术等着我们探索。
现在,轮到你了!赶紧打开你的代码编辑器,亲手为你的RAG机器人进行一次“装备升级”吧!如果在实践中遇到任何问题,欢迎随时在评论区与我交流。
本文基于[datawhalechina/all-in-rag]项目整理,仅代表个人学习心得,不代表任何官方观点。小伙伴们有疑问欢迎交流讨论,一起学习一起进步!
- 点赞
- 收藏
- 关注作者
评论(0)