RAG从0~1,初入~优化

RAG技术完整实战指南:从基础到高级优化的全流程教学

作者 :布局

日期 :2026-09-02

标签 :RAG、LangChain、向量检索、混合检索、重排序

难度:⭐⭐⭐ 中级进阶


📋 目录

  1. 什么是RAG?为什么需要RAG?
  2. RAG的核心架构与工作流程
  3. 实战一:基础RAG问答系统搭建
  4. 实战二:混合检索技术(向量+BM25)
  5. 实战三:多查询生成优化召回率
  6. 实战四:重排序技术提升精准度
  7. 完整RAG系统架构总结
  8. 最佳实践与避坑指南

1. 什么是RAG?为什么需要RAG?

1.1 RAG的定义

RAG(Retrieval-Augmented Generation,检索增强生成) 是一种将外部知识检索大语言模型生成能力结合的技术架构。

简单来说:

  • 传统LLM:只能依靠训练时的知识,无法获取实时信息,容易"幻觉"(编造内容)
  • RAG系统:先从知识库检索相关文档,再让LLM基于检索结果回答,确保答案有据可查

1.2 RAG解决的核心问题

问题 传统LLM RAG方案
知识时效性 仅限训练截止日期前的知识 实时检索最新文档
专业领域知识 训练数据可能不足 注入企业私有知识库
答案可信度 无法溯源,容易幻觉 基于检索文档回答,可追溯
成本 需要频繁重训练模型 仅需更新知识库

1.3 RAG的典型应用场景

  • 企业知识库问答:员工手册、政策文档、技术文档查询
  • 客户服务:基于产品文档的智能客服
  • 法律/医疗咨询:基于专业文献的辅助决策
  • 代码助手:基于代码库的智能编程辅助

2. RAG的核心架构与工作流程

2.1 RAG系统的三大核心模块

#mermaid-svg-YcU9BgdFXNchCaPD{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-YcU9BgdFXNchCaPD .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-YcU9BgdFXNchCaPD .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-YcU9BgdFXNchCaPD .error-icon{fill:#552222;}#mermaid-svg-YcU9BgdFXNchCaPD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-YcU9BgdFXNchCaPD .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-YcU9BgdFXNchCaPD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-YcU9BgdFXNchCaPD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-YcU9BgdFXNchCaPD .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-YcU9BgdFXNchCaPD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-YcU9BgdFXNchCaPD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-YcU9BgdFXNchCaPD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-YcU9BgdFXNchCaPD .marker.cross{stroke:#333333;}#mermaid-svg-YcU9BgdFXNchCaPD svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-YcU9BgdFXNchCaPD p{margin:0;}#mermaid-svg-YcU9BgdFXNchCaPD .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-YcU9BgdFXNchCaPD .cluster-label text{fill:#333;}#mermaid-svg-YcU9BgdFXNchCaPD .cluster-label span{color:#333;}#mermaid-svg-YcU9BgdFXNchCaPD .cluster-label span p{background-color:transparent;}#mermaid-svg-YcU9BgdFXNchCaPD .label text,#mermaid-svg-YcU9BgdFXNchCaPD span{fill:#333;color:#333;}#mermaid-svg-YcU9BgdFXNchCaPD .node rect,#mermaid-svg-YcU9BgdFXNchCaPD .node circle,#mermaid-svg-YcU9BgdFXNchCaPD .node ellipse,#mermaid-svg-YcU9BgdFXNchCaPD .node polygon,#mermaid-svg-YcU9BgdFXNchCaPD .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-YcU9BgdFXNchCaPD .rough-node .label text,#mermaid-svg-YcU9BgdFXNchCaPD .node .label text,#mermaid-svg-YcU9BgdFXNchCaPD .image-shape .label,#mermaid-svg-YcU9BgdFXNchCaPD .icon-shape .label{text-anchor:middle;}#mermaid-svg-YcU9BgdFXNchCaPD .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-YcU9BgdFXNchCaPD .rough-node .label,#mermaid-svg-YcU9BgdFXNchCaPD .node .label,#mermaid-svg-YcU9BgdFXNchCaPD .image-shape .label,#mermaid-svg-YcU9BgdFXNchCaPD .icon-shape .label{text-align:center;}#mermaid-svg-YcU9BgdFXNchCaPD .node.clickable{cursor:pointer;}#mermaid-svg-YcU9BgdFXNchCaPD .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-YcU9BgdFXNchCaPD .arrowheadPath{fill:#333333;}#mermaid-svg-YcU9BgdFXNchCaPD .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-YcU9BgdFXNchCaPD .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-YcU9BgdFXNchCaPD .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YcU9BgdFXNchCaPD .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-YcU9BgdFXNchCaPD .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YcU9BgdFXNchCaPD .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-YcU9BgdFXNchCaPD .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-YcU9BgdFXNchCaPD .cluster text{fill:#333;}#mermaid-svg-YcU9BgdFXNchCaPD .cluster span{color:#333;}#mermaid-svg-YcU9BgdFXNchCaPD div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-YcU9BgdFXNchCaPD .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-YcU9BgdFXNchCaPD rect.text{fill:none;stroke-width:0;}#mermaid-svg-YcU9BgdFXNchCaPD .icon-shape,#mermaid-svg-YcU9BgdFXNchCaPD .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YcU9BgdFXNchCaPD .icon-shape p,#mermaid-svg-YcU9BgdFXNchCaPD .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-YcU9BgdFXNchCaPD .icon-shape .label rect,#mermaid-svg-YcU9BgdFXNchCaPD .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YcU9BgdFXNchCaPD .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-YcU9BgdFXNchCaPD .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-YcU9BgdFXNchCaPD :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ⚡ 在线阶段:问答查询
📦 离线阶段:知识库构建
检索
📄 文档加载

Loader
✂️ 文本分块

Splitter
🔢 向量化

Embedding
🗄️ 向量数据库

VectorStore
👤 用户查询
🔍 检索器

Retriever
📋 检索结果

Top-K文档
🤖 Prompt + LLM
💬 最终答案

2.2 工作流程详解

离线阶段(知识库构建):

  1. 文档加载:读取TXT、PDF、Word等格式文档
  2. 文本分块:将长文档切分成小块(Chunk),防止超过模型上下文限制
  3. 向量化:使用Embedding模型将文本转换为向量
  4. 存储:保存到向量数据库(如Chroma、Faiss)

在线阶段(问答查询):

  1. 用户提问:输入自然语言问题
  2. 查询向量化:将问题转换为向量
  3. 相似度检索:从向量库中检索Top-K最相关文档
  4. 上下文构建:将检索结果和问题拼接成Prompt
  5. LLM生成:大模型基于上下文生成答案

3. 实战一:基础RAG问答系统搭建

3.1 核心代码结构

3.2 关键技术点逐行解析

3.2.1 向量库构建与持久化
python 复制代码
def build_vectorstore():
    """构建/加载向量库:优先从持久化目录加载,避免重复调用嵌入 API"""
    persist_dir = "./chroma_db"
    collection_name = "employee_books"
    
    embeddings_api = OpenAIEmbeddings(
        model=os.getenv("ZAI_MODEL"),
        api_key=os.environ.get("ZAI_API_KEY"),
        base_url=os.environ.get("ZAI_BASE_URL"),
    )
    
    try:
        # 优先加载已有数据库,避免重复调用 Embedding API(节省成本)
        vectorstore = Chroma(
            persist_directory=persist_dir,
            embedding_function=embeddings_api,
            collection_name=collection_name,
        )
        if vectorstore._collection.count() == 0:
            raise ValueError("集合为空,需重新构建")
        print(f"✅ 已从持久化目录加载向量库,当前文档数:{vectorstore._collection.count()}")
        return vectorstore
    except Exception:
        # 数据库不存在时,走完整构建流程
        loader = TextLoader("./file/rag构建全流程方法.txt", encoding="utf-8")
        data = loader.load()
        
        # 文本分块:chunk_size=512, overlap=50 保证语义连贯性
        text_splitter = CharacterTextSplitter(
            chunk_size=512,    # 每块最大字符数
            chunk_overlap=50,  # 相邻块重叠字符数,避免语义在边界被切断
            separator="\n",
        )
        chunks = text_splitter.split_documents(data)
        
        # 构建向量数据库
        vs = Chroma.from_documents(
            documents=chunks,
            embedding=embeddings_api,
            persist_directory=persist_dir,
            collection_name=collection_name,
        )
        print(f"✅ 向量数据库构建完成,持久化路径:{persist_dir}")
        return vs

💡 知识点:

  • 持久化策略:首次运行构建数据库,后续运行直接加载,避免重复调用Embedding API(每次调用都要花钱!)
  • 分块参数选择
    • chunk_size=512:根据模型上下文窗口和业务场景调整(通常200-1000字符)
    • chunk_overlap=50:重叠区域确保重要信息不会被切断
    • separator="\n":按段落分割,保持语义完整
3.2.2 RAG链路的LCEL构建
python 复制代码
def demo_basic_rag(vectorstore):
    # 1. 创建检索器
    retriever = vectorstore.as_retriever(
        search_type="similarity",  # 相似度检索
        search_kwargs={"k": 3}     # 返回 Top3 最相关文档
    )
    
    # 2. 初始化 LLM
    llm = ChatOpenAI(
        model=os.getenv("DEEPSEEK_MODEL", "deepseek-v4-flash"),
        base_url=os.getenv("DEEPSEEK_BASE_URL"),
        api_key=os.getenv("DEEPSEEK_API_KEY"),
        temperature=0.7
    )
    
    # 3. 构建 Prompt 模板
    template = """你是一个专业的企业HR助手。请严格根据以下参考资料回答用户问题。
    【参考资料】
    {context}
    【用户问题】
    {question}
    【回答规则】
    1. 只基于参考资料内容回答,不要编造信息
    2. 如果参考资料中没有答案,明确说明"根据现有资料无法回答此问题"
    3. 回答简洁准确,分点说明
    """
    prompt = ChatPromptTemplate.from_template(template)
    
    # 4. 格式化检索结果的辅助函数
    def format_docs(docs):
        return "\n\n".join(f"[文档{i + 1}]: {doc.page_content}" for i, doc in enumerate(docs))
    
    # 5. LCEL 组装 RAG 链
    rag_chain = (
        {"context": retriever | format_docs, "question": RunnablePassthrough()}
        | prompt
        | llm
        | StrOutputParser()
    )
    
    return rag_chain, retriever

💡 LCEL(LangChain Expression Language)链式调用解析:

python 复制代码
{"context": retriever | format_docs, "question": RunnablePassthrough()}

这一行是核心!它的执行流程:

  1. retriever:接收用户问题,从向量库检索Top-K文档
  2. format_docs:将检索到的Document对象格式化为字符串
  3. RunnablePassthrough():原封不动传递用户问题
  4. 最终形成一个字典:{"context": "检索结果", "question": "用户问题"}
python 复制代码
| prompt | llm | StrOutputParser()
  1. prompt:将字典传入模板,生成完整Prompt
  2. llm:调用大模型生成答案
  3. StrOutputParser():将LLM输出解析为纯文本字符串

3.3 运行效果示例

复制代码
✅ 已从持久化目录加载向量库,当前文档数:45
✅ RAG 链路构建完成!

--------------------------------------------------
❓ 问题: 公司报销流程是什么?
💡 回答: 根据参考资料,公司报销流程如下:
1. 员工提交报销申请及相关票据
2. 部门主管审核
3. 财务部门复核
4. 3-5个工作日内完成报销

--------------------------------------------------
❓ 问题: 工作满3年有多少天年假?
💡 回答: 根据员工手册,工作满3年的员工享有7天年假。

--------------------------------------------------
❓ 问题: 公司的股票代码是多少?
💡 回答: 根据现有资料无法回答此问题。参考资料中未提及公司股票代码信息。

💡 关键观察:

  • 前两个问题能准确回答,因为知识库中有相关信息
  • 第三个问题(股票代码)知识库中没有,系统明确告知"无法回答",没有幻觉

4. 实战二:混合检索技术(向量+BM25)

4.1 为什么需要混合检索?

单一向量检索的局限性:

检索方式 优势 劣势 适用场景
向量检索 理解语义,支持同义词、改写 对专有名词、代号、编号不敏感 "请假流程是什么?"(语义理解)
BM25关键词检索 精确匹配关键词,对专有名词敏感 无法理解语义,不支持同义词 "A-001号文件内容"(精确匹配)
混合检索 兼顾语义理解和精确匹配 需要调整权重参数 企业知识库、技术文档等专业场景

