什么是 RAG?
High Contrast
Dark Mode
Light Mode
Sepia
Forest
4 min read802 words

什么是 RAG?

RAG(Retrieval-Augmented Generation,检索增强生成)是当前企业级 LLM 应用中最重要的技术架构。它通过将外部知识库与大语言模型结合,让 AI 能够基于最新、最准确的信息生成回答。

为什么需要 RAG?

LLM 的局限性

大语言模型虽然强大,但存在几个关键问题:

graph TB A[LLM 的局限性] --> B[知识截止日期] A --> C[幻觉问题] A --> D[缺乏私有数据] A --> E[无法溯源] B --> B1[训练数据有截止时间
无法获取最新信息] C --> C1[可能生成看似合理
实际错误的内容] D --> D1[无法访问企业内部
文档和知识] E --> E1[无法说明答案来源
难以验证准确性] style A fill:#ffebee,stroke:#c62828,stroke-width:3px style B fill:#fff3e0,stroke:#e65100,stroke-width:2px style C fill:#fff3e0,stroke:#e65100,stroke-width:2px style D fill:#fff3e0,stroke:#e65100,stroke-width:2px style E fill:#fff3e0,stroke:#e65100,stroke-width:2px
问题 说明 影响
知识截止 模型只知道训练时的数据 无法回答最新问题
幻觉 模型"编造"不存在的信息 输出不可信
无私有数据 不了解企业内部知识 无法处理业务问题
无法溯源 不知道答案从哪来 难以审计和验证

RAG 如何解决这些问题

RAG 的核心思想非常直观:先检索,再生成

graph LR A[用户提问] --> B[检索相关文档] B --> C[将文档作为上下文] C --> D[LLM 基于上下文生成] D --> E[带引用的回答] style A fill:#e3f2fd,stroke:#1976d2,stroke-width:2px style B fill:#e8f5e9,stroke:#388e3c,stroke-width:2px style C fill:#fff3e0,stroke:#f57c00,stroke-width:2px style D fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px style E fill:#e8f5e9,stroke:#388e3c,stroke-width:2px

RAG 的核心流程

一个完整的 RAG 系统包含两个主要阶段:索引阶段查询阶段

索引阶段(离线)

graph TB subgraph 索引阶段 A[原始文档] --> B[文档加载] B --> C[文本切分] C --> D[向量化 Embedding] D --> E[存入向量数据库] end A1[PDF] --> A A2[Word] --> A A3[Markdown] --> A A4[网页] --> A style A fill:#e3f2fd,stroke:#1976d2,stroke-width:2px style E fill:#c8e6c9,stroke:#388e3c,stroke-width:3px

查询阶段(在线)

graph TB subgraph 查询阶段 Q[用户问题] --> Q1[问题向量化] Q1 --> Q2[向量检索] Q2 --> Q3[获取相关文档] Q3 --> Q4[构建 Prompt] Q4 --> Q5[LLM 生成回答] Q5 --> Q6[返回答案+引用] end DB[(向量数据库)] --> Q2 style Q fill:#e3f2fd,stroke:#1976d2,stroke-width:2px style Q6 fill:#c8e6c9,stroke:#388e3c,stroke-width:3px style DB fill:#fff3e0,stroke:#f57c00,stroke-width:2px

代码示例:最简 RAG 系统

下面用 Python 实现一个最简单的 RAG 系统,帮助你理解核心流程:

"""
最简 RAG 系统示例
演示 RAG 的核心流程:索引 → 检索 → 生成
"""
from openai import OpenAI
client = OpenAI()
# ============================================
# 第一步:准备知识库(模拟文档)
# ============================================
documents = [
{
"id": 1,
"title": "公司年假政策",
"content": "员工入职满1年可享受5天年假,满3年享受10天,满5年享受15天。年假需提前3天申请。"
},
{
"id": 2,
"title": "报销流程",
"content": "员工报销需在费用发生后30天内提交,金额500元以下主管审批,500元以上需部门经理审批。"
},
{
"id": 3,
"title": "远程办公政策",
"content": "员工每周可申请2天远程办公,需提前1天在系统中申请,主管审批后生效。"
},
]
# ============================================
# 第二步:创建向量索引
# ============================================
def get_embedding(text: str) -> list[float]:
"""获取文本的向量表示"""
response = client.embeddings.create(
model="text-embedding-3-small",
input=text
)
return response.data[0].embedding
def cosine_similarity(a: list[float], b: list[float]) -> float:
"""计算余弦相似度"""
dot_product = sum(x * y for x, y in zip(a, b))
norm_a = sum(x ** 2 for x in a) ** 0.5
norm_b = sum(x ** 2 for x in b) ** 0.5
return dot_product / (norm_a * norm_b)
# 为所有文档创建向量
print("正在创建文档向量...")
for doc in documents:
doc["embedding"] = get_embedding(doc["content"])
print(f"已为 {len(documents)} 篇文档创建向量索引")
# ============================================
# 第三步:检索相关文档
# ============================================
def retrieve(query: str, top_k: int = 2) -> list[dict]:
"""检索与查询最相关的文档"""
query_embedding = get_embedding(query)
# 计算相似度并排序
scored_docs = []
for doc in documents:
score = cosine_similarity(query_embedding, doc["embedding"])
scored_docs.append({"doc": doc, "score": score})
scored_docs.sort(key=lambda x: x["score"], reverse=True)
return scored_docs[:top_k]
# ============================================
# 第四步:生成回答
# ============================================
def generate_answer(query: str) -> str:
"""RAG 完整流程:检索 + 生成"""
# 检索相关文档
results = retrieve(query, top_k=2)
# 构建上下文
context = "\n\n".join([
f"【{r['doc']['title']}】\n{r['doc']['content']}"
for r in results
])
# 构建 Prompt
prompt = f"""请根据以下参考资料回答用户的问题。
如果参考资料中没有相关信息,请坦诚说明。
请在回答中标注信息来源。
## 参考资料
{context}
## 用户问题
{query}
## 回答"""
# 调用 LLM 生成
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
temperature=0.3
)
return response.choices[0].message.content
# ============================================
# 测试
# ============================================
question = "我工作两年了,能请几天年假?"
print(f"\n问题:{question}")
print(f"\n回答:{generate_answer(question)}")

输出示例:

问题:我工作两年了,能请几天年假?
回答:根据公司年假政策,员工入职满1年可享受5天年假。
您工作两年,已满1年但未满3年,因此您目前可以享受5天年假。
待您工作满3年后,年假将增加至10天。
请注意,年假需提前3天申请。
——来源:《公司年假政策》

RAG vs 其他方案

RAG vs 微调(Fine-tuning)

对比维度 RAG 微调
知识更新 实时更新,修改文档即可 需要重新训练
成本 低,只需向量数据库 高,需要 GPU 训练
可溯源 可以引用来源文档 无法追溯来源
适用场景 知识密集型问答 风格/格式学习
实施周期 天级别 周级别
幻觉控制 受限于检索结果,更可控 仍可能产生幻觉

RAG vs 长上下文(Long Context)

对比维度 RAG 长上下文
数据量 支持海量数据 受限于上下文窗口
成本 只传递相关片段 每次传递全部内容
精度 精确检索相关段落 可能"大海捞针"
延迟 检索+生成 长文本处理较慢
适用场景 大规模知识库 少量文档精读
graph TB A{你的需求是什么?} --> B{数据量大吗?} A --> C{需要实时更新吗?} A --> D{需要学习风格吗?} B -->|大于100MB| E[选择 RAG] B -->|小于100MB| F[可考虑长上下文] C -->|是| E C -->|否| G{预算充足吗?} D -->|是| H[选择微调] D -->|否| E G -->|是| H G -->|否| E style E fill:#c8e6c9,stroke:#388e3c,stroke-width:3px style H fill:#fff3e0,stroke:#f57c00,stroke-width:2px style F fill:#e3f2fd,stroke:#1976d2,stroke-width:2px

RAG 的应用场景

1. 企业知识库问答

最常见的应用,让员工通过自然语言查询公司内部文档。

2. 客服机器人

基于产品文档和 FAQ 自动回答客户问题。

3. 法律/合规检索

在海量法规文件中快速找到相关条款。

4. 技术文档助手

帮助开发者快速查找 API 文档和代码示例。

5. 医疗辅助

基于医学文献辅助诊断建议(需严格审核)。

本章小结

下一章:我们将深入了解 RAG 的系统架构,学习如何设计一个生产级的 RAG 系统。