LangChain 1.3 完全教程:从入门到精通-Part 9: RAG(检索增强生成)

文章目录

Part 9: RAG(检索增强生成)

9.1 RAG 概述

为什么需要 RAG

大语言模型(LLM)有四大固有限制,RAG(Retrieval-Augmented Generation,检索增强生成)正是为了解决这些问题而诞生的:

  1. 知识过时:模型的训练数据有截止日期,无法获取最新信息
  2. 幻觉问题:模型可能"一本正经地胡说八道",生成不准确的内容
  3. 缺乏私有数据:模型不了解你公司的内部文档、业务知识
  4. 无法引用来源:模型无法告诉用户答案来自哪里
RAG vs 微调 vs 提示工程
维度 提示工程 RAG 微调
知识更新 每次手动更新 自动(更新文档即可) 需要重新训练
私有数据 受限于上下文窗口 无限制 可以学习
幻觉控制 好(有来源约束) 中等
实现难度
成本 中(嵌入+存储+检索) 高(训练成本)
可解释性 好(可追溯来源)
适用场景 简单任务 知识密集型任务 风格/格式适配
数据量要求 中等 大量
RAG 工作流程

#mermaid-svg-2FEEBtNueGNrAC8C{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-2FEEBtNueGNrAC8C .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2FEEBtNueGNrAC8C .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2FEEBtNueGNrAC8C .error-icon{fill:#552222;}#mermaid-svg-2FEEBtNueGNrAC8C .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2FEEBtNueGNrAC8C .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2FEEBtNueGNrAC8C .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2FEEBtNueGNrAC8C .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2FEEBtNueGNrAC8C .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2FEEBtNueGNrAC8C .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2FEEBtNueGNrAC8C .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2FEEBtNueGNrAC8C .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2FEEBtNueGNrAC8C .marker.cross{stroke:#333333;}#mermaid-svg-2FEEBtNueGNrAC8C svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2FEEBtNueGNrAC8C p{margin:0;}#mermaid-svg-2FEEBtNueGNrAC8C .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-2FEEBtNueGNrAC8C .cluster-label text{fill:#333;}#mermaid-svg-2FEEBtNueGNrAC8C .cluster-label span{color:#333;}#mermaid-svg-2FEEBtNueGNrAC8C .cluster-label span p{background-color:transparent;}#mermaid-svg-2FEEBtNueGNrAC8C .label text,#mermaid-svg-2FEEBtNueGNrAC8C span{fill:#333;color:#333;}#mermaid-svg-2FEEBtNueGNrAC8C .node rect,#mermaid-svg-2FEEBtNueGNrAC8C .node circle,#mermaid-svg-2FEEBtNueGNrAC8C .node ellipse,#mermaid-svg-2FEEBtNueGNrAC8C .node polygon,#mermaid-svg-2FEEBtNueGNrAC8C .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2FEEBtNueGNrAC8C .rough-node .label text,#mermaid-svg-2FEEBtNueGNrAC8C .node .label text,#mermaid-svg-2FEEBtNueGNrAC8C .image-shape .label,#mermaid-svg-2FEEBtNueGNrAC8C .icon-shape .label{text-anchor:middle;}#mermaid-svg-2FEEBtNueGNrAC8C .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-2FEEBtNueGNrAC8C .rough-node .label,#mermaid-svg-2FEEBtNueGNrAC8C .node .label,#mermaid-svg-2FEEBtNueGNrAC8C .image-shape .label,#mermaid-svg-2FEEBtNueGNrAC8C .icon-shape .label{text-align:center;}#mermaid-svg-2FEEBtNueGNrAC8C .node.clickable{cursor:pointer;}#mermaid-svg-2FEEBtNueGNrAC8C .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-2FEEBtNueGNrAC8C .arrowheadPath{fill:#333333;}#mermaid-svg-2FEEBtNueGNrAC8C .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2FEEBtNueGNrAC8C .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2FEEBtNueGNrAC8C .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2FEEBtNueGNrAC8C .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-2FEEBtNueGNrAC8C .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2FEEBtNueGNrAC8C .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-2FEEBtNueGNrAC8C .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2FEEBtNueGNrAC8C .cluster text{fill:#333;}#mermaid-svg-2FEEBtNueGNrAC8C .cluster span{color:#333;}#mermaid-svg-2FEEBtNueGNrAC8C 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-2FEEBtNueGNrAC8C .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-2FEEBtNueGNrAC8C rect.text{fill:none;stroke-width:0;}#mermaid-svg-2FEEBtNueGNrAC8C .icon-shape,#mermaid-svg-2FEEBtNueGNrAC8C .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2FEEBtNueGNrAC8C .icon-shape p,#mermaid-svg-2FEEBtNueGNrAC8C .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-2FEEBtNueGNrAC8C .icon-shape .label rect,#mermaid-svg-2FEEBtNueGNrAC8C .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2FEEBtNueGNrAC8C .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-2FEEBtNueGNrAC8C .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-2FEEBtNueGNrAC8C :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 生成阶段(在线)
检索阶段(在线)
索引阶段(离线)
原始文档

PDF/网页/数据库
文档加载器

Document Loader
文本分割器

Text Splitter
嵌入模型

Embedding Model
向量数据库

Vector Store
用户问题
嵌入模型

Embedding Model
向量检索

Similarity Search
相关文档

Top-K Results
提示模板

Prompt Template
大语言模型

LLM
最终答案

9.2 基础 RAG 实现

python 复制代码
"""
基础 RAG 完整端到端 Demo
流程: 加载 → 分割 → 嵌入 → 存储 → 检索 → 生成
"""
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_chroma import Chroma
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_core.documents import Document

# ==========================================
# 第一步:准备文档数据
# ==========================================