实际案例:

  • 用户问:"RAG系统的核心思路"
    • 向量检索:能召回"检索增强生成的设计理念"(理解了"核心思路"的语义)
    • BM25检索:能召回包含"RAG"、"系统"这些关键词的文档
    • 混合检索:两者结合,召回更全面

4.2 核心代码实现

python 复制代码
from langchain_community.retrievers import BM25Retriever
from langchain_classic.retrievers import EnsembleRetriever

def demo_hybrid_search(chunks, vectorstore):
    # 1. BM25 关键词检索器
    bm25_retriever = BM25Retriever.from_documents(chunks)
    bm25_retriever.k = 5  # 召回多一点
    
    # 2. 向量检索器
    vector_retriever = vectorstore.as_retriever(search_kwargs={"k": 5})
    
    # 3. 融合检索器
    ensemble_retriever = EnsembleRetriever(
        retrievers=[bm25_retriever, vector_retriever],
        weights=[0.4, 0.6]  # 关键词权重 40%,向量权重 60%
    )
    
    query = "RAG系统的核心思路"
    
    # 对比三种检索方式
    bm25_docs = bm25_retriever.invoke(query)
    vec_docs = vector_retriever.invoke(query)
    ensemble_docs = ensemble_retriever.invoke(query)
    
    return ensemble_retriever

4.3 技术细节深度解析

4.3.1 BM25算法原理

BM25(Best Matching 25)是一种基于词频-逆文档频率(TF-IDF) 改进的排序算法:

核心公式:
#mermaid-svg-1dkzAC9NmOLL9G3w{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-1dkzAC9NmOLL9G3w .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-1dkzAC9NmOLL9G3w .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-1dkzAC9NmOLL9G3w .error-icon{fill:#552222;}#mermaid-svg-1dkzAC9NmOLL9G3w .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1dkzAC9NmOLL9G3w .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-1dkzAC9NmOLL9G3w .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1dkzAC9NmOLL9G3w .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1dkzAC9NmOLL9G3w .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-1dkzAC9NmOLL9G3w .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1dkzAC9NmOLL9G3w .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1dkzAC9NmOLL9G3w .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1dkzAC9NmOLL9G3w .marker.cross{stroke:#333333;}#mermaid-svg-1dkzAC9NmOLL9G3w svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1dkzAC9NmOLL9G3w p{margin:0;}#mermaid-svg-1dkzAC9NmOLL9G3w .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-1dkzAC9NmOLL9G3w .cluster-label text{fill:#333;}#mermaid-svg-1dkzAC9NmOLL9G3w .cluster-label span{color:#333;}#mermaid-svg-1dkzAC9NmOLL9G3w .cluster-label span p{background-color:transparent;}#mermaid-svg-1dkzAC9NmOLL9G3w .label text,#mermaid-svg-1dkzAC9NmOLL9G3w span{fill:#333;color:#333;}#mermaid-svg-1dkzAC9NmOLL9G3w .node rect,#mermaid-svg-1dkzAC9NmOLL9G3w .node circle,#mermaid-svg-1dkzAC9NmOLL9G3w .node ellipse,#mermaid-svg-1dkzAC9NmOLL9G3w .node polygon,#mermaid-svg-1dkzAC9NmOLL9G3w .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-1dkzAC9NmOLL9G3w .rough-node .label text,#mermaid-svg-1dkzAC9NmOLL9G3w .node .label text,#mermaid-svg-1dkzAC9NmOLL9G3w .image-shape .label,#mermaid-svg-1dkzAC9NmOLL9G3w .icon-shape .label{text-anchor:middle;}#mermaid-svg-1dkzAC9NmOLL9G3w .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-1dkzAC9NmOLL9G3w .rough-node .label,#mermaid-svg-1dkzAC9NmOLL9G3w .node .label,#mermaid-svg-1dkzAC9NmOLL9G3w .image-shape .label,#mermaid-svg-1dkzAC9NmOLL9G3w .icon-shape .label{text-align:center;}#mermaid-svg-1dkzAC9NmOLL9G3w .node.clickable{cursor:pointer;}#mermaid-svg-1dkzAC9NmOLL9G3w .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-1dkzAC9NmOLL9G3w .arrowheadPath{fill:#333333;}#mermaid-svg-1dkzAC9NmOLL9G3w .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-1dkzAC9NmOLL9G3w .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-1dkzAC9NmOLL9G3w .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1dkzAC9NmOLL9G3w .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-1dkzAC9NmOLL9G3w .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1dkzAC9NmOLL9G3w .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-1dkzAC9NmOLL9G3w .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-1dkzAC9NmOLL9G3w .cluster text{fill:#333;}#mermaid-svg-1dkzAC9NmOLL9G3w .cluster span{color:#333;}#mermaid-svg-1dkzAC9NmOLL9G3w div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-1dkzAC9NmOLL9G3w .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-1dkzAC9NmOLL9G3w rect.text{fill:none;stroke-width:0;}#mermaid-svg-1dkzAC9NmOLL9G3w .icon-shape,#mermaid-svg-1dkzAC9NmOLL9G3w .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1dkzAC9NmOLL9G3w .icon-shape p,#mermaid-svg-1dkzAC9NmOLL9G3w .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-1dkzAC9NmOLL9G3w .icon-shape .label rect,#mermaid-svg-1dkzAC9NmOLL9G3w .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1dkzAC9NmOLL9G3w .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-1dkzAC9NmOLL9G3w .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-1dkzAC9NmOLL9G3w :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} BM25计算流程
查询词 qi
计算IDF

逆文档频率
文档 D
计算词频 f
饱和函数

避免过度奖励高频词
BM25分数
文档长度归一化

k1=1.5, b=0.75

数学公式:

复制代码
Score(D, Q) = Σ IDF(qi) · (f(qi, D) · (k1 + 1)) / (f(qi, D) + k1 · (1 - b + b · |D| / avgdl))

参数说明:

  • f(qi, D):词qi在文档D中的频率
  • |D|:文档D的长度
  • avgdl:所有文档的平均长度
  • k1:调节参数(通常1.2-2.0,默认1.5)
  • b:长度归一化参数(0-1,默认0.75)

与TF-IDF的关键区别:
#mermaid-svg-DiPIZXhMXzbzAICV{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-DiPIZXhMXzbzAICV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-DiPIZXhMXzbzAICV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-DiPIZXhMXzbzAICV .error-icon{fill:#552222;}#mermaid-svg-DiPIZXhMXzbzAICV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-DiPIZXhMXzbzAICV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-DiPIZXhMXzbzAICV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-DiPIZXhMXzbzAICV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-DiPIZXhMXzbzAICV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-DiPIZXhMXzbzAICV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-DiPIZXhMXzbzAICV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-DiPIZXhMXzbzAICV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-DiPIZXhMXzbzAICV .marker.cross{stroke:#333333;}#mermaid-svg-DiPIZXhMXzbzAICV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-DiPIZXhMXzbzAICV p{margin:0;}#mermaid-svg-DiPIZXhMXzbzAICV .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-DiPIZXhMXzbzAICV .cluster-label text{fill:#333;}#mermaid-svg-DiPIZXhMXzbzAICV .cluster-label span{color:#333;}#mermaid-svg-DiPIZXhMXzbzAICV .cluster-label span p{background-color:transparent;}#mermaid-svg-DiPIZXhMXzbzAICV .label text,#mermaid-svg-DiPIZXhMXzbzAICV span{fill:#333;color:#333;}#mermaid-svg-DiPIZXhMXzbzAICV .node rect,#mermaid-svg-DiPIZXhMXzbzAICV .node circle,#mermaid-svg-DiPIZXhMXzbzAICV .node ellipse,#mermaid-svg-DiPIZXhMXzbzAICV .node polygon,#mermaid-svg-DiPIZXhMXzbzAICV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-DiPIZXhMXzbzAICV .rough-node .label text,#mermaid-svg-DiPIZXhMXzbzAICV .node .label text,#mermaid-svg-DiPIZXhMXzbzAICV .image-shape .label,#mermaid-svg-DiPIZXhMXzbzAICV .icon-shape .label{text-anchor:middle;}#mermaid-svg-DiPIZXhMXzbzAICV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-DiPIZXhMXzbzAICV .rough-node .label,#mermaid-svg-DiPIZXhMXzbzAICV .node .label,#mermaid-svg-DiPIZXhMXzbzAICV .image-shape .label,#mermaid-svg-DiPIZXhMXzbzAICV .icon-shape .label{text-align:center;}#mermaid-svg-DiPIZXhMXzbzAICV .node.clickable{cursor:pointer;}#mermaid-svg-DiPIZXhMXzbzAICV .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-DiPIZXhMXzbzAICV .arrowheadPath{fill:#333333;}#mermaid-svg-DiPIZXhMXzbzAICV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-DiPIZXhMXzbzAICV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-DiPIZXhMXzbzAICV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DiPIZXhMXzbzAICV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-DiPIZXhMXzbzAICV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DiPIZXhMXzbzAICV .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-DiPIZXhMXzbzAICV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-DiPIZXhMXzbzAICV .cluster text{fill:#333;}#mermaid-svg-DiPIZXhMXzbzAICV .cluster span{color:#333;}#mermaid-svg-DiPIZXhMXzbzAICV div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-DiPIZXhMXzbzAICV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-DiPIZXhMXzbzAICV rect.text{fill:none;stroke-width:0;}#mermaid-svg-DiPIZXhMXzbzAICV .icon-shape,#mermaid-svg-DiPIZXhMXzbzAICV .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DiPIZXhMXzbzAICV .icon-shape p,#mermaid-svg-DiPIZXhMXzbzAICV .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-DiPIZXhMXzbzAICV .icon-shape .label rect,#mermaid-svg-DiPIZXhMXzbzAICV .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DiPIZXhMXzbzAICV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-DiPIZXhMXzbzAICV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-DiPIZXhMXzbzAICV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} BM25: 饱和增长
词频 1
分数: 0.95
词频 5
分数: 2.1
词频 10
分数: 2.8
✅ 优势: 词频收益递减,更公平
TF-IDF: 线性增长
词频 1
分数: 1.0
词频 5
分数: 5.0
词频 10
分数: 10.0
⚠️ 问题: 长文档容易占优

4.3.2 权重调整策略
python 复制代码
weights=[0.4, 0.6]  # BM25权重40%,向量权重60%

如何确定权重?

业务场景 推荐配置 原因
技术文档、API文档 [0.5, 0.5] 专业术语、函数名需要精确匹配
通用问答、客服 [0.3, 0.7] 更依赖语义理解,用户表述多样
法律文档、合同 [0.6, 0.4] 条款编号、专有名词更重要
代码搜索 [0.7, 0.3] 函数名、类名必须精确匹配

实战建议:

  1. 初期使用 [0.4, 0.6] 作为基线
  2. 准备10-20个测试Query,覆盖典型场景
  3. 逐步调整权重,观察召回效果
  4. 使用A/B测试验证用户满意度

4.4 运行效果对比

复制代码
========================================================
【高级技术】混合检索 Ensemble Retrieval
========================================================

【对比测试】三种检索方式效果对比
--------------------------------------------------

🔍 BM25关键词检索 (5条):
  1. RAG系统的核心流程包括:文档加载、文本分块、向量化...
  2. 系统架构设计需要考虑RAG的性能优化...
  3. 核心技术栈:LangChain + Chroma + OpenAI...
  4. 思路梳理:先检索后生成,确保答案有据可查...
  5. RAG与传统问答系统的对比分析...

🔍 向量语义检索 (5条):
  1. 检索增强生成的设计理念是将外部知识库与LLM结合...
  2. RAG系统的核心流程包括:文档加载、文本分块、向量化...
  3. 构建企业知识库问答系统的最佳实践...
  4. 如何提升RAG系统的召回率和准确率...
  5. 向量数据库的选择与性能对比...

🏆 混合检索最终结果 (6条):
  1. RAG系统的核心流程包括:文档加载、文本分块、向量化...
  2. 检索增强生成的设计理念是将外部知识库与LLM结合...
  3. 思路梳理:先检索后生成,确保答案有据可查...
  4. 系统架构设计需要考虑RAG的性能优化...
  5. 核心技术栈:LangChain + Chroma + OpenAI...
  6. 构建企业知识库问答系统的最佳实践...

💡 优势:混合检索兼顾语义理解和精确关键词匹配
   适合企业知识库、产品文档等专业场景

