RAG 系统原理与最小 Python 实现
语言模型受训练数据限制:知识静态,无法覆盖时事,且易产生幻觉——编造事实。上下文窗口限制进一步恶化问题,输入超限时会被截断。检索增强生成(RAG) 通过在生成过程中从外部源拉取相关上下文,显著降低错误并提供来源引用。
RAG 将文档、数据库或代码仓库连接起来,使响应可验证且实时更新。对于模型未训练过的细分领域尤为关键。
文档分块
分块是将文本拆分成片段(chunks)以便索引。LLM 只能处理有限数量的 chunks,因此分块质量直接决定事实检索效果。
大块增加噪声、提高成本并降低搜索精度。小块则破坏上下文,将相关概念割裂。常见策略:
- 固定字符或 token 长度。
- 语义边界(段落、标题)。
- 重叠以保持连续性。
需根据语料库和查询经验调优参数。
RAG 工作流程阶段
RAG 涵盖预处理、检索和生成三个阶段:
- 索引:将 chunks 转为向量并存入向量数据库。
- 检索:查询向量化,提取 top 相关 chunks。
- 增强:结果排序,去重并清理噪声。
- 生成:将上下文注入查询,供 LLM 生成。
基础朴素 RAG 保持简洁。高级 RAG 增加查询重写和重排序。模块化 RAG 使用路由和结果融合等模块。
RAG 与微调对比
RAG 无需修改模型即可扩展知识访问,与需要数据和大量计算的微调不同。提示工程仅优化查询,无法引入外部数据。
| 方法 | 优势 | 劣势 |
|------|------|------|
| RAG | 数据新鲜、低成本、可引用来源 | 依赖检索质量 |
| 微调 | 深度定制 | 资源消耗大、易过时 |
RAG 适用于动态数据;微调适合静态领域。
最小 RAG 流水线:动手实践
我们用 Python 构建朴素 RAG,基于维基百科(500 篇文章)和 Qwen2.5-3B-Instruct。借助 LangChain、Chroma 和 sentence-transformers。
步骤 1:加载与分块
import torch
from datasets import load_dataset
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_core.documents import Document
device = "cuda" if torch.cuda.is_available() else "cpu"
dataset = load_dataset(
"wikimedia/wikipedia",
"20231101.ru",
split="train[:500]"
)
documents = [
Document(
page_content=row["text"],
metadata={"title": row["title"]}
)
for row in dataset
]
splitter = RecursiveCharacterTextSplitter(
chunk_size=800,
chunk_overlap=100
)
chunks = splitter.split_documents(documents)
print("Chunks:", len(chunks))
生成约 17,892 个 800 字符 chunks,重叠 100 字符。
步骤 2:向量索引
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import HuggingFaceEmbeddings
embedding_model = HuggingFaceEmbeddings(
model_name="sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2",
model_kwargs={"device": device}
)
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embedding_model
)
使用多语言 MiniLM 嵌入,Chroma 存储。
步骤 3:检索
import textwrap
def search(query, k=3, width=80):
results = vectorstore.similarity_search(query, k=k)
for i, doc in enumerate(results, 1):
print(f"\n--- Result {i} ---")
print("Title:", doc.metadata["title"])
print(textwrap.fill(doc.page_content[:1000], width))
search("Who is Pushkin?")
从普希金相关文章中提取片段。
步骤 4:生成
添加 LLM 从检索 chunks 生成(代码省略,完整版见源码)。
关键要点
- RAG 通过外部上下文减少幻觉,无需调整模型。
- 成功关键在于分块和嵌入:针对领域定制。
- 从朴素 RAG 开始,生产环境扩展到高级/模块化。
- LangChain 简化原型开发。
- 用真实数据集测试优化。
— Editorial Team
暂无评论。