Spring AI从零构建企业级RAG系统,解决大模型幻觉问题
文章详细介绍了如何使用Spring AI框架构建企业级RAG系统,通过文档处理、向量存储、检索匹配和增强生成四大模块,解决大模型"幻觉"问题。系统支持多格式文档处理,实现基于私有知识库的准确回答生成,并提供完整的技术选型、依赖配置、服务实现和异常处理方案,适用于企业客服、内部培训和技术支持等多种场景。
本专题旨在展示 Spring AI 框架与检索增强生成(RAG)技术结合的广泛应用价值。我们会深入探讨其在企业内部知识库问答、智能客服响应、教育领域答疑、技术文档助手、医疗知识查询等多个场景的落地实践 —— 每个场景均提供详细实现实例、核心问题分析及解决方案,帮助您直观理解 RAG 技术如何解决大模型 “幻觉” 问题,以及 Spring AI 如何简化 AI 系统开发流程。让我们一同解锁基于私有知识库的智能生成能力。
在这篇文章中,我们将以 Java Spring AI 框架为核心,示范如何从 0 到 1 构建一套企业级 AI 知识库检索增强生成(RAG)系统,覆盖文档处理、向量存储、检索匹配、增强生成全流程。

一、目标和需求
RAG 系统的核心目标是让大模型基于私有知识库内容生成准确、可控的回答,避免 “无中生有” 的幻觉问题,同时支持知识库的动态更新与高效检索。该系统在企业培训、客户服务、技术支持等领域具有极高实用价值。
1. 系统目标
- 实现 “检索 - 生成” 闭环:准确检索知识库中与用户问题相关的内容,结合大模型生成贴合知识库、无幻觉的回答,支持企业私有数据的安全复用。
- 构建高可用架构:支持大规模文档(GB 级)的导入与解析,应对每秒 100+ 并发查询请求,保证系统响应延迟低于 3 秒。
- 适配多场景需求:支持 PDF、Word、Markdown 等主流文档格式,提供灵活的知识库管理能力(新增、删除、更新文档)。
2. 核心需求
- 文档自动化处理:系统需自动完成文档解析(提取文本)、文本分块(优化检索粒度)、向量嵌入(转化为机器可识别的向量),并存储到向量数据库。
- 友好用户接口:提供两类核心接口 —— 文档上传接口(支持批量上传)、问答查询接口(接收用户问题,返回带引用来源的回答)。
- 检索优化能力:针对低相关性检索结果,支持上下文重排序、关键词过滤;针对短文本问题,自动补充语义扩展,提升检索准确性。
- 异常与日志监控:对文档解析失败、向量数据库连接中断、大模型调用超时等异常场景,提供明确的错误提示与日志记录,便于问题排查。
二、核心技术选型
RAG 系统的实现依赖 “文档处理 - 向量存储 - 检索匹配 - 增强生成” 四大模块,结合 Spring AI 框架的生态优势,技术栈选型如下:
| 技术组件 | 作用说明 |
|---|---|
| Spring AI 0.8.1 | 核心开发框架,简化大模型(如通义千问)、向量存储(如 Milvus)的集成流程,提供统一 API。 |
| Milvus 2.3.0 | 开源向量数据库,用于存储文档分块的向量数据,支持高效的近似最近邻(ANN)检索。 |
| Apache Tika 2.9.0 | 文档解析工具,支持 PDF、Word、Excel 等 1000+ 格式的文本提取,解决多格式文档处理难题。 |
| LangChain4j 0.24.0 | 辅助文本分块与检索链管理,提供递归字符分块(RecursiveCharacterTextSplitter)能力,优化分块粒度。 |
| 通义千问 API(qwen-turbo) | 大模型服务,用于基于检索到的上下文生成自然语言回答,支持国内网络环境,响应速度快。 |
三、添加项目依赖
在 Maven 项目中,通过以下依赖引入 Spring AI、Milvus、Tika 等核心组件(需提前配置 Spring 官方仓库或阿里云镜像):
<!-- Spring Boot 基础依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.2.5</version>
</dependency>
<!-- Spring AI 核心依赖(集成大模型与向量存储) -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-core</artifactId>
<version>0.8.1</version>
</dependency>
<!-- Spring AI 通义千问集成(大模型) -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-qwen</artifactId>
<version>0.8.1</version>
</dependency>
<!-- Spring AI Milvus 集成(向量数据库) -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-milvus-store</artifactId>
<version>0.8.1</version>
</dependency>
<!-- 文档解析:Apache Tika -->
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-parsers-standard-package</artifactId>
<version>2.9.0</version>
</dependency>
<!-- 文本分块:LangChain4j -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-core</artifactId>
<version>0.24.0</version>
</dependency>
<!-- 工具类:Apache Commons IO(文件处理) -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.15.1</version>
</dependency>
同时,在 application.yml 中配置大模型与向量数据库的连接信息(需替换为实际环境参数):
spring:
# 通义千问大模型配置(需申请 API 密钥)
ai:
qwen:
api-key: your-qwen-api-key
base-url: https://dashscope.aliyuncs.com/compatible-mode/v1
# Milvus 向量数据库配置
milvus:
host: 127.0.0.1
port: 19530
database-name: rag_knowledge_base
collection-name: document_chunks
embedding-dimension: 1536 # 通义千问 embedding 维度
四、关键服务实现
RAG 系统的核心逻辑封装在 3 个服务中:DocumentProcessingService(文档处理)、KnowledgeBaseRetrievalService(知识库检索)、RagGenerationService(增强生成)。以下是完整代码实现:
1. 文档处理服务:DocumentProcessingService
负责文档解析、文本分块、向量嵌入与存储,是知识库构建的核心:
import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
import dev.langchain4j.data.document.splitter.DocumentSplitters;
import dev.langchain4j.data.segment.TextSegment;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.parser.AutoDetectParser;
import org.apache.tika.sax.BodyContentHandler;
import org.springframework.ai.embedding.EmbeddingClient;
import org.springframework.ai.milvus.MilvusVectorStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.xml.sax.ContentHandler;
import java.io.File;
import java.io.FileInputStream;
import java.util.List;
@Service
public class DocumentProcessingService {
// 注入 Spring AI 向量存储(Milvus)与嵌入客户端(通义千问)
@Autowired
private MilvusVectorStore milvusVectorStore;
@Autowired
private EmbeddingClient embeddingClient;
/**
* 处理上传的文档:解析 -> 分块 -> 嵌入 -> 存储
* @param documentPath 本地文档路径(如 /upload/enterprise_manual.pdf)
* @return 处理结果(成功/失败 + 分块数量)
*/
public String processDocument(String documentPath) {
try {
// 1. 文档解析:提取文本(支持多格式)
File documentFile = new File(documentPath);
String documentText = parseDocumentText(documentFile);
if (documentText.isEmpty()) {
return "文档解析失败:未提取到文本(可能是空白文档或损坏文件)";
}
// 2. 文本分块:按语义分割(避免跨段落语义断裂)
// 分块大小:512 字符,重叠度:50 字符(保证上下文连续性)
Document document = Document.from(documentText, "企业知识库文档");
List<TextSegment> textSegments = DocumentSplitters
.recursive(512, 50)
.split(document);
// 3. 向量嵌入与存储:将分块转化为向量并写入 Milvus
milvusVectorStore.add(embeddingClient.embed(textSegments));
return String.format("文档处理成功!共生成 %d 个文本分块,已存入知识库", textSegments.size());
} catch (Exception e) {
return "文档处理失败:" + e.getMessage();
}
}
/**
* 解析文档文本(基于 Apache Tika)
*/
private String parseDocumentText(File documentFile) throws Exception {
ContentHandler contentHandler = new BodyContentHandler(-1); // -1 表示不限制文本长度
Metadata metadata = new Metadata();
AutoDetectParser parser = new AutoDetectParser(); // 自动识别文档格式
try (FileInputStream inputStream = new FileInputStream(documentFile)) {
parser.parse(inputStream, contentHandler, metadata);
return contentHandler.toString().trim();
}
}
/**
* 从知识库删除文档(按文档路径匹配)
*/
public String deleteDocument(String documentPath) {
// 实际场景需通过文档元数据(如路径)筛选并删除,此处简化逻辑
milvusVectorStore.delete(documentPath);
return "文档已从知识库删除:" + documentPath;
}
}
2. 知识库检索服务:KnowledgeBaseRetrievalService
负责接收用户问题,生成查询向量,从 Milvus 检索最相关的文本分块:
import org.springframework.ai.embedding.EmbeddingClient;
import org.springframework.ai.milvus.MilvusVectorStore;
import org.springframework.ai.search.SearchResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class KnowledgeBaseRetrievalService {
@Autowired
private MilvusVectorStore milvusVectorStore;
@Autowired
private EmbeddingClient embeddingClient;
/**
* 检索与用户问题最相关的知识库内容
* @param userQuery 用户问题(如“企业员工年假天数是多少?”)
* @param topK 返回Top K个相关分块(默认5)
* @return 相关文本分块列表(带相似度得分)
*/
public List<String> retrieveRelevantContext(String userQuery, int topK) {
// 1. 生成问题的向量表示
var queryEmbedding = embeddingClient.embed(userQuery);
// 2. 从 Milvus 检索 Top K 相关分块(相似度阈值:0.6,过滤低相关结果)
List<SearchResult> searchResults = milvusVectorStore.similaritySearch(
queryEmbedding, topK, 0.6f
);
// 3. 提取并返回相关文本(保留相似度得分,便于后续排序)
return searchResults.stream()
.map(result -> String.format(
"【相似度:%.2f】%s",
result.getScore(),
result.getContent()
))
.collect(Collectors.toList());
}
}
3. 增强生成服务:RagGenerationService
结合检索到的上下文与大模型,生成准确回答,并标注引用来源:
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
public class RagGenerationService {
// 注入 Spring AI 聊天客户端(通义千问)
@Autowired
private ChatClient qwenChatClient;
/**
* 检索增强生成:基于知识库上下文生成回答
* @param userQuery 用户问题
* @param relevantContext 检索到的相关上下文
* @return 带引用来源的回答
*/
public String generateEnhancedAnswer(String userQuery, List<String> relevantContext) {
// 1. 构建 Prompt 模板(明确要求大模型基于上下文回答,避免幻觉)
String promptTemplate = """
请严格基于以下知识库内容回答用户问题,不要添加外部信息:
{relevant_context}
用户问题:{user_query}
回答要求:
1. 先直接回答问题,语言简洁明了;
2. 最后标注“引用来源”,列出使用的上下文片段(保留相似度得分);
3. 若上下文无法回答问题,直接回复“知识库中未找到相关信息”。
""";
// 2. 填充模板参数(上下文拼接为字符串)
String contextStr = String.join("\n\n", relevantContext);
Map<String, Object> params = new HashMap<>();
params.put("relevant_context", contextStr);
params.put("user_query", userQuery);
// 3. 生成 Prompt 并调用大模型
Prompt prompt = new PromptTemplate(promptTemplate, params).create();
ChatResponse response = qwenChatClient.call(prompt);
// 4. 返回生成结果(去除多余空格,格式化引用来源)
return response.getResult().getOutput().trim()
.replace("引用来源:", "\n\n引用来源:");
}
}
五、异常处理与优化
1. 常见异常场景与处理
通过 @ControllerAdvice 实现全局异常处理,覆盖 RAG 系统核心异常:
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
@ControllerAdvice
public class RagGlobalExceptionHandler {
// 文档上传大小超限(如超过 100MB)
@ExceptionHandler(MaxUploadSizeExceededException.class)
public ResponseEntity<String> handleMaxSizeException(MaxUploadSizeExceededException e) {
return new ResponseEntity<>("文档上传失败:文件大小超过限制(最大支持 100MB)", HttpStatus.BAD_REQUEST);
}
// 向量数据库连接异常
@ExceptionHandler(org.milvus.client.exception.ConnectFailedException.class)
public ResponseEntity<String> handleMilvusConnectionException(Exception e) {
return new ResponseEntity<>("知识库连接失败:请检查 Milvus 服务是否正常运行", HttpStatus.SERVICE_UNAVAILABLE);
}
// 大模型 API 调用异常(超时、密钥错误)
@ExceptionHandler(org.springframework.ai.client.AiClientException.class)
public ResponseEntity<String> handleAiClientException(Exception e) {
return new ResponseEntity<>("回答生成失败:" + e.getMessage() + "(请检查 API 密钥或网络)", HttpStatus.BAD_REQUEST);
}
// 检索结果为空
@ExceptionHandler(IllegalStateException.class)
public ResponseEntity<String> handleEmptyRetrievalException(IllegalStateException e) {
if (e.getMessage().contains("no relevant context")) {
return new ResponseEntity<>("知识库中未找到相关信息,请尝试调整问题表述或补充知识库文档", HttpStatus.OK);
}
return new ResponseEntity<>("系统异常:" + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
2. 系统优化策略
-
分块粒度优化
针对技术文档(如 API 手册),将分块大小调整为 1024 字符,保留更多代码片段上下文;针对短文本(如 FAQ),分块大小设为 256 字符,提升检索精度。
-
向量索引优化
在 Milvus 中创建 IVF_FLAT 索引(小规模数据)或 HNSW 索引(大规模数据),将检索速度提升 5-10 倍。
-
缓存机制
对高频查询(如 “企业考勤制度”)的检索结果缓存 1 小时,减少向量数据库与大模型的重复调用,降低延迟。
六、实际运用展示
通过 RagController 整合上述服务,提供 HTTP 接口供客户端调用(支持文档上传与问答查询):
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.util.List;
@RestController
@RequestMapping("/rag")
public class RagController {
@Autowired
private DocumentProcessingService documentProcessingService;
@Autowired
private KnowledgeBaseRetrievalService retrievalService;
@Autowired
private RagGenerationService generationService;
// 文档上传接口(支持批量上传)
@PostMapping("/document/upload")
public String uploadDocument(
@RequestParam("files") MultipartFile[] files,
@RequestParam(value = "uploadPath", defaultValue = "/tmp/rag_upload") String uploadPath
) {
// 创建本地临时目录
File uploadDir = new File(uploadPath);
if (!uploadDir.exists()) uploadDir.mkdirs();
StringBuilder result = new StringBuilder();
for (MultipartFile file : files) {
try {
// 保存文件到本地
String localFilePath = uploadPath + File.separator + file.getOriginalFilename();
file.transferTo(new File(localFilePath));
// 调用服务处理文档
String processResult = documentProcessingService.processDocument(localFilePath);
result.append(file.getOriginalFilename()).append(":").append(processResult).append("\n");
} catch (Exception e) {
result.append(file.getOriginalFilename()).append(":处理失败:").append(e.getMessage()).append("\n");
}
}
return result.toString();
}
// 问答查询接口(支持自定义 Top K 检索数量)
@GetMapping("/query")
public String queryAnswer(
@RequestParam("question") String userQuestion,
@RequestParam(value = "topK", defaultValue = "5") int topK
) {
// 1. 检索相关上下文
List<String> relevantContext = retrievalService.retrieveRelevantContext(userQuestion, topK);
if (relevantContext.isEmpty()) {
throw new IllegalStateException("no relevant context found");
}
// 2. 生成增强回答
return generationService.generateEnhancedAnswer(userQuestion, relevantContext);
}
// 文档删除接口
@DeleteMapping("/document/delete")
public String deleteDocument(@RequestParam("documentPath") String documentPath) {
return documentProcessingService.deleteDocument(documentPath);
}
}
接口调用示例
- 文档上传(Postman 调用):
- 请求地址:
http://localhost:8080/rag/document/upload - 请求方式:POST
- 请求参数:
files(选择本地 PDF/Word 文件)、uploadPath(可选) - 响应示例:
enterprise_manual.pdf:文档处理成功!共生成 42 个文本分块,已存入知识库
- 问答查询(浏览器调用):
- 请求地址:
http://localhost:8080/rag/query?question=企业员工年假天数是多少?&topK=3 - 响应示例:
企业员工年假天数根据工龄确定:1. 工龄满1年不满10年:5天/年;2. 工龄满10年不满20年:10天/年;3. 工龄满20年及以上:15天/年。
引用来源:
【相似度:0.92】企业考勤与休假制度(2024版):年假天数标准:1. 工龄满1年不满10年:5天/年;2. 工龄满10年不满20年:10天/年;3. 工龄满20年及以上:15天/年。年假需提前3天申请,经部门负责人审批后生效。
【相似度:0.75】员工福利手册:年假属于法定带薪假期,未休年假可按日工资的300%折算(包含正常工资),折算截止时间为次年3月31日。
【相似度:0.68】人力资源管理制度:员工入职满1年后方可享受年假,试用期不计入工龄;离职员工未休年假按实际在职天数折算。
七、总结
本文基于 Spring AI 框架,完整实现了一套企业级 RAG 系统,从技术选型、依赖配置到核心服务代码,提供了可直接落地的解决方案。该系统通过 Spring AI 简化了大模型与向量存储的集成,通过 Apache Tika 与 LangChain4j 解决了多格式文档处理与分块优化问题,最终实现了 “文档入库 - 问题检索 - 增强生成” 的全流程自动化。
与传统大模型问答相比,本系统的核心优势在于:基于私有知识库生成回答,避免幻觉、支持动态更新知识库、提供可追溯的引用来源,可直接应用于企业客服、内部培训、技术支持等场景。
如何系统学习掌握AI大模型?
AI大模型作为人工智能领域的重要技术突破,正成为推动各行各业创新和转型的关键力量。抓住AI大模型的风口,掌握AI大模型的知识和技能将变得越来越重要。
学习AI大模型是一个系统的过程,需要从基础开始,逐步深入到更高级的技术。
这里给大家精心整理了一份
全面的AI大模型学习资源,包括:AI大模型全套学习路线图(从入门到实战)、精品AI大模型学习书籍手册、视频教程、实战学习、面试题等,资料免费分享!

1. 成长路线图&学习规划
要学习一门新的技术,作为新手一定要先学习成长路线图,方向不对,努力白费。
这里,我们为新手和想要进一步提升的专业人士准备了一份详细的学习成长路线图和规划。可以说是最科学最系统的学习成长路线。

2. 大模型经典PDF书籍
书籍和学习文档资料是学习大模型过程中必不可少的,我们精选了一系列深入探讨大模型技术的书籍和学习文档,它们由领域内的顶尖专家撰写,内容全面、深入、详尽,为你学习大模型提供坚实的理论基础。(书籍含电子版PDF)

3. 大模型视频教程
对于很多自学或者没有基础的同学来说,书籍这些纯文字类的学习教材会觉得比较晦涩难以理解,因此,我们提供了丰富的大模型视频教程,以动态、形象的方式展示技术概念,帮助你更快、更轻松地掌握核心知识。

4. 大模型行业报告
行业分析主要包括对不同行业的现状、趋势、问题、机会等进行系统地调研和评估,以了解哪些行业更适合引入大模型的技术和应用,以及在哪些方面可以发挥大模型的优势。

5. 大模型项目实战
学以致用 ,当你的理论知识积累到一定程度,就需要通过项目实战,在实际操作中检验和巩固你所学到的知识,同时为你找工作和职业发展打下坚实的基础。

6. 大模型面试题
面试不仅是技术的较量,更需要充分的准备。
在你已经掌握了大模型技术之后,就需要开始准备面试,我们将提供精心整理的大模型面试题库,涵盖当前面试中可能遇到的各种技术问题,让你在面试中游刃有余。

全套的AI大模型学习资源已经整理打包,有需要的小伙伴可以
微信扫描下方CSDN官方认证二维码,免费领取【保证100%免费】

更多推荐


所有评论(0)