💡 分析:

  • BM25精确匹配了"RAG"、"系统"、"核心"、"思路"等关键词
  • 向量检索理解了"核心思路"的语义,召回了"设计理念"、"最佳实践"
  • 混合检索融合了两者优势,召回最全面

5. 实战三:多查询生成优化召回率

5.1 多查询生成的核心思想

问题场景:

用户问:"我出差住酒店能报多少钱?"

这个问题可以有多种表述方式:

  • "差旅费住宿标准是什么?"
  • "出差住宿报销额度上限"
  • "公司对酒店费用的报销规定"

如果知识库中恰好使用了第二种表述,而用户用第一种问法,向量检索可能召回不全

解决方案:

使用LLM将用户的原始问题改写成3-5个不同表述的等价查询 ,然后对每个查询分别检索,最后合并去重,提升召回率。

5.2 核心代码实现

python 复制代码
def demo_multi_query(retriever):
    llm = ChatOpenAI(
        model=os.getenv("DEEPSEEK_MODEL", "deepseek-v4-flash"),
        base_url=os.getenv("DEEPSEEK_BASE_URL"),
        api_key=os.getenv("DEEPSEEK_API_KEY"),
        temperature=0.7  # 生成多个变体需要一点随机性
    )
    
    # 多查询生成 Prompt
    multi_query_prompt = ChatPromptTemplate.from_template("""你是一个查询优化专家。请根据用户的原始问题,生成3个不同表述的语义等价查询。
要求:
1. 从不同角度改写问题
2. 使用不同的关键词和句式
3. 每个查询单独一行

原始问题:{question}

生成的查询列表:
""")
    
    # 生成多个查询
    query_chain = multi_query_prompt | llm | StrOutputParser()
    
    original_query = "我出差住酒店能报多少钱?"
    queries_text = query_chain.invoke(original_query)
    queries = [q.strip() for q in queries_text.strip().split("\n") if q.strip()]
    
    # 多路检索融合
    all_docs = []
    for q in queries:
        docs = retriever.invoke(q)
        all_docs.extend(docs)
    
    # 去重(基于内容哈希)
    def content_hash(text: str) -> str:
        return hashlib.md5(text.encode("utf-8")).hexdigest()
    
    seen = set()
    unique_docs = []
    for doc in all_docs:
        h = content_hash(doc.page_content)
        if h not in seen:
            seen.add(h)
            unique_docs.append(doc)
    
    print(f"\n多路检索共召回 {len(all_docs)} 条,去重后 {len(unique_docs)} 条")
    
    return unique_docs

5.3 技术细节解析

5.3.1 为什么temperature设置为0.7?
python 复制代码
temperature=0.7  # 生成多个变体需要一点随机性
  • temperature=0:输出确定性高,每次生成的改写可能相同(无效)
  • temperature=0.7:适度随机,生成多样化的改写
  • temperature>1.0:过于随机,可能偏离原始语义
5.3.2 去重策略:为什么用MD5哈希?
python 复制代码
def content_hash(text: str) -> str:
    return hashlib.md5(text.encode("utf-8")).hexdigest()

为什么不直接用Python的set去重?

python 复制代码
# ❌ 错误做法:直接用 set(all_docs)
# 原因:Document对象无法直接哈希,且同一文档的不同实例无法识别为重复

MD5哈希的优势:

  • 基于文档内容去重,不依赖对象引用
  • 跨进程稳定(hashlib比内置hash()更可靠)
  • 时间复杂度O(n),性能优秀
5.3.3 多查询生成的Prompt优化技巧

核心要求:

  1. 从不同角度改写:避免只是简单的同义词替换
  2. 使用不同的关键词和句式:触发不同的检索路径
  3. 每个查询单独一行:便于解析

进阶优化:

python 复制代码
# 可以加入领域提示词,提升改写质量
multi_query_prompt = ChatPromptTemplate.from_template("""你是一个企业HR领域的查询优化专家...
背景:我们的知识库包含员工手册、差旅政策、报销制度等文档。
任务:将用户的口语化提问改写成3个专业的、不同角度的查询...
""")

5.4 运行效果示例

复制代码
========================================================
【高级技术】多查询生成 Multi-Query
========================================================

原始问题: 我出差住酒店能报多少钱?

生成的 3 个变体查询:
  1. 差旅费住宿标准是什么?
  2. 出差期间酒店费用的报销额度是多少?
  3. 公司对员工出差住宿的报销上限规定

多路检索共召回 15 条,去重后 8 条

去重后的文档:
  1. 差旅费管理办法:一线城市住宿标准500元/晚,二线城市300元/晚...
  2. 报销制度补充说明:超出标准部分需部门总监特批...
  3. 员工手册第8章:差旅管理:出差前需提交申请...
  4. 财务制度:住宿费凭正规发票报销,不接受收据...
  5. 差旅政策FAQ:Q: 出差住五星级酒店能报销吗?A: ...
  6. 2024年差旅标准调整通知:根据市场变化,住宿标准上调...
  7. 报销流程:差旅费报销需在返回后7个工作日内提交...
  8. 异地项目驻场补贴标准:长期驻场人员住宿标准单独核定...

💡 适用场景:用户查询表述模糊、长尾问题、专业术语场景

💡 效果分析:

  • 原始单一查询可能只召回2-3条相关文档
  • 多查询生成后召回了15条(有重复),去重后8条
  • 召回率提升2-3倍,覆盖了标准、流程、FAQ、通知等多种文档类型

6. 实战四:重排序技术提升精准度

6.1 为什么需要重排序?

检索的两阶段策略:
#mermaid-svg-7zwKpGqbIuvvRCE6{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7zwKpGqbIuvvRCE6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7zwKpGqbIuvvRCE6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7zwKpGqbIuvvRCE6 .error-icon{fill:#552222;}#mermaid-svg-7zwKpGqbIuvvRCE6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7zwKpGqbIuvvRCE6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7zwKpGqbIuvvRCE6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7zwKpGqbIuvvRCE6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7zwKpGqbIuvvRCE6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7zwKpGqbIuvvRCE6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7zwKpGqbIuvvRCE6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7zwKpGqbIuvvRCE6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7zwKpGqbIuvvRCE6 .marker.cross{stroke:#333333;}#mermaid-svg-7zwKpGqbIuvvRCE6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7zwKpGqbIuvvRCE6 p{margin:0;}#mermaid-svg-7zwKpGqbIuvvRCE6 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7zwKpGqbIuvvRCE6 .cluster-label text{fill:#333;}#mermaid-svg-7zwKpGqbIuvvRCE6 .cluster-label span{color:#333;}#mermaid-svg-7zwKpGqbIuvvRCE6 .cluster-label span p{background-color:transparent;}#mermaid-svg-7zwKpGqbIuvvRCE6 .label text,#mermaid-svg-7zwKpGqbIuvvRCE6 span{fill:#333;color:#333;}#mermaid-svg-7zwKpGqbIuvvRCE6 .node rect,#mermaid-svg-7zwKpGqbIuvvRCE6 .node circle,#mermaid-svg-7zwKpGqbIuvvRCE6 .node ellipse,#mermaid-svg-7zwKpGqbIuvvRCE6 .node polygon,#mermaid-svg-7zwKpGqbIuvvRCE6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7zwKpGqbIuvvRCE6 .rough-node .label text,#mermaid-svg-7zwKpGqbIuvvRCE6 .node .label text,#mermaid-svg-7zwKpGqbIuvvRCE6 .image-shape .label,#mermaid-svg-7zwKpGqbIuvvRCE6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-7zwKpGqbIuvvRCE6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7zwKpGqbIuvvRCE6 .rough-node .label,#mermaid-svg-7zwKpGqbIuvvRCE6 .node .label,#mermaid-svg-7zwKpGqbIuvvRCE6 .image-shape .label,#mermaid-svg-7zwKpGqbIuvvRCE6 .icon-shape .label{text-align:center;}#mermaid-svg-7zwKpGqbIuvvRCE6 .node.clickable{cursor:pointer;}#mermaid-svg-7zwKpGqbIuvvRCE6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7zwKpGqbIuvvRCE6 .arrowheadPath{fill:#333333;}#mermaid-svg-7zwKpGqbIuvvRCE6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7zwKpGqbIuvvRCE6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7zwKpGqbIuvvRCE6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7zwKpGqbIuvvRCE6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7zwKpGqbIuvvRCE6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7zwKpGqbIuvvRCE6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7zwKpGqbIuvvRCE6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7zwKpGqbIuvvRCE6 .cluster text{fill:#333;}#mermaid-svg-7zwKpGqbIuvvRCE6 .cluster span{color:#333;}#mermaid-svg-7zwKpGqbIuvvRCE6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-7zwKpGqbIuvvRCE6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7zwKpGqbIuvvRCE6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-7zwKpGqbIuvvRCE6 .icon-shape,#mermaid-svg-7zwKpGqbIuvvRCE6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7zwKpGqbIuvvRCE6 .icon-shape p,#mermaid-svg-7zwKpGqbIuvvRCE6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7zwKpGqbIuvvRCE6 .icon-shape .label rect,#mermaid-svg-7zwKpGqbIuvvRCE6 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7zwKpGqbIuvvRCE6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7zwKpGqbIuvvRCE6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7zwKpGqbIuvvRCE6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 第二阶段: 精排序 Rerank
第一阶段: 粗召回 Recall
📚 全量文档库

10万+文档
🔍 快速检索

向量/BM25/混合
📦 召回结果

Top-50~100
✅ 目标: 高召回率

宁可多召回,不可漏掉

⚡ 速度: 50-200ms
⬇️ 传递
⚖️ 精准打分

Reranker/LLM
🎯 精选结果

Top-3~5
✅ 目标: 高准确率

最相关的排最前

⏱️ 速度: 500-1000ms
🤖 LLM生成答案

为什么不直接用精排?

  • 精排模型计算成本高(比Embedding慢10-100倍)
  • 如果对全库所有文档精排,延迟不可接受
  • 粗召回+精排:兼顾召回率和性能

6.2 两种重排序方案对比

方案 优势 劣势 适用场景
Cohere Rerank API 效果最好,专业模型 需要API Key,有成本 生产环境,对准确率要求高
LLM打分重排 无需额外API,利用现有LLM 速度较慢,成本较高 小规模场景,快速验证
本地Reranker 无API成本,速度快 需要部署模型(bge-reranker) 有GPU资源,注重隐私

6.3 核心代码实现

6.3.1 方案一:Cohere Rerank API
python 复制代码
from langchain_classic.retrievers import ContextualCompressionRetriever
from langchain_classic.retrievers.document_compressors import CohereRerank

def demo_reranking(retriever):
    # 初始化重排序器
    reranker = CohereRerank(
        model="rerank-multilingual-v3.0",
        cohere_api_key=os.getenv("COHERE_API_KEY"),
        top_n=3  # 从粗召回结果中精选Top3
    )
    
    # 包装检索器:先粗召回再精排
    compression_retriever = ContextualCompressionRetriever(
        base_compressor=reranker,
        base_retriever=retriever
    )
    
    query = "年假怎么算?"
    
    # 原始检索结果(Top5)
    raw_docs = retriever.invoke(query)
    
    # 重排序后结果(Top3)
    reranked_docs = compression_retriever.invoke(query)
    
    return compression_retriever
6.3.2 方案二:LLM打分重排序
python 复制代码
def llm_reranking(retriever, query):
    llm = ChatOpenAI(
        model=os.getenv("DEEPSEEK_MODEL"),
        base_url=os.getenv("DEEPSEEK_BASE_URL"),
        api_key=os.getenv("DEEPSEEK_API_KEY"),
        temperature=0  # 打分任务需要确定性
    )
    
    # 打分Prompt
    score_prompt = ChatPromptTemplate.from_template("""请评估以下文档与问题的相关程度,给出0-10的分数。只返回数字。

问题:{question}
文档:{document}

分数:""")
    
    score_chain = score_prompt | llm | StrOutputParser()
    
    # 粗召回
    raw_docs = retriever.invoke(query)
    
    # 逐个打分
    scored_docs = []
    for doc in raw_docs:
        try:
            score = float(score_chain.invoke({
                "question": query,
                "document": doc.page_content[:300]  # 截取前300字符,避免超Token
            }).strip())
        except Exception:
            score = 0
        scored_docs.append((score, doc))
    
    # 按分数降序排序
    scored_docs.sort(key=lambda x: x[0], reverse=True)
    
    return [doc for score, doc in scored_docs[:3]]

6.4 技术细节深度解析

6.4.1 Cohere Rerank模型原理

