目录
一、从一个具体问题说起
假设你刚入职一家公司,同事问你:「年假怎么算?」
一个刚毕业的聪明学生,读了四年书,但没读过你们公司的员工手册。他大概率会答:「一般年假是 5 到 15 天吧,具体看公司规定。」——听起来像那么回事,但对你没用。
换个场景:这个学生手边正好有一本《员工手册》。他先翻到「假期制度」那一章,读到「入职满 1 年年假 10 天,满 5 年 15 天」,然后照着念给你听,还告诉你出自第三章第五条。
大模型就是那个学生。它不缺聪明,它缺的是那本手册。
RAG 做的事,就是给它配一本手册,并且教会它在回答前先去翻。这个词拆开看很直白:
- Retrieval(检索):先从知识库里找出和问题相关的内容
- Augmented(增强):把这些内容作为上下文塞进 Prompt
- Generation(生成):模型基于这份上下文组织回答
RAG = Retrieval Augmented Generation = 检索增强生成。
二、大模型的三个短板
模型本身没问题的场景不需要 RAG。需要用 RAG 的场景,都是被下面三件事逼出来的:
| 短板 | 表现 | RAG 的作用 |
|---|---|---|
| 知识过时 | 训练数据停在某个时间点,之后发生的事一概不知 | 从知识库实时检索最新内容 |
| 幻觉 | 不知道的时候不承认,而是编一个听起来合理的答案 | 给它真实材料,把回答锚定在事实里 |
| 私域知识 | 企业内部的制度、文档、代码,训练时根本没见过 | 接入内部知识库,不必重训模型 |
这三个问题有别的解法:重训模型、继续预训练、微调、或者干脆把文档贴进 Prompt。它们各有代价:
- 重训/继续预训练:成本极高,且知识一更新就要重来一遍,适合注入「能力」而不是「事实」。
- 微调:能教模型一种表达风格或任务模式,但事实类知识记不牢、不好更新,验证成本也高。
- 长上下文硬塞:几十页文档塞进 Prompt 确实可行,但 token 成本和延迟随文档量线性上涨,且模型对超长上下文中段的注意力会衰减(lost in the middle)。
RAG 的定位很清楚:它解决的是「事实从哪来」的问题,而不是「模型能力够不够」的问题。 需要模型学会一种新格式、新语气,那是微调的活儿;需要模型知道昨天刚更新的规章制度,那是 RAG 的活儿。两者不冲突,实践中经常一起用。
三、RAG 的完整链路
一条完整的 RAG 链路分成离线索引和在线问答两条路径。
离线部分只在文档入库时跑一次:
graph LR
A["文档<br/>PDF / Word / Markdown"] --> B["解析<br/>Apache Tika"]
B --> C["切分<br/>Splitter"]
C --> D["向量化<br/>Embedding Model"]
D --> E[("向量库<br/>PostgreSQL + pgvector")]
style E fill:#e6f3ff
在线部分每来一个问题都要跑一遍:
graph LR
Q["用户提问"] --> QE["Query 向量化"]
QE --> R["检索<br/>Top-K"]
R --> RK["Rerank 精排"]
RK --> P["组装 Prompt"]
P --> L["调用 LLM"]
L --> A["带出处的回答"]
style RK fill:#e6ffe6
后面四篇 Demo,本质上都是在这条链路上做加固:
| 环节 | 朴素做法的问题 | 对应的优化 |
|---|---|---|
| 检索 | 纯向量检索对关键词不敏感 | 混合检索(BM25 + 向量 + RRF) |
| 排序 | 粗排结果不够准 | Rerank 精排 |
| 切分 | 按固定长度硬切,语义被拦腰截断 | 结构感知切分 |
| 效果 | 全凭感觉,说不清好坏 | 量化评估 |
四、技术选型
这套 Demo 选的是 Java 技术栈,理由很简单:大多数企业后端就是 Spring Boot,能直接落地的方案比纸面最优的方案更有价值。
| 环节 | 选型 | 为什么 | 类比 |
|---|---|---|---|
| 后端框架 | Spring Boot 3 | Java 生态标准,企业里到处都是 | 房子的地基 |
| AI 框架 | Spring AI | Spring 官方出品,和现有工程无缝集成 | 现成的工具箱 |
| 向量存储 | PostgreSQL + pgvector | 不用额外引入一套数据库,事务和备份都复用 | 在已有仓库里划一格存货架 |
| Embedding | OpenAI text-embedding-3-small | 效果与成本平衡得好 | 把文字翻译成数字的翻译器 |
| LLM | GPT-4o / 通义千问 | 成熟稳定,国内可用后者替代 | 最终开口说话的脑子 |
| 文档解析 | Apache Tika | 一个库吃下 PDF、Word、Excel、HTML | 什么格式都能读的阅读器 |
向量库单独说明一下。专用向量数据库(Milvus、Qdrant、Weaviate)在亿级向量、高并发检索上确实更强,但代价是引入一个新的存储系统:多一套部署、多一套监控、多一套备份,还要处理它和业务库之间的数据一致性问题。在文档量还停留在几万到几十万 chunk 的阶段,pgvector 加个 HNSW 索引完全够用,等真的撞到性能天花板再换也不迟。
五、环境准备
需要的东西不多:
JDK 17+ → 推荐 JDK 21
Maven 3.9+ → 构建工具
PostgreSQL 15+ → 需要 pgvector 插件
IDEA → 推荐 IntelliJ IDEA
OpenAI API Key → 或者用通义千问 API
5.1 起一个带 pgvector 的 PostgreSQL
Docker 是最省事的路子:
# 拉取带 pgvector 的 PostgreSQL 镜像
docker pull pgvector/pgvector:pg16
# 启动容器
docker run -d \
--name rag-postgres \
-e POSTGRES_PASSWORD=postgres \
-e POSTGRES_DB=rag_db \
-p 5432:5432 \
pgvector/pgvector:pg16
# 连接测试
docker exec -it rag-postgres psql -U postgres -d rag_db
# 在 psql 中启用 pgvector 扩展
CREATE EXTENSION IF NOT EXISTS vector;
不想用 Docker 的话,本地编译安装也可以:
# 1. 安装 PostgreSQL 16
# 2. 下载 pgvector: https://github.com/pgvector/pgvector
# 3. 编译安装 pgvector
cd pgvector
make
make install
# 4. 连接数据库,启用扩展
psql -U postgres -d rag_db
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION 只需要执行一次,但它建在哪个库上是有区别的——它是库级别的扩展,换个数据库就得重新建一次。这个坑在第一次跑 Demo 时会以「类型 vector 不存在」的形式出现。
5.2 验证装好了没
-- 连接到 rag_db 数据库后执行
SELECT * FROM pg_extension WHERE extname = 'vector';
-- 测试向量操作
SELECT '[1,2,3]'::vector <=> '[1,2,4]'::vector AS cosine_distance;
-- 应该返回一个小于 1 的数,表示两个向量的距离
第二条语句里的 <=> 是 pgvector 的余弦距离算子。注意它返回的是距离而不是相似度,所以两个向量越像,这个值越小——排序时要用升序,这一点在写 SQL 时很容易搞反。
5.3 拿一个 API Key
OpenAI(效果最好):
- 访问 https://platform.openai.com/
- 注册账号并充值($10 够跑完这个专栏)
- 创建 API Key
通义千问(国内替代):
- 访问 https://dashscope.console.aliyun.com/
- 开通模型服务
- 创建 API Key
换供应商时要注意 Embedding 模型一旦选定就不能中途更换。不同模型的向量空间互不相通,换个模型意味着库里已有的向量全部作废,必须重新入库。这个决定要在灌数据之前做。
六、这个专栏怎么读
六篇的顺序是刻意排的,每一步都在解决上一步暴露出来的问题:
| 篇目 | 内容 | 解决的问题 |
|---|---|---|
| 一 | 概述与环境 | 把地基打好 |
| 二 | 最简 RAG | 先跑通链路,再谈优化 |
| 三 | 混合检索 | 纯向量检索漏关键词 |
| 四 | Rerank 重排序 | 粗排结果不够准 |
| 五 | 评估指标 | 优化到底有没有用,得拿数据说话 |
| 六 | 整合与总结 | 把散落的模块拼成完整项目 |
文档里的代码都是可直接运行的完整实现。建议边读边敲,尤其在 Demo 1 上多花点时间——后面所有的优化,本质都是在同一个链条上换零件,链路本身没吃透的话,后面的对比实验会做不出来。
七、常见误区
❌「有了 RAG 就不会有幻觉了」 RAG 只是把模型的眼睛按在了检索结果上,它降低幻觉,但不消除。检索没召回正确文档时,模型照样会拿着无关材料硬编;上下文里没有答案时,它也未必老实说「不知道」。所以 Demo 5 要单独测 Faithfulness——回答是否忠实于检索到的文档,和检索本身准不准是两件事。
❌「向量检索比关键词检索高级,可以完全替代它」 两者捕捉的是不同信号。向量检索理解语义,但对专有名词、编号、错误码这类精确实体不敏感;BM25 恰恰相反。搜「GPT-4」时向量检索可能给你「大模型」这种语义相近但不是一个东西的结果。这也是 Demo 2 要做混合检索的原因。
❌「chunk 切得越小,检索越准」 切小了,每个 chunk 的语义更聚焦,但上下文也被切碎了,模型拿到半句话没法回答。切大了,噪声变多,向量被稀释得更难匹配。chunk size 没有普适最优解,它取决于你的文档结构和你打算问的问题类型,必须靠评估来调。
❌「RAG 比微调便宜」 只有在「注入事实」这个用途下才成立。RAG 的隐藏成本在工程侧:文档解析、切分策略、增量更新、检索调优、评估体系,每一项都要持续投入。真正便宜的错觉来自——它上线快,但维护不便宜。
八、自检清单
- 说得出 RAG 三个字分别对应哪一步
- 能说清楚 RAG 解决的是「事实从哪来」还是「模型能力够不够」
- 能画出离线索引和在线问答两条链路,并说出各自的步骤
- 知道 pgvector 是库级别的扩展,换库要重建
- 知道
<=>返回的是距离而非相似度,排序要升序 - 知道 Embedding 模型不能中途更换,以及为什么
- 能说出至少两个「不需要 RAG」的场景
附:延伸阅读
相关资料
| 资源 | 说明 |
|---|---|
| pgvector README | 索引类型、距离算子的权威说明 |
| Spring AI Reference | VectorStore、EmbeddingModel 抽象的设计意图 |
| Lost in the Middle | 长上下文里模型对中段信息注意力衰减的实证 |
| Retrieval-Augmented Generation (Lewis et al., 2020) | RAG 这个说法的原始论文 |
| RAGAS | 后面 Demo 5 要用到的评估框架 |
关联笔记
- 系统扩展:从 0 到 1000 万用户 —— 向量库该不该单独部署,本质是同一个存储选型问题
最后更新:2026-09-24