raw_documents = [
    Document(page_content="LangChain 是一个用于开发大语言模型应用的框架。它提供了一套标准化的接口和工具,让开发者能够轻松地构建基于 LLM 的应用程序。", metadata={"source": "langchain_intro"}),
    Document(page_content="LangChain 的核心组件包括:Models(模型)、Prompts(提示)、Memory(记忆)、Indexes(索引)和 Chains(链)。这些组件可以灵活组合,构建复杂的应用。", metadata={"source": "langchain_components"}),
    Document(page_content="向量存储是 RAG 系统的核心。常用的向量数据库包括 Chroma、FAISS、Pinecone 等。它们能够高效地存储和检索文本的向量表示。", metadata={"source": "vector_stores"}),
    Document(page_content="RAG(检索增强生成)通过将外部知识库与 LLM 结合,有效解决了模型的幻觉问题和知识过时问题。用户的问题先在知识库中检索相关文档,再将文档作为上下文提供给 LLM 生成答案。", metadata={"source": "rag_concept"}),
    Document(page_content="文本分割是 RAG 的重要步骤。常用的分割策略包括按字符分割、按 Token 分割、按 Markdown 标题分割等。选择合适的分割策略对检索质量有很大影响。", metadata={"source": "text_splitters"}),
    Document(page_content="Agent(智能体)是 LangChain 的高级功能。Agent 能够根据用户需求自主选择和使用工具,完成复杂任务。常见的工具包括搜索、计算器、代码执行等。", metadata={"source": "agents"}),
    Document(page_content="LangChain Expression Language(LCEL)是 LangChain 的表达式语言。它提供了一种声明式的方式来组合链,支持流式输出、批处理和异步执行。", metadata={"source": "lcel"}),
    Document(page_content="提示工程是优化 LLM 输出的关键技术。好的提示应该清晰、具体,并提供足够的上下文。LangChain 提供了丰富的提示模板管理工具。", metadata={"source": "prompt_eng"}),
]

# ==========================================
# 第二步:分割文档
# ==========================================

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=50,
    separators=["\n\n", "\n", "。", " ", ""],
)

chunks = text_splitter.split_documents(raw_documents)
print(f"原始文档: {len(raw_documents)} 个")
print(f"分割后: {len(chunks)} 个块")

# ==========================================
# 第三步:创建向量存储
# ==========================================

embeddings = OpenAIEmbeddings(model="text-embedding-3-small")

vectorstore = Chroma.from_documents(
    documents=chunks,
    embedding=embeddings,
    collection_name="rag_demo",
)

# ==========================================
# 第四步:创建检索器
# ==========================================

retriever = vectorstore.as_retriever(
    search_type="similarity",
    search_kwargs={"k": 3},
)

# ==========================================
# 第五步:创建 RAG 提示模板
# ==========================================

template = """你是一个有帮助的 AI 助手。请根据以下提供的上下文信息来回答用户的问题。

如果你不知道答案,请直接说"我不知道",不要编造答案。
回答时请尽量引用上下文中的信息。

上下文信息:
{context}

用户问题:{question}

回答:"""

prompt = ChatPromptTemplate.from_template(template)

# ==========================================
# 第六步:构建 RAG 链
# ==========================================

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)


def format_docs(docs):
    """将检索到的文档格式化为字符串"""
    return "\n\n---\n\n".join(
        f"来源: {doc.metadata.get('source', '未知')}\n内容: {doc.page_content}"
        for doc in docs
    )


# 使用 LCEL 构建 RAG 链
rag_chain = (
    {
        "context": retriever | format_docs,  # 检索 → 格式化
        "question": RunnablePassthrough(),   # 直接传递问题
    }
    | prompt       # 填充提示模板
    | llm          # 调用 LLM
    | StrOutputParser()  # 解析输出
)

# ==========================================
# 第七步:执行 RAG 查询
# ==========================================

question = "什么是 RAG?它是如何工作的?"
answer = rag_chain.invoke(question)

print(f"\n问题: {question}")
print(f"回答: {answer}")

9.3 RAG 提示工程

标准 RAG 提示模板
python 复制代码
"""
RAG 提示模板合集
"""
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.messages import HumanMessage, SystemMessage

# ==========================================
# 1. 标准 RAG 提示(简洁版)
# ==========================================

standard_rag_prompt = ChatPromptTemplate.from_template(
    """根据以下上下文回答问题。如果上下文中没有相关信息,请说"我不知道"。

上下文:
{context}

问题:{question}

回答:"""
)

# ==========================================
# 2. 带引用的 RAG 提示
# ==========================================

citation_rag_prompt = ChatPromptTemplate.from_template(
    """你是一个专业的问答助手。请根据提供的上下文信息回答用户的问题。

要求:
1. 仅基于上下文中的信息回答
2. 在回答中标注信息来源(使用 [来源X] 格式)
3. 如果上下文中没有足够信息,请明确说明

上下文信息:
{context}

问题:{question}

请提供带引用的回答:"""
)

# ==========================================
# 3. 多语言 RAG 提示(中文优化)
# ==========================================

chinese_rag_prompt = ChatPromptTemplate.from_messages([
    ("system", """你是一个知识渊博的中文 AI 助手。

请遵循以下规则:
1. 使用中文回答所有问题
2. 仅根据提供的参考信息作答
3. 如果参考信息不足以回答问题,请诚实说明
4. 回答应条理清晰、重点突出
5. 适当使用列表和分段来组织回答

参考信息:
{context}"""),
    ("human", "{question}"),
])

# ==========================================
# 4. 带思考过程的 RAG 提示(Chain-of-Thought)
# ==========================================

