文章目录
-
- [Part 9: RAG(检索增强生成)](#Part 9: RAG(检索增强生成))
-
- [9.1 RAG 概述](#9.1 RAG 概述)
-
- [为什么需要 RAG](#为什么需要 RAG)
- [RAG vs 微调 vs 提示工程](#RAG vs 微调 vs 提示工程)
- [RAG 工作流程](#RAG 工作流程)
- [9.2 基础 RAG 实现](#9.2 基础 RAG 实现)
- [9.3 RAG 提示工程](#9.3 RAG 提示工程)
-
- [标准 RAG 提示模板](#标准 RAG 提示模板)
- [9.4 高级 RAG 技术](#9.4 高级 RAG 技术)
-
- [多查询检索 RAG](#多查询检索 RAG)
- [上下文压缩 RAG](#上下文压缩 RAG)
- [父子文档 RAG](#父子文档 RAG)
- [多向量存储 RAG](#多向量存储 RAG)
- [时间加权 RAG](#时间加权 RAG)
- [9.5 RAG 评估](#9.5 RAG 评估)
-
- 评估指标
- [RAGAS 评估框架 Demo](#RAGAS 评估框架 Demo)
- [9.6 RAG 最佳实践](#9.6 RAG 最佳实践)
- ==========================================
- [1. 文档预处理建议](#1. 文档预处理建议)
- =========================================="""
- ==========================================
- [2. Chunk 策略选择](#2. Chunk 策略选择)
- =========================================="""
- ==========================================
- [3. 检索策略选择](#3. 检索策略选择)
- =========================================="""
- ==========================================
- [4. 生成策略选择](#4. 生成策略选择)
- =========================================="""
- ==========================================
- [5. 常见问题排查](#5. 常见问题排查)
- =========================================="""
Part 9: RAG(检索增强生成)
9.1 RAG 概述
为什么需要 RAG
大语言模型(LLM)有四大固有限制,RAG(Retrieval-Augmented Generation,检索增强生成)正是为了解决这些问题而诞生的:
- 知识过时:模型的训练数据有截止日期,无法获取最新信息
- 幻觉问题:模型可能"一本正经地胡说八道",生成不准确的内容
- 缺乏私有数据:模型不了解你公司的内部文档、业务知识
- 无法引用来源:模型无法告诉用户答案来自哪里
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 调用频繁 | 缓存嵌入,使用更小模型 |
| 中文效果差 | 分割器不适合中文 | 使用中文优化的分隔符配置 |
| """ |