RAG 从零到一:构建你的第一个检索增强生成系统

RAG 从零到一:构建你的第一个检索增强生成系统

AI 核心技能系列 · 第 5 篇


导语

大模型很强,但它不知道你公司的内部文档、最新产品手册、私有数据。你问它"我们公司的退货政策是什么?"------它只能瞎编。

RAG(Retrieval-Augmented Generation,检索增强生成) 是解决这个问题的最主流方案:先从知识库中检索相关信息,再交给大模型生成回答。简单、有效、不需要训练模型。

RAG 也是企业 LLM 落地最多的应用模式------你能在市场上看到的大部分"企业知识问答"、"智能客服"、"文档助手",底层都是 RAG。


一、为什么需要 RAG

1.1 大模型的三大局限

局限 表现 例子
知识截止 不知道训练数据之后发生的事 "2026 年世界杯谁赢了?"→ 不知道
幻觉 自信地编造不存在的信息 编造不存在的论文引用
无法访问私有数据 不知道你公司的内部信息 公司制度、产品文档、客户数据

1.2 三种解决方案对比

方案 RAG Fine-tuning 长上下文
原理 先检索后生成 用数据重新训练模型 把所有数据塞进 Prompt
成本 低(只需向量库) 高(需要训练) 中(Token 费用高)
实时性 高(更新文档即可) 低(重新训练)
适用数据量 大(百万级文档) 中(万级数据) 小(受上下文限制)
最佳场景 企业知识问答 特定风格/格式 单次分析少量文档

决策指南:90% 的企业 LLM 应用,先试 RAG。Prompt Engineering + RAG 搞不定的,再考虑 Fine-tuning。

1.3 RAG 工作流程概览

arduino 复制代码
用户提问: "公司的年假政策是什么?"
    │
    ▼
┌──────────────┐
│ 1. 查询处理   │ → 将问题转换为向量
└──────┬───────┘
       │
       ▼
┌──────────────┐
│ 2. 检索       │ → 在向量库中找最相关的文档片段
└──────┬───────┘
       │
       ▼
┌──────────────┐     检索到的文档:
│ 3. 增强       │ →   "员工入职满一年后享有5天年假..."
└──────┬───────┘     "年假需提前3天申请..."
       │
       ▼
┌──────────────┐
│ 4. 生成       │ → LLM 基于检索到的文档生成回答
└──────┬───────┘
       │
       ▼
回答: "根据公司制度,入职满一年后享有5天年假,
      需要提前3天向主管提交申请..."

二、RAG 完整流程拆解

2.1 文档加载

python 复制代码
from langchain_community.document_loaders import (
    PyPDFLoader, TextLoader, Docx2txtLoader, 
    UnstructuredHTMLLoader, CSVLoader
)

# 支持多种格式
pdf_docs = PyPDFLoader("company_handbook.pdf").load()
txt_docs = TextLoader("faq.txt").load()
word_docs = Docx2txtLoader("policy.docx").load()
html_docs = UnstructuredHTMLLoader("product.html").load()
csv_docs = CSVLoader("customer_data.csv").load()

all_docs = pdf_docs + txt_docs + word_docs + html_docs + csv_docs
print(f"加载了 {len(all_docs)} 个文档片段")

2.2 文本分块(Chunking)

文档太长放不进一个 Embedding,需要切分成小块:

python 复制代码
from langchain.text_splitter import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,       # 每块最大 500 字符
    chunk_overlap=50,     # 相邻块重叠 50 字符(保持上下文连贯)
    separators=["\n\n", "\n", "。", ",", " ", ""],  # 按优先级切分
)

chunks = splitter.split_documents(all_docs)
print(f"分块后: {len(chunks)} 个块")

2.3 向量化与存储

python 复制代码
from langchain_openai import OpenAIEmbeddings
from langchain_chroma import Chroma

# 生成 Embedding 并存入向量库
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_documents(
    documents=chunks,
    embedding=embeddings,
    persist_directory="./chroma_db"
)

2.4 检索

python 复制代码
# 基础检索
retriever = vectorstore.as_retriever(
    search_type="similarity",
    search_kwargs={"k": 5}  # 返回 Top 5 最相关的文档块
)

