你好,我是方可乐。
一个正在深耕AI编程的30+的AI应用开发工程师。
用AI编程开发过Web应用网站,浏览器插件,微信小程序,AI智能体,RAG系统。
关注公众号&添加微信:ThinkFun666
领取【AI编程资料包】
去年6月份,我做了一个连自己都觉得疯狂的决定:裸辞,All in AI。
现在已经换赛道成功,继续在工作中探索。
继上节分享的向量模型选型,今天来分享下RAG中检索这方面的重点内容。
在 RAG 系统里,检索是最关键的一环。如果检索不到合适的内容,后面的大模型再强也没用。 那么问题来了:检索方案,有没有固定套路,如果没有,那到底该怎么选?
在 RAG 中,检索的核心目标是: 从知识库中找到与用户问题最相关的上下文片段,提供给大模型回答。
常见的检索方案有三种:
-
1. 向量检索(Vector Retrieval) -
1. 基于语义相似度。 -
2. 优点:能处理同义词、语义改写等问题。 -
3. 缺点:有时对关键词敏感度不高。 -
2. 混合检索(Hybrid Retrieval) -
1. 向量 + 关键词(BM25)组合。 -
2. 优点:既保留语义理解,又能保证关键词命中。 -
3. 缺点:需要权重调参,效果依赖数据分布。 -
3. 多路检索(Multi-Retrieval) -
1. 并行使用多个检索器,直接合并结果。 -
2. 优点:召回率高,不容易遗漏。 -
3. 缺点:候选片段可能冗余,需要后续 rerank 过滤。
先了解上面这些信息,下面我们上代码。
新建文件16_rag_retrieval_methods.py
# 演示向量检索 / 混合检索 / 多路检索(DeepSeek + 中文 bge)
import os
from dotenv import load_dotenv
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_openai import ChatOpenAI
from langchain.chains import RetrievalQA
from langchain_community.retrievers import BM25Retriever
from langchain.retrievers import EnsembleRetriever, MergerRetriever
load_dotenv()
# ========== 1. 准备环境 ==========
# 使用中文嵌入模型以更好地处理中文文本
embedding_model = HuggingFaceEmbeddings(model_name="BAAI/bge-small-zh-v1.5")
texts = [
"公司人事制度规定员工需提前申请年假。",
"财务制度要求报销需提供完整发票。",
"技术部门采用敏捷开发流程。",
"公司提供免费午餐。",
"员工请假需提前 3 天申请。",
"公司每年提供 10 天带薪年假。"
]
# 构建向量数据库
vectorstore = Chroma.from_texts(
texts,
embedding=embedding_model,
persist_directory="./chroma_db"
)
# 切换到 DeepSeek(OpenAI 兼容接口)
llm = ChatOpenAI(
model="deepseek-chat",
base_url=os.getenv("DEEPSEEK_BASE_URL", "https://api.deepseek.com/v1"),
api_key=os.getenv("DEEPSEEK_API_KEY"),
temperature=0,
)
query = "公司请假制度是什么?"
# ========== 2. 向量检索 ==========
vector_retriever = vectorstore.as_retriever(search_kwargs={"k": 2})
qa_chain = RetrievalQA.from_chain_type(llm=llm, retriever=vector_retriever)
print("\n=== 向量检索结果(候选片段 + 答案) ===")
# 透明打印候选片段与得分
vector_results = vectorstore.similarity_search_with_score(query, k=2)
for i, (doc, score) in enumerate(vector_results, start=1):
try:
score_val = float(score)
except Exception:
score_val = score
print(f"[V{i}] score={score_val} content={doc.page_content}")
print("→ LLM 回答:")
print(qa_chain.invoke(query))
# ========== 3. 混合检索(向量 + BM25) ==========
bm25_retriever = BM25Retriever.from_texts(texts)
bm25_retriever.k = 2
hybrid_retriever = EnsembleRetriever(
retrievers=[bm25_retriever, vector_retriever],
weights=[0.5, 0.5]
)
qa_chain = RetrievalQA.from_chain_type(llm=llm, retriever=hybrid_retriever)
print("\n=== 混合检索结果(候选片段 + 答案) ===")
# 透明打印候选片段(不含得分)
hybrid_docs = hybrid_retriever.get_relevant_documents(query)
for i, doc in enumerate(hybrid_docs[:2], start=1):
print(f"[H{i}] content={doc.page_content}")
print("→ LLM 回答:")
print(qa_chain.invoke(query))
# ========== 4. 多路检索 ==========
multi_retriever = MergerRetriever(
retrievers=[bm25_retriever, vector_retriever],
)
qa_chain = RetrievalQA.from_chain_type(llm=llm, retriever=multi_retriever)
print("\n=== 多路检索结果(候选片段 + 答案) ===")
multi_docs = multi_retriever.get_relevant_documents(query)
for i, doc in enumerate(multi_docs[:2], start=1):
print(f"[M{i}] content={doc.page_content}")
print("→ LLM 回答:")
print(qa_chain.invoke(query))
运行截图
下一篇文章,单独分享下 Rerank 策略,看看如何在候选文档很多的情况下,让大模型只看最相关的内容,从而进一步提升 RAG 的效果。
对AI应用开发感兴趣的同学,欢迎加微信申请入群交流学习。
想要学习AI应用开发的同学,可以参照我的代码跑起来,举一反三,一天一个脚印的进步,我相信,会足够坚实。
接下来100天,我会记录从Java程序员到AI应用开发工程师的完整转型路:每一个困惑、每一次突破、每一个真实瞬间。
当前是35/100。
如果你也想了解AI应用开发到底是什么,如果你也在考虑转型但还在犹豫,那就跟着我的记录,一起探索。
继续折腾中,有问题随时交流 🤝
微信号:ThinkFun666
我的免费AI编程交流群,欢迎加入讨论AI编程玩法(说明需求和来意,申请入群)
推荐阅读:
我就喜欢干这种傻事!为什么转型AI应用开发,我不建议你直接从看书开始学起?

