RAG 详解:将自有数据添加到 LLM 应用
你已经使用强大的 LLM(如 GPT-4 或 Claude)构建了一个聊天机器人,但它给出的是通用答案,并且不知道你公司的内部文档。如何让它回答关于你自己数据的问题?微调成本高昂且静态。解决方案是检索增强生成(RAG)。
在本文中,我们将用实用的术语解释 RAG,展示如何逐步实现它,并分享构建可靠 LLM 应用程序的最佳实践,这些应用程序利用你的私有数据。
什么是 RAG?
RAG 结合了两种强大的技术:检索(从知识库中查找相关信息)和生成(使用 LLM 生成答案)。RAG 不单纯依赖 LLM 的预训练知识,而是动态获取相关文档并将其包含在提示中,使模型的响应基于你的数据。
把它想象成 AI 的开卷考试:它可以在回答之前在你的文档中查找答案。
为什么使用 RAG?
- 最新信息:无需重新训练模型即可更新知识库。
- 成本效益:无需昂贵的微调或大型上下文窗口。
- 准确性:通过将答案基于检索到的事实来减少幻觉。
- 隐私:你的数据保留在你的向量数据库中;只有相关片段被发送到 LLM。
- 灵活性:可与任何 LLM(OpenAI、Anthropic、本地模型)和任何文档类型配合使用。
RAG 的工作原理:核心组件
典型的 RAG 系统有四个主要部分:
- 文档摄入:加载并预处理你的文档(PDF、网页、数据库)。
- 嵌入模型:将文本块转换为捕获语义含义的数值向量。
- 向量数据库:存储并索引这些向量,以便快速进行相似性搜索。
- LLM:使用检索到的上下文生成答案。
逐步指南:构建 RAG 管道
1. 准备文档
收集你的数据源:PDF、Markdown 文件、Confluence 页面或 SQL 表。清理文本(删除页眉、页脚、样板内容),并将其分割成 200–500 字的块,并带有一些重叠(例如 50 字)以保留上下文。
使用 Python 的示例:
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50
)
chunks = text_splitter.split_text(your_document)
2. 生成嵌入
使用嵌入模型(如 OpenAI 的 text-embedding-3-small 或开源模型 all-MiniLM-L6-v2)将每个块转换为向量。
from openai import OpenAI
client = OpenAI()
def get_embedding(text):
response = client.embeddings.create(
model="text-embedding-3-small",
input=text
)
return response.data[0].embedding
3. 存储到向量数据库
选择向量数据库:Pinecone、Weaviate、Qdrant,甚至带有 pgvector 的 PostgreSQL。存储每个块的嵌入及其元数据(来源、页码)。
import pinecone
pinecone.init(api_key="YOUR_KEY", environment="us-west1-gcp")
index = pinecone.Index("rag-demo")
vectors = [(f"chunk-{i}", get_embedding(chunk), {"text": chunk}) for i, chunk in enumerate(chunks)]
index.upsert(vectors=vectors)
4. 检索相关块
当用户提问时,嵌入查询并搜索最相似的 top-k 个块。
query_embedding = get_embedding(user_question)
results = index.query(vector=query_embedding, top_k=3, include_metadata=True)
context = "\n\n".join([match.metadata["text"] for match in results.matches])
5. 生成答案
构建一个包含检索到的上下文的提示,并要求 LLM 基于它回答。
prompt = f"""Answer the question based only on the following context:
{context}
Question: {user_question}
Answer:"""
response = client.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": prompt}]
)
print(response.choices[0].message.content)
RAG 与微调对比
| 方面 | RAG | 微调 |
|---|---|---|
| 数据新鲜度 | 实时更新 | 重新训练前静态 |
| 成本 | 低(嵌入 + 存储) | 高(训练计算) |
| 实现 | 模块化,更容易 | 复杂,需要 ML 专业知识 |
| 使用场景 | 基于文档的问答 | 风格适应、领域术语 |
RAG 的最佳实践
- 明智分块:重叠块以避免切断句子;考虑语义分块。
- 使用混合搜索:结合向量搜索和关键词搜索(BM25)以提高召回率。
- 重新排序结果:使用交叉编码器按相关性对检索到的块重新排序。
- 谨慎提示:指示 LLM 引用来源,如果上下文不足则说“我不知道”。
- 监控和评估:记录查询和响应;使用命中率和答案相关性等指标。
常见陷阱及避免方法
- 检索效果差:如果嵌入无法捕获领域特定含义,尝试微调嵌入模型或使用领域特定模型。
- 上下文长度限制:不要塞入太多块;优先考虑顶部结果并保持提示简洁。
- 幻觉:即使有上下文,LLM 也可能编造事实。使用严格的提示并考虑验证步骤。
- 索引过时:设置管道在文档更改时重新嵌入。
常见问题
RAG 和微调有什么区别?
RAG 在推理时检索相关信息并将其包含在提示中,而微调调整模型权重以学习新模式。RAG 更适合动态的事实知识;微调更适合风格或领域适应。
RAG 需要向量数据库吗?
不一定——你可以使用任何搜索索引(例如 Elasticsearch),甚至对于小数据集使用内存中的余弦相似度。但像 Pinecone 或 Qdrant 这样的向量数据库针对快速、可扩展的相似性搜索进行了优化。
如何评估 RAG 系统?
衡量检索质量(precision@k、recall)和生成质量(答案相关性、忠实度)。像 Ragas 或 TruLens 这样的工具可以帮助自动化评估。
结论
RAG 是一种实用且经济高效的方式,可以将你自己的数据添加到 LLM 应用程序中。通过遵循上述步骤并遵守最佳实践,你可以构建使用私有知识库准确回答问题的 AI 助手。从小处着手,迭代,并始终进行评估。
在为 RAG 管道准备文档时,你通常需要将多个 PDF 合并为一个文件以便于摄入。使用我们的免费 PDF 合并工具 快速安全地合并 PDF。