results = retriever.invoke("公司年假政策")
for doc in results:
    print(f"[{doc.metadata.get('source', 'unknown')}] {doc.page_content[:100]}...")

2.5 生成

python 复制代码
from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate

llm = ChatOpenAI(model="gpt-4o", temperature=0)

prompt = ChatPromptTemplate.from_template("""
基于以下参考资料回答用户的问题。如果资料中没有相关信息,请明确说明"根据现有资料无法回答"。
不要编造任何不在资料中的信息。

参考资料:
{context}

用户问题:{question}

回答:
""")

# 组装 RAG 链
from langchain.schema.runnable import RunnablePassthrough

rag_chain = (
    {"context": retriever, "question": RunnablePassthrough()}
    | prompt
    | llm
)

answer = rag_chain.invoke("公司的年假政策是什么?")
print(answer.content)

三、分块策略深入对比

分块策略直接影响检索质量------块太大,语义不精确;块太小,丢失上下文。

策略 做法 优点 缺点 适用场景
固定长度 每 N 个字符切一刀 简单 可能切断语义 快速原型
递归字符分割 按段落→句子→字符递归切分 尽量保持语义完整 块大小不均匀 通用场景(最推荐)
语义分块 根据语义相似度变化切分 语义最精确 计算成本高 高质量需求
文档结构分块 按标题、段落、章节切分 保持文档结构 需要结构化文档 技术文档/手册

分块大小经验值

  • 通用推荐:chunk_size=500-1000,overlap=50-100
  • 精确检索:chunk_size=200-500(小块更精确)
  • 上下文丰富:chunk_size=1000-2000(大块信息更完整)

四、检索优化技巧

基础向量检索的效果往往不够好。以下是递进式的优化手段:

4.1 混合检索

python 复制代码
from langchain.retrievers import EnsembleRetriever
from langchain_community.retrievers import BM25Retriever

# BM25 关键词检索
bm25_retriever = BM25Retriever.from_documents(chunks, k=5)

# 向量检索
vector_retriever = vectorstore.as_retriever(search_kwargs={"k": 5})

# 混合:各占 50% 权重
hybrid_retriever = EnsembleRetriever(
    retrievers=[bm25_retriever, vector_retriever],
    weights=[0.5, 0.5]
)

4.2 查询改写(Query Rewriting)

用户的问题可能不够好------太简短、有歧义、或者用词和文档不一致。

python 复制代码
rewrite_prompt = ChatPromptTemplate.from_template("""
将以下用户问题改写为更适合搜索的形式。生成 3 个不同角度的查询:

原始问题:{question}

改写后的查询(每行一个):
""")

# 用 LLM 改写查询,每个查询分别检索,合并结果

4.3 重排序(Reranking)

初步检索返回 Top 20,用更精准的 Cross-Encoder 模型重排,取 Top 5。

python 复制代码
from sentence_transformers import CrossEncoder

reranker = CrossEncoder("BAAI/bge-reranker-v2-m3")

# 初步检索 Top 20
initial_results = retriever.invoke(query, k=20)

# 重排序
pairs = [(query, doc.page_content) for doc in initial_results]
scores = reranker.predict(pairs)

# 按分数排序,取 Top 5
reranked = sorted(zip(initial_results, scores), key=lambda x: x[1], reverse=True)[:5]

4.4 优化技巧层级

yaml 复制代码
Level 0: 基础向量检索(baseline)
   ↓ +15% 效果
Level 1: 混合检索(向量 + BM25)
   ↓ +10% 效果
Level 2: 查询改写 + 多查询检索
   ↓ +10% 效果
Level 3: 重排序(Reranking)
   ↓ +5% 效果
Level 4: 上下文压缩 + 父文档检索

五、RAG 常见问题与调优

