RAG:让 AI 读你的私有文档再回答
大模型有个大问题:它不知道你公司的产品手册、合同、内部 FAQ。你直接问"我们公司退货政策是啥",它要么瞎编,要么说"我不知道"。RAG(检索增强生成)就是把你的文档喂给 AI,让它基于你自己的资料回答,减少胡说八道。
论RAG 七步流水线
① 文档加载:把 PDF、Word、Markdown 读进来。
② 切分:长文档切成一段一段(chunk),太长塞不进模型上下文,太短丢上下文。
③ 向量化(Embedding):把每段文字转成一串数字(向量),语义相近的文字向量也相近。
④ 存入向量库:把向量 + 原文存进 Redis / PGVector / Milvus。
⑤ 检索:用户提问时,把问题也向量化,去向量库找语义最相近的几段。
⑥ 拼 Prompt:把"找到的文档片段 + 用户问题"拼在一起发给大模型。
⑦ 生成:大模型基于这些片段回答,答得有依据。
Spring AI 里搭 RAG(核心是 VectorStore)
文档加载器与文本切分
RAG 第一步是把文档读进来。Spring AI 提供各种 DocumentReader:PDF、Word、Markdown、HTML、TXT。读完是一整篇,太长塞不进模型,必须切分。TokenTextSplitter 按 token 数切,每段留一点重叠(overlap)防止把一句话从中间切断。
| 加载器 | 读什么 |
|---|---|
| PagePdfDocumentReader | PDF 文件。扫描件 PDF 要先 OCR。 |
| TikaDocumentReader | 万能,Word/PPT/HTML 都能读(底层 Apache Tika)。 |
| MarkdownDocumentReader | Markdown,按标题结构切更合理。 |
| TextReader | 纯文本。 |
切分参数怎么调
向量数据库怎么选
| 向量库 | 适合场景 |
|---|---|
| Redis Stack | 已有 Redis,小数据量,上手快。 |
| PGVector | 已有 PostgreSQL,加个插件就能用,业务数据+向量放一起。 |
| Milvus | 大规模向量检索,专业向量数据库,生产大数据量选它。 |
| Chroma / 内存版 | 学习 demo 用,重启就丢,别上生产。 |
检索器与检索增强对话
RAG 的核心是 VectorStore(向量库)和 QuestionAnswerAdvisor(自动检索拼上下文的顾问)。配好 Advisor 后,你不用手动写"先检索再拼接"——Spring AI 自动帮你干:用户提问 → 向量化 → 查向量库拿最相关几段 → 拼进 Prompt → 发给模型。
手动检索(想自己控制时)
Embedding 模型:入库和检索必须用同一个
把文字变成向量的那个模型叫 Embedding 模型。你把文档入库时用的 Embedding 模型,和用户提问检索时用的,必须是同一个。不同模型产出的向量空间完全不同——你用模型 A 把文档存进去,用模型 B 把问题向量化去搜,语义再相关也搜不出来,因为坐标对不上。这是 RAG 新手最隐蔽的坑。
| 注意点 | 说明 |
|---|---|
| 模型一致 | 入库和检索用同一个 Embedding 模型,别中途换。 |
| 换了模型要重建索引 | 真要换 Embedding 模型,所有文档得重新向量化入库。 |
| 向量维度固定 | 每个模型输出固定维度(比如 1024),跟向量库配置对得上。 |
① 切分太大或太小:太大一段塞太多主题,检索不准;太小丢上下文。要调 chunk size 和 overlap。② Embedding 模型不匹配:入库和检索用的必须是同一个 Embedding 模型,换了模型向量空间对不上,检索全歪。③ 文档质量差:PDF 扫描件 OCR 出来一堆乱码,向量化了也是垃圾。先确保文档能正常读。
重排 Rerank:粗筛之后再精排一遍
向量检索是粗筛:靠语义相似度快速捞出 top-K(比如 20 段)。但相似度高不等于最该回答问题的那段——经常把沾边但没营养的排前面。重排(Rerank)是第二道精筛:用一个更强的跨编码模型(cross-encoder),把"问题 + 每段候选"一对对重新打分,挑出真正最相关的 top-3~5,再拼进 Prompt。
论为什么要两步走
cross-encoder 精排质量高但慢(每段都要和问题一起过一次模型),不可能对全库几十万段跑一遍。所以标准流水线是:向量粗筛 top20(快)→ Rerank 精排 top5(准)→ 拼进 Prompt。这样既快又准。Spring AI 里可用 QuestionAnswerAdvisor 配合重排模型,或接 Rerank Model,具体 API 以当前版本为准。
// 思路:先向量粗筛 20 段,再用 Rerank 模型精排留下最相关的 5 段 List<Document> rough = vectorStore.similaritySearch( SearchRequest.query(q).withTopK(20)); List<Document> refined = rerankModel.rank(q, rough, 5); // 精排留 5 段 // 把 refined 拼进 system prompt,再调模型
精排模型要额外调一次模型(或本地推理),多花 token/算力和延迟。文档少、问答简单时,向量检索 top5 直接用就够;只有粗筛召回不准、答非所问时,才上 Rerank。别一上来就堆最复杂的管线。