Spring AI 实战:从零构建 RAG 检索增强生成应用
本文介绍了基于Spring AI的RAG(检索增强生成)技术实现企业知识库问答系统的完整方案。文章首先解释了RAG技术的核心原理及其相比纯LLM的优势,包括支持私有数据、知识实时更新和回答可溯源等特点。随后详细解析了Spring AI RAG的核心组件:文档处理(Document)、文档读取器(DocumentReader)、文本分割器(TextSplitter)、向量化模型(EmbeddingM
前言
随着大语言模型(LLM)的普及,越来越多的开发者希望将 AI 能力集成到自己的 Java 应用中。然而,LLM 存在一个天然的局限性——知识截止日期,它无法感知你的私有数据、最新文档或企业内部知识库。
RAG(Retrieval-Augmented Generation,检索增强生成) 正是解决这一问题的核心技术。而 Spring AI 作为 Spring 生态对 AI 能力的官方集成框架,提供了一套优雅、开箱即用的 RAG 工具链。
本文将带你从原理到实战,完整走通一个基于 Spring AI 的 RAG 应用。
一、什么是 RAG?
RAG 的核心思路很简单:
用户提问 → 从知识库检索相关文档 → 将文档 + 问题一起喂给 LLM → 得到有依据的回答
相比直接问 LLM,RAG 的优势在于:
| 对比项 | 纯 LLM | RAG |
|---|---|---|
| 私有数据 | ❌ 不支持 | ✅ 支持 |
| 知识时效性 | ❌ 有截止日期 | ✅ 实时更新 |
| 回答可溯源 | ❌ 难以验证 | ✅ 可追溯来源 |
| 幻觉风险 | ⚠️ 较高 | ✅ 显著降低 |
二、Spring AI RAG 核心组件
Spring AI 将 RAG 流程抽象为以下几个核心组件:
2.1 Document(文档)
Document doc = new Document("Spring AI 是 Spring 官方推出的 AI 集成框架...",
Map.of("source", "官方文档", "page", "1"));
Document 是知识库的基本单元,包含文本内容和元数据(metadata)。
2.2 DocumentReader(文档读取器)
Spring AI 内置多种读取器:
// 读取 PDF
DocumentReader pdfReader = new PagePdfDocumentReader("classpath:knowledge.pdf");
// 读取文本文件
DocumentReader txtReader = new TextReader("classpath:docs.txt");
// 读取 Tika 支持的格式(Word、Excel 等)
DocumentReader tikaReader = new TikaDocumentReader(resource);
List<Document> documents = pdfReader.get();
2.3 TextSplitter(文本分割器)
LLM 有 Token 限制,长文档需要切分:
TextSplitter splitter = new TokenTextSplitter(
800, // chunk size(每块 token 数)
400, // overlap(重叠 token 数,保证上下文连贯)
5, // min chunk size
10000, // max chunk size
true // keep separator
);
List<Document> chunks = splitter.apply(documents);
2.4 EmbeddingModel(向量化模型)
将文本转换为向量,是语义检索的基础:
@Autowired
EmbeddingModel embeddingModel;
float[] vector = embeddingModel.embed("Spring AI 是什么?");
Spring AI 支持 OpenAI、Azure OpenAI、Ollama、DashScope 等多种 Embedding 模型。
2.5 VectorStore(向量数据库)
存储和检索向量化后的文档:
@Autowired
VectorStore vectorStore;
// 写入文档
vectorStore.add(chunks);
// 相似度检索
List<Document> results = vectorStore.similaritySearch(
SearchRequest.query("如何配置 Spring AI?")
.withTopK(5)
.withSimilarityThreshold(0.7)
);
Spring AI 支持的向量数据库包括:
- Chroma(本地开发首选)
- Milvus
- Pinecone
- Redis
- PgVector(PostgreSQL 扩展)
- Weaviate
- 等 20+ 种
三、完整实战:构建企业知识库问答系统
3.1 项目依赖
<dependencies>
<!-- Spring AI OpenAI Starter -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
<!-- Chroma 向量数据库 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-chroma-store-spring-boot-starter</artifactId>
</dependency>
<!-- PDF 读取 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pdf-document-reader</artifactId>
</dependency>
</dependencies>
<!-- Spring AI BOM -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
3.2 配置文件
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
chat:
options:
model: gpt-4o
embedding:
options:
model: text-embedding-3-small
vectorstore:
chroma:
host: localhost
port: 8000
collection-name: knowledge-base
3.3 文档摄入服务(Ingestion)
@Service
public class DocumentIngestionService {
@Autowired
private VectorStore vectorStore;
/**
* 将 PDF 文档摄入向量数据库
*/
public void ingestPdf(Resource pdfResource) {
// 1. 读取 PDF
DocumentReader reader = new PagePdfDocumentReader(pdfResource,
PdfDocumentReaderConfig.builder()
.withPageTopMargin(0)
.withPageExtractedTextFormatter(
ExtractedTextFormatter.builder()
.withNumberOfTopTextLinesToDelete(0)
.build())
.withPagesPerDocument(1)
.build());
List<Document> documents = reader.get();
// 2. 切分文档
TextSplitter splitter = new TokenTextSplitter();
List<Document> chunks = splitter.apply(documents);
// 3. 写入向量数据库(自动完成 Embedding)
vectorStore.add(chunks);
log.info("成功摄入 {} 个文档块", chunks.size());
}
}
3.4 RAG 问答服务(核心)
@Service
public class RagChatService {
@Autowired
private ChatClient.Builder chatClientBuilder;
@Autowired
private VectorStore vectorStore;
private static final String SYSTEM_PROMPT = """
你是一个专业的企业知识库助手。
请严格基于以下检索到的上下文内容回答用户问题。
如果上下文中没有相关信息,请明确告知用户,不要编造答案。
上下文:
{context}
""";
public String chat(String userQuestion) {
// 1. 从向量数据库检索相关文档
List<Document> relevantDocs = vectorStore.similaritySearch(
SearchRequest.query(userQuestion)
.withTopK(4)
.withSimilarityThreshold(0.65)
);
// 2. 拼接上下文
String context = relevantDocs.stream()
.map(Document::getContent)
.collect(Collectors.joining("\n\n---\n\n"));
// 3. 构建 Prompt 并调用 LLM
ChatClient chatClient = chatClientBuilder.build();
return chatClient.prompt()
.system(s -> s.text(SYSTEM_PROMPT).param("context", context))
.user(userQuestion)
.call()
.content();
}
}
3.5 使用 Spring AI 内置 RAG 流水线(推荐)
Spring AI 1.0 提供了更优雅的 QuestionAnswerAdvisor,一行代码搞定 RAG:
@Service
public class RagChatServiceV2 {
private final ChatClient chatClient;
public RagChatServiceV2(ChatClient.Builder builder, VectorStore vectorStore) {
this.chatClient = builder
.defaultAdvisors(
new QuestionAnswerAdvisor(
vectorStore,
SearchRequest.defaults()
.withTopK(5)
.withSimilarityThreshold(0.7)
)
)
.build();
}
public String chat(String question) {
return chatClient.prompt()
.user(question)
.call()
.content();
}
}
QuestionAnswerAdvisor 内部自动完成:检索 → 注入上下文 → 调用 LLM 的完整流程。
3.6 REST 接口
@RestController
@RequestMapping("/api/rag")
public class RagController {
@Autowired
private RagChatServiceV2 ragChatService;
@Autowired
private DocumentIngestionService ingestionService;
@PostMapping("/chat")
public ResponseEntity<String> chat(@RequestBody Map<String, String> body) {
String answer = ragChatService.chat(body.get("question"));
return ResponseEntity.ok(answer);
}
@PostMapping("/ingest")
public ResponseEntity<String> ingest(@RequestParam("file") MultipartFile file)
throws IOException {
ingestionService.ingestPdf(file.getResource());
return ResponseEntity.ok("文档摄入成功");
}
}
四、RAG 效果优化技巧
4.1 Chunk 策略优化
// 按句子分割,保留语义完整性
TokenTextSplitter splitter = new TokenTextSplitter(
512, // 适中的 chunk size
128, // 足够的 overlap
...
);
经验值:
- Chunk Size:256~1024 tokens(根据文档类型调整)
- Overlap:Chunk Size 的 10%~20%
- 技术文档建议更小的 chunk,叙述性文档可以更大
4.2 混合检索(Hybrid Search)
纯向量检索有时会漏掉关键词精确匹配的结果,结合 BM25 关键词检索效果更好:
// PgVector 支持混合检索
SearchRequest request = SearchRequest.query(question)
.withTopK(10)
.withFilterExpression("metadata.category == 'technical'"); // 元数据过滤
4.3 重排序(Reranking)
检索到的文档按相关性重新排序,提升 Top-K 质量:
// 使用 Cohere Rerank 或本地 Cross-Encoder 模型
List<Document> reranked = rerankingService.rerank(question, relevantDocs);
4.4 查询改写(Query Rewriting)
用户的原始问题可能不够清晰,先让 LLM 改写再检索:
String rewrittenQuery = chatClient.prompt()
.system("将用户问题改写为更适合语义检索的形式,保留核心意图")
.user(originalQuestion)
.call()
.content();
List<Document> docs = vectorStore.similaritySearch(
SearchRequest.query(rewrittenQuery).withTopK(5)
);
五、完整架构图
┌─────────────────────────────────────────────────────────┐
│ RAG 应用架构 │
├─────────────────────────────────────────────────────────┤
│ │
│ 【摄入阶段 Ingestion】 │
│ 原始文档 → DocumentReader → TextSplitter │
│ → EmbeddingModel → VectorStore │
│ │
│ 【查询阶段 Query】 │
│ 用户问题 → EmbeddingModel → VectorStore 相似检索 │
│ → 相关文档 + 原始问题 → LLM → 最终回答 │
│ │
└─────────────────────────────────────────────────────────┘
六、总结
Spring AI 的 RAG 实现非常完整,核心优势在于:
- ✅ 与 Spring 生态无缝集成,零学习成本
- ✅ 抽象层设计优雅,切换 LLM / 向量库只需改配置
- ✅
QuestionAnswerAdvisor开箱即用,几行代码完成 RAG - ✅ 支持 20+ 向量数据库,生产环境选择灵活
RAG 是当前 AI 应用落地最成熟的方案之一,结合 Spring AI 的工程化能力,可以快速构建出企业级的知识库问答、智能客服、文档助手等应用。
更多推荐
所有评论(0)