RAG:让 AI 只说有据可查的话

从原理到落地,一篇搞懂检索增强生成

February 12, 2026·10 min read·Yimin
#AI#RAG#LLM#向量数据库#Embedding

LLM 什么都敢说,但你只想让它说有根据的话。RAG 就是给 LLM 加一道「先查资料再开口」的规矩。

🎯 LLM 的三大硬伤

在聊 RAG 之前,先看看纯 LLM 在实际场景中会碰到什么问题。

硬伤一:幻觉——一本正经地胡说八道

你问 GPT:"我们公司的报销流程是什么?"

它会非常流畅地给你一套流程——问题是,全是编的。它从来没见过你公司的制度,但它不会告诉你"我不知道",而是用训练数据里的通用知识拼凑出一个看起来很合理的答案。

这就是 Hallucination(幻觉):模型生成了看似正确但实际无据的内容。在闲聊场景无所谓,但在医疗、法律、金融、企业知识等领域,幻觉是致命的。

硬伤二:过时——训练截止日期之后的世界它不知道

每个 LLM 都有训练数据截止日期。2024 年训练的模型不知道 2025 年发生了什么,更别提你上周更新的产品文档。

模型不会自动"跟上"你的知识更新。你今天发了一篇新的技术文档,GPT 明天并不会自动知道。

硬伤三:不懂你的私有数据

你的内部 Wiki、代码仓库、客户数据、业务规则——这些 LLM 在训练时从未见过。它可以帮你写 Python,但它不知道你公司内部的 OrderService 是怎么实现的。

三条路:怎么让 LLM "懂你的数据"

方案做法成本数据更新适用场景
Prompt 塞入把资料直接塞进 prompt几乎为零实时资料很少(< 几千字)
Fine-tuning用你的数据重新训练模型高(GPU + 工程)每次更新要重新训练需要改变模型风格/能力
RAG检索你的数据,塞进 prompt中(向量库 + Embedding)数据更新后重新索引即可大部分"基于私有数据回答"的场景

Prompt 塞入适合数据量极小的情况;Fine-tuning 适合你想改变模型本身的行为(比如让它说特定风格的话);而 RAG 是最通用、最灵活、最具性价比的方案——数据更新了,不用重新训练模型,只要更新索引就行。


🧠 RAG 是什么?一张图讲清楚

RAG = Retrieval-Augmented Generation = 检索增强生成。

拆开来看:

  • Retrieval(检索):在你的知识库里,找到和用户问题最相关的几段内容。
  • Augmented(增强):把这些内容塞进 prompt,作为模型回答时的"参考资料"。
  • Generation(生成):模型基于这些参考资料生成答案。
┌─────────────────────────────────────────────────────────────────┐
│                      RAG 全流程                                  │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  【离线:建索引】                                                 │
│                                                                  │
│   你的文档 ──→ 分块 ──→ Embedding ──→ 存入向量库                 │
│   (Markdown,     (按标题/段落     (文本变成        (pgvector,     │
│    PDF, HTML)     切成小段)       数字向量)        Qdrant 等)     │
│                                                                  │
│  ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─  │
│                                                                  │
│  【在线:问答】                                                   │
│                                                                  │
│   用户提问 ──→ 问题 Embedding ──→ 向量库检索 Top-K               │
│                                        │                         │
│                                        ▼                         │
│                              拼成 Prompt 上下文                   │
│                              ┌─────────────────┐                │
│                              │ System: 规则     │                │
│                              │ Context: 检索结果│                │
│                              │ User: 用户问题   │                │
│                              └────────┬────────┘                │
│                                       ▼                          │
│                                    LLM 生成                      │
│                                       │                          │
│                                       ▼                          │
│                               答案 + 引用来源                     │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

和搜索引擎有什么区别?

搜索引擎给你一堆链接,你自己点进去读。RAG 帮你读完了,然后用自然语言告诉你答案,还附上来源。

本质上,RAG 就是在 LLM 面前摆了一本参考书,然后说:"只看这本书回答,不准自己编。"


⚙️ RAG 五步流水线

RAG 的核心就是五步。每一步都有不同的技术选型和 trade-off。

Step 1:分块(Chunking)

为什么要分块?

你有一篇 5000 字的文章,用户问的问题可能只和其中一段有关。如果把整篇文章塞进 prompt:

  • 浪费 token,贵
  • 噪声大,模型可能被无关内容干扰
  • 向量化整篇文章,语义会被"稀释",检索不准