Cohere Rerank使用Cross-Encoder架构 ,与向量检索的Bi-Encoder有本质区别:
#mermaid-svg-No0nVQ6UYKFhtkNq{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-No0nVQ6UYKFhtkNq .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-No0nVQ6UYKFhtkNq .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-No0nVQ6UYKFhtkNq .error-icon{fill:#552222;}#mermaid-svg-No0nVQ6UYKFhtkNq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-No0nVQ6UYKFhtkNq .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-No0nVQ6UYKFhtkNq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-No0nVQ6UYKFhtkNq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-No0nVQ6UYKFhtkNq .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-No0nVQ6UYKFhtkNq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-No0nVQ6UYKFhtkNq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-No0nVQ6UYKFhtkNq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-No0nVQ6UYKFhtkNq .marker.cross{stroke:#333333;}#mermaid-svg-No0nVQ6UYKFhtkNq svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-No0nVQ6UYKFhtkNq p{margin:0;}#mermaid-svg-No0nVQ6UYKFhtkNq .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-No0nVQ6UYKFhtkNq .cluster-label text{fill:#333;}#mermaid-svg-No0nVQ6UYKFhtkNq .cluster-label span{color:#333;}#mermaid-svg-No0nVQ6UYKFhtkNq .cluster-label span p{background-color:transparent;}#mermaid-svg-No0nVQ6UYKFhtkNq .label text,#mermaid-svg-No0nVQ6UYKFhtkNq span{fill:#333;color:#333;}#mermaid-svg-No0nVQ6UYKFhtkNq .node rect,#mermaid-svg-No0nVQ6UYKFhtkNq .node circle,#mermaid-svg-No0nVQ6UYKFhtkNq .node ellipse,#mermaid-svg-No0nVQ6UYKFhtkNq .node polygon,#mermaid-svg-No0nVQ6UYKFhtkNq .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-No0nVQ6UYKFhtkNq .rough-node .label text,#mermaid-svg-No0nVQ6UYKFhtkNq .node .label text,#mermaid-svg-No0nVQ6UYKFhtkNq .image-shape .label,#mermaid-svg-No0nVQ6UYKFhtkNq .icon-shape .label{text-anchor:middle;}#mermaid-svg-No0nVQ6UYKFhtkNq .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-No0nVQ6UYKFhtkNq .rough-node .label,#mermaid-svg-No0nVQ6UYKFhtkNq .node .label,#mermaid-svg-No0nVQ6UYKFhtkNq .image-shape .label,#mermaid-svg-No0nVQ6UYKFhtkNq .icon-shape .label{text-align:center;}#mermaid-svg-No0nVQ6UYKFhtkNq .node.clickable{cursor:pointer;}#mermaid-svg-No0nVQ6UYKFhtkNq .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-No0nVQ6UYKFhtkNq .arrowheadPath{fill:#333333;}#mermaid-svg-No0nVQ6UYKFhtkNq .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-No0nVQ6UYKFhtkNq .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-No0nVQ6UYKFhtkNq .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-No0nVQ6UYKFhtkNq .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-No0nVQ6UYKFhtkNq .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-No0nVQ6UYKFhtkNq .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-No0nVQ6UYKFhtkNq .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-No0nVQ6UYKFhtkNq .cluster text{fill:#333;}#mermaid-svg-No0nVQ6UYKFhtkNq .cluster span{color:#333;}#mermaid-svg-No0nVQ6UYKFhtkNq div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-No0nVQ6UYKFhtkNq .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-No0nVQ6UYKFhtkNq rect.text{fill:none;stroke-width:0;}#mermaid-svg-No0nVQ6UYKFhtkNq .icon-shape,#mermaid-svg-No0nVQ6UYKFhtkNq .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-No0nVQ6UYKFhtkNq .icon-shape p,#mermaid-svg-No0nVQ6UYKFhtkNq .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-No0nVQ6UYKFhtkNq .icon-shape .label rect,#mermaid-svg-No0nVQ6UYKFhtkNq .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-No0nVQ6UYKFhtkNq .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-No0nVQ6UYKFhtkNq .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-No0nVQ6UYKFhtkNq :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Cross-Encoder (重排序)
Query + Doc
Encoder
相关性分数

0-1
Bi-Encoder (向量检索)
Query
Encoder
Query Vector
Document
Encoder
Doc Vector
Cosine

Similarity
相似度分数

对比分析:

特性 Bi-Encoder Cross-Encoder
编码方式 Query和Doc分别编码 Query+Doc一起编码
优势 Doc向量可预计算,检索快 能捕捉细粒度交互,准确率高
劣势 没有交互,语义理解有限 每对都要重算,速度慢
适用阶段 粗召回(从百万文档中召回Top-100) 精排序(从Top-100中精选Top-3)
6.4.2 为什么LLM打分时temperature=0?
python 复制代码
temperature=0  # 打分任务需要确定性
  • 打分是判断任务,不是生成任务,需要稳定输出
  • temperature=0:同一文档和问题,每次打分结果一致
  • 如果用temperature>0,同一查询多次运行,排序结果会不同(用户体验差)
6.4.3 为什么文档截取前300字符?
python 复制代码
document": doc.page_content[:300]  # 截取前300字符

原因:

  1. 成本控制:LLM按Token计费,全文传入成本高
  2. 速度优化:文本越长,推理越慢
  3. 效果足够:判断相关性,通常文档开头已包含核心信息

实战建议:

  • 短文档(<500字符):可以全文传入
  • 长文档(>1000字符):截取前200-400字符
  • 超长文档(>5000字符):先做文档摘要,再传入打分

6.5 运行效果对比

复制代码
========================================================
【高级技术】检索重排序 Reranking
========================================================

原始检索(Top5):
  1. 员工年假制度:工作满1年享有5天年假...
  2. 假期管理规定:年假、病假、事假的区别...
  3. 员工福利手册:年假可以累积但不可跨年...
  4. 考勤制度:请假需提前申请...
  5. 薪酬福利:年假期间工资照常发放...

🏆 重排序后(Top3):
  1. 员工年假制度:工作满1年享有5天年假,满3年7天,满10年10天...
  2. 假期管理规定:年假计算方式:按自然年度计算,入职时间不满一年的按比例折算...
  3. 员工福利手册:年假可以累积但不可跨年,未使用的年假在年底作废...

💡 原理:粗召回保证召回率,精排保证准确率
   常用模型:Cohere Rerank、BGE-Reranker、ColBERT

💡 效果分析:

  • 原始Top5中,第4条(考勤制度)相关性较弱
  • 重排序后,Top3都是直接回答"年假怎么算"的高质量文档
  • 准确率提升,给LLM的上下文更精准

7. 完整RAG系统架构总结

7.1 四大技术的协同工作流程

#mermaid-svg-h0WTLPBz0ZefOyud{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-h0WTLPBz0ZefOyud .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-h0WTLPBz0ZefOyud .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-h0WTLPBz0ZefOyud .error-icon{fill:#552222;}#mermaid-svg-h0WTLPBz0ZefOyud .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-h0WTLPBz0ZefOyud .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-h0WTLPBz0ZefOyud .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-h0WTLPBz0ZefOyud .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-h0WTLPBz0ZefOyud .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-h0WTLPBz0ZefOyud .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-h0WTLPBz0ZefOyud .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-h0WTLPBz0ZefOyud .marker{fill:#333333;stroke:#333333;}#mermaid-svg-h0WTLPBz0ZefOyud .marker.cross{stroke:#333333;}#mermaid-svg-h0WTLPBz0ZefOyud svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-h0WTLPBz0ZefOyud p{margin:0;}#mermaid-svg-h0WTLPBz0ZefOyud .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-h0WTLPBz0ZefOyud .cluster-label text{fill:#333;}#mermaid-svg-h0WTLPBz0ZefOyud .cluster-label span{color:#333;}#mermaid-svg-h0WTLPBz0ZefOyud .cluster-label span p{background-color:transparent;}#mermaid-svg-h0WTLPBz0ZefOyud .label text,#mermaid-svg-h0WTLPBz0ZefOyud span{fill:#333;color:#333;}#mermaid-svg-h0WTLPBz0ZefOyud .node rect,#mermaid-svg-h0WTLPBz0ZefOyud .node circle,#mermaid-svg-h0WTLPBz0ZefOyud .node ellipse,#mermaid-svg-h0WTLPBz0ZefOyud .node polygon,#mermaid-svg-h0WTLPBz0ZefOyud .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-h0WTLPBz0ZefOyud .rough-node .label text,#mermaid-svg-h0WTLPBz0ZefOyud .node .label text,#mermaid-svg-h0WTLPBz0ZefOyud .image-shape .label,#mermaid-svg-h0WTLPBz0ZefOyud .icon-shape .label{text-anchor:middle;}#mermaid-svg-h0WTLPBz0ZefOyud .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-h0WTLPBz0ZefOyud .rough-node .label,#mermaid-svg-h0WTLPBz0ZefOyud .node .label,#mermaid-svg-h0WTLPBz0ZefOyud .image-shape .label,#mermaid-svg-h0WTLPBz0ZefOyud .icon-shape .label{text-align:center;}#mermaid-svg-h0WTLPBz0ZefOyud .node.clickable{cursor:pointer;}#mermaid-svg-h0WTLPBz0ZefOyud .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-h0WTLPBz0ZefOyud .arrowheadPath{fill:#333333;}#mermaid-svg-h0WTLPBz0ZefOyud .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-h0WTLPBz0ZefOyud .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-h0WTLPBz0ZefOyud .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-h0WTLPBz0ZefOyud .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-h0WTLPBz0ZefOyud .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-h0WTLPBz0ZefOyud .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-h0WTLPBz0ZefOyud .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-h0WTLPBz0ZefOyud .cluster text{fill:#333;}#mermaid-svg-h0WTLPBz0ZefOyud .cluster span{color:#333;}#mermaid-svg-h0WTLPBz0ZefOyud div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-h0WTLPBz0ZefOyud .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-h0WTLPBz0ZefOyud rect.text{fill:none;stroke-width:0;}#mermaid-svg-h0WTLPBz0ZefOyud .icon-shape,#mermaid-svg-h0WTLPBz0ZefOyud .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-h0WTLPBz0ZefOyud .icon-shape p,#mermaid-svg-h0WTLPBz0ZefOyud .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-h0WTLPBz0ZefOyud .icon-shape .label rect,#mermaid-svg-h0WTLPBz0ZefOyud .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-h0WTLPBz0ZefOyud .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-h0WTLPBz0ZefOyud .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-h0WTLPBz0ZefOyud :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 💬 技术4: RAG问答
🎯 技术3: 重排序
🔍 技术2: 混合检索
🔄 技术1: 多查询生成
👤 用户查询:

我出差住酒店能报多少钱?
🤖 LLM改写查询
查询1: 差旅费住宿标准是什么?
查询2: 出差住宿报销额度
查询3: 公司酒店费用报销规定
混合检索1

向量60% + BM25 40%
混合检索2

向量60% + BM25 40%
混合检索3

向量60% + BM25 40%
10条文档
10条文档
10条文档
📦 合并去重
30条 → 15条

基于MD5哈希去重
⚖️ Reranker打分
精选Top-3文档
📝 构建Prompt

参考资料 + 用户问题
🤖 LLM生成答案
✅ 最终答案:

一线城市500元/晚

二线城市300元/晚

7.2 四大技术的性能指标对比

技术 提升维度 性能提升 成本增加 适用场景
基础RAG 基线 - - 所有场景
混合检索 召回率+准确率 +20-30% 低(BM25本地计算) 专业术语、代号多的场景
多查询生成 召回率 +30-50% 中(多次LLM调用) 用户表述多样、长尾问题
重排序 准确率 +15-25% 中到高(Rerank API成本) 对准确率要求高的场景
全部组合 综合 +60-80% 企业级生产环境

7.3 技术选型决策树

