最近在尝试将大模型能力集成到 Spring Boot 项目中时,发现虽然 OpenAI 的 API 很强大,但成本、网络和合规性常常成为拦路虎。与此同时,国产大模型如 DeepSeek 的崛起,以其出色的性能和极具竞争力的价格,为开发者提供了新的选择。如何快速、优雅地在 Spring 生态中集成这些 AI 能力,并构建起检索增强生成(RAG)等高级应用,是很多后端开发者面临的共同挑战。
本文将围绕SpringAI这一官方项目,手把手带你从零搭建一个完整的大模型应用开发环境。我们将以DeepSeek作为核心模型,贯穿ChatModel(对话)、Embedding(向量化)、RAG(知识库问答)三大核心场景,提供从环境配置、代码编写到生产级最佳实践的完整闭环教程。无论你是想为现有系统添加智能对话功能,还是希望构建一个基于私有知识库的问答助手,这篇文章都能为你提供可直接复用的解决方案。
1. SpringAI 与 DeepSeek:强强联合的技术栈
在开始动手之前,我们有必要理清几个核心概念,理解为什么选择这个技术组合。
1.1 什么是 SpringAI?
SpringAI 是 Spring 官方推出的一个项目,旨在为 Spring 应用程序集成人工智能功能提供一套简洁、一致的抽象 API。它的核心价值在于:
- 抽象与统一:它定义了一套标准的接口(如
ChatClient、EmbeddingClient),让开发者无需关心底层具体是 OpenAI、Azure OpenAI、DeepSeek 还是其他任何兼容 OpenAI API 的模型服务。只需更换配置,就能切换模型提供商。 - Spring 生态无缝集成:与 Spring Boot 的自动配置、依赖注入、外部化配置(
application.yml)完美融合,开发体验非常“Spring”。 - 功能全面:不仅支持基础的聊天补全(Chat Completion),还支持文本嵌入(Embedding)、图像生成、语音转录等,并内置了对 RAG、函数调用等高级模式的支持。
- 活跃的社区与迭代:作为 Spring 官方项目,其版本迭代迅速,能及时跟进 AI 领域的最新进展。
简单说,SpringAI 就像 JDBC 之于数据库,它为我们操作各种大模型提供了一个统一、便捷的编程界面。
1.2 为什么选择 DeepSeek?
在众多大模型中,DeepSeek 是一个特别值得关注的选项,尤其对于国内开发者:
- 卓越的性能:DeepSeek 的最新模型(如 V3、V4 Flash)在多项中英文基准测试中表现优异,推理、代码、数学能力突出,完全具备生产级应用的能力。
- 极致的成本优势:其 API 定价远低于国际主流厂商,例如,DeepSeek-V3 的输入价格仅为 GPT-4 Turbo 的约 1/30,这使得大规模应用和频繁调用成为可能。
- 对国内开发者友好:无需处理复杂的网络问题,访问稳定,响应速度快。
- 兼容 OpenAI API:DeepSeek 提供了与 OpenAI 完全兼容的 API 接口,这意味着所有基于 OpenAI SDK 或 SpringAI(底层也是 OpenAI 协议)开发的代码,几乎可以无缝迁移到 DeepSeek。
1.3 核心概念:ChatModel, Embedding 与 RAG
- ChatModel:指能够进行多轮对话、理解上下文的大型语言模型。在 SpringAI 中,通过
ChatClient与之交互,完成问答、创作、分析等任务。 - Embedding:指将文本、图像等数据转化为高维向量(一组数字)的技术。这个向量包含了数据的语义信息。语义相近的文本,其向量在空间中的距离也更近。这是实现语义搜索、文本分类、RAG 的基石。
- RAG (Retrieval-Augmented Generation):检索增强生成。这是一种解决大模型“幻觉”(编造信息)和知识滞后问题的架构。其核心流程是:
- 索引:将私有知识库(如 PDF、Word、公司文档)通过 Embedding 模型转化为向量,并存入向量数据库(如 Pinecone、Chroma、Milvus)。
- 检索:当用户提问时,将问题也转化为向量,并在向量数据库中搜索与之最相关的知识片段。
- 增强:将检索到的相关片段作为上下文,与用户问题一起提交给 ChatModel。
- 生成:ChatModel 基于提供的上下文生成更准确、可靠的答案。
接下来,我们将进入实战环节,一步步搭建起这个技术栈。
2. 环境准备与项目初始化
2.1 基础环境要求
- JDK: 17 或更高版本(推荐 17、21 LTS)。
- 构建工具: Maven 3.6+ 或 Gradle 7.x+。本文使用 Maven 示例。
- IDE: IntelliJ IDEA, VS Code, Eclipse 等均可。
- DeepSeek API Key: 前往 DeepSeek 平台注册账号并创建 API Key。
2.2 创建 Spring Boot 项目
使用 Spring Initializr 或 IDE 内置工具创建项目。
- Project: Maven
- Language: Java
- Spring Boot: 3.2.x (SpringAI 需要 Boot 3.x)
- Dependencies:
Spring Web(用于构建 Web 接口)Lombok(简化代码,可选但推荐)Spring AI(核心依赖)
生成的pom.xml中已经包含了spring-boot-starter-parent。我们需要手动添加 SpringAI 的 BOM(物料清单)和具体模块的依赖。
2.3 配置 Maven 依赖与仓库
SpringAI 的版本号由独立的 BOM 管理。更新你的pom.xml文件:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.2.5</version> <!-- 使用稳定的 3.2.x 版本 --> <relativePath/> </parent> <groupId>com.example</groupId> <artifactId>spring-ai-deepseek-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>spring-ai-deepseek-demo</name> <description>Demo project for Spring AI with DeepSeek</description> <properties> <java.version>17</java.version> <!-- 定义 Spring AI 版本 --> <spring-ai.version>1.0.0-M3</version> <!-- 请检查官网使用最新稳定版 --> </properties> <dependencyManagement> <dependencies> <!-- 引入 Spring AI BOM 统一管理所有 AI 模块版本 --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-bom</artifactId> <version>${spring-ai.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- Spring AI OpenAI 模块 (兼容 DeepSeek API) --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-openai-spring-boot-starter</artifactId> </dependency> <!-- 后续 RAG 需要向量数据库和文件解析,可先引入 --> <!-- <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-tika-document-reader</artifactId> </dependency> --> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>关键点说明:
dependencyManagement中引入spring-ai-bom,这是管理所有 SpringAI 模块版本的最佳实践。- 我们依赖
spring-ai-openai-spring-boot-starter。因为 DeepSeek 兼容 OpenAI API,所以使用这个 starter 即可。 - 向量数据库和文档读取的依赖已被注释,我们会在 RAG 章节引入。
3. 基础配置与 ChatModel 初体验
3.1 配置 DeepSeek API
在src/main/resources/application.yml中配置 DeepSeek:
spring: application: name: spring-ai-deepseek-demo # Spring AI 配置 spring: ai: openai: # DeepSeek 的 API 基础地址 base-url: https://api.deepseek.com # 在 DeepSeek 平台获取的 API Key api-key: ${DEEPSEEK_API_KEY:your-deepseek-api-key-here} # 推荐使用环境变量 # 指定使用的模型,例如 deepseek-chat, deepseek-coder 等,请查阅 DeepSeek 最新文档 chat: options: model: deepseek-chat # 嵌入模型配置(为后续 Embedding 做准备) embedding: options: model: text-embedding-3-small # DeepSeek 可能使用兼容 OpenAI 的嵌入模型名,或自有模型,需确认安全提醒:永远不要将真实的 API Key 硬编码在代码或配置文件中提交到版本控制系统(如 Git)。上述配置中的${DEEPSEEK_API_KEY}表示从环境变量中读取。你可以在启动应用前设置环境变量,或使用 IDE 的运行配置注入,生产环境则使用配置中心或 Secrets 管理工具。
3.2 创建第一个 Chat 对话服务
让我们创建一个简单的 Service 来调用 DeepSeek 进行对话。
// 文件路径:src/main/java/com/example/demo/service/ChatService.java package com.example.demo.service; import lombok.RequiredArgsConstructor; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.stereotype.Service; import reactor.core.publisher.Flux; @Service @RequiredArgsConstructor public class ChatService { private final ChatClient chatClient; /** * 同步调用,获取完整的回复 */ public String chatSync(String message) { return chatClient.prompt() .user(message) .call() .content(); } /** * 流式调用,适用于需要逐字显示的场景(如聊天界面) */ public Flux<String> chatStream(String message) { return chatClient.prompt() .user(new UserMessage(message)) .stream() .map(response -> response.getResult() != null ? response.getResult().getOutput().getContent() : ""); } /** * 带系统指令的对话,可以设定 AI 的角色和行为 */ public String chatWithSystem(String systemInstruction, String userMessage) { return chatClient.prompt() .system(s -> s.text(systemInstruction)) // 设置系统指令 .user(userMessage) .call() .content(); } }3.3 创建 REST 控制器暴露接口
// 文件路径:src/main/java/com/example/demo/controller/ChatController.java package com.example.demo.controller; import com.example.demo.service.ChatService; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; @RestController @RequestMapping("/api/chat") @RequiredArgsConstructor public class ChatController { private final ChatService chatService; @PostMapping("/sync") public String chatSync(@RequestBody ChatRequest request) { return chatService.chatSync(request.getMessage()); } @PostMapping(value = "/stream", produces = "text/event-stream") public Flux<String> chatStream(@RequestBody ChatRequest request) { return chatService.chatStream(request.getMessage()); } @PostMapping("/with-role") public String chatWithRole(@RequestBody RoleChatRequest request) { return chatService.chatWithSystem(request.getSystemInstruction(), request.getUserMessage()); } // 简单的请求体 public record ChatRequest(String message) {} public record RoleChatRequest(String systemInstruction, String userMessage) {} }3.4 运行与测试
- 启动 Spring Boot 应用。
- 使用
curl、Postman 或任何 HTTP 客户端进行测试。
测试同步接口:
curl -X POST http://localhost:8080/api/chat/sync \ -H "Content-Type: application/json" \ -d '{"message": "请用Java写一个Hello World程序"}'测试流式接口(观察逐字输出效果):
curl -X POST http://localhost:8080/api/chat/stream \ -H "Content-Type: application/json" \ -d '{"message": "简要介绍Spring框架的核心特性"}' \ -N至此,你已经成功在 Spring Boot 中集成了 DeepSeek 的 Chat 能力。接下来,我们探索更强大的 Embedding 功能。
4. 深入 Embedding:文本向量化实战
Embedding 是将文本转化为数值向量的过程,是构建语义搜索、智能推荐和 RAG 系统的核心。
4.1 Embedding 客户端配置
在application.yml中,我们已经配置了spring.ai.openai.embedding.options.model。SpringAI 会自动配置一个EmbeddingClientBean。
4.2 创建 Embedding 服务
// 文件路径:src/main/java/com/example/demo/service/EmbeddingService.java package com.example.demo.service; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.document.Document; import org.springframework.ai.embedding.EmbeddingClient; import org.springframework.ai.embedding.EmbeddingResponse; import org.springframework.stereotype.Service; import java.util.List; @Service @Slf4j @RequiredArgsConstructor public class EmbeddingService { private final EmbeddingClient embeddingClient; /** * 为单个文本生成嵌入向量 */ public List<Double> embedText(String text) { EmbeddingResponse response = embeddingClient.embedForResponse(List.of(text)); // 通常返回一个 List 的 List,第一个元素就是第一个文本的向量 return response.getResults().get(0).getOutput(); } /** * 为多个文本批量生成嵌入向量(更高效) */ public List<List<Double>> embedTexts(List<String> texts) { EmbeddingResponse response = embeddingClient.embedForResponse(texts); return response.getResults().stream() .map(embedding -> embedding.getOutput()) .toList(); } /** * 计算两个文本的余弦相似度(0~1,越接近1越相似) */ public double calculateSimilarity(String text1, String text2) { List<Double> vector1 = embedText(text1); List<Double> vector2 = embedText(text2); return cosineSimilarity(vector1, vector2); } /** * 余弦相似度计算工具方法 */ private double cosineSimilarity(List<Double> vectorA, List<Double> vectorB) { if (vectorA.size() != vectorB.size()) { throw new IllegalArgumentException("Vectors must have the same dimension"); } double dotProduct = 0.0; double normA = 0.0; double normB = 0.0; for (int i = 0; i < vectorA.size(); i++) { dotProduct += vectorA.get(i) * vectorB.get(i); normA += Math.pow(vectorA.get(i), 2); normB += Math.pow(vectorB.get(i), 2); } if (normA == 0 || normB == 0) { return 0.0; } return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB)); } /** * 为 Spring AI Document 对象生成嵌入向量(为 RAG 准备) */ public void embedDocument(Document document) { EmbeddingResponse response = embeddingClient.embedForResponse(List.of(document.getContent())); if (!response.getResults().isEmpty()) { document.setEmbedding(response.getResults().get(0).getOutput()); } } }4.3 测试 Embedding 功能
可以创建一个简单的测试 Controller 或单元测试来验证。
// 文件路径:src/test/java/com/example/demo/service/EmbeddingServiceTest.java package com.example.demo.service; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest class EmbeddingServiceTest { @Autowired private EmbeddingService embeddingService; @Test void testEmbedText() { String text = "Spring Framework provides comprehensive infrastructure support for developing Java applications."; List<Double> embedding = embeddingService.embedText(text); assertThat(embedding).isNotEmpty(); assertThat(embedding.size()).isGreaterThan(100); // 向量维度通常很高(如1536) System.out.println("向量维度: " + embedding.size()); System.out.println("前5个值: " + embedding.subList(0, 5)); } @Test void testSimilarity() { String text1 = "机器学习是人工智能的一个分支。"; String text2 = "深度学习是机器学习的一个子领域。"; String text3 = "今天天气真好,适合去公园散步。"; double sim12 = embeddingService.calculateSimilarity(text1, text2); double sim13 = embeddingService.calculateSimilarity(text1, text3); System.out.println("相似文本相似度: " + sim12); // 预期较高,如 0.7+ System.out.println("不相关文本相似度: " + sim13); // 预期较低,如 0.2- assertThat(sim12).isGreaterThan(sim13); } }运行测试,你会看到文本被成功转换为高维向量,并且语义相近的文本其向量相似度更高。这为接下来的 RAG 打下了基础——我们需要用同样的模型将知识库和问题都转化为向量,然后在向量空间中找到最匹配的知识。
5. 构建 RAG 知识库问答系统
这是最激动人心的部分。我们将构建一个完整的 RAG 系统,让模型能够基于我们提供的私有文档回答问题。
5.1 引入向量数据库与文档读取依赖
修改pom.xml,取消注释并添加以下依赖。这里我们使用PgVector(基于 PostgreSQL)作为向量数据库,因为它功能强大且与 SpringAI 集成好。你也可以选择 Chroma、Milvus 等。
<!-- 在 dependencies 部分添加 --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-tika-document-reader</artifactId> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <scope>runtime</scope> </dependency>5.2 配置 PostgreSQL 与 PgVector
- 安装 PostgreSQL:确保本地或远程有一个 PostgreSQL 数据库(版本 >= 14)。
- 安装 PgVector 扩展:在数据库中执行
CREATE EXTENSION IF NOT EXISTS vector;。 - 配置连接:更新
application.yml。
# 追加到 application.yml spring: datasource: url: jdbc:postgresql://localhost:5432/rag_demo # 你的数据库 username: postgres password: yourpassword driver-class-name: org.postgresql.Driver sql: init: mode: always # 启动时初始化表结构(仅用于演示,生产环境慎用) # Spring AI Vector Store 配置 spring: ai: vectorstore: pgvector: # 向量维度,必须与 Embedding 模型输出维度一致!DeepSeek/OpenAI text-embedding-3-small 是 1536 dimensions: 1536 # 向量相似度计算方式,cosine 是常用且效果好的 distance-type: cosine-distance # 初始化时删除并重建表(开发环境方便,生产环境不要用) initialize-schema: true5.3 核心组件:文档加载、向量化与存储
SpringAI 提供了VectorStore抽象和Document对象,极大简化了流程。
// 文件路径:src/main/java/com/example/demo/service/RagService.java package com.example.demo.service; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.chat.client.ChatClient; import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.document.Document; import org.springframework.ai.reader.tika.TikaDocumentReader; import org.springframework.ai.transformer.splitter.TokenTextSplitter; import org.springframework.ai.vectorstore.SearchRequest; import org.springframework.ai.vectorstore.VectorStore; import org.springframework.core.io.Resource; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.List; import java.util.stream.Collectors; @Service @Slf4j @RequiredArgsConstructor public class RagService { private final VectorStore vectorStore; private final ChatClient chatClient; private final EmbeddingService embeddingService; /** * 上传文档(PDF, Word, TXT等)并存入向量数据库 */ public void ingestDocument(MultipartFile file) throws IOException { // 1. 保存临时文件 Path tempFile = Files.createTempFile("upload-", file.getOriginalFilename()); Files.copy(file.getInputStream(), tempFile, StandardCopyOption.REPLACE_EXISTING); // 2. 使用 Tika 读取文档内容(支持多种格式) TikaDocumentReader documentReader = new TikaDocumentReader(tempFile.toUri().toString()); List<Document> documents = documentReader.read(); // 3. 文本分割(防止文档过长,超出模型上下文) TokenTextSplitter textSplitter = new TokenTextSplitter(1000, 200, 10, 1000, true); // 参数:块大小、重叠大小等 List<Document> splitDocuments = textSplitter.apply(documents); // 4. 为每个文档块生成嵌入向量并存储 splitDocuments.forEach(embeddingService::embedDocument); vectorStore.add(splitDocuments); log.info("成功注入文档: {},分割为 {} 个块", file.getOriginalFilename(), splitDocuments.size()); // 清理临时文件 Files.deleteIfExists(tempFile); } /** * 基于知识库进行问答(RAG 核心) */ public String askQuestion(String question) { // 1. 检索:在向量库中搜索与问题最相关的文档块 List<Document> relevantDocs = vectorStore.similaritySearch( SearchRequest.query(question).withTopK(4) // 返回最相关的4个片段 ); if (relevantDocs.isEmpty()) { return "知识库中未找到相关信息。"; } // 2. 构建上下文 String context = relevantDocs.stream() .map(Document::getContent) .collect(Collectors.joining("\n\n---\n\n")); // 3. 构建增强后的提示词(Prompt Engineering) String systemPrompt = """ 你是一个专业的助手,请严格根据以下提供的信息来回答问题。 如果信息不足以回答问题,请如实告知“根据已知信息无法回答该问题”,不要编造信息。 相关信息: %s 用户问题:%s """.formatted(context, question); // 4. 调用模型生成答案 return chatClient.prompt() .system(s -> s.text(systemPrompt)) .user(question) .call() .content(); } /** * 清空向量数据库(用于测试或重置) */ public void clearStore() { // 注意:PgVectorStore 的 deleteAll 实现可能需要检查,这里演示概念 // 实际可能需要执行 SQL `TRUNCATE TABLE vector_store` log.warn("清空向量存储..."); // vectorStore.deleteAll(); // 如果 store 实现了该方法 } }5.4 创建 RAG 控制器
// 文件路径:src/main/java/com/example/demo/controller/RagController.java package com.example.demo.controller; import com.example.demo.service.RagService; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; @RestController @RequestMapping("/api/rag") @RequiredArgsConstructor public class RagController { private final RagService ragService; @PostMapping("/ingest") public ResponseEntity<String> ingestDocument(@RequestParam("file") MultipartFile file) { try { ragService.ingestDocument(file); return ResponseEntity.ok("文档 '" + file.getOriginalFilename() + "' 已成功注入知识库。"); } catch (IOException e) { return ResponseEntity.internalServerError().body("文档处理失败: " + e.getMessage()); } } @PostMapping("/ask") public ResponseEntity<String> askQuestion(@RequestBody QuestionRequest request) { String answer = ragService.askQuestion(request.getQuestion()); return ResponseEntity.ok(answer); } @PostMapping("/clear") public ResponseEntity<String> clearStore() { ragService.clearStore(); return ResponseEntity.ok("知识库已清空。"); } public record QuestionRequest(String question) {} }5.5 运行与测试 RAG 系统
- 确保 PostgreSQL 运行且 PgVector 扩展已安装。
- 启动应用,SpringAI 会自动创建
vector_store表。 - 使用
curl或 Postman 上传一个 PDF 或 TXT 文档(比如一篇技术文章或产品手册)。
上传文档:
curl -X POST http://localhost:8080/api/rag/ingest \ -H "Content-Type: multipart/form-data" \ -F "file=@/path/to/your/document.pdf"进行问答:
curl -X POST http://localhost:8080/api/rag/ask \ -H "Content-Type: application/json" \ -d '{"question": "文档中提到的核心技术是什么?"}'现在,你的模型回答将基于你上传的文档内容,而不是其固有的知识,有效减少了“幻觉”并提升了专业领域回答的准确性。
6. 常见问题与排查思路
在集成 SpringAI 与 DeepSeek 的过程中,你可能会遇到以下问题:
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
启动报错:No qualifying bean of type 'EmbeddingClient' | 1. SpringAI 依赖未正确引入。 2. application.yml中 OpenAI/DeepSeek 配置缺失或错误。 | 1. 检查pom.xml中spring-ai-openai-spring-boot-starter依赖。2. 检查 spring.ai.openai.api-key和base-url配置。 |
| 调用 Chat 或 Embedding API 超时或连接失败 | 1. 网络问题,无法访问 DeepSeek API。 2. API Key 无效或过期。 3. base-url配置错误。 | 1. 使用curl或浏览器测试https://api.deepseek.com连通性。2. 在 DeepSeek 平台验证 API Key。 3. 确认 base-url末尾没有多余的/。 |
Embedding 维度错误,如ERROR: vector must have 1536 dimensions | 向量数据库配置的维度 (dimensions) 与 Embedding 模型实际输出的维度不匹配。 | 1. 确认使用的 Embedding 模型名称。 2. text-embedding-3-small维度是 1536,text-embedding-3-large是 3072。确保spring.ai.vectorstore.pgvector.dimensions与之对应。 |
| RAG 检索结果不相关 | 1. 文本分割策略不合理(块太大或太小)。 2. Embedding 模型不适合该领域语言。 3. 检索返回的文档块数量 ( topK) 不合适。 | 1. 调整TokenTextSplitter的块大小和重叠大小。2. 尝试不同的 Embedding 模型(如果 DeepSeek 提供多个)。 3. 调整 withTopK()参数,尝试 3-10。 |
流式响应 (/stream) 不工作或格式不对 | 1. 客户端未正确处理 Server-Sent Events (SSE)。 2. 控制器 produces 属性未设置或设置错误。 | 1. 确保前端使用 EventSource 或兼容 SSE 的库。 2. 检查控制器 @PostMapping的produces = "text/event-stream"。 |
| 文档解析(Tika)乱码或失败 | 1. 文档格式复杂或损坏。 2. 系统缺少必要的字体或库。 | 1. 尝试先将文档转为纯文本 (.txt) 上传测试。 2. 考虑使用更专业的解析库或云服务处理特定格式。 |
7. 最佳实践与进阶建议
将 SpringAI 和 DeepSeek 用于生产环境,需要考虑更多工程化细节。
7.1 配置管理
- API Key 安全:必须使用环境变量或配置中心(如 Apollo, Nacos)管理,切勿硬编码。
- 多环境配置:为开发、测试、生产环境配置不同的
application-{profile}.yml,可能使用不同的模型或 API 端点。 - 连接池与超时:SpringAI 底层使用 RestTemplate 或 WebClient,建议配置合理的连接超时、读取超时和重试策略。
spring: ai: openai: base-url: ${DEEPSEEK_BASE_URL:https://api.deepseek.com} api-key: ${DEEPSEEK_API_KEY} chat: options: model: ${CHAT_MODEL:deepseek-chat} temperature: 0.7 # 控制创造性,业务回答建议较低如0.1 max-tokens: 2000 # 可配置 HTTP 客户端 client: connect-timeout: 10s read-timeout: 30s7.2 性能与成本优化
- Embedding 缓存:对不变的文本(如知识库文档)进行 Embedding 后,将向量结果缓存起来(如 Redis),避免重复调用产生费用和延迟。
- 异步处理:文档注入(Ingestion)过程耗时较长,应改为异步任务(如使用
@Async或消息队列),避免阻塞 HTTP 请求。 - 批量 Embedding:始终使用
embedForResponse(List<String>)进行批量处理,而非循环调用单条接口。 - 模型选择:根据场景选择模型。简单任务可用更小、更快的模型;复杂推理再用大模型。关注 DeepSeek 官方的最新模型和价格。
7.3 RAG 系统优化
- 分块策略:这是 RAG 效果的关键。除了按 Token 分割,可以尝试按段落、标题进行语义分割,或使用更高级的
SemanticTextSplitter。 - 元数据过滤:在存储 Document 时,可以附加元数据(如来源、章节、日期)。检索时,除了语义相似度,还可以结合元数据进行过滤,提升精度。
- 重排序:初步检索出 Top K 个片段后,可以使用一个更小的、专门用于判断相关性的模型(重排序模型)对结果进行二次排序,选取最相关的几个。
- 多路召回:结合关键词搜索(如 Elasticsearch)和向量搜索,进行混合检索,应对不同查询类型。
- 引用溯源:在返回答案时,同时返回引用片段的来源信息,增加可信度。
7.4 监控与可观测性
- 日志记录:详细记录 Chat 和 Embedding 的请求参数、耗时、Token 使用量。这对于成本分析和调试至关重要。
- 链路追踪:在微服务架构中,集成 Sleuth/Zipkin 等,追踪一次 AI 调用的完整链路。
- 指标监控:通过 Micrometer 暴露 Prometheus 指标,监控 API 调用成功率、延迟、Token 消耗速率等。
7.5 走向 Agentic RAG
基础的 RAG 是被动的问答。更高级的模式是Agentic RAG,让 AI Agent 主动利用 RAG 知识库和其他工具来完成任务。例如:
- 用户提出复杂任务:“基于我们上一季度的销售报告,写一份总结邮件。”
- Agent 规划步骤:a. 从知识库检索销售报告。 b. 分析数据。 c. 撰写邮件草稿。
- Agent 调用 RAG 检索报告,调用 Python 工具分析数据,最后调用 Chat 模型撰写邮件。 SpringAI 正在积极集成对 Agent 框架的支持,这是未来可以深入探索的方向。
通过本文的教程,你已经掌握了使用 SpringAI 集成 DeepSeek 进行 Chat、Embedding 和构建 RAG 系统的全流程。从简单的对话接口到复杂的私有知识库问答,这套组合为 Java 后端开发者打开了高效开发大模型应用的大门。建议你从本文的示例代码出发,逐步将其融入你的实际业务场景,并持续关注 SpringAI 和 DeepSeek 的官方更新,以利用最新的特性和优化。