所以要把长文档切成一块一块的(chunk),每块是一个语义相对完整的段落,检索时按块来找。

怎么切?

策略做法优点缺点
固定大小每 500 token 切一刀简单粗暴可能切断句子和段落
按段落/换行遇到双换行就切保留段落完整性段落大小不一
按标题遇到 ## 就切语义最完整依赖文档有结构
递归切分先按标题,再按段落,再按句子灵活实现较复杂
滑动窗口固定大小 + 前后重叠 N 个 token不丢上下文chunk 之间有冗余

最佳实践

  • 优先利用文档本身的结构(标题、章节),语义完整性最好
  • 设一个上限(比如 1500 字),超过的 section 二次切分
  • 太短的块(比如只有一句话)可以跳过,信息量不够
  • 每个 chunk 带上元数据(标题、来源、标签、日期),方便后面展示引用来源

Step 2:向量化(Embedding)

分好块之后,要把每块文本变成一个向量(一串数字),这样才能做语义相似度计算。

Embedding 的详细原理可以参考《给 AI 装一个大脑:长期记忆系统解析》中的「记忆存储:向量化的艺术」章节。这里简要回顾核心概念。

Embedding 做了什么事?

把一段文字通过模型压缩成一个固定长度的数字列表(比如 1536 个浮点数)。语义相近的文本,Embedding 出来的向量在空间中距离更近

  • "如何设置 Nginx 反向代理" 和 "Nginx 代理配置教程" → 向量很近
  • "如何设置 Nginx 反向代理" 和 "今天天气真好" → 向量很远

这就是为什么 RAG 能做语义检索而不只是关键词匹配——用户问"怎么配代理",即使你的文档标题是"Nginx 反向代理设置指南",也能匹配上。

模型怎么选?

模型维度特点适用
OpenAI text-embedding-3-small1536性价比高,效果好大部分场景的首选
OpenAI text-embedding-3-large3072更精准,更贵对精度有极高要求
Cohere embed-v31024多语言好多语言混合场景
bge-large-zh-v1.51024开源、中文优化中文为主 + 想自托管
all-MiniLM-L6-v2384开源、轻量、快资源有限、对延迟敏感

注意:一旦选定了 Embedding 模型,后续所有的 chunk 和查询都必须用同一个模型。换模型 = 重新建索引。


Step 3:存储与索引

向量算出来了,要存到能做高效相似度搜索的地方。

选型:专用向量库 vs 数据库扩展

方案代表优点缺点适用规模
PG + pgvectorPostgreSQL 扩展复用已有 PG、运维简单、SQL 生态百万级以上性能下降< 100 万向量
Qdrant专用向量库高性能、丰富过滤、Rust 编写多一个组件要运维百万到亿级
Milvus分布式向量库水平扩展、企业级重、部署复杂亿级以上
Pinecone全托管 SaaS零运维贵、数据在别人手里想省事 + 预算够
Chroma轻量嵌入式零配置、开发方便不适合生产本地开发/原型

怎么选?一个简单决策树:

你已经有 PostgreSQL 了吗?
  ├─ 是 → 数据量 < 100 万条?
  │       ├─ 是 → pgvector(最省事)
  │       └─ 否 → 考虑 Qdrant / Milvus
  └─ 否 → 是否想自托管?
          ├─ 是 → Qdrant
          └─ 否 → Pinecone

向量索引(加速检索)

数据量小(几千到几万条)时,顺序扫描(逐条比较)就够快。数据量大了需要建向量索引:

索引类型原理特点
IVFFlat先聚类,查询时只搜最近的几个聚类需要先有数据才能建、精度可调
HNSW基于图的近邻搜索查询更快、精度更高、占内存更多

一般建议:先不建索引跑起来,等数据量上来或查询变慢了,再加 HNSW


Step 4:检索(Retrieval)

用户问了一个问题,现在要从向量库里找到最相关的 K 个 chunk

基本流程

用户问题: "Temporal 怎么做中断恢复?"
              │
              ▼
      Embedding 模型 → 问题向量 [0.12, -0.34, 0.56, ...]
              │
              ▼
      向量库: 计算和每个 chunk 向量的距离
              │
              ▼
      返回距离最近的 Top-K 个 chunk
      (附带相似度分数,如 0.87, 0.82, 0.79 ...)

相似度怎么算?