cot_rag_prompt = ChatPromptTemplate.from_template(
    """请根据以下上下文信息回答问题。请先进行分析推理,然后给出最终答案。

上下文:
{context}

问题:{question}

请按以下步骤回答:
第一步:从上下文中提取与问题相关的关键信息
第二步:分析这些信息与问题的关系
第三步:综合信息给出最终答案

分析过程:"""
)

# ==========================================
# 5. 对话式 RAG 提示(带历史消息)
# ==========================================

conversational_rag_prompt = ChatPromptTemplate.from_messages([
    ("system", """你是一个有帮助的 AI 助手。请根据上下文信息和对话历史回答用户的问题。

上下文信息:
{context}

请基于以上信息回答用户当前的问题。如果信息不足,请说明。"""),
    MessagesPlaceholder(variable_name="chat_history"),  # 对话历史
    ("human", "{question}"),
])

# ==========================================
# 6. 摘要式 RAG 提示
# ==========================================

summary_rag_prompt = ChatPromptTemplate.from_template(
    """请根据以下文档内容,对用户的问题提供一个全面的摘要回答。

文档内容:
{context}

问题:{question}

要求:
- 综合所有相关文档的信息
- 提供全面但不冗余的回答
- 按主题或要点组织内容
- 标注关键信息点

摘要回答:"""
)

# ==========================================
# 使用示例
# ==========================================

from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

# 使用带引用的提示
context = "LangChain 是一个 LLM 应用框架。RAG 结合了检索和生成。"
question = "LangChain 和 RAG 有什么关系?"

chain = citation_rag_prompt | llm
response = chain.invoke({"context": context, "question": question})
print(response.content)

9.4 高级 RAG 技术

多查询检索 RAG

#mermaid-svg-Ge1aXnty1C48ZN2k{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-Ge1aXnty1C48ZN2k .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Ge1aXnty1C48ZN2k .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Ge1aXnty1C48ZN2k .error-icon{fill:#552222;}#mermaid-svg-Ge1aXnty1C48ZN2k .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Ge1aXnty1C48ZN2k .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Ge1aXnty1C48ZN2k .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Ge1aXnty1C48ZN2k .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Ge1aXnty1C48ZN2k .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Ge1aXnty1C48ZN2k .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Ge1aXnty1C48ZN2k .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Ge1aXnty1C48ZN2k .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Ge1aXnty1C48ZN2k .marker.cross{stroke:#333333;}#mermaid-svg-Ge1aXnty1C48ZN2k svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Ge1aXnty1C48ZN2k p{margin:0;}#mermaid-svg-Ge1aXnty1C48ZN2k .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Ge1aXnty1C48ZN2k .cluster-label text{fill:#333;}#mermaid-svg-Ge1aXnty1C48ZN2k .cluster-label span{color:#333;}#mermaid-svg-Ge1aXnty1C48ZN2k .cluster-label span p{background-color:transparent;}#mermaid-svg-Ge1aXnty1C48ZN2k .label text,#mermaid-svg-Ge1aXnty1C48ZN2k span{fill:#333;color:#333;}#mermaid-svg-Ge1aXnty1C48ZN2k .node rect,#mermaid-svg-Ge1aXnty1C48ZN2k .node circle,#mermaid-svg-Ge1aXnty1C48ZN2k .node ellipse,#mermaid-svg-Ge1aXnty1C48ZN2k .node polygon,#mermaid-svg-Ge1aXnty1C48ZN2k .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Ge1aXnty1C48ZN2k .rough-node .label text,#mermaid-svg-Ge1aXnty1C48ZN2k .node .label text,#mermaid-svg-Ge1aXnty1C48ZN2k .image-shape .label,#mermaid-svg-Ge1aXnty1C48ZN2k .icon-shape .label{text-anchor:middle;}#mermaid-svg-Ge1aXnty1C48ZN2k .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Ge1aXnty1C48ZN2k .rough-node .label,#mermaid-svg-Ge1aXnty1C48ZN2k .node .label,#mermaid-svg-Ge1aXnty1C48ZN2k .image-shape .label,#mermaid-svg-Ge1aXnty1C48ZN2k .icon-shape .label{text-align:center;}#mermaid-svg-Ge1aXnty1C48ZN2k .node.clickable{cursor:pointer;}#mermaid-svg-Ge1aXnty1C48ZN2k .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Ge1aXnty1C48ZN2k .arrowheadPath{fill:#333333;}#mermaid-svg-Ge1aXnty1C48ZN2k .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Ge1aXnty1C48ZN2k .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Ge1aXnty1C48ZN2k .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Ge1aXnty1C48ZN2k .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Ge1aXnty1C48ZN2k .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Ge1aXnty1C48ZN2k .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Ge1aXnty1C48ZN2k .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Ge1aXnty1C48ZN2k .cluster text{fill:#333;}#mermaid-svg-Ge1aXnty1C48ZN2k .cluster span{color:#333;}#mermaid-svg-Ge1aXnty1C48ZN2k 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-Ge1aXnty1C48ZN2k .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Ge1aXnty1C48ZN2k rect.text{fill:none;stroke-width:0;}#mermaid-svg-Ge1aXnty1C48ZN2k .icon-shape,#mermaid-svg-Ge1aXnty1C48ZN2k .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Ge1aXnty1C48ZN2k .icon-shape p,#mermaid-svg-Ge1aXnty1C48ZN2k .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Ge1aXnty1C48ZN2k .icon-shape .label rect,#mermaid-svg-Ge1aXnty1C48ZN2k .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Ge1aXnty1C48ZN2k .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Ge1aXnty1C48ZN2k .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Ge1aXnty1C48ZN2k :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 用户问题
LLM 生成多个查询变体
查询变体 1
查询变体 2
查询变体 3
向量数据库
合并去重结果
LLM 生成答案

