291 字
1 分钟
RAG 检索增强生成原理
一、RAG 概述
1.1 为什么需要 RAG
graph TB
subgraph LLM[" LLM 局限性"]
direction TB
A[" 知识截止<br/>训练数据有时效性"]
B[" 幻觉回答<br/>编造不存在的事实"]
C[" 私有知识缺失<br/>无法访问企业内部数据"]
end
subgraph RAG[" RAG 解决方案"]
direction TB
D[" 外部知识检索<br/>实时获取最新信息"]
E[" 上下文注入<br/>基于事实生成"]
F[" 生成时引用<br/>可追溯来源"]
end
A -.->|解决| D
B -.->|解决| E
C -.->|解决| F
style LLM fill:#ffcccc,stroke:#ff6666
style RAG fill:#ccffcc,stroke:#66cc66
graph TB
subgraph Problem["问题场景"]
P1["用户问: 公司最新财报数据?"]
P2["LLM: 我不知道..."]
P2 --> P3["无法回答"]
end
subgraph Solution["RAG 方案"]
S1["用户问: 公司最新财报数据?"]
S1 --> S2[" 检索知识库"]
S2 --> S3[" 获取财报文档"]
S3 --> S4[" LLM + Context"]
S4 --> S5["基于事实回答"]
end
Problem --> Solution
style Problem fill:#ffe6e6
style Solution fill:#e6ffe6
| LLM 问题 | RAG 解决 |
|---|---|
| 知识过时 | 实时检索最新内容 |
| 幻觉回答 | 基于检索事实生成 |
| 私有知识缺失 | 连接自有知识库 |
| 无法引用来源 | 提供可追溯的 Context |
1.2 RAG 完整架构
flowchart TB
subgraph Offline[" 离线处理流程"]
direction LR
O1[" 原始文档"] --> O2[" 文档切分<br/>Chunk Splitter"]
O2 --> O3[" Embedding<br/>向量化模型"]
O3 --> O4[" 向量数据库<br/>Milvus/Qdrant"]
end
subgraph Online[" 在线查询流程"]
direction TB
Q1[" 用户 Query"] --> Q2[" Query 改写"]
Q2 --> Q3[" Query Embedding"]
Q3 --> Q4[" 向量检索<br/>Top-K 召回"]
Q4 --> Q5{" 混合检索?"}
Q5 -->|是| Q6[" RRF 融合排序"]
Q5 -->|否| Q7[" 候选文档"]
Q6 --> Q7
Q7 --> Q8[" 重排序<br/>Reranker"]
Q8 --> Q9[" Context 组装"]
end
subgraph Generation[" LLM 生成"]
direction LR
G1[" Context"] --> G2[" LLM"]
Q1 --> G2
G2 --> G3[" 答案输出"]
G3 --> G4[" 引用标注"]
end
O4 -.->|索引| Q4
Q9 --> G1
style Offline fill:#e3f2fd,stroke:#1976d2
style Online fill:#fff3e0,stroke:#f57c00
style Generation fill:#e8f5e9,stroke:#388e3c
flowchart LR
subgraph QueryFlow["用户查询流程"]
A["用户 Query"] --> B["Query 改写"]
B --> C["向量检索"]
C --> D["Context 组装"]
D --> E["LLM 生成"]
E --> F["引用标注"]
end
subgraph IndexFlow["离线索引流程"]
G["文档切分"] --> H["Embedding"]
H --> I["向量入库"]
end
style QueryFlow fill:#f3e5f5
style IndexFlow fill:#e0f7fa
二、文档处理
2.1 文档切分
class TextSplitter: def split(self, text: str, chunk_size: int = 500) -> list[str]: # 1. 文本标准化 text = self.normalize(text)
# 2. 按段落/句子切分 chunks = self.split_by_paragraph(text)
# 3. 合并过小片段 chunks = self.merge_small_chunks(chunks, chunk_size)
return chunks2.2 切分策略
# 固定大小切分def fixed_split(text, chunk_size=500, overlap=50): chunks = [] start = 0 while start < len(text): end = start + chunk_size chunks.append(text[start:end]) start = end - overlap return chunks
# 语义切分(保留句子完整性)def semantic_split(text): sentences = split_by_sentence(text) chunks = [] current = [] for sent in sentences: current.append(sent) if len(' '.join(current)) > chunk_size: chunks.append(' '.join(current[:-1])) current = [sent] return chunks三、检索流程
3.0 检索架构概览
flowchart TB
subgraph Input[" 输入"]
Q["用户查询"]
end
subgraph Rewrite[" Query 处理"]
R1["同义词扩展"]
R2["意图识别"]
R3["查询改写"]
end
subgraph Retrieve[" 检索层"]
direction TB
V1["向量检索<br/>语义相似"]
K1["关键词检索<br/>BM25"]
H1["混合检索<br/>RRF 融合"]
end
subgraph Rerank[" 重排序"]
R4["Cross-Encoder<br/>精确打分"]
R5["Top-K 选择"]
end
subgraph Output[" 输出"]
O["相关文档"]
end
Q --> R1 --> R2 --> R3
R3 --> V1
R3 --> K1
V1 --> H1
K1 --> H1
H1 --> R4 --> R5 --> O
style Input fill:#e1f5fe
style Rewrite fill:#fff8e1
style Retrieve fill:#f3e5f5
style Rerank fill:#e8f5e9
style Output fill:#fce4ec
3.1 Query 改写
# Query 优化class QueryRewriter: def rewrite(self, query: str) -> str: # 1. 隐式表述展开 query = self.expand_abbreviations(query)
# 2. 同义词扩展 query = self.expand_synonyms(query)
# 3. 假设类型注入 if "如何" in query: query += " 请提供具体步骤"
return query3.2 混合检索
flowchart TB
subgraph Query[" 用户查询"]
Q["'如何优化 RAG 检索效果?'"]
end
subgraph VectorSearch[" 向量检索"]
direction TB
V1["Query Embedding"]
V2["向量相似度计算<br/>Cosine/L2"]
V3["语义相关文档"]
V1 --> V2 --> V3
end
subgraph KeywordSearch[" 关键词检索"]
direction TB
K1["分词处理"]
K2["BM25 打分"]
K3["关键词匹配文档"]
K1 --> K2 --> K3
end
subgraph Fusion[" RRF 融合"]
direction TB
F1["倒数排名融合<br/>RRF Score"]
F2["综合排序"]
F3["统一候选集"]
F1 --> F2 --> F3
end
subgraph Result[" 检索结果"]
R["Top-K 相关文档"]
end
Q --> V1
Q --> K1
V3 --> F1
K3 --> F1
F3 --> R
style Query fill:#e8eaf6
style VectorSearch fill:#e3f2fd
style KeywordSearch fill:#fff3e0
style Fusion fill:#f3e5f5
style Result fill:#e8f5e9
# 混合检索 = 关键词 + 向量class HybridRetriever: def retrieve(self, query: str, top_k: int = 10): # 向量检索 vector_results = self.vector_index.search( self.embed(query), top_k * 2 )
# 关键词检索 bm25_results = self.bm25_index.search(query, top_k * 2)
# RRF 融合 fused = self.rrf_fusion( vector_results, bm25_results, top_k )
return fused
# RRF (Reciprocal Rank Fusion)def rrf(results_list, k=60): scores = {} for results in results_list: for i, doc in enumerate(results): doc_id = doc.id scores[doc_id] = scores.get(doc_id, 0) + 1 / (k + i + 1) return sorted(scores.items(), key=lambda x: -x[1])四、Context 组装
4.0 Context 组装流程
flowchart LR
subgraph Input[" 输入"]
Q["用户问题"]
D["检索文档<br/>Top-K"]
end
subgraph Build[" 组装过程"]
direction TB
B1["Token 计数"]
B2["文档排序<br/>相关度优先"]
B3["窗口截断<br/>Max Tokens"]
B4["格式化拼接"]
B1 --> B2 --> B3 --> B4
end
subgraph Template[" Prompt 模板"]
T["""
系统: 你是一个助手
Context: {documents}
问题: {query}
请基于 Context 回答:
"""]
end
subgraph Output[" 输出"]
P["完整 Prompt"]
end
Q --> Build
D --> Build
Build --> Template
Template --> P
style Input fill:#e3f2fd
style Build fill:#fff8e1
style Template fill:#f3e5f5
style Output fill:#e8f5e9
4.1 Context 窗口管理
class ContextBuilder: def __init__(self, max_tokens: int = 4000): self.max_tokens = max_tokens
def build(self, query: str, retrieved_docs: list) -> str: context_parts = [f"问题: {query}\n"] used_tokens = self.count_tokens(context_parts[0])
for doc in retrieved_docs: doc_text = f"参考文档:\n{doc.content}" doc_tokens = self.count_tokens(doc_text)
if used_tokens + doc_tokens > self.max_tokens: break
context_parts.append(doc_text) used_tokens += doc_tokens
return '\n\n'.join(context_parts)4.2 重排序
flowchart TB
subgraph Input[" 候选文档"]
D1["Doc 1: 相关度 0.72"]
D2["Doc 2: 相关度 0.68"]
D3["Doc 3: 相关度 0.65"]
D4["Doc 4: 相关度 0.61"]
D5["Doc 5: 相关度 0.58"]
end
subgraph Reranker[" Cross-Encoder 重排序"]
direction TB
R1["Query + Doc 联合编码"]
R2["Attention 交互"]
R3["精确相关性打分"]
R1 --> R2 --> R3
end
subgraph Output[" 重排序结果"]
O1["Doc 3: 0.95 "]
O2["Doc 1: 0.89 "]
O3["Doc 5: 0.82 "]
O4["Doc 2: 0.45 ⬇"]
O5["Doc 4: 0.32 ⬇"]
end
Input --> Reranker
Reranker --> Output
style Input fill:#e3f2fd
style Reranker fill:#fff8e1
style Output fill:#e8f5e9
class Reranker: def rerank(self, query: str, docs: list, top_k: int = 5): # 使用交叉编码器重排序 pairs = [(query, doc.content) for doc in docs] scores = self.cross_encoder.predict(pairs)
ranked = sorted( zip(docs, scores), key=lambda x: -x[1] )[:top_k]
return [doc for doc, _ in ranked]五、工程实践
5.1 评估指标
# RAG 评估指标metrics = { # 检索质量 "retrieval_recall": "相关文档召回比例", "retrieval_precision": "召回文档中相关比例",
# 生成质量 "answer_relevance": "答案与问题的相关性", "faithfulness": "答案对 Context 的忠实度", "answer_correctness": "答案正确性"}5.2 常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 检索不到相关内容 | 切分不当/Embedding 质量差 | 调整 chunk_size,使用更好 Embedding |
| Context 太长超限 | 文档太多/太长 | 重排序、摘要压缩 |
| 引用不准确 | 文档切分破坏完整性 | 增加元数据、父子文档 |
5.3 生产部署
# RAG 系统架构rag_pipeline: document_store: milvus vector_store: openai_embedder llm: gpt-4-turbo
retrieval: top_k: 10 rerank: true hybrid_search: true
generation: max_tokens: 2000 temperature: 0.1六、总结
mindmap
root((RAG 系统))
文档处理
智能切分
固定大小
语义切分
父子文档
元数据管理
检索
向量检索
HNSW/IVF
关键词检索
BM25
混合检索
RRF 融合
重排序
Cross-Encoder
精确打分
生成
上下文压缩
引用标注
多轮对话
| 模块 | 关键技术 |
|---|---|
| 文档处理 | 智能切分、父子文档 |
| 检索 | 向量检索、关键词检索、混合检索 |
| 重排序 | 交叉编码器 |
| 生成 | 上下文压缩、引用标注 |
支持与分享
如果这篇文章对你有帮助,欢迎支持作者或分享给更多人
部分信息可能已经过时
相关文章 智能推荐
1
让AI拥有知识:RAG检索增强生成详解
AI 让AI拥有知识——RAG检索增强生成详解
2
Embedding 与向量搜索原理
AI 深入解析文本 Embedding 模型与向量检索——模型原理、索引结构、相似度计算与工程实践。
3
AI 工程化实践
AI AI 工程化全景导览——从提示词工程到多模态系统,梳理大模型落地的核心工程能力与知识体系
4
RAG 优化策略深度解析
AI 深入解析 RAG 检索增强生成的优化策略——Query 改写、混合检索、重排序、Context 压缩与引用追踪。
5
RAG 与 Long Context:LLM 的知识增强
AI 深度解读 RAG 与 Long Context 技术——从基础检索增强到最新长上下文处理方案