#mermaid-svg-3x04jxB77VJjrrYz{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3x04jxB77VJjrrYz .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3x04jxB77VJjrrYz .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3x04jxB77VJjrrYz .error-icon{fill:#552222;}#mermaid-svg-3x04jxB77VJjrrYz .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3x04jxB77VJjrrYz .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3x04jxB77VJjrrYz .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3x04jxB77VJjrrYz .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3x04jxB77VJjrrYz .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3x04jxB77VJjrrYz .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3x04jxB77VJjrrYz .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3x04jxB77VJjrrYz .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3x04jxB77VJjrrYz .marker.cross{stroke:#333333;}#mermaid-svg-3x04jxB77VJjrrYz svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3x04jxB77VJjrrYz p{margin:0;}#mermaid-svg-3x04jxB77VJjrrYz .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3x04jxB77VJjrrYz .cluster-label text{fill:#333;}#mermaid-svg-3x04jxB77VJjrrYz .cluster-label span{color:#333;}#mermaid-svg-3x04jxB77VJjrrYz .cluster-label span p{background-color:transparent;}#mermaid-svg-3x04jxB77VJjrrYz .label text,#mermaid-svg-3x04jxB77VJjrrYz span{fill:#333;color:#333;}#mermaid-svg-3x04jxB77VJjrrYz .node rect,#mermaid-svg-3x04jxB77VJjrrYz .node circle,#mermaid-svg-3x04jxB77VJjrrYz .node ellipse,#mermaid-svg-3x04jxB77VJjrrYz .node polygon,#mermaid-svg-3x04jxB77VJjrrYz .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3x04jxB77VJjrrYz .rough-node .label text,#mermaid-svg-3x04jxB77VJjrrYz .node .label text,#mermaid-svg-3x04jxB77VJjrrYz .image-shape .label,#mermaid-svg-3x04jxB77VJjrrYz .icon-shape .label{text-anchor:middle;}#mermaid-svg-3x04jxB77VJjrrYz .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3x04jxB77VJjrrYz .rough-node .label,#mermaid-svg-3x04jxB77VJjrrYz .node .label,#mermaid-svg-3x04jxB77VJjrrYz .image-shape .label,#mermaid-svg-3x04jxB77VJjrrYz .icon-shape .label{text-align:center;}#mermaid-svg-3x04jxB77VJjrrYz .node.clickable{cursor:pointer;}#mermaid-svg-3x04jxB77VJjrrYz .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3x04jxB77VJjrrYz .arrowheadPath{fill:#333333;}#mermaid-svg-3x04jxB77VJjrrYz .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3x04jxB77VJjrrYz .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3x04jxB77VJjrrYz .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3x04jxB77VJjrrYz .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3x04jxB77VJjrrYz .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3x04jxB77VJjrrYz .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3x04jxB77VJjrrYz .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3x04jxB77VJjrrYz .cluster text{fill:#333;}#mermaid-svg-3x04jxB77VJjrrYz .cluster span{color:#333;}#mermaid-svg-3x04jxB77VJjrrYz div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3x04jxB77VJjrrYz .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3x04jxB77VJjrrYz rect.text{fill:none;stroke-width:0;}#mermaid-svg-3x04jxB77VJjrrYz .icon-shape,#mermaid-svg-3x04jxB77VJjrrYz .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3x04jxB77VJjrrYz .icon-shape p,#mermaid-svg-3x04jxB77VJjrrYz .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3x04jxB77VJjrrYz .icon-shape .label rect,#mermaid-svg-3x04jxB77VJjrrYz .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3x04jxB77VJjrrYz .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3x04jxB77VJjrrYz .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3x04jxB77VJjrrYz :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 通用文档

客服FAQ
技术文档/专业领域

API文档/法律




预算充足

追求极致
预算有限

平衡性价比
🚀 开始构建RAG系统
📚 文档类型?
⭐ 基础RAG
⭐ 基础RAG

  • 混合检索
    🗣️ 用户表述

多样吗?

  • 多查询生成
    ✅ 直接使用
    🎯 对准确率

要求极高?

  • 重排序
  • 多查询生成
    📊 效果评估
    💰 考虑预算

和延迟
🏆 Cohere Rerank

  • 全部技术组合
    💡 LLM打分重排

或仅混合检索
🎉 部署上线


8. 最佳实践与避坑指南

8.1 文档分块(Chunking)的黄金法则

8.1.1 分块大小的选择
python 复制代码
text_splitter = CharacterTextSplitter(
    chunk_size=512,      # ← 关键参数
    chunk_overlap=50,    # ← 关键参数
    separator="\n",
)

经验公式:

复制代码
最佳chunk_size = min(
    LLM上下文窗口 / (Top_K × 2),  # 避免超过上下文限制
    1024,                          # 单个chunk不宜过长
    文档平均段落长度 × 3           # 保持语义完整性
)

实战案例:

文档类型 推荐chunk_size 推荐overlap 原因
技术文档 300-500 50-100 代码段和配置不宜切断
法律合同 500-800 100-150 条款需要上下文
客服FAQ 200-400 30-50 问答对较短,独立性强
长篇文章 800-1200 150-200 段落较长,需要更多上下文
8.1.2 常见错误

错误1:chunk_size过小(<200)

python 复制代码
chunk_size=100  # 太小了!

后果:

  • 单个chunk语义不完整,检索召回的片段无法回答问题
  • LLM需要拼接多个chunk才能理解完整意思

正确做法:

python 复制代码
chunk_size=400  # 确保一个完整语义单元

错误2:overlap=0(无重叠)

python 复制代码
chunk_overlap=0  # 危险!

后果:

复制代码
原文:...员工工作满1年享有5天年假,满3年享有7天年假...
切分:Chunk1: ...员工工作满1年享有5天年假
      Chunk2: ,满3年享有7天年假...

关键信息"满3年享有7天年假"被切断,Chunk2缺少主语!

正确做法:

python 复制代码
chunk_overlap=50  # 确保语义连贯性

8.2 向量数据库的持久化策略

错误3:每次运行都重新构建向量库

python 复制代码
# 反面示例:每次都调用Embedding API
vectorstore = Chroma.from_documents(
    documents=chunks,
    embedding=embeddings_api,
)

后果:

  • Embedding API按Token计费,重复调用浪费钱
  • 构建向量库耗时长,开发体验差

正确做法:持久化+增量更新

python 复制代码
def build_vectorstore():
    persist_dir = "./chroma_db"
    try:
        # 优先加载已有数据库
        vectorstore = Chroma(
            persist_directory=persist_dir,
            embedding_function=embeddings_api,
            collection_name="my_docs",
        )
        if vectorstore._collection.count() > 0:
            print(f"✅ 加载已有向量库,文档数:{vectorstore._collection.count()}")
            return vectorstore
    except Exception:
        pass
    
    # 数据库不存在时,才构建
    vectorstore = Chroma.from_documents(
        documents=chunks,
        embedding=embeddings_api,
        persist_directory=persist_dir,
        collection_name="my_docs",
    )
    print("✅ 向量库构建完成")
    return vectorstore

进阶:增量更新策略

python 复制代码
# 新增文档时,只对新文档做Embedding
new_chunks = load_new_documents()
vectorstore.add_documents(new_chunks)  # 增量添加

8.3 Prompt工程的关键技巧

8.3.1 防止LLM幻觉的Prompt设计

错误4:没有明确约束LLM只能基于检索结果回答

python 复制代码
template = """根据以下资料回答问题:
{context}

问题:{question}
"""

后果:

  • LLM可能忽略检索结果,凭自己的知识回答(幻觉)
  • 答案无法溯源

正确做法:强约束+明确告知后果

python 复制代码
template = """你是一个严谨的助手。请**严格**根据以下参考资料回答用户问题。

【参考资料】
{context}

【用户问题】
{question}

【回答规则】
1. **只基于参考资料内容回答**,不要使用你的通用知识
2. 如果参考资料中没有答案,必须明确说明"根据现有资料无法回答此问题"
3. 回答时引用文档编号,如"根据[文档1]..."

【回答】:
"""

关键技巧:

  • 加粗、大写、重复强调约束
  • 给出"无法回答"的示例模板
  • 要求引用文档编号(可追溯)
8.3.2 提升答案质量的Prompt优化
python 复制代码
template = """你是一个专业的{domain}领域专家助手。

【参考资料】
{context}

【用户问题】
{question}

【回答要求】
1. 优先直接回答问题的核心(结论前置)
2. 如果有多个答案,分点列出
3. 如果涉及数字、日期、标准,务必精确引用
4. 如果参考资料中有多个版本的信息,以最新版本为准
5. 回答简洁,避免冗余

【回答】:
"""

实战效果对比:

Prompt类型 答案示例
无约束 "年假制度根据工龄有所不同,具体情况建议咨询HR部门..."(废话多,未给出具体答案)
强约束+结构化 "根据文档1,年假计算如下:\n1. 工作满1年:5天\n2. 工作满3年:7天\n3. 工作满10年:10天"(清晰、可追溯)

8.4 性能优化的实战技巧

8.4.1 延迟优化