python 复制代码
"""
多查询检索 RAG Demo
"""
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_chroma import Chroma
from langchain.retrievers.multi_query import MultiQueryRetriever
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_core.documents import Document

embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

# 准备数据
documents = [
    Document(page_content="LangChain 支持多种嵌入模型,包括 OpenAI、HuggingFace 等。"),
    Document(page_content="向量数据库的选择取决于应用场景和数据规模。"),
    Document(page_content="RAG 系统的质量取决于文档预处理和检索策略。"),
    Document(page_content="文本分割是 RAG 管道中的关键步骤。"),
    Document(page_content="评估 RAG 系统需要关注检索质量和生成质量两个维度。"),
]

vectorstore = Chroma.from_documents(documents, embeddings)

# 创建多查询检索器
multi_retriever = MultiQueryRetriever.from_llm(
    retriever=vectorstore.as_retriever(k=3),
    llm=llm,
)

# 构建 RAG 链
prompt = ChatPromptTemplate.from_template(
    "根据以下上下文回答问题:\n\n{context}\n\n问题:{question}\n\n回答:"
)

def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)

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

answer = rag_chain.invoke("如何提高 RAG 系统的质量?")
print(f"回答: {answer}")
上下文压缩 RAG

#mermaid-svg-CSjDTeH4bYew2ouc{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-CSjDTeH4bYew2ouc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CSjDTeH4bYew2ouc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CSjDTeH4bYew2ouc .error-icon{fill:#552222;}#mermaid-svg-CSjDTeH4bYew2ouc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CSjDTeH4bYew2ouc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CSjDTeH4bYew2ouc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CSjDTeH4bYew2ouc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CSjDTeH4bYew2ouc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CSjDTeH4bYew2ouc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CSjDTeH4bYew2ouc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CSjDTeH4bYew2ouc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CSjDTeH4bYew2ouc .marker.cross{stroke:#333333;}#mermaid-svg-CSjDTeH4bYew2ouc svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CSjDTeH4bYew2ouc p{margin:0;}#mermaid-svg-CSjDTeH4bYew2ouc .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-CSjDTeH4bYew2ouc .cluster-label text{fill:#333;}#mermaid-svg-CSjDTeH4bYew2ouc .cluster-label span{color:#333;}#mermaid-svg-CSjDTeH4bYew2ouc .cluster-label span p{background-color:transparent;}#mermaid-svg-CSjDTeH4bYew2ouc .label text,#mermaid-svg-CSjDTeH4bYew2ouc span{fill:#333;color:#333;}#mermaid-svg-CSjDTeH4bYew2ouc .node rect,#mermaid-svg-CSjDTeH4bYew2ouc .node circle,#mermaid-svg-CSjDTeH4bYew2ouc .node ellipse,#mermaid-svg-CSjDTeH4bYew2ouc .node polygon,#mermaid-svg-CSjDTeH4bYew2ouc .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-CSjDTeH4bYew2ouc .rough-node .label text,#mermaid-svg-CSjDTeH4bYew2ouc .node .label text,#mermaid-svg-CSjDTeH4bYew2ouc .image-shape .label,#mermaid-svg-CSjDTeH4bYew2ouc .icon-shape .label{text-anchor:middle;}#mermaid-svg-CSjDTeH4bYew2ouc .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-CSjDTeH4bYew2ouc .rough-node .label,#mermaid-svg-CSjDTeH4bYew2ouc .node .label,#mermaid-svg-CSjDTeH4bYew2ouc .image-shape .label,#mermaid-svg-CSjDTeH4bYew2ouc .icon-shape .label{text-align:center;}#mermaid-svg-CSjDTeH4bYew2ouc .node.clickable{cursor:pointer;}#mermaid-svg-CSjDTeH4bYew2ouc .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-CSjDTeH4bYew2ouc .arrowheadPath{fill:#333333;}#mermaid-svg-CSjDTeH4bYew2ouc .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-CSjDTeH4bYew2ouc .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-CSjDTeH4bYew2ouc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CSjDTeH4bYew2ouc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-CSjDTeH4bYew2ouc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CSjDTeH4bYew2ouc .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-CSjDTeH4bYew2ouc .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-CSjDTeH4bYew2ouc .cluster text{fill:#333;}#mermaid-svg-CSjDTeH4bYew2ouc .cluster span{color:#333;}#mermaid-svg-CSjDTeH4bYew2ouc 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-CSjDTeH4bYew2ouc .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CSjDTeH4bYew2ouc rect.text{fill:none;stroke-width:0;}#mermaid-svg-CSjDTeH4bYew2ouc .icon-shape,#mermaid-svg-CSjDTeH4bYew2ouc .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CSjDTeH4bYew2ouc .icon-shape p,#mermaid-svg-CSjDTeH4bYew2ouc .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-CSjDTeH4bYew2ouc .icon-shape .label rect,#mermaid-svg-CSjDTeH4bYew2ouc .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CSjDTeH4bYew2ouc .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CSjDTeH4bYew2ouc .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CSjDTeH4bYew2ouc :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 用户问题
基础检索器

获取 k=10
压缩器

过滤/压缩
精简文档

k=3
LLM 生成答案

python 复制代码
"""
上下文压缩 RAG Demo
"""
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_chroma import Chroma
from langchain.retrievers import ContextualCompressionRetriever
from langchain_community.document_compressors import LLMChainExtractor
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_core.documents import Document

embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

