RAG 检索增强生成

从零构建企业知识库问答系统 —— 理解检索、增强、生成的每个环节

纯 LLM 的三大硬伤 vs RAG 的解决方案

问题纯 LLMRAG 解决
知识过时 训练数据截止到某日期,不知道"今天"的事 检索最新文档 实时更新知识库
幻觉 遇到不知道的会"编"一个看起来像真的答案 提供参考答案 约束 LLM 生成
私有知识盲区 没见过企业内部文档 接入内部文档 让 LLM 基于事实作答

直观演示:有 RAG vs 没有 RAG

没有 RAG

我们公司年假多少天?
作为AI助手,我无法访问贵公司的内部政策。建议您查阅员工手册或咨询HR部门。
LLM 没见过你的员工手册 → 无法回答

有 RAG

我们公司年假多少天?
根据[文档1]《办公政策》,公司年假为15天,带薪病假7天。办公制度为混合办公,每周一三五在办公室,周二周四可远程。
检索到《办公政策》→ 基于事实回答 ✅

RAG 是什么 —— 一句话

RAG = 检索 (Retrieve) + 增强 (Augment) + 生成 (Generate)
先从知识库"检索"相关文档 → 把文档和问题一起"增强"到 Prompt → LLM "生成"有据可查的答案

适用场景

企业知识库

员工手册、技术文档、产品FAQ → 智能问答机器人

客服系统

产品说明 + 历史工单 → 自动回答用户问题

法律/医疗

法规库/文献库 → 辅助律师/医生查找信息

RAG 完整流程

📝
用户提问
"年假多少天?"
🔍
检索 Retrieve
找相关文档
📎
增强 Augment
拼入 Prompt
🤖
生成 Generate
LLM 作答
答案
"年假15天"

阶段一:检索 (Retrieval)

把用户问题和知识库文档都转成向量,用余弦相似度找最接近的。

核心指标:召回率 (Recall) = 相关文档被检索到的比例。越高越好。
这是 RAG 系统的上限——检索没找到的文档,LLM 永远看不到。
技术架构 (语义相似度 92%)最相关
产品介绍 (语义相似度 45%)
公司介绍 (语义相似度 12%)弱相关

阶段二:增强 (Augmentation) —— Prompt 模板设计

[System]
你是一个基于内部知识库回答问题的助手。
规则:
1. 优先使用下面提供的文档内容回答
2. 如果文档中没有相关信息,请说"知识库中暂无相关信息"
3. 回答时引用文档编号

[Context - 检索到的文档]
[文档1] 标题:办公政策 | 内容:年假15天,带薪病假7天...
[文档2] 标题:公司介绍 | 内容:AI学习科技有限公司成立于2024年...

[User Question]
我们公司年假多少天?

请基于以上知识库内容回答用户问题。
设计要点: ① 上下文放在问题之前(LLM 对前面内容关注度更高)
② 标注来源编号 → 用户可追溯
③ 明确"找不到就说不知道"→ 降低幻觉

阶段三:生成 (Generation)

LLM 基于增强后的 Prompt,用 temperature=0.2(低温度 = 少幻觉)生成答案。

参数建议:temperature=0.1~0.3(事实问答) max_tokens=500(控制长度)

为什么必须分块?

不分块

第一章 公司介绍 ...
第二章 技术架构 ...
第三章 办公政策 ...
→ 50 页混成一个向量,语义被"平均"

分块后

块1: 公司介绍(约 500 字)
块2: 技术架构(约 500 字)
块3: 办公政策(约 500 字)
→ 每块语义独立,检索精准命中

滑动窗口分块演示

chunk_size=200字, chunk_overlap=50字

块1 (200字)
RAG是一种结合检索和生成的AI技术。它的核心思想是:在LLM生成答案之前,先从外部知识库中检索相关文档,然后将这些文档作为上下文提供给LLM...
块2 (200字,前50字=块1末尾)
← overlap → 先检索相关文档,然后将这些文档作为上下文提供给LLM,从而生成更准确、更有依据的答案。RAG的工作流程分为三个阶段:检索、增强、生成...
块3 (200字,前50字=块2末尾)
← overlap → RAG的工作流程分为三个阶段:检索、增强、生成。为什么需要RAG?LLM存在知识过时、幻觉、私有知识盲区三大问题...
overlap 的作用:防止关键信息恰好被切在边界上。比如"检索、增强、生成"如果被切到两个块里,两个块都无法完整理解。

分块策略对比

策略做法适用场景推荐
固定大小每 500 字硬切通用文档
语义分句按句号/段落自然切规范文本⭐⭐
递归分块先段落→再句子→最后硬切任何文本⭐⭐⭐
滑动窗口固定大小 + overlap结合以上策略⭐⭐⭐

关键参数建议

chunk_size

中文建议 300-800 字

  • 太小 → 语义不完整
  • 太大 → 检索精度下降

chunk_overlap

建议 chunk_size 的 10-20%

  • 太小 → 容易被切断
  • 太大 → 冗余过多

TF-IDF vs Embedding vs 混合检索 —— 直观对比

TF-IDF 关键词检索

"技术栈"

❌ "FastAPI框架..." (字面不同)
✅ "技术栈包括..." (字面匹配)
❌ "PostgreSQL..." (字面不同)

Embedding 语义检索

"技术栈"

✅ "FastAPI框架..." (语义理解)
✅ "技术栈包括..."
✅ "PostgreSQL..." (语义理解)

混合检索 (工业级)

"API-Key配置"

✅ "API-Key配置指南" (BM25命中)
✅ "密钥管理系统" (向量命中)
✅ "认证与授权" (向量命中)
混合检索 = 向量检索 + BM25 关键词检索 → RRF 融合 → 取两者之长

