mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
291 字
1 分钟
RAG 检索增强生成原理
2025-02-07

一、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 chunks

2.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 query

3.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 精确打分 生成 上下文压缩 引用标注 多轮对话
模块关键技术
文档处理智能切分、父子文档
检索向量检索、关键词检索、混合检索
重排序交叉编码器
生成上下文压缩、引用标注

支持与分享

如果这篇文章对你有帮助,欢迎支持作者或分享给更多人

RAG 检索增强生成原理
https://blog.souloss.com/posts/ai-engineering/rag-principles/
作者
Souloss
发布于
2025-02-07
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时