大数跨境

LangChain 实战第18课:RAG向量检索答案不够准?教你用 Rerank 提升问答精准度

LangChain 实战第18课:RAG向量检索答案不够准?教你用 Rerank 提升问答精准度 玩AI的方可乐
2025-10-06
2
导读:手把手教学,10 分钟见结果

 

你好,我是方可乐。
一个正在深耕AI编程的30+的AI应用开发工程师。
用AI编程开发过Web应用网站,浏览器插件,

微信小程序,AI智能体,RAG系统。

注公众号&添加微信:ThinkFun666

领取【AI编程资料包】



 

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 

去年6月份,我做了一个连自己都觉得疯狂的决定:裸辞,All in AI。

现在已经换赛道成功,继续在工作中探索。


 

上一篇文章分享了3个主流的检索方案,细心的同学去跑了代码并且进行更多的实践后,可能会发现这样一个问题。

一次检索出来的结果,有时候准,有时候很不准。

是的,仅靠向量相似度,结果可能“相关性不够精准”或“信息重复”。

这个时候,我们就需要用到一个叫做Rerank的东西,来提升检索的精准度。

Rerank 是什么

就是字面的意思,重排序。在初步检索得到一批候选后,使用更复杂模型或者算法进行“第二道筛选”与重排序。

  • • 常见方式:
    • • Cross-Encoder Rerank:提升相关性与回答准确度。
    • • MMR(最大边际相关性):提升多样性与覆盖面。

Cross-Encoder Rerank

  • • 是什么
    • • 一种“模型方法”。它用一个训练好的模型,把 问题(query) 和 候选文档 拼在一起,逐对打分,算出它们的相关性。
  • • 怎么理解
    • • 可以想象成:你问了一个问题,把答案候选一个个拿出来,让“专家”逐一打分,哪个更靠谱,哪个更不相关。
  • • 优缺点
    • • ✅ 优点:最准确。它能很好地理解上下文,问答效果提升明显。
    • • ❌ 缺点:算得慢。因为要给每个候选都算一次分,耗时和算力都高。
  • • 什么时候用
    • • 通常先用向量检索筛一批候选(比如前 20 个),再用 Cross-Encoder 精排,挑出 3~5 个最相关的结果。
  • • 常见模型
    • • 中文:BAAI/bge-reranker-baseBAAI/bge-reranker-largeBAAI/bge-reranker-v2-m3
    • • 英文:cross-encoder/ms-marco-MiniLM-L-6-v2

MMR

  • • 是什么
    • • 一种“算法方法”。它不依赖模型,而是用公式对检索结果重新排序。
  • • 怎么理解
    • • 你问了一个问题,搜出来的 10 篇文章,有的内容差不多。MMR 的目标是:既给你相关的答案,又保证答案之间不重复。就像点外卖,不能全推荐“黄焖鸡”,要多来几个口味。
  • • 原理
    • • 打分分成两部分:
      • • 要和问题相关(保证答案靠谱)。
      • • 不能和已经选过的内容太像(保证多样化)。
    • • 通过一个参数 λ(lambda) 来平衡:越大越偏向相关性,越小越偏向多样性。
  • • 优缺点
    • • ✅ 优点:能覆盖不同角度的信息,避免答案雷同。
    • • ❌ 缺点:相关性没 Cross-Encoder 判断得准。
  • • 什么时候用
    • • 适合场景:科研文献检索、知识推荐、去重、或者你希望答案覆盖多个角度的时候。
  • • 怎么用
    • • 在 LangChain 里直接用 MaximalMarginalRelevanceRetriever,不用额外模型就能跑。

代码示例

老规矩,下面我们直接上代码。

新建代码文件:17_rerank_mmr_practice.py


           
           
           
           
            
           
           
           
           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.retrievers import ContextualCompressionRetriever
from
 langchain.retrievers.document_compressors import CrossEncoderReranker
from
 langchain_community.cross_encoders import HuggingFaceCrossEncoder

def
 _trim(text: str, max_len: int = 80) -> str:
    return
 (text[: max_len] + "...") if len(text) > max_len else text

def
 main():
    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_17",
    )

    # 切换到 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. 普通相似度(Baseline) ==========

    baseline_retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
    qa_baseline = RetrievalQA.from_chain_type(llm=llm, retriever=baseline_retriever)
    print