算法公式直觉常用于
余弦相似度(Cosine)向量方向越一致,越相似最常用,OpenAI 推荐
L2 距离(欧氏距离)向量越近,越相似pgvector 默认
内积(Dot Product)综合方向和长度归一化后等价于余弦

对于大部分场景,余弦相似度是默认选择,不需要纠结。

三种检索策略

策略做法优点缺点
纯语义检索只用向量相似度理解同义词、自然语言精确关键词可能漏掉
纯关键词检索(BM25)经典全文搜索精确词匹配好不懂同义词、自然语言
混合检索(Hybrid)向量 + BM25,结果合并排序兼得两家之长实现稍复杂

举个例子说明差异:

用户问:"k8s pod 重启了怎么办"

纯语义检索 → 能找到标题为"Kubernetes 容器频繁重启排查"的 chunk ✅
纯关键词  → 搜 "k8s" 和 "pod",但文档里写的是 "Kubernetes",漏了 ❌

用户问:"ERROR: ORA-12541"

纯语义检索 → 不理解错误码,可能找到无关的"数据库连接"chunk ❌
纯关键词  → 精确匹配 "ORA-12541",命中 ✅

所以很多生产系统会做 Hybrid Search:两路都跑,结果合并。


Step 5:生成(Generation)

检索到了 Top-K 个 chunk,最后一步是拼 prompt 让 LLM 回答。

Prompt 的典型结构

┌──────────────────────────────────────────────────┐
│ System Prompt(规则)                              │
│ ┌──────────────────────────────────────────────┐ │
│ │ 你是一个知识助手。                             │ │
│ │ 只根据下面的「参考段落」回答。                   │ │
│ │ 如果参考段落里没有,就说"没有找到相关内容"。     │ │
│ │ 回答末尾列出引用的来源。                        │ │
│ └──────────────────────────────────────────────┘ │
│                                                    │
│ Context(检索结果)                                 │
│ ┌──────────────────────────────────────────────┐ │
│ │ --- 参考段落 1 ---                              │ │
│ │ 文章:《Temporal 工作流指南》                    │ │
│ │ 章节:中断恢复机制                              │ │
│ │ (chunk 原文内容...)                           │ │
│ │                                                │ │
│ │ --- 参考段落 2 ---                              │ │
│ │ 文章:《分布式任务调度》                         │ │
│ │ (chunk 原文内容...)                           │ │
│ └──────────────────────────────────────────────┘ │
│                                                    │
│ User Message(用户问题)                            │
│ ┌──────────────────────────────────────────────┐ │
│ │ Temporal 怎么做中断恢复?                       │ │
│ └──────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────┘

防幻觉的关键在 System Prompt 里:

  • "只根据参考段落回答"——限制模型的知识边界
  • "没有就说没有"——鼓励模型承认无知,而不是编造
  • "列出来源"——强制模型关联答案和具体段落,可追溯

温度(temperature)怎么设?

效果适用
0.0 - 0.3几乎只选最高概率的 token,输出稳定RAG 问答(推荐)
0.5 - 0.7有一定随机性,表达更自然创意写作
0.8 - 1.0输出多样,容易跑偏头脑风暴

RAG 场景要的是准确和稳定,所以温度建议 0.1 ~ 0.3。


🏛️ RAG 能干什么?典型用途

RAG 不只是"问答机器人"。下面是几个典型场景,帮你理解它的适用范围。

场景痛点RAG 怎么解决例子
企业知识库员工查内部文档靠搜索关键词,找不到、读不完索引全部内部文档,用自然语言提问直接得到答案"新员工入职需要准备什么?"
客服系统客服翻 FAQ 手册回答,效率低、质量不一致索引产品文档 + 历史工单,自动生成回答建议"我的订单物流一直不更新怎么办?"
代码问答新人看不懂项目,README 过时索引代码仓库 + 设计文档,问"这个服务怎么部署"GitHub Copilot 的 @workspace 就是这个思路
法律/合规法规文件海量,靠人逐条查索引法规库,自然语言检索相关条款"跨境数据传输需要满足哪些条件?"
医疗辅助医生需要快速查最新诊疗指南索引最新指南和论文,辅助诊断建议"2 型糖尿病的最新用药方案?"
个人知识管理笔记越来越多,找不到以前写的东西索引自己的笔记/博客,自然语言问答"我之前写过关于 Redis 分布式锁的内容在哪?"
产品文档用户在文档站翻了半天找不到想要的在文档站加"问一下"入口,RAG 直接回答Stripe、Vercel 等产品的 AI 文档助手
教育/培训学员对课件内容有疑问,不知道去哪找索引课件,学员直接提问"上节课讲的设计模式里,工厂模式和抽象工厂有什么区别?"