documents = [
    Document(page_content="LangChain 是一个开源框架,由 Harrison Chase 创建于 2022 年。它的目标是简化 LLM 应用的开发流程。"),
    Document(page_content="Python 是一种通用编程语言,由 Guido van Rossum 于 1991 年发布。它以简洁的语法和丰富的库著称。"),
    Document(page_content="LangChain 的核心模块包括模型接口、提示管理、链、代理、记忆和检索。"),
    Document(page_content="JavaScript 是 Web 开发的核心语言,可以用于前端和后端开发。Node.js 使其可以运行在服务器端。"),
    Document(page_content="RAG 通过检索外部知识来增强 LLM 的回答质量,有效减少幻觉。"),
]

vectorstore = Chroma.from_documents(documents, embeddings)

# 创建压缩检索器
compressor = LLMChainExtractor.from_llm(llm)
compression_retriever = ContextualCompressionRetriever(
    base_compressor=compressor,
    base_retriever=vectorstore.as_retriever(k=5),
)

# 构建 RAG 链
prompt = ChatPromptTemplate.from_template(
    "根据以下上下文回答问题:\n\n{context}\n\n问题:{question}\n\n回答:"
)

def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)

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

answer = rag_chain.invoke("LangChain 有哪些核心模块?")
print(f"回答: {answer}")
父子文档 RAG

#mermaid-svg-9IL0a1KPCfhq9DWj{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-9IL0a1KPCfhq9DWj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9IL0a1KPCfhq9DWj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9IL0a1KPCfhq9DWj .error-icon{fill:#552222;}#mermaid-svg-9IL0a1KPCfhq9DWj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9IL0a1KPCfhq9DWj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9IL0a1KPCfhq9DWj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9IL0a1KPCfhq9DWj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9IL0a1KPCfhq9DWj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9IL0a1KPCfhq9DWj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9IL0a1KPCfhq9DWj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9IL0a1KPCfhq9DWj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9IL0a1KPCfhq9DWj .marker.cross{stroke:#333333;}#mermaid-svg-9IL0a1KPCfhq9DWj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9IL0a1KPCfhq9DWj p{margin:0;}#mermaid-svg-9IL0a1KPCfhq9DWj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-9IL0a1KPCfhq9DWj .cluster-label text{fill:#333;}#mermaid-svg-9IL0a1KPCfhq9DWj .cluster-label span{color:#333;}#mermaid-svg-9IL0a1KPCfhq9DWj .cluster-label span p{background-color:transparent;}#mermaid-svg-9IL0a1KPCfhq9DWj .label text,#mermaid-svg-9IL0a1KPCfhq9DWj span{fill:#333;color:#333;}#mermaid-svg-9IL0a1KPCfhq9DWj .node rect,#mermaid-svg-9IL0a1KPCfhq9DWj .node circle,#mermaid-svg-9IL0a1KPCfhq9DWj .node ellipse,#mermaid-svg-9IL0a1KPCfhq9DWj .node polygon,#mermaid-svg-9IL0a1KPCfhq9DWj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9IL0a1KPCfhq9DWj .rough-node .label text,#mermaid-svg-9IL0a1KPCfhq9DWj .node .label text,#mermaid-svg-9IL0a1KPCfhq9DWj .image-shape .label,#mermaid-svg-9IL0a1KPCfhq9DWj .icon-shape .label{text-anchor:middle;}#mermaid-svg-9IL0a1KPCfhq9DWj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-9IL0a1KPCfhq9DWj .rough-node .label,#mermaid-svg-9IL0a1KPCfhq9DWj .node .label,#mermaid-svg-9IL0a1KPCfhq9DWj .image-shape .label,#mermaid-svg-9IL0a1KPCfhq9DWj .icon-shape .label{text-align:center;}#mermaid-svg-9IL0a1KPCfhq9DWj .node.clickable{cursor:pointer;}#mermaid-svg-9IL0a1KPCfhq9DWj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-9IL0a1KPCfhq9DWj .arrowheadPath{fill:#333333;}#mermaid-svg-9IL0a1KPCfhq9DWj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9IL0a1KPCfhq9DWj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9IL0a1KPCfhq9DWj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9IL0a1KPCfhq9DWj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-9IL0a1KPCfhq9DWj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9IL0a1KPCfhq9DWj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-9IL0a1KPCfhq9DWj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9IL0a1KPCfhq9DWj .cluster text{fill:#333;}#mermaid-svg-9IL0a1KPCfhq9DWj .cluster span{color:#333;}#mermaid-svg-9IL0a1KPCfhq9DWj 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-9IL0a1KPCfhq9DWj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9IL0a1KPCfhq9DWj rect.text{fill:none;stroke-width:0;}#mermaid-svg-9IL0a1KPCfhq9DWj .icon-shape,#mermaid-svg-9IL0a1KPCfhq9DWj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9IL0a1KPCfhq9DWj .icon-shape p,#mermaid-svg-9IL0a1KPCfhq9DWj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-9IL0a1KPCfhq9DWj .icon-shape .label rect,#mermaid-svg-9IL0a1KPCfhq9DWj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9IL0a1KPCfhq9DWj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-9IL0a1KPCfhq9DWj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-9IL0a1KPCfhq9DWj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 检索
索引
原始长文档
父分割器

大块
返回父文档
父文档 2
子分割器

小块
子块 1.1
子块 1.2
子块 2.1
子块 2.2
向量数据库
文档存储
查询
生成答案

python 复制代码
"""
父子文档 RAG Demo
"""
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_chroma import Chroma
from langchain.retrievers import ParentDocumentRetriever
from langchain.storage import InMemoryStore
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_core.documents import Document

embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