("\n=== 普通相似度检索(候选片段 + 得分 + 答案) ===")
    baseline_results = vectorstore.similarity_search_with_score(query, k=5)
    for
 i, (doc, score) in enumerate(baseline_results[:3], start=1):
        try
:
            score_val = float(score)
        except
 Exception:
            score_val = score
        print
(f"[S{i}] score={score_val:.4f} content={_trim(doc.page_content)}")
    print
("→ LLM 回答:")
    print
(qa_baseline.invoke(query))

    # ========== 3. Cross-Encoder Rerank(提升相关性) ==========

    # 先用向量检索取较多候选,再用交叉编码器精排到 top_n

    base_retriever = vectorstore.as_retriever(search_kwargs={"k": 8})
    model = HuggingFaceCrossEncoder(model_name="BAAI/bge-reranker-base")
    compressor = CrossEncoderReranker(model=model, top_n=3)
    compression_retriever = ContextualCompressionRetriever(
        base_compressor=compressor,
        base_retriever=base_retriever,
    )
    qa_rerank = RetrievalQA.from_chain_type(llm=llm, retriever=compression_retriever)

    print
("\n=== Cross-Encoder Rerank(初筛候选 + 精排得分 + 答案) ===")
    initial_docs = base_retriever.invoke(query)
    for
 i, doc in enumerate(initial_docs, start=1):
        print
(f"[C0-{i}] content={_trim(doc.page_content)}")

    compressed_docs = compression_retriever.invoke(query)
    # 为透明打印,显式计算 rerank 分数

    pairs = [(query, d.page_content) for d in compressed_docs]
    scores = model.score(pairs)
    for
 i, (doc, sc) in enumerate(zip(compressed_docs, scores), start=1):
        print
(f"[CR{i}] rerank_score={sc:.4f} content={_trim(doc.page_content)}")
    print
("→ LLM 回答:")
    print
(qa_rerank.invoke(query))

    # ========== 4. MMR(提升多样性) ==========

    # 通过 lambda_mult 控制相关性与多样性的权衡

    mmr_retriever = vectorstore.as_retriever(
        search_type="mmr",
        search_kwargs={"k": 3, "fetch_k": 20, "lambda_mult": 0.5},
    )
    qa_mmr = RetrievalQA.from_chain_type(llm=llm, retriever=mmr_retriever)
    print
("\n=== MMR 检索(多样性候选 + 答案) ===")
    mmr_docs = mmr_retriever.invoke(query)
    for
 i, doc in enumerate(mmr_docs, start=1):
        print
(f"[M{i}] content={_trim(doc.page_content)}")
    print
("→ LLM 回答:")
    print
(qa_mmr.invoke(query))

if
 __name__ == "__main__":
    main()

运行结果:

可以从运行结果看到,三种不同的排序策略的差异与对回答质量的影响。

具体怎么选择,还是需要具体场景具体分析。

 

 

 

 

 

 

 

 


 

 


 

 

 

 

 

 

 

 

 

对AI应用开发感兴趣的同学,欢迎加微信申请入群交流学习。

想要学习AI应用开发的同学,可以参照我的代码跑起来,举一反三,一天一个脚印的进步,我相信,会足够坚实。


接下来100天,我会记录从Java程序员到AI应用开发工程师的完整转型路:每一个困惑、每一次突破、每一个真实瞬间。

当前是36/100。

如果你也想了解AI应用开发到底是什么,如果你也在考虑转型但还在犹豫,那就跟着我的记录,一起探索。


继续折腾中,有问题随时交流 🤝
微信号:ThinkFun666

我的免费AI编程交流群,欢迎加入讨论AI编程玩法(说明需求和来意,申请入群)



 

 



推荐阅读:

我就喜欢干这种傻事!为什么转型AI应用开发,我不建议你直接从看书开始学起?

猛然醒悟:原来 AI 编程这件事情,是有门槛的,90%的新手卡在这些地方

30岁转行 AI 应用开发:多久能学会?答案出乎意料

30岁,我放弃写了7年的Java,成功转型AI应用开发

30岁,转型AI应用开发,需要会什么技术?

30岁,空窗期一年,靠什么成功转型AI应用开发?

30岁,没报课,如何从0自学转型AI应用开发?

【声明】内容源于网络
0
0
玩AI的方可乐
1234
内容 502
粉丝 0
玩AI的方可乐 1234
总阅读204
粉丝0
内容502