RRF (Reciprocal Rank Fusion) 融合算法

公式: score(d) = Σ 1/(k + rank_i(d)) (k=60)

文档向量排名BM25 排名RRF 分数最终排名
API-Key 配置指南 #3#1 1/(60+3) + 1/(60+1) = 0.0323 #1
密钥管理系统 #1#3 1/(60+1) + 1/(60+3) = 0.0323 #2
认证与授权 #2#5 1/(60+2) + 1/(60+5) = 0.0315 #3
两路排名都靠前 → RRF 总分高 → 最终排名靠前。不需要原始分数,只看排名。

Cross-Encoder 重排序:粗排 + 精排

10000 文档
知识库
粗排 Top-20
向量+BM25
精排 Top-3
Cross-Encoder
返回用户
最终结果
Bi-Encoder (双塔)Cross-Encoder (交叉)
速度
精度一般
预缓存可以(文档向量预计算)不可以(每次重新计算)
适用阶段粗召回精排

多轮对话演示:为什么需要记忆?

没有记忆(单轮)

AI Tutor月费多少钱?
根据[文档2]产品介绍,AI Tutor月费为29元。
那年费呢?
抱歉,知识库中暂无相关信息。
(LLM 不知道"那"指什么)

有记忆(多轮)

AI Tutor月费多少钱?
根据[文档2]产品介绍,AI Tutor月费为29元。
那年费呢?
根据[文档2]产品介绍,AI Tutor年费为299元。如果选择年付,相比月付(29×12=348元)可节省49元。

多轮对话的原理

就是把历史消息都塞进 LLM 的 messages 列表:

messages = [
    {"role": "system", "content": "你是知识库助手..."},           ← System Prompt
    {"role": "user", "content": "AI Tutor月费多少钱?"},          ← 历史第1轮
    {"role": "assistant", "content": "月费29元。"},               ← 历史第1轮
    {"role": "user", "content": [检索结果 + "那年费呢?"]}        ← 当前问题
]
LLM 看到完整对话历史 → 理解"那"指的是 AI Tutor → 正确回答

Token 预算管理

问题:对话越来越长,最终超出 LLM 的 Context Window(如 8K/32K tokens)
解决方案:限制历史轮数 (max_history_turns=10),超出后自动丢弃最早的消息。
保持总 token 数在安全范围内。
# 每轮对话后
max_messages = max_history_turns * 2  # user + assistant = 1轮
if len(history) > max_messages:
    history = history[-max_messages:]  # 只保留最近的

从 Demo 到生产 —— 5 步渐进演进

Step 1

基础版本

TF-IDF 检索
硬编码文档
简单 Prompt
理解 RAG 核心链路

召回率 ~40%
Step 2

向量数据库

ChromaDB
bert-base-chinese
持久化
语义搜索

召回率 ~60%
Step 3

多 PDF

批量导入
自动分块
来源追溯
知识库管理

召回率 ~60%
Step 4

聊天记忆

多轮对话
历史管理
Token 预算
上下文理解

召回率 ~60%
Step 5

召回优化

向量+BM25
RRF融合
Cross-Encoder
工业级召回

召回率 ~85%
Step 1-4 的召回率相同(都是纯向量检索),区别在于文档管理、记忆等维度。
Step 5 通过混合检索 + 重排序实现了召回率的质的飞跃。

每一步的关键技术变化

维度Step 1Step 2Step 3Step 4Step 5
检索方式 TF-IDF 向量 向量 向量 混合+重排
Embedding BERT 768d BERT 768d BERT 768d BERT 768d
向量数据库 无 (sklearn) ChromaDB ChromaDB ChromaDB ChromaDB
分块 整篇文档 整篇文档 递归 500字 递归 500字 递归 500字
多轮对话 10 轮记忆 10 轮记忆
关键词检索 TF-IDF BM25
重排序 Cross-Encoder

性能提升全景

指标Step 1Step 5提升
召回率@5~40%~85%2.1x
对同义词完全不支持良好
多轮对话不支持支持
持久化ChromaDB
文档量~100 篇~10000 篇100x
单次查询延迟<50ms<200ms可接受

完整 RAG 系统架构

离线索引阶段
PDF/网页/数据库
文档分块
RecursiveChunker
Embedding
bert-base-chinese
向量数据库
ChromaDB (HNSW)
═══════════════════════════════════════
在线查询阶段
用户提问
混合检索
向量+BM25+RRF
重排序
Cross-Encoder
Prompt 增强
Context + Question
LLM 生成
DeepSeek/OpenAI
答案

技术选型一览

组件选择理由
文档解析PyMuPDF (fitz)中文 PDF 提取最稳定
分词jieba轻量中文分词
Embeddingbert-base-chinese本地缓存,离线可用,768维
向量数据库ChromaDB轻量,零配置,pip install 即可
LLMDeepSeek Chat中文好,价格低
关键词检索BM25 (rank-bm25)比 TF-IDF 更精准
重排序bge-reranker-base中文 Cross-Encoder 精排

Embedding 模型选型对比

模型维度特点适用阶段
bert-base-chinese 768 通用中文,本地离线 入门
BAAI/bge-large-zh-v1.5 1024 中文 SOTA,召回率 +10-15% 进阶
shibing624/text2vec-base-chinese 768 CoSENT 训练,句向量专用 可选

进阶方向(学完 5 步之后)

更好 Embedding

bge-large-zh-v1.5 替换 bert-base-chinese,召回率再提升 10-15%

Web UI

Streamlit / Gradio 构建交互界面

评估体系

RAGAS 框架量化评估检索和生成质量