# 长文档
long_doc = Document(page_content="""
人工智能技术发展报告(2026年)

第一章:大语言模型

大语言模型(LLM)是当前 AI 领域最重要的技术突破。以 GPT-4、Claude、Gemini 为代表的新一代模型,在自然语言理解、代码生成、数学推理等方面展现了惊人的能力。

这些模型的核心是 Transformer 架构,通过在海量文本数据上进行预训练,学习到了丰富的语言知识和世界知识。参数规模从数十亿到数千亿不等。

第二章:多模态 AI

多模态 AI 能够同时处理文本、图像、音频等多种类型的数据。GPT-4V 可以理解图像内容并回答相关问题,Gemini 原生支持多模态输入。

多模态技术的发展使得 AI 应用场景更加广泛,包括图像描述、视觉问答、文档理解等。

第三章:AI Agent

AI Agent 是能够自主决策和执行任务的智能体。它通过工具调用与外部世界交互,能够完成复杂的、多步骤的任务。

LangChain、AutoGen、CrewAI 等框架为 Agent 的开发提供了丰富的工具和抽象。Agent 的能力边界正在不断扩展。

第四章:AI 安全与对齐

随着 AI 能力的增强,确保 AI 系统安全、可控、符合人类价值观变得至关重要。
RLHF(基于人类反馈的强化学习)是目前主要的对齐技术。
 Constitutional AI、DPO 等新方法也在不断涌现。
""", metadata={"source": "ai_report_2026"})

# 配置分割器
parent_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100)
child_splitter = RecursiveCharacterTextSplitter(chunk_size=200, chunk_overlap=20)

# 创建向量存储和文档存储
vectorstore = Chroma(embedding_function=embeddings, collection_name="parent_child_rag")
docstore = InMemoryStore()

# 创建父子文档检索器
retriever = ParentDocumentRetriever(
    vectorstore=vectorstore,
    docstore=docstore,
    child_splitter=child_splitter,
    parent_splitter=parent_splitter,
)

retriever.add_documents([long_doc])

# 构建 RAG 链
prompt = ChatPromptTemplate.from_template(
    "根据以下上下文回答问题:\n\n{context}\n\n问题:{question}\n\n回答:"
)

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()
)

# 检索 ------ 返回的是父文档(完整章节上下文)
answer = rag_chain.invoke("什么是 AI Agent?")
print(f"回答: {answer}")
多向量存储 RAG
python 复制代码
"""
多向量存储 RAG Demo
使用文档摘要进行检索,返回原始文档
"""
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_chroma import Chroma
from langchain.storage import InMemoryStore
from langchain.retrievers.multi_vector import MultiVectorRetriever
from langchain_core.documents import Document
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough

embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

# 准备文档
documents = [
    Document(page_content="LangChain 是一个强大的 LLM 应用开发框架,支持多种模型提供商和工具集成。它提供了模块化的组件设计,包括模型接口、提示模板、输出解析器、链、代理和记忆系统。"),
    Document(page_content="向量数据库是存储和检索文本嵌入的专用数据库。常见的向量数据库包括 Chroma(适合本地开发)、FAISS(高性能)、Pinecone(云端托管)和 Qdrant(功能丰富)。选择取决于数据规模和部署需求。"),
    Document(page_content="RAG(检索增强生成)是一种将外部知识库与大语言模型结合的技术。它通过检索相关文档作为上下文,让 LLM 基于真实信息生成答案,有效减少幻觉。RAG 的关键步骤包括文档加载、分割、嵌入、存储和检索。"),
]

# 为每个文档生成摘要
summaries = []
for doc in documents:
    summary = llm.invoke(f"请用一句话总结以下文档的核心内容:\n\n{doc.page_content}")
    summaries.append(summary.content)

print("文档摘要:")
for i, summary in enumerate(summaries):
    print(f"  {i+1}. {summary}")

# 创建多向量检索器
docstore = InMemoryStore()
vectorstore = Chroma(embedding_function=embeddings, collection_name="multi_vector_rag")

retriever = MultiVectorRetriever(
    vectorstore=vectorstore,
    docstore=docstore,
    id_key="doc_id",
)

# 存储摘要向量 → 原始文档的映射
doc_ids = [f"doc_{i}" for i in range(len(documents))]
retriever.vectorstore.add_texts(
    texts=summaries,
    ids=[f"summary_{i}" for i in range(len(summaries))],
    metadatas=[{"doc_id": doc_id} for doc_id in doc_ids],
)

# 存储原始文档
for doc_id, doc in zip(doc_ids, documents):
    retriever.docstore.mset([(doc_id, doc)])

# 构建 RAG 链
prompt = ChatPromptTemplate.from_template(
    "根据以下上下文回答问题:\n\n{context}\n\n问题:{question}\n\n回答:"
)

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()
)

# 检索 ------ 通过摘要匹配,返回原始完整文档
answer = rag_chain.invoke("向量数据库有哪些选择?")
print(f"\n回答: {answer}")
时间加权 RAG
python 复制代码
"""
时间加权 RAG Demo
对文档进行时间衰减加权,优先检索较新的文档
"""
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_chroma import Chroma
from langchain.retrievers import TimeWeightedVectorStoreRetriever
from langchain_core.documents import Document
from datetime import datetime, timedelta
import math

embeddings = OpenAIEmbeddings(model="text-embedding-3-small")

# 准备带时间戳的文档
now = datetime.now()
documents = [
    Document(
        page_content="2026年最新:GPT-5 发布,支持100万 Token 上下文窗口。",
        metadata={"source": "news", "timestamp": now.isoformat()}
    ),
    Document(
        page_content="2025年:GPT-4o 发布,支持多模态输入。",
        metadata={"source": "news", "timestamp": (now - timedelta(days=365)).isoformat()}
    ),
    Document(
        page_content="2024年:GPT-4 Turbo 发布,速度提升3倍。",
        metadata={"source": "news", "timestamp": (now - timedelta(days=730)).isoformat()}
    ),
    Document(
        page_content="2023年:GPT-4 发布,首次支持多模态。",
        metadata={"source": "news", "timestamp": (now - timedelta(days=1095)).isoformat()}
    ),
]