瓶颈分析:
#mermaid-svg-CrdYAXPiaBdVJWnD{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-CrdYAXPiaBdVJWnD .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CrdYAXPiaBdVJWnD .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CrdYAXPiaBdVJWnD .error-icon{fill:#552222;}#mermaid-svg-CrdYAXPiaBdVJWnD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CrdYAXPiaBdVJWnD .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CrdYAXPiaBdVJWnD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CrdYAXPiaBdVJWnD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CrdYAXPiaBdVJWnD .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CrdYAXPiaBdVJWnD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CrdYAXPiaBdVJWnD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CrdYAXPiaBdVJWnD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CrdYAXPiaBdVJWnD .marker.cross{stroke:#333333;}#mermaid-svg-CrdYAXPiaBdVJWnD svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CrdYAXPiaBdVJWnD p{margin:0;}#mermaid-svg-CrdYAXPiaBdVJWnD .mermaid-main-font{font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-CrdYAXPiaBdVJWnD .exclude-range{fill:#eeeeee;}#mermaid-svg-CrdYAXPiaBdVJWnD .section{stroke:none;opacity:0.2;}#mermaid-svg-CrdYAXPiaBdVJWnD .section0{fill:rgba(102, 102, 255, 0.49);}#mermaid-svg-CrdYAXPiaBdVJWnD .section2{fill:#fff400;}#mermaid-svg-CrdYAXPiaBdVJWnD .section1,#mermaid-svg-CrdYAXPiaBdVJWnD .section3{fill:white;opacity:0.2;}#mermaid-svg-CrdYAXPiaBdVJWnD .sectionTitle0{fill:#333;}#mermaid-svg-CrdYAXPiaBdVJWnD .sectionTitle1{fill:#333;}#mermaid-svg-CrdYAXPiaBdVJWnD .sectionTitle2{fill:#333;}#mermaid-svg-CrdYAXPiaBdVJWnD .sectionTitle3{fill:#333;}#mermaid-svg-CrdYAXPiaBdVJWnD .sectionTitle{text-anchor:start;font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-CrdYAXPiaBdVJWnD .grid .tick{stroke:lightgrey;opacity:0.8;shape-rendering:crispEdges;}#mermaid-svg-CrdYAXPiaBdVJWnD .grid .tick text{font-family:"trebuchet ms",verdana,arial,sans-serif;fill:#333;}#mermaid-svg-CrdYAXPiaBdVJWnD .grid path{stroke-width:0;}#mermaid-svg-CrdYAXPiaBdVJWnD .today{fill:none;stroke:red;stroke-width:2px;}#mermaid-svg-CrdYAXPiaBdVJWnD .task{stroke-width:2;}#mermaid-svg-CrdYAXPiaBdVJWnD .taskText{text-anchor:middle;font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-CrdYAXPiaBdVJWnD .taskTextOutsideRight{fill:black;text-anchor:start;font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-CrdYAXPiaBdVJWnD .taskTextOutsideLeft{fill:black;text-anchor:end;}#mermaid-svg-CrdYAXPiaBdVJWnD .task.clickable{cursor:pointer;}#mermaid-svg-CrdYAXPiaBdVJWnD .taskText.clickable{cursor:pointer;fill:#003163!important;font-weight:bold;}#mermaid-svg-CrdYAXPiaBdVJWnD .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163!important;font-weight:bold;}#mermaid-svg-CrdYAXPiaBdVJWnD .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163!important;font-weight:bold;}#mermaid-svg-CrdYAXPiaBdVJWnD .taskText0,#mermaid-svg-CrdYAXPiaBdVJWnD .taskText1,#mermaid-svg-CrdYAXPiaBdVJWnD .taskText2,#mermaid-svg-CrdYAXPiaBdVJWnD .taskText3{fill:white;}#mermaid-svg-CrdYAXPiaBdVJWnD .task0,#mermaid-svg-CrdYAXPiaBdVJWnD .task1,#mermaid-svg-CrdYAXPiaBdVJWnD .task2,#mermaid-svg-CrdYAXPiaBdVJWnD .task3{fill:#8a90dd;stroke:#534fbc;}#mermaid-svg-CrdYAXPiaBdVJWnD .taskTextOutside0,#mermaid-svg-CrdYAXPiaBdVJWnD .taskTextOutside2{fill:black;}#mermaid-svg-CrdYAXPiaBdVJWnD .taskTextOutside1,#mermaid-svg-CrdYAXPiaBdVJWnD .taskTextOutside3{fill:black;}#mermaid-svg-CrdYAXPiaBdVJWnD .active0,#mermaid-svg-CrdYAXPiaBdVJWnD .active1,#mermaid-svg-CrdYAXPiaBdVJWnD .active2,#mermaid-svg-CrdYAXPiaBdVJWnD .active3{fill:#bfc7ff;stroke:#534fbc;}#mermaid-svg-CrdYAXPiaBdVJWnD .activeText0,#mermaid-svg-CrdYAXPiaBdVJWnD .activeText1,#mermaid-svg-CrdYAXPiaBdVJWnD .activeText2,#mermaid-svg-CrdYAXPiaBdVJWnD .activeText3{fill:black!important;}#mermaid-svg-CrdYAXPiaBdVJWnD .done0,#mermaid-svg-CrdYAXPiaBdVJWnD .done1,#mermaid-svg-CrdYAXPiaBdVJWnD .done2,#mermaid-svg-CrdYAXPiaBdVJWnD .done3{stroke:grey;fill:lightgrey;stroke-width:2;}#mermaid-svg-CrdYAXPiaBdVJWnD .doneText0,#mermaid-svg-CrdYAXPiaBdVJWnD .doneText1,#mermaid-svg-CrdYAXPiaBdVJWnD .doneText2,#mermaid-svg-CrdYAXPiaBdVJWnD .doneText3{fill:black!important;}#mermaid-svg-CrdYAXPiaBdVJWnD .doneText0.taskTextOutsideLeft,#mermaid-svg-CrdYAXPiaBdVJWnD .doneText0.taskTextOutsideRight,#mermaid-svg-CrdYAXPiaBdVJWnD .doneText1.taskTextOutsideLeft,#mermaid-svg-CrdYAXPiaBdVJWnD .doneText1.taskTextOutsideRight,#mermaid-svg-CrdYAXPiaBdVJWnD .doneText2.taskTextOutsideLeft,#mermaid-svg-CrdYAXPiaBdVJWnD .doneText2.taskTextOutsideRight,#mermaid-svg-CrdYAXPiaBdVJWnD .doneText3.taskTextOutsideLeft,#mermaid-svg-CrdYAXPiaBdVJWnD .doneText3.taskTextOutsideRight{fill:black!important;}#mermaid-svg-CrdYAXPiaBdVJWnD .crit0,#mermaid-svg-CrdYAXPiaBdVJWnD .crit1,#mermaid-svg-CrdYAXPiaBdVJWnD .crit2,#mermaid-svg-CrdYAXPiaBdVJWnD .crit3{stroke:#ff8888;fill:red;stroke-width:2;}#mermaid-svg-CrdYAXPiaBdVJWnD .activeCrit0,#mermaid-svg-CrdYAXPiaBdVJWnD .activeCrit1,#mermaid-svg-CrdYAXPiaBdVJWnD .activeCrit2,#mermaid-svg-CrdYAXPiaBdVJWnD .activeCrit3{stroke:#ff8888;fill:#bfc7ff;stroke-width:2;}#mermaid-svg-CrdYAXPiaBdVJWnD .doneCrit0,#mermaid-svg-CrdYAXPiaBdVJWnD .doneCrit1,#mermaid-svg-CrdYAXPiaBdVJWnD .doneCrit2,#mermaid-svg-CrdYAXPiaBdVJWnD .doneCrit3{stroke:#ff8888;fill:lightgrey;stroke-width:2;cursor:pointer;shape-rendering:crispEdges;}#mermaid-svg-CrdYAXPiaBdVJWnD .milestone{transform:rotate(45deg) scale(0.8,0.8);}#mermaid-svg-CrdYAXPiaBdVJWnD .milestoneText{font-style:italic;}#mermaid-svg-CrdYAXPiaBdVJWnD .doneCritText0,#mermaid-svg-CrdYAXPiaBdVJWnD .doneCritText1,#mermaid-svg-CrdYAXPiaBdVJWnD .doneCritText2,#mermaid-svg-CrdYAXPiaBdVJWnD .doneCritText3{fill:black!important;}#mermaid-svg-CrdYAXPiaBdVJWnD .doneCritText0.taskTextOutsideLeft,#mermaid-svg-CrdYAXPiaBdVJWnD .doneCritText0.taskTextOutsideRight,#mermaid-svg-CrdYAXPiaBdVJWnD .doneCritText1.taskTextOutsideLeft,#mermaid-svg-CrdYAXPiaBdVJWnD .doneCritText1.taskTextOutsideRight,#mermaid-svg-CrdYAXPiaBdVJWnD .doneCritText2.taskTextOutsideLeft,#mermaid-svg-CrdYAXPiaBdVJWnD .doneCritText2.taskTextOutsideRight,#mermaid-svg-CrdYAXPiaBdVJWnD .doneCritText3.taskTextOutsideLeft,#mermaid-svg-CrdYAXPiaBdVJWnD .doneCritText3.taskTextOutsideRight{fill:black!important;}#mermaid-svg-CrdYAXPiaBdVJWnD .vert{stroke:navy;}#mermaid-svg-CrdYAXPiaBdVJWnD .vertText{font-size:15px;text-anchor:middle;fill:navy!important;}#mermaid-svg-CrdYAXPiaBdVJWnD .activeCritText0,#mermaid-svg-CrdYAXPiaBdVJWnD .activeCritText1,#mermaid-svg-CrdYAXPiaBdVJWnD .activeCritText2,#mermaid-svg-CrdYAXPiaBdVJWnD .activeCritText3{fill:black!important;}#mermaid-svg-CrdYAXPiaBdVJWnD .titleText{text-anchor:middle;font-size:18px;fill:#333;font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-CrdYAXPiaBdVJWnD :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 000 ms 000 ms 000 ms 000 ms 000 ms 000 ms 000 ms 000 ms 000 ms 000 ms Embedding查询向量化 总延迟 向量检索 Reranker打分 LLM生成答案 查询阶段 检索阶段 重排序阶段 生成阶段 RAG系统延迟分布

性能瓶颈:

  • LLM生成:占比最大(1-3秒),70%的延迟
  • 重排序:次要瓶颈(500-1000ms),25%的延迟
  • 检索+Embedding:相对较快(<200ms),5%的延迟

优化策略:
#mermaid-svg-s5kJ31uvKasIvdLT{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-s5kJ31uvKasIvdLT .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-s5kJ31uvKasIvdLT .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-s5kJ31uvKasIvdLT .error-icon{fill:#552222;}#mermaid-svg-s5kJ31uvKasIvdLT .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-s5kJ31uvKasIvdLT .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-s5kJ31uvKasIvdLT .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-s5kJ31uvKasIvdLT .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-s5kJ31uvKasIvdLT .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-s5kJ31uvKasIvdLT .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-s5kJ31uvKasIvdLT .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-s5kJ31uvKasIvdLT .marker{fill:#333333;stroke:#333333;}#mermaid-svg-s5kJ31uvKasIvdLT .marker.cross{stroke:#333333;}#mermaid-svg-s5kJ31uvKasIvdLT svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-s5kJ31uvKasIvdLT p{margin:0;}#mermaid-svg-s5kJ31uvKasIvdLT .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-s5kJ31uvKasIvdLT .cluster-label text{fill:#333;}#mermaid-svg-s5kJ31uvKasIvdLT .cluster-label span{color:#333;}#mermaid-svg-s5kJ31uvKasIvdLT .cluster-label span p{background-color:transparent;}#mermaid-svg-s5kJ31uvKasIvdLT .label text,#mermaid-svg-s5kJ31uvKasIvdLT span{fill:#333;color:#333;}#mermaid-svg-s5kJ31uvKasIvdLT .node rect,#mermaid-svg-s5kJ31uvKasIvdLT .node circle,#mermaid-svg-s5kJ31uvKasIvdLT .node ellipse,#mermaid-svg-s5kJ31uvKasIvdLT .node polygon,#mermaid-svg-s5kJ31uvKasIvdLT .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-s5kJ31uvKasIvdLT .rough-node .label text,#mermaid-svg-s5kJ31uvKasIvdLT .node .label text,#mermaid-svg-s5kJ31uvKasIvdLT .image-shape .label,#mermaid-svg-s5kJ31uvKasIvdLT .icon-shape .label{text-anchor:middle;}#mermaid-svg-s5kJ31uvKasIvdLT .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-s5kJ31uvKasIvdLT .rough-node .label,#mermaid-svg-s5kJ31uvKasIvdLT .node .label,#mermaid-svg-s5kJ31uvKasIvdLT .image-shape .label,#mermaid-svg-s5kJ31uvKasIvdLT .icon-shape .label{text-align:center;}#mermaid-svg-s5kJ31uvKasIvdLT .node.clickable{cursor:pointer;}#mermaid-svg-s5kJ31uvKasIvdLT .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-s5kJ31uvKasIvdLT .arrowheadPath{fill:#333333;}#mermaid-svg-s5kJ31uvKasIvdLT .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-s5kJ31uvKasIvdLT .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-s5kJ31uvKasIvdLT .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-s5kJ31uvKasIvdLT .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-s5kJ31uvKasIvdLT .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-s5kJ31uvKasIvdLT .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-s5kJ31uvKasIvdLT .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-s5kJ31uvKasIvdLT .cluster text{fill:#333;}#mermaid-svg-s5kJ31uvKasIvdLT .cluster span{color:#333;}#mermaid-svg-s5kJ31uvKasIvdLT div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-s5kJ31uvKasIvdLT .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-s5kJ31uvKasIvdLT rect.text{fill:none;stroke-width:0;}#mermaid-svg-s5kJ31uvKasIvdLT .icon-shape,#mermaid-svg-s5kJ31uvKasIvdLT .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-s5kJ31uvKasIvdLT .icon-shape p,#mermaid-svg-s5kJ31uvKasIvdLT .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-s5kJ31uvKasIvdLT .icon-shape .label rect,#mermaid-svg-s5kJ31uvKasIvdLT .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-s5kJ31uvKasIvdLT .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-s5kJ31uvKasIvdLT .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-s5kJ31uvKasIvdLT :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 优化方案
🎯 优化目标
并行化
缓存
模型选择
多查询并行检索

串行3×200ms → 并行200ms
热门查询缓存

命中率30% = 节省30%延迟
更快的Embedding

200ms → 50ms

  1. 并行化多查询生成
python 复制代码
# ❌ 串行:总耗时 = 3 × 检索延迟
for q in queries:
    docs = retriever.invoke(q)
    all_docs.extend(docs)

# ✅ 并行:总耗时 = max(检索延迟)
import asyncio
async def parallel_retrieve():
    tasks = [retriever.ainvoke(q) for q in queries]
    results = await asyncio.gather(*tasks)
    return [doc for result in results for doc in result]
  1. 缓存热门查询
python 复制代码
from functools import lru_cache

@lru_cache(maxsize=1000)
def cached_retrieve(query: str):
    return retriever.invoke(query)
  1. 使用更快的Embedding模型
    | 模型 | 维度 | 延迟 | 效果 |
    |------|-----|------|------|
    | text-embedding-3-large | 3072 | ~200ms | ⭐⭐⭐⭐⭐ |
    | text-embedding-3-small | 1536 | ~100ms | ⭐⭐⭐⭐ |
    | bge-large-zh-v1.5(本地) | 1024 | ~50ms | ⭐⭐⭐⭐ |
8.4.2 成本优化

成本构成分析:
#mermaid-svg-jdahytZtuER4oH5w{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jdahytZtuER4oH5w .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jdahytZtuER4oH5w .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jdahytZtuER4oH5w .error-icon{fill:#552222;}#mermaid-svg-jdahytZtuER4oH5w .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jdahytZtuER4oH5w .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jdahytZtuER4oH5w .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jdahytZtuER4oH5w .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jdahytZtuER4oH5w .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jdahytZtuER4oH5w .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jdahytZtuER4oH5w .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jdahytZtuER4oH5w .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jdahytZtuER4oH5w .marker.cross{stroke:#333333;}#mermaid-svg-jdahytZtuER4oH5w svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jdahytZtuER4oH5w p{margin:0;}#mermaid-svg-jdahytZtuER4oH5w .pieCircle{stroke:#000000;stroke-width:2px;opacity:0.7;}#mermaid-svg-jdahytZtuER4oH5w .pieOuterCircle{stroke:#000000;stroke-width:1px;fill:none;}#mermaid-svg-jdahytZtuER4oH5w .pieTitleText{text-anchor:middle;font-size:25px;fill:#000000;font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-jdahytZtuER4oH5w .slice{font-family:"trebuchet ms",verdana,arial,sans-serif;fill:#000000;font-size:17px;}#mermaid-svg-jdahytZtuER4oH5w .legend text{fill:#000000;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:17px;}#mermaid-svg-jdahytZtuER4oH5w :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 60% 25% 15% RAG系统月度成本分布(1万次查询) LLM调用成本 Embedding成本 Rerank API成本