问题 症状 诊断 解决方案
检索不到 回答"无法找到相关信息" 查看检索结果的相似度分数 调整分块策略/换 Embedding 模型
检索到但答案错 回答偏离问题 检查检索到的文档是否真的相关 添加重排序/查询改写
混入无关信息 回答中夹杂不相关内容 检索返回了噪声文档 提高相似度阈值/减少 Top K
延迟太高 响应时间 > 5秒 定位是检索慢还是生成慢 向量库索引优化/流式输出
回答太泛 没有具体细节 分块太大,信息被稀释 减小 chunk_size
幻觉 编造不在文档中的信息 Prompt 约束不够强 强化"只基于资料回答"的指令

六、进阶:生产级 RAG 的注意事项

6.1 数据更新策略

策略 做法 适用场景
全量重建 删除旧索引,重新构建 数据量小,更新不频繁
增量更新 只添加新文档/更新变化的文档 数据量大,实时性要求高
版本管理 每个版本一个索引,切换生效 需要回滚能力

6.2 权限控制

不同用户应该看到不同的数据。在 metadata 中标记权限:

python 复制代码
# 存储时带上权限标签
vectorstore.add_documents(
    documents=hr_docs,
    metadata=[{"access_level": "hr_only", "department": "hr"} for _ in hr_docs]
)

# 检索时过滤
retriever = vectorstore.as_retriever(
    search_kwargs={
        "k": 5,
        "filter": {"access_level": "hr_only"}
    }
)

6.3 评估指标

指标 含义 如何计算
Context Precision 检索到的文档中有多少是相关的 相关文档数 / 检索文档数
Context Recall 应该被检索到的文档有多少被找到了 被检索到的相关文档数 / 总相关文档数
Faithfulness 回答是否忠实于检索到的文档 LLM-as-Judge 评估
Answer Relevancy 回答是否回答了用户的问题 LLM-as-Judge 评估

七、职业视角

  • RAG 是目前企业 LLM 落地最多的方向------AI 应用工程师岗位几乎必考
  • 面试必问:RAG 的完整流程?检索效果不好怎么优化?RAG vs Fine-tuning 怎么选?
  • 相关岗位:AI 应用工程师、LLM 工程师、搜索工程师
  • 建议:自己动手搭一个完整的 RAG Demo 放到 GitHub 上,比任何证书都有说服力

总结

  1. 为什么 RAG:解决大模型知识截止、幻觉、无法访问私有数据三大问题
  2. 完整流程:文档加载 → 分块 → 向量化 → 检索 → 生成
  3. 分块是关键:推荐递归字符分割,chunk_size 500-1000
  4. 检索优化:混合检索 → 查询改写 → 重排序,递进式提升
  5. 生产考量:数据更新、权限控制、评估指标缺一不可

本文是 AI 核心技能系列 第 5 篇,共 12 篇。上一篇:Embedding 与向量数据库 | 下一篇:Function Calling:让大模型连接真实世界

关注公众号「coft」,获取完整系列更新、配套代码和学习路线图。一起交流 AI 转行经验,助力职业跃升,迈向高薪岗位。

相关推荐
billhan20161 小时前
Function Calling:让大模型连接真实世界
人工智能
程序员飞哥2 小时前
Block科技公司裁员四千人,竟然是因为 AI ?
人工智能·后端·程序员
大模型真好玩2 小时前
大模型训练全流程实战指南工具篇(七)——EasyDataset文档处理流程
人工智能·langchain·deepseek
billhan20162 小时前
Embedding 与向量数据库:语义理解的基础设施
人工智能
OpenBayes贝式计算2 小时前
解决视频模型痛点,TurboDiffusion 高效视频扩散生成系统;Google Streetview 涵盖多个国家的街景图像数据集
人工智能·深度学习·机器学习
OpenBayes贝式计算2 小时前
OCR教程汇总丨DeepSeek/百度飞桨/华中科大等开源创新技术,实现OCR高精度、本地化部署
人工智能·深度学习·机器学习
我要改名叫嘟嘟3 小时前
年后上班三天之后,忽然想作的一次记录
人工智能·程序员
飞哥数智坊3 小时前
SWE-bench 退役:当 AI 评测沦为“刷题游戏”,我们还能信谁?
人工智能
爱可生开源社区4 小时前
2026 年,优秀的 DBA 需要具备哪些素质?
数据库·人工智能·dba