欢迎光临
我们一直在努力

SpringAI系列|第5篇:RAG检索增强生成

Spring AI 实战系列|第 5 篇 RAG 检索增强生成(原理剖析 + 管线搭建 + 企业知识库实战)

📊 本文约 12000 字,阅读时间约 40 分钟。

系列说明:本文是《Spring AI 实战系列》第 5 篇,深入讲解 RAG(检索增强生成)的原理、Spring AI RAG 管线搭建、Advisor 高级用法,以及企业知识库实战。 前置知识:已完成第 4 篇,掌握 Embedding 和向量数据库基础。

文章目录

  • Spring AI 实战系列|第 5 篇 RAG 检索增强生成(原理剖析 + 管线搭建 + 企业知识库实战)
    • 前言
    • 一、RAG 核心原理
      • 1.1 传统 AI 对话的问题
      • 1.2 RAG 的解决方案
      • 1.3 RAG 的完整流程
    • 二、手动搭建 RAG 管线
      • 2.1 项目结构
      • 2.2 依赖配置
      • 2.3 配置文件
      • 2.4 文档索引服务
      • 2.5 RAG 查询服务
      • 2.6 Controller 层
      • 2.7 启动时自动加载文档
    • 三、QuestionAnswerAdvisor — 声明式 RAG
      • 3.1 基础用法
      • 3.2 自定义搜索参数
      • 3.3 自定义 Prompt 模板
      • 3.4 手动 RAG vs Advisor 对比
    • 四、自定义 Advisor 与高级 RAG
      • 4.1 Advisor 接口理解
      • 4.2 自定义 RAG Advisor(带摘要压缩)
      • 4.3 多个 Advisor 组合
      • 4.4 检索结果重排序(RRF)
    • 五、企业知识库实战
      • 5.1 系统架构
      • 5.2 文档上传处理
      • 5.3 带对话记忆的智能问答
      • 5.4 Docker Compose 部署
    • 六、RAG 效果评估
      • 6.1 评估维度
      • 6.2 忠实度自动评估
      • 6.3 RAG 的局限性
    • 七、常见问题
    • 参考资源
    • 写在最后

前言

去年有客户反馈,其 AI 客服经常\”胡说八道\”——向用户推荐根本不存在的功能,并编造产品参数。

此即典型的 AI 幻觉问题。大模型的知识来源于训练数据,存在截止日期,且倾向于\”脑补\”。

RAG(Retrieval-Augmented Generation,检索增强生成)正是解决此问题的核心方案。它使 AI 在回答之前,先从企业知识库中检索相关信息,基于事实进行回答。

本文从原理到实战,将 RAG 论述透彻。


一、RAG 核心原理

1.1 传统 AI 对话的问题

用户问:你们产品的退款政策是什么?

AI 回答(基于训练数据\”猜\”的):
\”根据一般电商平台的规则,7 天无理由退款…\”

❌ 问题:这不是你们公司的实际政策!

大模型存在三个固有缺陷:

缺陷
说明
后果
幻觉 编造不存在的信息 给出错误答案
知识截止 训练数据有日期限制 无法获知最新信息
无私有知识 无法访问企业内部文档 无法回答业务问题
不可溯源 无法追溯答案来源 出现问题难以排查

1.2 RAG 的解决方案

用户问:你们产品的退款政策是什么?

Step 1: 检索 → 从知识库找到《退款政策 v2.3.pdf》
\”我司支持 30 天无理由退款,需保持商品完好…\”

Step 2: 增强 → 将检索到的内容连同用户问题一并提交给 AI

Step 3: 生成 → AI 基于检索到的内容回答
\”根据我司退款政策(v2.3),支持 30 天无理由退款…\” ✅

AI 的回答基于企业真实文档,可溯源、不编造。

1.3 RAG 的完整流程

RAG 架构图

RAG 分为两个阶段:

离线阶段(索引):

原始文档 → 读取解析 → 文本清洗 → 分块切分
→ Embedding 向量化 → 存入向量数据库