vectorstore = Chroma.from_documents(documents, embeddings, collection_name="time_weighted_rag")

# 创建时间加权检索器
retriever = TimeWeightedVectorStoreRetriever(
    vectorstore=vectorstore,
    decay_rate=0.01,          # 衰减率(越小衰减越慢)
    k=3,                       # 返回结果数量
    search_kwargs={"k": 3},
)

# 模拟时间衰减 ------ 降低旧文档的相关性分数
# retriever.add_documents(documents)  # 每次调用 add_documents 都会更新时间

results = retriever.invoke("最新的 GPT 模型是什么?")
print("时间加权检索结果:")
for doc in results:
    print(f"  [{doc.metadata.get('timestamp', 'N/A')[:10]}] {doc.page_content[:50]}...")

9.5 RAG 评估

评估指标

#mermaid-svg-oOF5aKeuwhFOKWOW{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-oOF5aKeuwhFOKWOW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-oOF5aKeuwhFOKWOW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-oOF5aKeuwhFOKWOW .error-icon{fill:#552222;}#mermaid-svg-oOF5aKeuwhFOKWOW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-oOF5aKeuwhFOKWOW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-oOF5aKeuwhFOKWOW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-oOF5aKeuwhFOKWOW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-oOF5aKeuwhFOKWOW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-oOF5aKeuwhFOKWOW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-oOF5aKeuwhFOKWOW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-oOF5aKeuwhFOKWOW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-oOF5aKeuwhFOKWOW .marker.cross{stroke:#333333;}#mermaid-svg-oOF5aKeuwhFOKWOW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-oOF5aKeuwhFOKWOW p{margin:0;}#mermaid-svg-oOF5aKeuwhFOKWOW .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-oOF5aKeuwhFOKWOW .cluster-label text{fill:#333;}#mermaid-svg-oOF5aKeuwhFOKWOW .cluster-label span{color:#333;}#mermaid-svg-oOF5aKeuwhFOKWOW .cluster-label span p{background-color:transparent;}#mermaid-svg-oOF5aKeuwhFOKWOW .label text,#mermaid-svg-oOF5aKeuwhFOKWOW span{fill:#333;color:#333;}#mermaid-svg-oOF5aKeuwhFOKWOW .node rect,#mermaid-svg-oOF5aKeuwhFOKWOW .node circle,#mermaid-svg-oOF5aKeuwhFOKWOW .node ellipse,#mermaid-svg-oOF5aKeuwhFOKWOW .node polygon,#mermaid-svg-oOF5aKeuwhFOKWOW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-oOF5aKeuwhFOKWOW .rough-node .label text,#mermaid-svg-oOF5aKeuwhFOKWOW .node .label text,#mermaid-svg-oOF5aKeuwhFOKWOW .image-shape .label,#mermaid-svg-oOF5aKeuwhFOKWOW .icon-shape .label{text-anchor:middle;}#mermaid-svg-oOF5aKeuwhFOKWOW .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-oOF5aKeuwhFOKWOW .rough-node .label,#mermaid-svg-oOF5aKeuwhFOKWOW .node .label,#mermaid-svg-oOF5aKeuwhFOKWOW .image-shape .label,#mermaid-svg-oOF5aKeuwhFOKWOW .icon-shape .label{text-align:center;}#mermaid-svg-oOF5aKeuwhFOKWOW .node.clickable{cursor:pointer;}#mermaid-svg-oOF5aKeuwhFOKWOW .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-oOF5aKeuwhFOKWOW .arrowheadPath{fill:#333333;}#mermaid-svg-oOF5aKeuwhFOKWOW .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-oOF5aKeuwhFOKWOW .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-oOF5aKeuwhFOKWOW .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oOF5aKeuwhFOKWOW .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-oOF5aKeuwhFOKWOW .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oOF5aKeuwhFOKWOW .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-oOF5aKeuwhFOKWOW .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-oOF5aKeuwhFOKWOW .cluster text{fill:#333;}#mermaid-svg-oOF5aKeuwhFOKWOW .cluster span{color:#333;}#mermaid-svg-oOF5aKeuwhFOKWOW 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-oOF5aKeuwhFOKWOW .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-oOF5aKeuwhFOKWOW rect.text{fill:none;stroke-width:0;}#mermaid-svg-oOF5aKeuwhFOKWOW .icon-shape,#mermaid-svg-oOF5aKeuwhFOKWOW .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oOF5aKeuwhFOKWOW .icon-shape p,#mermaid-svg-oOF5aKeuwhFOKWOW .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-oOF5aKeuwhFOKWOW .icon-shape .label rect,#mermaid-svg-oOF5aKeuwhFOKWOW .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oOF5aKeuwhFOKWOW .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-oOF5aKeuwhFOKWOW .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-oOF5aKeuwhFOKWOW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} RAG 评估
检索质量评估
生成质量评估
端到端评估
召回率 Recall
精确率 Precision
MRR 平均倒数排名
NDCG 归一化折损累计增益
忠实度 Faithfulness
答案相关性 Answer Relevancy
上下文利用率
答案正确性
完整性

RAGAS 评估框架 Demo
python 复制代码
"""
RAGAS 评估框架 Demo
安装: pip install ragas
"""
# RAGAS (RAG Assessment) 是专门用于评估 RAG 系统的开源框架

# from ragas import evaluate
# from ragas.metrics import (
#     faithfulness,        # 忠实度:答案是否基于上下文
#     answer_relevancy,    # 答案相关性:答案是否回答了问题
#     context_precision,   # 上下文精确率:检索到的文档是否相关
#     context_recall,      # 上下文召回率:是否检索到了所有相关文档
# )
# from datasets import Dataset