成本优化策略:
#mermaid-svg-YpDICTeCkiBdFDF9{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-YpDICTeCkiBdFDF9 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-YpDICTeCkiBdFDF9 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-YpDICTeCkiBdFDF9 .error-icon{fill:#552222;}#mermaid-svg-YpDICTeCkiBdFDF9 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-YpDICTeCkiBdFDF9 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-YpDICTeCkiBdFDF9 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-YpDICTeCkiBdFDF9 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-YpDICTeCkiBdFDF9 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-YpDICTeCkiBdFDF9 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-YpDICTeCkiBdFDF9 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-YpDICTeCkiBdFDF9 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-YpDICTeCkiBdFDF9 .marker.cross{stroke:#333333;}#mermaid-svg-YpDICTeCkiBdFDF9 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-YpDICTeCkiBdFDF9 p{margin:0;}#mermaid-svg-YpDICTeCkiBdFDF9 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-YpDICTeCkiBdFDF9 .cluster-label text{fill:#333;}#mermaid-svg-YpDICTeCkiBdFDF9 .cluster-label span{color:#333;}#mermaid-svg-YpDICTeCkiBdFDF9 .cluster-label span p{background-color:transparent;}#mermaid-svg-YpDICTeCkiBdFDF9 .label text,#mermaid-svg-YpDICTeCkiBdFDF9 span{fill:#333;color:#333;}#mermaid-svg-YpDICTeCkiBdFDF9 .node rect,#mermaid-svg-YpDICTeCkiBdFDF9 .node circle,#mermaid-svg-YpDICTeCkiBdFDF9 .node ellipse,#mermaid-svg-YpDICTeCkiBdFDF9 .node polygon,#mermaid-svg-YpDICTeCkiBdFDF9 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-YpDICTeCkiBdFDF9 .rough-node .label text,#mermaid-svg-YpDICTeCkiBdFDF9 .node .label text,#mermaid-svg-YpDICTeCkiBdFDF9 .image-shape .label,#mermaid-svg-YpDICTeCkiBdFDF9 .icon-shape .label{text-anchor:middle;}#mermaid-svg-YpDICTeCkiBdFDF9 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-YpDICTeCkiBdFDF9 .rough-node .label,#mermaid-svg-YpDICTeCkiBdFDF9 .node .label,#mermaid-svg-YpDICTeCkiBdFDF9 .image-shape .label,#mermaid-svg-YpDICTeCkiBdFDF9 .icon-shape .label{text-align:center;}#mermaid-svg-YpDICTeCkiBdFDF9 .node.clickable{cursor:pointer;}#mermaid-svg-YpDICTeCkiBdFDF9 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-YpDICTeCkiBdFDF9 .arrowheadPath{fill:#333333;}#mermaid-svg-YpDICTeCkiBdFDF9 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-YpDICTeCkiBdFDF9 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-YpDICTeCkiBdFDF9 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YpDICTeCkiBdFDF9 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-YpDICTeCkiBdFDF9 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YpDICTeCkiBdFDF9 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-YpDICTeCkiBdFDF9 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-YpDICTeCkiBdFDF9 .cluster text{fill:#333;}#mermaid-svg-YpDICTeCkiBdFDF9 .cluster span{color:#333;}#mermaid-svg-YpDICTeCkiBdFDF9 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-YpDICTeCkiBdFDF9 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-YpDICTeCkiBdFDF9 rect.text{fill:none;stroke-width:0;}#mermaid-svg-YpDICTeCkiBdFDF9 .icon-shape,#mermaid-svg-YpDICTeCkiBdFDF9 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-YpDICTeCkiBdFDF9 .icon-shape p,#mermaid-svg-YpDICTeCkiBdFDF9 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-YpDICTeCkiBdFDF9 .icon-shape .label rect,#mermaid-svg-YpDICTeCkiBdFDF9 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-YpDICTeCkiBdFDF9 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-YpDICTeCkiBdFDF9 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-YpDICTeCkiBdFDF9 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 💰 总成本优化
Embedding优化
LLM优化
Rerank优化
✅ 持久化向量库

避免重复调用
✅ 使用更便宜模型

3-small vs 3-large
✅ 本地部署

bge-large-zh
✅ 选择高性价比模型

DeepSeek vs GPT-4
✅ 限制上下文长度

截断到300字符
✅ 使用Prompt缓存

固定部分缓存
✅ 本地Reranker

bge-reranker
✅ LLM打分替代

复用现有LLM
✅ 选择性重排

简单查询跳过
节省90%

Embedding成本
节省80%

API费用
节省95%

LLM费用
节省30%

Token消耗
节省100%

API费用

  1. Embedding成本优化
python 复制代码
# 使用更便宜的模型
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")  # 比large便宜5倍

# 持久化,避免重复调用
vectorstore = Chroma(persist_directory="./chroma_db", ...)
  1. LLM成本优化
python 复制代码
# 选择性价比高的模型
llm = ChatOpenAI(
    model="deepseek-v4-flash",  # 比GPT-4便宜100倍,效果接近
    temperature=0.7
)

# 限制上下文长度
def format_docs(docs):
    return "\n\n".join(f"[文档{i+1}]: {doc.page_content[:300]}" for i, doc in enumerate(docs))
  1. Rerank成本优化
python 复制代码
# 方案1:使用本地Reranker(一次性成本)
from sentence_transformers import CrossEncoder
reranker = CrossEncoder('BAAI/bge-reranker-large')

# 方案2:只对高价值查询做重排序
def smart_rerank(query, docs):
    if len(docs) <= 3:
        return docs  # 结果已经很少,不需要重排
    if is_simple_query(query):
        return docs[:3]  # 简单查询,直接截断
    return rerank_api(query, docs)  # 复杂查询,调用API

8.5 效果评估与迭代

8.5.1 构建测试集
python 复制代码
# test_queries.json
test_cases = [
    {
        "query": "工作满3年有多少天年假?",
        "expected_docs": ["员工手册-第5章-假期管理"],
        "expected_answer_contains": ["7天", "工作满3年"]
    },
    {
        "query": "差旅费住宿标准",
        "expected_docs": ["差旅管理办法"],
        "expected_answer_contains": ["一线城市", "500元"]
    },
    # ... 添加10-20个典型查询
]
8.5.2 评估指标

#mermaid-svg-vjpDXApGpln5xBUk{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vjpDXApGpln5xBUk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vjpDXApGpln5xBUk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vjpDXApGpln5xBUk .error-icon{fill:#552222;}#mermaid-svg-vjpDXApGpln5xBUk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vjpDXApGpln5xBUk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vjpDXApGpln5xBUk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vjpDXApGpln5xBUk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vjpDXApGpln5xBUk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vjpDXApGpln5xBUk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vjpDXApGpln5xBUk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vjpDXApGpln5xBUk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vjpDXApGpln5xBUk .marker.cross{stroke:#333333;}#mermaid-svg-vjpDXApGpln5xBUk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vjpDXApGpln5xBUk p{margin:0;}#mermaid-svg-vjpDXApGpln5xBUk .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-vjpDXApGpln5xBUk .cluster-label text{fill:#333;}#mermaid-svg-vjpDXApGpln5xBUk .cluster-label span{color:#333;}#mermaid-svg-vjpDXApGpln5xBUk .cluster-label span p{background-color:transparent;}#mermaid-svg-vjpDXApGpln5xBUk .label text,#mermaid-svg-vjpDXApGpln5xBUk span{fill:#333;color:#333;}#mermaid-svg-vjpDXApGpln5xBUk .node rect,#mermaid-svg-vjpDXApGpln5xBUk .node circle,#mermaid-svg-vjpDXApGpln5xBUk .node ellipse,#mermaid-svg-vjpDXApGpln5xBUk .node polygon,#mermaid-svg-vjpDXApGpln5xBUk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vjpDXApGpln5xBUk .rough-node .label text,#mermaid-svg-vjpDXApGpln5xBUk .node .label text,#mermaid-svg-vjpDXApGpln5xBUk .image-shape .label,#mermaid-svg-vjpDXApGpln5xBUk .icon-shape .label{text-anchor:middle;}#mermaid-svg-vjpDXApGpln5xBUk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vjpDXApGpln5xBUk .rough-node .label,#mermaid-svg-vjpDXApGpln5xBUk .node .label,#mermaid-svg-vjpDXApGpln5xBUk .image-shape .label,#mermaid-svg-vjpDXApGpln5xBUk .icon-shape .label{text-align:center;}#mermaid-svg-vjpDXApGpln5xBUk .node.clickable{cursor:pointer;}#mermaid-svg-vjpDXApGpln5xBUk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-vjpDXApGpln5xBUk .arrowheadPath{fill:#333333;}#mermaid-svg-vjpDXApGpln5xBUk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vjpDXApGpln5xBUk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vjpDXApGpln5xBUk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vjpDXApGpln5xBUk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vjpDXApGpln5xBUk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vjpDXApGpln5xBUk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-vjpDXApGpln5xBUk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vjpDXApGpln5xBUk .cluster text{fill:#333;}#mermaid-svg-vjpDXApGpln5xBUk .cluster span{color:#333;}#mermaid-svg-vjpDXApGpln5xBUk div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-vjpDXApGpln5xBUk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vjpDXApGpln5xBUk rect.text{fill:none;stroke-width:0;}#mermaid-svg-vjpDXApGpln5xBUk .icon-shape,#mermaid-svg-vjpDXApGpln5xBUk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vjpDXApGpln5xBUk .icon-shape p,#mermaid-svg-vjpDXApGpln5xBUk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-vjpDXApGpln5xBUk .icon-shape .label rect,#mermaid-svg-vjpDXApGpln5xBUk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vjpDXApGpln5xBUk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vjpDXApGpln5xBUk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vjpDXApGpln5xBUk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 评估体系
RAG系统评估
检索质量
答案质量
性能指标
召回率 Recall

正确文档被召回的比例
准确率 Precision

召回文档的相关度
MRR

首个正确结果的排名
答案完整性

包含所有关键信息
答案准确性

无幻觉、可追溯
答案可读性

结构清晰、表达流畅
平均延迟

P50/P95/P99
吞吐量

QPS
成本

每千次查询费用

核心评估代码:

python 复制代码
def evaluate_rag_system(test_cases):
    metrics = {
        "recall": 0,      # 召回率:正确文档是否被召回
        "precision": 0,   # 准确率:召回的文档中有多少是相关的
        "answer_quality": 0,  # 答案质量:是否包含关键信息
        "avg_latency": 0,  # 平均延迟
    }
    
    for case in test_cases:
        start = time.time()
        
        # 检索
        docs = retriever.invoke(case["query"])
        
        # 检查是否召回了预期文档
        recalled = any(exp_doc in doc.metadata.get("source", "") 
                      for doc in docs 
                      for exp_doc in case["expected_docs"])
        
        if recalled:
            metrics["recall"] += 1
        
        # 生成答案
        answer = rag_chain.invoke(case["query"])
        
        # 检查答案质量
        if all(keyword in answer for keyword in case["expected_answer_contains"]):
            metrics["answer_quality"] += 1
        
        metrics["avg_latency"] += time.time() - start
    
    # 计算平均指标
    n = len(test_cases)
    metrics["recall"] /= n
    metrics["precision"] = metrics["answer_quality"] / n
    metrics["answer_quality"] /= n
    metrics["avg_latency"] /= n
    
    return metrics

9. 快速回顾:5分钟重构RAG系统

9.1 核心知识点速查表

模块 核心API 关键参数 作用
文档加载 TextLoader encoding="utf-8" 读取文档
文本分块 CharacterTextSplitter chunk_size=512, chunk_overlap=50 切分文档
向量化 OpenAIEmbeddings model, api_key, base_url 文本→向量
向量库 Chroma persist_directory, embedding_function 存储+检索
检索器 vectorstore.as_retriever() search_kwargs={"k": 3} 检索Top-K
混合检索 EnsembleRetriever retrievers, weights=[0.4, 0.6] 融合BM25+向量
多查询 ChatPromptTemplate 改写Prompt 生成变体查询
重排序 CohereRerank top_n=3 精排Top-N
RAG链 LCEL `retriever format_docs`