在线阶段(查询):

用户提问 → 问题 Embedding → 向量相似度搜索
→ 获取 Top-K 相关文档 → 构建 Prompt(检索结果 + 用户问题)
→ LLM 生成回答 → 返回给用户


二、手动搭建 RAG 管线

先以手动方式实现一遍,以理解每个环节的作用。

2.1 项目结构

rag-demo/
├── src/main/java/com/example/rag/
│ ├── RagApplication.java
│ ├── config/RagConfig.java
│ ├── service/
│ │ ├── DocumentIndexService.java # 文档索引(离线)
│ │ └── RagQueryService.java # RAG 查询(在线)
│ └── controller/RagController.java
├── src/main/resources/
│ ├── application.yml
│ └── documents/ # 知识库文档目录
└── pom.xml

2.2 依赖配置

<dependencies>
<!– Spring Boot Web –>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!– Spring AI OpenAI(含 Chat + Embedding) –>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>

<!– PGvector 向量数据库 –>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId>
</dependency>

<!– PDF 文档读取 –>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pdf-document-reader</artifactId>
</dependency>
</dependencies>

2.3 配置文件

spring:
ai:
openai:
api-key: ${

OPENAI_API_KEY}
chat:
options:
model: gpt4o
temperature: 0.3 # RAG 场景建议低温度,减少发散
embedding:
options:
model: textembedding3small
vectorstore:
pgvector:
index-type: hnsw
distance-type: cosine
dimensions: 1536 # 必须与 Embedding 模型维度一致
initialize-schema: true # 自动建表
datasource:
url: jdbc:postgresql://localhost:5432/vectordb
username: postgres
password: password

2.4 文档索引服务

负责将各种格式的文档读入、分块、存入向量库:

@Service
public class DocumentIndexService {

private final VectorStore vectorStore;

public DocumentIndexService(VectorStore vectorStore) {


this.vectorStore = vectorStore;
}

/** 索引 PDF 文档 */
public void indexPdf(Resource pdfResource) {


PagePdfDocumentReader reader = new PagePdfDocumentReader(pdfResource);
List<Document> documents = reader.read();

// 附加元数据
documents.forEach(doc -> doc.getMetadata().put(\”source\”, pdfResource.getFilename()));

vectorStore.add(documents);
System.out.println(\”PDF 索引完成: \” + pdfResource.getFilename() + \”, 共 \” + documents.size() + \” 页\”);
}

/** 索引 Markdown 文档(按标题分块) */
public void indexMarkdown(Resource mdResource) throws IOException {


String content = new String(mdResource.getInputStream().readAllBytes(), StandardCharsets.UTF_8);

// 按二级标题分块
List<String> chunks = Arrays.stream(content.split(\”(?=^##\\\\s)\”, Pattern.MULTILINE))
.filter(s -> !s.trim().isEmpty())
.collect(Collectors.toList());

List<Document> documents = new ArrayList<>();
for (int i = 0; i < chunks.size(); i++) {


documents.add(new Document(chunks.get(i), Map.of(
\”source\”, mdResource.getFilename(),
\”type\”, \”markdown\”,
\”chunkIndex\”, String.valueOf(i)
)));
}

vectorStore.add(documents);
System.out.println(\”Markdown 索引完成: \” + mdResource.getFilename());
}

/** 索引纯文本(按 Token 分块) */
public void indexText(String content, String sourceName) {


TokenTextSplitter splitter = new TokenTextSplitter(500, 100, 50, true);
List<String> chunks = splitter.split(content);

List<Document> documents = chunks.stream()
.map(chunk -> new Document(chunk, Map.of(\”source\”, sourceName, \”type\”, \”text\”)))
.collect(Collectors.toList());

vectorStore.add(documents);
}
}

2.5 RAG 查询服务

核心逻辑:检索 → 组装 Prompt → 生成回答:

@Service
public<

赞(0)
未经允许不得转载:171主机测评 » SpringAI系列|第5篇:RAG检索增强生成
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址