一个核心判断标准:如果你的场景是**"基于一堆已有文档回答问题"**,那大概率适合 RAG。

什么不适合 RAG?

场景为什么不适合更好的方案
需要实时计算"今天股票涨了多少" → 需要调 API,不是查文档Function Calling / Agent
需要多步推理"对比 A 公司和 B 公司过去三年的财报" → 要多次查询 + 计算Agent + Tools
需要改变模型风格"用周杰伦的风格写歌词" → 不是知识问题Fine-tuning
数据量极小只有几段话 → 直接塞 prompt 就行Prompt Engineering

⚠️ RAG 的难点与挑战

RAG 的原理不难,但要做好并不容易。以下是业界公认的几大难点。

难点一:分块质量——切得不好,后面全废

分块是 RAG 的第一步,也是最容易被低估的一步。

  • 切太碎:一块只有一句话,检索到了也没有上下文,模型没法回答
  • 切太大:一块包含多个主题,向量被"稀释",检索不准
  • 切断结构:表格、代码块、列表被拦腰切断,信息残缺
  • 丢失关联:section A 里说"如上所述"指的是 section B,切开后这个关联就丢了

这也是为什么"按文档结构切"比"固定字数切"好很多的原因——利用人类写作时已有的语义边界。

难点二:检索质量——找到的不一定是对的

这是 RAG 效果好坏的关键。

  • 语义鸿沟(Semantic Gap):用户的提问方式和文档的写法不一致。用户说"怎么让服务不挂",文档写的是"高可用架构设计"
  • 长尾查询:冷门问题、专业术语、缩写,向量模型可能没见过
  • 否定意图:"怎么避免死锁" vs "怎么实现死锁"——语义很像,意图相反
  • 多意图:一个问题包含多个子问题,一次检索只能覆盖一部分

难点三:幻觉——模型说"有"但其实"没有"

即使你在 System Prompt 里写了"只根据参考段落回答",模型仍然可能:

  • 混入训练数据里的知识,而不是从你提供的段落里来的
  • 回答正确,但标错了来源
  • 检索结果不相关,但模型"硬答"了

完全消除幻觉目前还是业界难题。降低幻觉的方法更多是工程手段:低温度、约束 prompt、答案校验、人工抽查。

难点四:评估困难——怎么知道 RAG 好不好?

和传统软件不同,RAG 的质量很难用简单的单元测试衡量。

环节要评估什么指标
检索找到的是不是该找的?Recall@K、MRR、nDCG
生成回答是否忠于检索结果?Faithfulness(忠实度)
生成回答是否真正解答了问题?Relevance(相关度)
端到端用户满意吗?人工评分、用户反馈

要做好评估,需要一批标注好的 QA 对(问题 + 期望答案 + 期望命中的段落),然后持续跑评估。大部分项目初期没有这个,只能靠"试一试,感觉对不对"。

难点五:规模与性能

规模典型延迟瓶颈
千级 chunk< 100ms无瓶颈
万级 chunk100-500ms需要向量索引(HNSW)
百万级 chunk500ms-2s需要专用向量库 + 优化
千万级以上要架构设计分片、缓存、异步

对于大部分个人项目和中小企业,千级到万级就够了。百万级以上才需要真正操心性能。

难点六:多跳推理——一次检索不够

有些问题不是一次检索就能回答的:

用户: "对比 Temporal 和 Cadence 的错误处理机制"

需要:
  1. 先检索 Temporal 的错误处理 → 拿到相关段落
  2. 再检索 Cadence 的错误处理 → 拿到相关段落
  3. 综合两组段落,生成对比回答

简单的单轮 RAG 只做一次检索,如果库里没有一个 chunk 同时覆盖两个话题,就答不好。这需要多步检索甚至 Agent 来编排整个流程


🚀 进阶:让 RAG 更好的实践

基础 RAG(分块 → Embedding → 检索 → 生成)已经能跑起来了。以下是业界常用的进阶手段,每个都解决一个特定的痛点。

Query 改写(Query Rewriting)