# ==========================================
# 准备评估数据
# ==========================================

# eval_data = {
#     "question": [
#         "什么是 RAG?",
#         "LangChain 的核心组件有哪些?",
#     ],
#     "answer": [
#         "RAG 是检索增强生成技术...",
#         "LangChain 的核心组件包括...",
#     ],
#     "contexts": [
#         ["RAG 通过检索外部知识来增强 LLM 的回答质量。"],
#         ["LangChain 的核心模块包括模型接口、提示管理、链、代理、记忆和检索。"],
#     ],
#     "ground_truth": [  # 标准答案(可选,用于计算更准确的指标)
#         "RAG(Retrieval-Augmented Generation)是一种将外部知识库与大语言模型结合的技术。",
#         "LangChain 的核心组件包括 Models、Prompts、Memory、Indexes 和 Chains。",
#     ],
# }

# dataset = Dataset.from_dict(eval_data)

# ==========================================
# 执行评估
# ==========================================

# result = evaluate(
#     dataset=dataset,
#     metrics=[
#         faithfulness,
#         answer_relevancy,
#         context_precision,
#         context_recall,
#     ],
# )

# print(result.to_pandas())

# ==========================================
# 评估指标说明
# =========================================="""
| 指标 | 含义 | 分数范围 | 理想值 |
|------|------|---------|--------|
| faithfulness | 答案中的所有声明是否都能从上下文中推导出来 | 0-1 | 接近 1 |
| answer_relevancy | 答案与问题的相关程度 | 0-1 | 接近 1 |
| context_precision | 检索到的文档中相关文档的比例 | 0-1 | 接近 1 |
| context_recall | 所有相关文档是否都被检索到 | 0-1 | 接近 1 |
"""

9.6 RAG 最佳实践

RAG 最佳实践总结

==========================================

1. 文档预处理建议

=========================================="""

  • 去除无用的页眉、页脚、水印
  • 保留文档的结构信息(标题、段落、列表)
  • 对表格数据进行特殊处理(转为文本或单独存储)
  • 去除重复内容
  • 标准化格式(统一编码、去除多余空白)
  • 为文档添加有意义的元数据(来源、日期、类别等)
    """

==========================================

2. Chunk 策略选择

=========================================="""

文档类型 推荐 Chunk 大小 推荐分割器 说明
技术文档 500-800 RecursiveCharacterTextSplitter 按段落和句子分割
法律合同 1000-2000 RecursiveCharacterTextSplitter 保持完整条款
代码文件 200-500 按函数/类分割 保持代码完整性
Markdown 500-1000 MarkdownHeaderTextSplitter 按标题层级分割
HTML 网页 500-1000 HTMLHeaderTextSplitter 按标签层级分割
Q&A 对 每条一个 自定义分割器 问题+答案作为一个块
学术论文 800-1200 按章节分割 保持章节完整性
"""

==========================================

3. 检索策略选择

=========================================="""

场景 推荐检索策略 说明
通用问答 similarity (k=3-5) 最简单有效
需要多样性 mmr (lambda=0.5) 避免重复结果
精确匹配 EnsembleRetriever BM25 + 向量
长文档 ParentDocumentRetriever 小块检索,大块返回
结构化查询 SelfQueryRetriever 自动解析过滤条件
高召回率 MultiQueryRetriever 多角度检索
高精度 ContextualCompressionRetriever 压缩过滤
"""

==========================================

4. 生成策略选择

=========================================="""

  • temperature: RAG 场景建议 0-0.3(需要准确性)
  • 提示模板: 明确要求"仅基于上下文回答"
  • 引用来源: 要求模型标注信息来源
  • 不确定性处理: 明确告诉模型"不知道就说不知道"
  • 流式输出: 使用 .stream() 提升用户体验
    """

==========================================

5. 常见问题排查

=========================================="""

问题 可能原因 解决方案
检索不到相关文档 Chunk 太大/分割不合理 减小 chunk_size,优化分隔符
答案不准确 检索到的文档不相关 改进检索策略,使用重排序
答案包含幻觉 提示不够严格 强化提示约束,使用引用格式
响应太慢 检索文档太多 减少 k 值,使用压缩检索
成本太高 嵌入和 LLM 调用频繁 缓存嵌入,使用更小模型
中文效果差 分割器不适合中文 使用中文优化的分隔符配置
"""

相关推荐
义嘉泰1 小时前
把显示、触控和手写笔都管起来
人工智能·芯片
Soari1 小时前
GitHub 开源项目解析:supermemoryai/supermemory —— AI 时代的持久记忆引擎
人工智能·github·开源项目·mcp·ai记忆引擎·下文搜索
实在智能RPA1 小时前
2026 金融信创替代转型:Agent如何赋能业务平稳过渡?
大数据·人工智能·ai·金融
_Aaron___1 小时前
RAG 知识库越用越脏?先把“增量更新”设计清楚
java·人工智能
装不满的克莱因瓶1 小时前
实现矩阵的点积:从数学原理到 NumPy 实战
人工智能·线性代数·算法·机器学习·矩阵·numpy
HyperAI超神经1 小时前
在线教程丨英伟达开源LocateAnything,3B模型可实现图像+视频的目标指向/开放词汇目标检测/指代表达定位/OCR文本定位等功能
人工智能·目标检测·计算机视觉·ocr·目标文本定位
库拉大叔1 小时前
KULAAI 一站式 AI 编程与模型聚合平台效果实测
人工智能
诸葛务农1 小时前
溶液纳米颗粒净化技术及其在光刻胶纳过滤和提纯中的应用(上)
人工智能
oort1231 小时前
VLStream 全开源决策式 AI 视频平台 技术视角完整说明
大数据·开发语言·人工智能·经验分享·python·开源·音视频