Spring Boot整合三大Java AI框架:从基础对话到RAG智能体实战
最近在尝试将AI大模型能力集成到Java后端项目中,发现市面上的资料要么是Python生态的,要么就是概念讲解居多,真正能跑通、能落地的Java+AI整合方案少之又少。尤其是在Spring Boot项目中,如何优雅地接入OpenAI、DeepSeek等大模型,并实现RAG、智能体(Agent)等高级功能,往往需要自己摸索很久。
本文将以Spring Boot 3.x为基础,整合Spring AI、Spring AI Alibaba以及LangChain4j三大主流Java AI框架,通过一个完整的智能客服Agent案例,手把手带你从零搭建一个可运行、可扩展的Java AI应用。无论你是想为现有系统增加AI对话能力,还是探索RAG知识库问答,这篇文章都能提供一套可直接复用的代码模板和避坑指南。
1. 背景与核心概念:为什么Java开发者需要关注AI大模型?
在AI浪潮下,Python凭借其丰富的库(如LangChain、LlamaIndex)在AI应用开发中占据主导。然而,企业级后端服务大量基于Java(特别是Spring Boot)构建。让Java后端直接调用Python服务会引入额外的复杂度、网络延迟和运维成本。因此,在Java生态中直接集成AI能力,成为了降本增效的关键需求。
核心框架解析:
- Spring AI :Spring官方推出的AI应用框架。它提供了统一的
ChatClient、EmbeddingClient等抽象接口,让开发者能以类似使用JdbcTemplate的方式操作不同的大模型(OpenAI、Azure OpenAI、Ollama等)。其核心理念是“约定优于配置”,通过简单的依赖和配置即可快速接入。 - Spring AI Alibaba :阿里巴巴基于Spring AI规范开发的扩展套件。它最大的价值在于提供了对阿里云灵积模型服务平台(DashScope)上通义千问等模型的直接支持,并且集成了阿里云OSS向量存储等能力,对于国内开发者而言,访问速度更快、合规性更友好。
- LangChain4j :Java版的LangChain。它提供了极其丰富和灵活的AI应用构建模块,如链(Chain)、智能体(Agent)、工具(Tool)、记忆(Memory)和检索器(Retriever)。其设计更偏向于灵活的编程模型,适合构建复杂的AI工作流。
如何选择?
- 追求快速集成、统一接口 :首选Spring AI。
- 主要使用国内模型(通义千问) :选择Spring AI Alibaba。
- 需要构建复杂Agent、自定义工作流 :LangChain4j功能更强大。
- 全都要 :在实际项目中,它们可以共存。例如,用Spring AI做基础的对话,用LangChain4j构建高级Agent。
本文将演示三者如何在一个项目中协同工作。
2. 环境准备与版本说明
在开始编码前,请确保你的开发环境符合以下要求。版本兼容性是成功的第一步。
基础环境:
- 操作系统 :Windows 10/11, macOS, 或 Linux (Ubuntu 20.04+)。本文演示基于macOS/Windows。
- Java :JDK 17 或 21(推荐17,长期支持版)。Spring Boot 3.x必须使用JDK 17+。
- 构建工具 :Apache Maven 3.6+ 或 Gradle 7.x+。本文使用Maven。
- IDE :IntelliJ IDEA(推荐)或 VS Code with Java插件。
核心依赖版本: 这是最容易出错的环节,务必核对。以下版本为2024年中旬的稳定组合。
<!-- 在项目的 pom.xml 中 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.5</version> <!-- 使用稳定的3.2.x版本 -->
<relativePath/>
</parent>
<properties>
<java.version>17</java.version>
<!-- 重要:Spring AI 版本与 Boot 版本有对应关系 -->
<spring-ai.version>0.8.1</spring-ai.version>
</properties>
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring AI OpenAI (接入OpenAI/DeepSeek) -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
<!-- Spring AI Alibaba (接入通义千问) -->
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-ai-spring-boot-starter</artifactId>
<version>2023.0.1.0</version> <!-- 注意此版本号独立 -->
</dependency>
<!-- LangChain4j 核心 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
<version>0.31.0</version>
</dependency>
<!-- LangChain4j OpenAI 适配器 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
<version>0.31.0</version>
</dependency>
<!-- 测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencyManagement>
<!-- 必须:Spring AI 的依赖管理 -->
<dependencyManagement>
<dependencies>
<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>
项目结构预览: 创建完成后,你的项目结构应类似于:
java-ai-demo
├── src/main/java/com/example/ai
│ ├── config // 配置类
│ ├── controller // 控制器
│ ├── service // 业务层
│ │ ├── impl
│ │ └── agent // Agent相关服务
│ ├── tool // Agent工具定义
│ └── JavaAiDemoApplication.java // 启动类
├── src/main/resources
│ ├── application.yml // 主配置文件
│ └── documents // 存放用于RAG的文档
└── pom.xml
3. 核心配置与多模型接入
我们将配置两个模型:一个国际模型(DeepSeek,性价比高)和一个国内模型(通义千问)。 application.yml 是配置的核心。
3.1 配置文件详解
在 src/main/resources/application.yml 中,进行如下配置:
# 应用基础配置
spring:
application:
name: java-ai-demo
# Spring AI - OpenAI 兼容配置 (用于DeepSeek)
# DeepSeek的API与OpenAI兼容,base-url指向其端点
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY:sk-your-deepseek-api-key-here} # 从环境变量读取,优先使用环境变量保证安全
base-url: https://api.deepseek.com # DeepSeek API地址
chat:
options:
model: deepseek-chat # 使用的模型名称
temperature: 0.7 # 创造性,0-2之间,越高越随机
max-tokens: 2000 # 单次回复最大token数
# Spring AI Alibaba - 通义千问配置
# 注意:这里的配置项前缀与Spring AI OpenAI不同
alibaba:
ai:
dashscope:
api-key: ${DASHSCOPE_API_KEY:sk-your-dashscope-api-key-here} # 阿里云灵积API Key
chat:
options:
model: qwen-max # 可选 qwen-turbo, qwen-plus, qwen-max
temperature: 0.8
# LangChain4j 配置(部分通过代码配置)
langchain4j:
open-ai:
api-key: ${OPENAI_API_KEY} # 可以复用同一个key
base-url: ${spring.ai.openai.base-url} # 复用base-url
model-name: ${spring.ai.openai.chat.options.model} # 复用模型名
关键点说明:
- API密钥安全 :
${VARIABLE_NAME:default-value}语法表示优先从系统环境变量读取,找不到则使用默认值。 强烈建议 将OPENAI_API_KEY和DASHSCOPE_API_KEY设置为环境变量,避免密钥硬编码在代码中泄露。 - 模型端点 :DeepSeek完全兼容OpenAI API协议,所以
base-url改为其官方端点即可。其他国产模型如智谱GLM、月之暗面Kimi也有类似兼容方案。 - 配置隔离 :Spring AI Alibaba使用了独立的配置前缀
alibaba.ai.dashscope,与spring.ai.openai并列,避免了冲突。
3.2 配置类与Bean注入
为了让不同框架的客户端在Spring容器中可用,我们需要一个配置类。
// 文件路径:src/main/java/com/example/ai/config/AiConfig.java
package com.example.ai.config;
import org.springframework.ai.alibaba.dashscope.AlibabaDashCopeChatModel;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.ai.openai.api.OpenAiApi;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestClient;
@Configuration
public class AiConfig {
@Value("${spring.ai.openai.api-key}")
private String openaiApiKey;
@Value("${spring.ai.openai.base-url}")
private String openaiBaseUrl;
@Value("${spring.ai.openai.chat.options.model}")
private String openaiModel;
@Value("${alibaba.ai.dashscope.api-key}")
private String dashscopeApiKey;
/**
* 配置 Spring AI 的 OpenAiChatModel (用于DeepSeek)
* 这是一个标准的Spring AI ChatModel Bean
*/
@Bean
@Qualifier("deepSeekChatModel") // 使用限定符区分Bean
public ChatModel deepSeekChatModel() {
OpenAiApi openAiApi = new OpenAiApi(openaiBaseUrl, openaiApiKey, RestClient.builder());
OpenAiChatOptions options = OpenAiChatOptions.builder()
.model(openaiModel)
.temperature(0.7)
.maxTokens(2000)
.build();
return new OpenAiChatModel(openAiApi, options);
}
/**
* 配置 Spring AI Alibaba 的 ChatModel (用于通义千问)
*/
@Bean
@Qualifier("qwenChatModel")
public ChatModel qwenChatModel() {
// AlibabaDashCopeChatModel 实现了 Spring AI 统一的 ChatModel 接口
return new AlibabaDashCopeChatModel(dashscopeApiKey);
}
/**
* 配置 LangChain4j 的 OpenAiChatModel
* 注意:这是LangChain4j自己的类,与Spring AI的无关
*/
@Bean
@Qualifier("langChainOpenAiModel")
public dev.langchain4j.model.openai.OpenAiChatModel langChainOpenAiModel() {
return dev.langchain4j.model.openai.OpenAiChatModel.builder()
.apiKey(openaiApiKey)
.baseUrl(openaiBaseUrl)
.modelName(openaiModel)
.temperature(0.7)
.maxTokens(2000)
.logRequests(true) // 开启请求日志,调试有用
.logResponses(true)
.build();
}
}
为什么需要 @Qualifier ? 当容器中存在多个同类型( ChatModel 或 OpenAiChatModel )的Bean时,Spring无法自动选择注入哪一个。使用 @Qualifier 注解给Bean起个名字,在注入时指定这个名字,就能精确选择。
4. 基础对话功能实战
我们先实现最简单的功能:通过HTTP API与不同模型进行对话。
4.1 创建统一对话服务
首先,创建一个服务层,封装对不同模型的调用。
// 文件路径:src/main/java/com/example/ai/service/ChatService.java
package com.example.ai.service;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.SystemPromptTemplate;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
@Service
public class ChatService {
private final ChatModel deepSeekChatModel;
private final ChatModel qwenChatModel;
// 通过构造器注入,并指定使用哪个Bean
public ChatService(
@Qualifier("deepSeekChatModel") ChatModel deepSeekChatModel,
@Qualifier("qwenChatModel") ChatModel qwenChatModel) {
this.deepSeekChatModel = deepSeekChatModel;
this.qwenChatModel = qwenChatModel;
}
/**
* 使用DeepSeek模型进行简单对话
*/
public String chatWithDeepSeek(String userMessage) {
// 最简单的调用:将用户输入包装成UserMessage
UserMessage message = new UserMessage(userMessage);
// 调用模型的call方法
return deepSeekChatModel.call(message).getResult().getOutput().getContent();
}
/**
* 使用通义千问模型进行简单对话
*/
public String chatWithQwen(String userMessage) {
UserMessage message = new UserMessage(userMessage);
return qwenChatModel.call(message).getResult().getOutput().getContent();
}
/**
* 带系统提示词(System Prompt)的对话 - 以DeepSeek为例
* 系统提示词用于设定AI的角色和行为
*/
public String chatWithSystemPrompt(String userMessage) {
// 1. 定义系统提示词模板
String systemText = """
你是一个专业的Java技术专家,擅长Spring Boot和微服务架构。
你的回答应该简洁、准确,并且包含代码示例。
如果用户的问题与Java无关,请礼貌地拒绝回答。
""";
SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate(systemText);
Message systemMessage = systemPromptTemplate.createMessage();
// 2. 创建用户消息
UserMessage userMsg = new UserMessage(userMessage);
// 3. 构建Prompt(包含系统消息和用户消息)
Prompt prompt = new Prompt(List.of(systemMessage, userMsg));
// 4. 调用模型
return deepSeekChatModel.call(prompt).getResult().getOutput().getContent();
}
/**
* 多轮对话示例(简化版,实际需要持久化历史记录)
*/
public String multiTurnChat(List<Message> conversationHistory, String newUserInput) {
// 将历史记录和新输入合并为一个新的Prompt
conversationHistory.add(new UserMessage(newUserInput));
Prompt prompt = new Prompt(conversationHistory);
return deepSeekChatModel.call(prompt).getResult().getOutput().getContent();
}
}
4.2 创建RESTful控制器
暴露HTTP接口供前端或测试调用。
// 文件路径:src/main/java/com/example/ai/controller/ChatController.java
package com.example.ai.controller;
import com.example.ai.service.ChatService;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/chat")
public class ChatController {
private final ChatService chatService;
// 简易内存存储,用于演示多轮对话。生产环境请用Redis或数据库。
private final Map<String, List<org.springframework.ai.chat.messages.Message>> sessionHistory = new HashMap<>();
public ChatController(ChatService chatService) {
this.chatService = chatService;
}
@PostMapping("/deepseek")
public Map<String, String> chatWithDeepSeek(@RequestBody Map<String, String> request) {
String message = request.get("message");
String response = chatService.chatWithDeepSeek(message);
return Map.of("model", "DeepSeek", "response", response);
}
@PostMapping("/qwen")
public Map<String, String> chatWithQwen(@RequestBody Map<String, String> request) {
String message = request.get("message");
String response = chatService.chatWithQwen(message);
return Map.of("model", "Qwen", "response", response);
}
@PostMapping("/deepseek-with-role")
public Map<String, String> chatWithRole(@RequestBody Map<String, String> request) {
String message = request.get("message");
String response = chatService.chatWithSystemPrompt(message);
return Map.of("model", "DeepSeek (Java Expert)", "response", response);
}
@PostMapping("/multi-turn")
public Map<String, String> multiTurnChat(@RequestBody Map<String, String> request,
@RequestHeader(value = "Session-Id", defaultValue = "default") String sessionId) {
String userInput = request.get("message");
// 获取或创建该会话的历史记录
List<org.springframework.ai.chat.messages.Message> history = sessionHistory.getOrDefault(sessionId, new ArrayList<>());
// 调用服务(这里简化,实际应将AI回复也加入history)
String response = chatService.chatWithDeepSeek(userInput); // 简单调用,未传递历史
// 更新历史(生产环境需要更复杂的状态管理)
// history.add(new UserMessage(userInput));
// history.add(new AssistantMessage(response));
// sessionHistory.put(sessionId, history);
return Map.of("sessionId", sessionId, "response", response);
}
}
4.3 运行与测试
- 启动应用 :运行
JavaAiDemoApplication的main方法。 - 使用工具测试 :使用Postman、curl或任何HTTP客户端测试接口。
- 请求示例 (POST
http://localhost:8080/api/chat/deepseek){ "message": "用Java写一个Hello World程序" } - 预期响应 :
{ "model": "DeepSeek", "response": "以下是Java的Hello World程序...\n```java\npublic class HelloWorld {\n public static void main(String[] args) {\n System.out.println(\"Hello, World!\");\n }\n}\n```" }
- 请求示例 (POST
至此,你已经完成了Spring Boot与两个大模型的基础集成。但这只是开始,真正的威力在于RAG和Agent。
5. 进阶实战:构建RAG智能客服Agent
我们将实现一个更复杂的场景:一个智能客服Agent,它能根据我们提供的内部知识库(如产品手册PDF)回答问题,并在无法回答时,自动调用“人工客服”工具。
这个案例将综合运用:
- LangChain4j :构建Agent工作流,管理工具。
- Spring AI :用于文档嵌入(Embedding)和向量存储(VectorStore)。
- 自定义工具 :让Agent能执行特定操作。
5.1 文档加载与向量化(RAG核心)
首先,我们需要将知识库文档(如TXT、PDF)转换为向量并存储。
// 文件路径:src/main/java/com/example/ai/service/RagService.java
package com.example.ai.service;
import org.springframework.ai.document.Document;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.reader.TextReader;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;
import org.springframework.util.FileCopyUtils;
import jakarta.annotation.PostConstruct;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.List;
@Service
public class RagService {
private final EmbeddingModel embeddingModel; // Spring AI会自动注入
private final ResourceLoader resourceLoader;
private VectorStore vectorStore;
public RagService(EmbeddingModel embeddingModel, ResourceLoader resourceLoader) {
this.embeddingModel = embeddingModel;
this.resourceLoader = resourceLoader;
}
/**
* 项目启动时,初始化向量存储并加载知识库文档
*/
@PostConstruct
public void init() throws IOException {
// 1. 初始化一个内存向量存储(生产环境可用PgVector、Redis等)
this.vectorStore = new SimpleVectorStore(embeddingModel);
// 2. 加载资源文件下的文档
Resource resource = resourceLoader.getResource("classpath:documents/product_manual.txt");
String content = FileCopyUtils.copyToString(
new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8));
// 3. 创建Document对象
Document document = new Document(content);
document.getMetadata().put("source", "product_manual.txt");
// 4. 文本分割(将长文档切分为小块,便于检索)
TokenTextSplitter splitter = new TokenTextSplitter(500, 100, 10, 1000); // 参数:块大小、重叠大小等
List<Document> splitDocuments = splitter.apply(List.of(document));
// 5. 将分割后的文档添加到向量存储(会自动调用EmbeddingModel生成向量)
vectorStore.add(splitDocuments);
System.out.println("知识库文档已加载并向量化,共 " + splitDocuments.size() + " 个片段。");
}
/**
* 相似性检索:根据用户问题,从知识库中找出最相关的文档片段
*/
public List<Document> searchRelevantDocuments(String query) {
// 检索最相关的4个文档片段
return vectorStore.similaritySearch(query, 4);
}
/**
* 构建RAG提示词:将检索到的上下文和用户问题组合
*/
public String buildRagPrompt(String userQuestion) {
List<Document> relevantDocs = searchRelevantDocuments(userQuestion);
if (relevantDocs.isEmpty()) {
return userQuestion; // 没有相关上下文,直接返回原问题
}
StringBuilder contextBuilder = new StringBuilder();
contextBuilder.append("请根据以下上下文信息回答问题。如果上下文信息不足以回答问题,请如实说明。\n\n");
contextBuilder.append("【上下文开始】\n");
for (Document doc : relevantDocs) {
contextBuilder.append(doc.getContent()).append("\n---\n");
}
contextBuilder.append("【上下文结束】\n\n");
contextBuilder.append("问题:").append(userQuestion);
return contextBuilder.toString();
}
}
5.2 定义Agent工具
Agent的强大之处在于可以使用工具。我们定义一个“转接人工客服”的工具。
// 文件路径:src/main/java/com/example/ai/tool/ManualCustomerServiceTool.java
package com.example.ai.tool;
import dev.langchain4j.agent.tool.Tool;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@Component // 注册为Spring Bean
public class ManualCustomerServiceTool {
/**
* 当AI无法回答用户问题时,调用此工具转接人工客服。
* @Tool 注解是LangChain4j的标记,表示这是一个可供Agent使用的工具。
* @param userId 用户ID
* @param question 用户的问题
* @return 转接结果描述
*/
@Tool("当问题超出知识范围或用户明确要求时,将对话转接给人工客服。请提供用户ID和问题摘要。")
public String transferToManualService(String userId, String question) {
// 这里模拟转接逻辑,实际项目中可能是发送消息到工单系统、通知客服人员等。
String ticketId = "TICKET-" + System.currentTimeMillis();
String timestamp = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
// 记录日志或持久化到数据库
System.out.printf("[人工客服转接] 工单ID: %s, 用户: %s, 时间: %s, 问题: %s%n",
ticketId, userId, timestamp, question);
return String.format("已为您创建工单【%s】并转接至人工客服。客服人员将在15分钟内通过系统消息与您联系。", ticketId);
}
}
5.3 构建智能体(Agent)服务
这是最核心的部分,我们将使用LangChain4j的 AiServices 来创建一个集成了工具、记忆和RAG上下文的智能体。
// 文件路径:src/main/java/com/example/ai/service/agent/CustomerServiceAgent.java
package com.example.ai.service.agent;
import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;
import com.example.ai.tool.ManualCustomerServiceTool;
import com.example.ai.service.RagService;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
/**
* 定义智能体的交互接口。
* LangChain4j的AiServices会根据这个接口动态生成实现类。
*/
interface Assistant {
// @SystemMessage 定义系统提示词,设定Agent的角色和行为准则
@SystemMessage("""
你是一个专业的智能客服助手,负责解答关于公司产品的疑问。
你的知识来源于提供的产品手册。请严格根据已知信息回答。
如果用户的问题超出你的知识范围,或者用户明确要求人工服务,请果断调用`transferToManualService`工具。
回答请保持友好、专业、简洁。
""")
// @UserMessage 中的 {{context}} 和 {{question}} 是模板变量,由调用者传入
String chat(@V("context") String context, @V("question") String question);
}
@Service
public class CustomerServiceAgent {
private final Assistant assistant;
private final RagService ragService;
public CustomerServiceAgent(
@Qualifier("langChainOpenAiModel") ChatLanguageModel chatLanguageModel, // 注入LangChain4j的模型
ManualCustomerServiceTool manualTool,
RagService ragService) {
this.ragService = ragService;
// 1. 创建聊天记忆,保留最近10轮对话
ChatMemory chatMemory = MessageWindowChatMemory.withMaxMessages(10);
// 2. 使用AiServices构建智能体
// 它会将接口、模型、工具、记忆绑定在一起
this.assistant = AiServices.builder(Assistant.class)
.chatLanguageModel(chatLanguageModel)
.tools(manualTool) // 注入工具
.chatMemory(chatMemory) // 注入记忆
.build();
}
/**
* 主服务方法:处理用户问题,集成RAG和Agent
*/
public String answerQuestion(String userId, String userQuestion) {
// 步骤1:通过RAG检索相关知识
String ragContext = ragService.buildRagPrompt(userQuestion);
// 步骤2:让Agent基于上下文和工具进行回答
// 这里将RAG构建的完整提示词(包含上下文)作为`context`变量传入
// 将原始用户问题作为`question`变量传入,供Agent在需要时传递给工具
String answer = assistant.chat(ragContext, userQuestion);
return answer;
}
}
5.4 创建Agent控制器
// 文件路径:src/main/java/com/example/ai/controller/AgentController.java
package com.example.ai.controller;
import com.example.ai.service.agent.CustomerServiceAgent;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
@RequestMapping("/api/agent")
public class AgentController {
private final CustomerServiceAgent customerServiceAgent;
public AgentController(CustomerServiceAgent customerServiceAgent) {
this.customerServiceAgent = customerServiceAgent;
}
@PostMapping("/customer-service")
public Map<String, String> handleCustomerQuery(@RequestBody Map<String, String> request) {
String userId = request.getOrDefault("userId", "anonymous");
String question = request.get("question");
String answer = customerServiceAgent.answerQuestion(userId, question);
return Map.of(
"userId", userId,
"question", question,
"answer", answer
);
}
}
5.5 准备知识库文档并测试
- 在
src/main/resources/documents/目录下创建product_manual.txt,内容例如:产品名称:AI助手API 版本:v2.1 主要功能:提供自然语言对话、代码生成、文本总结能力。 计费方式:按调用次数计费,每月前1000次免费。 技术支持:如需人工帮助,请在对话中明确说“转人工客服”。 故障处理:如遇API超时,请检查网络并重试,或联系技术支持。 - 重启应用,观察日志,确认文档加载成功。
- 测试场景1:知识库内问题
- 请求 :
POST /api/agent/customer-service - Body :
{"userId": "user123", "question": "你们的AI助手API怎么收费?"} - 预期 :Agent从知识库检索到“计费方式”片段,并生成回答。
- 请求 :
- 测试场景2:知识库外问题/要求转人工
- 请求Body :
{"userId": "user123", "question": "我想咨询一下企业定制方案,转人工客服。"} - 预期 :Agent识别到“转人工客服”关键词或判断超出知识范围,调用
transferToManualService工具,返回工单创建成功的消息。观察控制台输出的转接日志。
- 请求Body :
6. 常见问题与排查思路
在集成过程中,你可能会遇到以下典型问题:
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
启动报错: No qualifying bean of type 'EmbeddingModel' |
Spring AI 的自动配置未生效或依赖冲突。 | 1. 检查 pom.xml 中 spring-ai-openai-spring-boot-starter 依赖是否正确。 2. 确保 spring-ai-bom 的依赖管理已正确引入。 3. 尝试在启动类添加 @EnableAutoConfiguration 。 |
调用API时报错: 401 Unauthorized |
API密钥错误、过期或未设置。 | 1. 检查 application.yml 中的 api-key ,或对应的环境变量 OPENAI_API_KEY 、 DASHSCOPE_API_KEY 是否已设置且正确。 2. 在代码中打印密钥前几位(勿泄露完整密钥)确认已注入。 3. 前往对应平台检查密钥状态和余额。 |
调用DeepSeek报错: 404 Not Found |
base-url 配置错误或模型名称 model 不对。 |
1. 确认 base-url 为 https://api.deepseek.com 。 2. 确认 model 为 deepseek-chat 或 deepseek-coder 。 3. 查阅DeepSeek官方文档确认最新API地址。 |
| LangChain4j的Agent不调用工具 | 工具方法签名不符合要求,或提示词未引导。 | 1. 确保工具方法使用 @Tool 注解,且描述清晰。 2. 确保工具方法是 public 的。 3. 在 @SystemMessage 中明确指示Agent在何种条件下调用工具。 |
| RAG检索结果不相关 | 文档分割策略不合理或嵌入模型不匹配。 | 1. 调整 TokenTextSplitter 的块大小( chunkSize )和重叠大小( chunkOverlap )。 2. 尝试不同的嵌入模型(如OpenAI的 text-embedding-3-small )。 3. 检查检索时返回的文档数量( similaritySearch 的第二个参数)。 |
内存溢出 ( OutOfMemoryError ) |
加载的文档过大,或向量存储未使用外部数据库。 | 1. 对于大文档,务必进行有效的文本分割。 2. 生产环境务必使用外部向量数据库(如PgVector, Redis, Milvus),避免使用内存存储 SimpleVectorStore 。 |
| 中文回答质量差 | 系统提示词未指定语言,或模型对中文优化不足。 | 1. 在 @SystemMessage 或系统提示词中明确要求“请使用中文回答”。 2. 对于中文场景,可优先考虑 qwen-turbo 、 glm-4 等国内模型。 |
7. 最佳实践与工程建议
将AI能力集成到生产级Java项目,除了功能实现,还需关注以下方面:
-
配置与密钥管理
- 永远不要 将API密钥提交到代码仓库。使用环境变量、配置中心(如Apollo、Nacos)或云厂商的密钥管理服务。
- 为不同环境(开发、测试、生产)配置不同的模型和参数,生产环境建议使用更稳定的模型版本。
-
性能与稳定性
- 设置超时与重试 :在
RestClient或HTTP客户端配置合理的连接超时、读取超时,并实现重试机制(如使用Spring Retry)。 - 实现熔断降级 :使用Resilience4j或Sentinel为AI服务调用添加熔断器,防止因模型服务不稳定导致整个应用雪崩。
- 异步化处理 :对于耗时的AI生成任务,使用
@Async或消息队列(如RabbitMQ、Kafka)异步处理,避免阻塞HTTP请求线程。
- 设置超时与重试 :在
-
可观测性与监控
- 全链路日志 :记录每次AI调用的请求、响应、耗时和Token使用量。LangChain4j和Spring AI都支持开启日志。
- 指标监控 :通过Micrometer将调用次数、成功率、延迟等指标暴露给Prometheus,并配置Grafana看板。
- 成本监控 :由于AI API按Token计费,必须监控Token消耗,设置预算告警。
-
RAG优化
- 高质量数据预处理 :清洗、去重、格式化你的知识库文档。垃圾输入会导致垃圾输出。
- 多路召回与重排序 :不要只依赖向量相似度。可结合关键词检索(如Elasticsearch),并对召回结果进行重排序,提升准确率。
- 引用溯源 :在回答中注明引用的文档片段来源,增加可信度。
-
Agent设计
- 工具设计要精确 :工具的功能应单一、明确,输入输出定义清晰。避免让Agent去猜测如何调用一个模糊的工具。
- 限制Agent权限 :赋予Agent的工具应是安全的、无副作用的。特别是涉及写数据库、调用外部API、发送消息等操作,必须有严格的权限和审计。
- 人工审核闭环 :对于关键业务(如客服、审核),设计“人工审核”环节。Agent可生成草稿,由人最终确认后发出。
-
版本与依赖管理
- Spring AI和LangChain4j迭代较快,锁定小版本号,升级前在测试环境充分验证。
- 注意Spring Boot主版本与Spring AI版本的兼容性,参考官方文档的版本矩阵。
掌握Spring AI、Spring AI Alibaba和LangChain4j这三大框架,你就能在熟悉的Java和Spring生态中,自如地构建各类AI增强型应用。从简单的对话接口到复杂的RAG智能体,其核心模式都是相通的: 配置模型 -> 处理数据 -> 设计流程 -> 集成工具 。建议你从本文的示例项目出发,尝试替换不同的模型、接入真实的业务数据、设计更有趣的工具,逐步搭建起符合自己业务需求的AI应用。
更多推荐



所有评论(0)