RAG 实战(一):RAG 是什么,以及环境怎么搭

目录


一、从一个具体问题说起

假设你刚入职一家公司,同事问你:「年假怎么算?」

一个刚毕业的聪明学生,读了四年书,但没读过你们公司的员工手册。他大概率会答:「一般年假是 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 3Java 生态标准,企业里到处都是房子的地基
AI 框架Spring AISpring 官方出品,和现有工程无缝集成现成的工具箱
向量存储PostgreSQL + pgvector不用额外引入一套数据库,事务和备份都复用在已有仓库里划一格存货架
EmbeddingOpenAI text-embedding-3-small效果与成本平衡得好把文字翻译成数字的翻译器
LLMGPT-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(效果最好):

  1. 访问 https://platform.openai.com/
  2. 注册账号并充值($10 够跑完这个专栏)
  3. 创建 API Key

通义千问(国内替代):

  1. 访问 https://dashscope.console.aliyun.com/
  2. 开通模型服务
  3. 创建 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 ReferenceVectorStore、EmbeddingModel 抽象的设计意图
Lost in the Middle长上下文里模型对中段信息注意力衰减的实证
Retrieval-Augmented Generation (Lewis et al., 2020)RAG 这个说法的原始论文
RAGAS后面 Demo 5 要用到的评估框架

关联笔记


最后更新:2026-09-24