9.2 30秒重构检查清单

#mermaid-svg-86NgEbSNFct9hElj{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-86NgEbSNFct9hElj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-86NgEbSNFct9hElj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-86NgEbSNFct9hElj .error-icon{fill:#552222;}#mermaid-svg-86NgEbSNFct9hElj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-86NgEbSNFct9hElj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-86NgEbSNFct9hElj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-86NgEbSNFct9hElj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-86NgEbSNFct9hElj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-86NgEbSNFct9hElj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-86NgEbSNFct9hElj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-86NgEbSNFct9hElj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-86NgEbSNFct9hElj .marker.cross{stroke:#333333;}#mermaid-svg-86NgEbSNFct9hElj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-86NgEbSNFct9hElj p{margin:0;}#mermaid-svg-86NgEbSNFct9hElj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-86NgEbSNFct9hElj .cluster-label text{fill:#333;}#mermaid-svg-86NgEbSNFct9hElj .cluster-label span{color:#333;}#mermaid-svg-86NgEbSNFct9hElj .cluster-label span p{background-color:transparent;}#mermaid-svg-86NgEbSNFct9hElj .label text,#mermaid-svg-86NgEbSNFct9hElj span{fill:#333;color:#333;}#mermaid-svg-86NgEbSNFct9hElj .node rect,#mermaid-svg-86NgEbSNFct9hElj .node circle,#mermaid-svg-86NgEbSNFct9hElj .node ellipse,#mermaid-svg-86NgEbSNFct9hElj .node polygon,#mermaid-svg-86NgEbSNFct9hElj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-86NgEbSNFct9hElj .rough-node .label text,#mermaid-svg-86NgEbSNFct9hElj .node .label text,#mermaid-svg-86NgEbSNFct9hElj .image-shape .label,#mermaid-svg-86NgEbSNFct9hElj .icon-shape .label{text-anchor:middle;}#mermaid-svg-86NgEbSNFct9hElj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-86NgEbSNFct9hElj .rough-node .label,#mermaid-svg-86NgEbSNFct9hElj .node .label,#mermaid-svg-86NgEbSNFct9hElj .image-shape .label,#mermaid-svg-86NgEbSNFct9hElj .icon-shape .label{text-align:center;}#mermaid-svg-86NgEbSNFct9hElj .node.clickable{cursor:pointer;}#mermaid-svg-86NgEbSNFct9hElj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-86NgEbSNFct9hElj .arrowheadPath{fill:#333333;}#mermaid-svg-86NgEbSNFct9hElj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-86NgEbSNFct9hElj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-86NgEbSNFct9hElj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-86NgEbSNFct9hElj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-86NgEbSNFct9hElj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-86NgEbSNFct9hElj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-86NgEbSNFct9hElj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-86NgEbSNFct9hElj .cluster text{fill:#333;}#mermaid-svg-86NgEbSNFct9hElj .cluster span{color:#333;}#mermaid-svg-86NgEbSNFct9hElj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-86NgEbSNFct9hElj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-86NgEbSNFct9hElj rect.text{fill:none;stroke-width:0;}#mermaid-svg-86NgEbSNFct9hElj .icon-shape,#mermaid-svg-86NgEbSNFct9hElj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-86NgEbSNFct9hElj .icon-shape p,#mermaid-svg-86NgEbSNFct9hElj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-86NgEbSNFct9hElj .icon-shape .label rect,#mermaid-svg-86NgEbSNFct9hElj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-86NgEbSNFct9hElj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-86NgEbSNFct9hElj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-86NgEbSNFct9hElj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 进阶优化选配
基础RAG必备




✅ 文档已分块

chunk_size合理
✅ 向量库已持久化

避免重复构建
✅ Prompt防幻觉

强约束规则
✅ 检索器k值

3-5合理
专业术语多?
➕ 混合检索

BM25+向量
用户表述多样?
➕ 多查询生成

LLM改写
准确率要求高?
➕ 重排序

Cohere/LLM
预算有限?
➕ 本地模型

bge系列
🎯 部署上线

9.3 最小可运行代码

python 复制代码
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_chroma import Chroma
from langchain_text_splitters import CharacterTextSplitter
from langchain_community.document_loaders import TextLoader
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
import os

# 1. 加载文档并分块
chunks = CharacterTextSplitter(
    chunk_size=512, chunk_overlap=50
).split_documents(TextLoader("./data.txt", encoding="utf-8").load())

# 2. 构建向量库
embeddings = OpenAIEmbeddings(
    model=os.getenv("EMBEDDING_MODEL"),
    api_key=os.getenv("API_KEY"),
    base_url=os.getenv("BASE_URL"),
)
vectorstore = Chroma.from_documents(
    documents=chunks,
    embedding=embeddings,
    persist_directory="./db"
)

# 3. 创建检索器
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})

# 4. 初始化LLM
llm = ChatOpenAI(
    model=os.getenv("LLM_MODEL"),
    api_key=os.getenv("API_KEY"),
    base_url=os.getenv("BASE_URL"),
)

# 5. 构建Prompt
template = """根据以下资料回答问题:
{context}
问题:{question}
"""
prompt = ChatPromptTemplate.from_template(template)

# 6. 组装RAG链
def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)

rag_chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | prompt | llm | StrOutputParser()
)

# 7. 使用
answer = rag_chain.invoke("你的问题")
print(answer)

10. 总结与展望

10.1 本文核心要点

通过四个实战示例,我们完整学习了:

  1. 基础RAG:文档加载→分块→向量化→检索→生成,掌握了完整链路
  2. 混合检索:BM25+向量检索,兼顾关键词匹配和语义理解,召回率+20-30%
  3. 多查询生成:LLM改写查询,覆盖多种表述,召回率+30-50%
  4. 重排序:粗召回+精排序,提升准确率+15-25%

10.2 实战能力清单

学完本文,你应该能够:

5分钟搭建 :基础RAG问答系统

10分钟优化 :加入混合检索或多查询生成

理解原理 :知道每个技术解决什么问题,如何选型

避坑经验 :知道常见错误和最佳实践

独立调优:根据业务场景调整参数和架构

10.3 进阶学习路线

#mermaid-svg-lFsHduLkdIZ9UoY9{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-lFsHduLkdIZ9UoY9 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-lFsHduLkdIZ9UoY9 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-lFsHduLkdIZ9UoY9 .error-icon{fill:#552222;}#mermaid-svg-lFsHduLkdIZ9UoY9 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-lFsHduLkdIZ9UoY9 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-lFsHduLkdIZ9UoY9 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-lFsHduLkdIZ9UoY9 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-lFsHduLkdIZ9UoY9 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-lFsHduLkdIZ9UoY9 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-lFsHduLkdIZ9UoY9 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-lFsHduLkdIZ9UoY9 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-lFsHduLkdIZ9UoY9 .marker.cross{stroke:#333333;}#mermaid-svg-lFsHduLkdIZ9UoY9 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-lFsHduLkdIZ9UoY9 p{margin:0;}#mermaid-svg-lFsHduLkdIZ9UoY9 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-lFsHduLkdIZ9UoY9 .cluster-label text{fill:#333;}#mermaid-svg-lFsHduLkdIZ9UoY9 .cluster-label span{color:#333;}#mermaid-svg-lFsHduLkdIZ9UoY9 .cluster-label span p{background-color:transparent;}#mermaid-svg-lFsHduLkdIZ9UoY9 .label text,#mermaid-svg-lFsHduLkdIZ9UoY9 span{fill:#333;color:#333;}#mermaid-svg-lFsHduLkdIZ9UoY9 .node rect,#mermaid-svg-lFsHduLkdIZ9UoY9 .node circle,#mermaid-svg-lFsHduLkdIZ9UoY9 .node ellipse,#mermaid-svg-lFsHduLkdIZ9UoY9 .node polygon,#mermaid-svg-lFsHduLkdIZ9UoY9 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-lFsHduLkdIZ9UoY9 .rough-node .label text,#mermaid-svg-lFsHduLkdIZ9UoY9 .node .label text,#mermaid-svg-lFsHduLkdIZ9UoY9 .image-shape .label,#mermaid-svg-lFsHduLkdIZ9UoY9 .icon-shape .label{text-anchor:middle;}#mermaid-svg-lFsHduLkdIZ9UoY9 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-lFsHduLkdIZ9UoY9 .rough-node .label,#mermaid-svg-lFsHduLkdIZ9UoY9 .node .label,#mermaid-svg-lFsHduLkdIZ9UoY9 .image-shape .label,#mermaid-svg-lFsHduLkdIZ9UoY9 .icon-shape .label{text-align:center;}#mermaid-svg-lFsHduLkdIZ9UoY9 .node.clickable{cursor:pointer;}#mermaid-svg-lFsHduLkdIZ9UoY9 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-lFsHduLkdIZ9UoY9 .arrowheadPath{fill:#333333;}#mermaid-svg-lFsHduLkdIZ9UoY9 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-lFsHduLkdIZ9UoY9 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-lFsHduLkdIZ9UoY9 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lFsHduLkdIZ9UoY9 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-lFsHduLkdIZ9UoY9 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lFsHduLkdIZ9UoY9 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-lFsHduLkdIZ9UoY9 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-lFsHduLkdIZ9UoY9 .cluster text{fill:#333;}#mermaid-svg-lFsHduLkdIZ9UoY9 .cluster span{color:#333;}#mermaid-svg-lFsHduLkdIZ9UoY9 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-lFsHduLkdIZ9UoY9 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-lFsHduLkdIZ9UoY9 rect.text{fill:none;stroke-width:0;}#mermaid-svg-lFsHduLkdIZ9UoY9 .icon-shape,#mermaid-svg-lFsHduLkdIZ9UoY9 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lFsHduLkdIZ9UoY9 .icon-shape p,#mermaid-svg-lFsHduLkdIZ9UoY9 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-lFsHduLkdIZ9UoY9 .icon-shape .label rect,#mermaid-svg-lFsHduLkdIZ9UoY9 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lFsHduLkdIZ9UoY9 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-lFsHduLkdIZ9UoY9 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-lFsHduLkdIZ9UoY9 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 当前技能树
基础RAG
混合检索
多查询生成
重排序
🎓 本文掌握的技术
🚀 进阶方向
Agent + RAG
知识图谱增强
多模态RAG
在线学习
让LLM自主决定

何时检索、检索什么

🔧 LangChain Agent
结合图数据库

支持复杂推理

🔧 Neo4j + LLM
处理图片、表格、PDF

多模态理解

🔧 GPT-4V + LangChain
根据用户反馈

持续优化检索策略

🔧 强化学习 + A/B测试
📚 推荐资源
LangChain官方文档

python.langchain.com
Pinecone RAG实践

pinecone.io/learn/rag
OpenAI Embeddings

platform.openai.com/docs

下一步可以探索:

  1. Agent+RAG:让LLM自主决定何时检索、检索什么
  2. 知识图谱增强:结合图数据库,支持复杂推理
  3. 多模态RAG:处理图片、表格、PDF等复杂文档
  4. 在线学习:根据用户反馈持续优化检索策略

推荐资源:


声明:本文所有代码均为学习示例。

相关推荐
王志来1379447300820 分钟前
场景驱动选型:4U工控机箱如何匹配多元化工业需求
大数据·人工智能·python
2601_9623815836 分钟前
[Python人工智能] 九.gensim词向量Word2Vec安装及《庆余年》中文短文本相似度计算
人工智能·python·tensorflow·word2vec·文本相似度
️学习的小王39 分钟前
Flask实现云栖笔记|开箱即用网页版本地笔记本
笔记·python·flask
quantdash_cc1 小时前
批量请求和循环请求有什么区别?从 API 请求次数看量化数据获取的工程设计
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash
码农刚子1 小时前
告别影楼和付费 App,5 分钟本地搭一个证件照自由平台|HivisionIDPhotos 开箱实测
python·图像识别
Tizzy JJ1 小时前
Python + pytest 接口自动化测试框架实战:从零搭建企业级项目骨架
开发语言·python·pytest
Zane19941 小时前
手写"判断key存不存在"的模板代码太烦?一文讲透 collections 四件套
后端·python
海兰1 小时前
【 Python 量化交易】第8章:量化工具箱
开发语言·python
TheBestRucy1 小时前
Python九阳神功之柒:数据分析三剑客·执剑问道
开发语言·python·数据分析