解决的问题:用户的提问可能很口语、很模糊,直接用它检索效果不好。

做法:在检索之前,先让 LLM 把用户问题"翻译"成更适合检索的形式。

用户原始问题:  "那个东西上次挂了怎么搞的来着"
      ↓ LLM 改写
改写后的查询:  "服务故障排查流程和恢复步骤"
      ↓
用改写后的查询去检索 → 命中率更高

HyDE(Hypothetical Document Embeddings)

解决的问题:问题和文档的表达方式天然不同(一个是疑问句,一个是陈述句),向量相似度不够高。

做法:让 LLM 先假设性地写一段答案,然后用这段答案的向量去检索。因为"假设答案"和文档的表达方式更接近。

用户问题:  "怎么配置 Nginx 反向代理?"
      ↓ LLM 生成假设答案
假设答案:  "Nginx 反向代理的配置需要在 server 块中设置
           proxy_pass 指向后端服务..."
      ↓ 用假设答案的向量去检索
检索结果 → 和真实文档的向量更接近 → 命中率更高

Hybrid Search(混合检索)

解决的问题:纯语义检索漏精确关键词,纯关键词不懂同义词。

做法:同时跑向量检索和 BM25 全文搜索,用 RRF(Reciprocal Rank Fusion) 等算法合并两路结果的排名。

Reranker(重排序)

解决的问题:向量检索是"粗筛",Top-K 里可能混入不太相关的结果。

做法:先用向量检索拿一个较大的候选集(比如 Top-20),然后用一个更精准的 Cross-encoder 模型对每个候选逐一打分,重新排序,取真正最相关的 Top-5。

向量检索 Top-20(粗筛,快但不够准)
      ↓
Reranker 逐一精排(慢但准)
      ↓
最终 Top-5(又快又准)

Cross-encoder 之所以更准,是因为它同时看问题和文档,而不是分别编码后比较距离。代价是慢——所以先粗筛再精排。

上下文压缩(Context Compression)

解决的问题:检索到的 chunk 里只有一小部分和问题相关,其它都是噪声。

做法:检索后、生成前,用 LLM 对每个 chunk 做一轮"压缩":只保留和问题相关的句子,删掉无关内容。这样塞进 prompt 的全是精华。

Self-RAG / CRAG(自反思 RAG)

解决的问题:模型不知道检索结果够不够好,"硬答"导致幻觉。

做法:让模型在回答之前先判断

  1. 这个问题需要检索吗?(有些常识题不需要)
  2. 检索到的段落和问题相关吗?(不相关就重新检索或拒绝回答)
  3. 我的答案忠于检索结果吗?(自我检查)

相当于给 RAG 加了一层"自省"能力。

Agentic RAG(Agent 驱动的 RAG)

解决的问题:复杂问题需要多步检索、多数据源、甚至调用工具。

做法:不再是固定的"检索一次 → 生成"流水线,而是让 Agent 自己决定:

  • 要不要检索?检索哪个知识库?
  • 检索结果够不够?要不要再检索一次?
  • 需要调 API 补充实时数据吗?
  • 多个子问题分别检索,最后合并回答

这已经不是纯 RAG 了,而是 RAG 作为 Agent 的一个工具


📝 总结

RAG 的核心价值

一句话:让 LLM 基于你的数据回答问题,有理有据、可追溯、可更新。

不需要重新训练模型,不需要 GPU 集群,一套向量库 + Embedding + 检索逻辑 + Prompt 工程就能跑起来。

你的场景适合 RAG 吗?

条件适合 RAG不适合 RAG
数据形态文本文档(PDF、Markdown、HTML、Wiki)结构化数据(数据库表)、实时数据流
交互方式自然语言问答精确查询(SQL)、批量分析
数据更新定期或按需更新每秒级实时变化
答案来源需要可追溯、可引用不关心出处
团队规模1 人也能搞起来

从简单开始

不要一上来就追求 Hybrid Search + Reranker + Self-RAG + Agent。

基础 RAG 已经能解决 80% 的问题。 先跑通「分块 → Embedding → 检索 → 生成」,看看效果,再根据实际问题决定要不要加进阶手段。

就像这个博客的"问博客"功能——最基础的 RAG 流水线,pgvector + OpenAI Embedding + 简单的 Top-K 检索,已经足够让你通过自然语言查询自己写过的所有文章了。

先跑起来,再优化。这是工程的正确姿势。