前言
Spring AI 是 Spring 官方推出的 Java AI 应用开发框架,核心目标是简化 AI 功能与 Java 应用的集成,遵循"约定优于配置"的 Spring 核心理念,提供统一 API 屏蔽不同 AI 模型厂商的差异,让 Java 开发者像使用 Spring Boot 一样便捷地开发 AI 驱动的应用。
Spring AI Alibaba 并非简单的"阿里云适配版",而是以 Spring AI 为底座,深度集成阿里云灵积模型服务(DashScope),提供 Agent 框架、工作流编排、多智能体协同、可视化调试等企业级能力的完整解决方案,同时完美适配通义千问、DeepSeek 等阿里云主流大模型,是 Java 开发者落地阿里云 AI 应用的首选框架。
本教程面向有 Spring Boot 基础的 Java 开发者,从环境搭建、核心概念、基础实战,到进阶特性、企业级优化,全程基于阿里云生态,手把手教你掌握 Spring AI Alibaba 的使用,规避常见坑点,实现从入门到企业级落地的全流程突破。
第一章:环境准备(精准适配阿里云)
1.1 核心依赖说明
Spring AI Alibaba 目前稳定版本为 1.1.2.0,需搭配 Spring Boot 3.4.x 及以上版本(推荐 3.4.5)、JDK 17 及以上,核心依赖分为 Spring AI 基础依赖和阿里云专属依赖,通过 Maven 或 Gradle 引入,优先使用 BOM 管理版本,避免版本冲突。
1.2 环境搭建步骤(Maven 示例)
1.2.1 配置 Maven 仓库
Spring AI 相关工件发布在 Spring 里程碑仓库和快照仓库,阿里云相关依赖需配置阿里云 Maven 仓库,在 pom.xml 中添加如下配置:
XML
<repositories>
<!-- Spring 里程碑仓库 -->
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled> </snapshots> </repository>
<!-- 阿里云 Maven 仓库 -->
<repository> <id>aliyunmaven</id> <url>https://maven.aliyun.com/repository/public</url> </repository> </repositories> <dependencyManagement> <dependencies>
<!-- Spring Boot 父工程 -->
<dependency> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId> <version>3.4.5</version> <type>pom</type> <scope>import</scope> </dependency>
<!-- Spring AI BOM 管理版本 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.1.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- Spring AI Alibaba BOM 管理版本 -->
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2023.0.1.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
1.2.2 引入核心依赖
根据需求引入对应依赖,核心依赖包括基础启动器、阿里云灵积服务接入依赖、Web 依赖(用于接口暴露),示例如下:
XML
<dependencies>
<!-- Spring Boot Web 依赖(用于暴露接口) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring AI Alibaba 核心依赖(接入阿里云灵积服务) -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-ai</artifactId>
</dependency>
<!-- Spring AI 聊天模型基础依赖 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model</artifactId>
</dependency>
<!-- 测试依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
1.2.3 阿里云 API Key 获取
Spring AI Alibaba 核心依赖阿里云灵积模型服务(DashScope),需先获取 API Key 才能调用通义千问等模型,步骤如下:
-
访问 阿里云灵积模型服务官网,注册并登录阿里云账号;
-
进入「控制台」→「API Key 管理」,创建 API Key(记录 Key 和 Secret,后续配置使用);
-
开通对应模型权限(如通义千问 Qwen-2、DeepSeek 等,新用户有免费额度,足够入门学习)。
1.2.4 配置文件编写
在 application.yml 中配置阿里云灵积服务的 API Key、模型信息等,核心配置如下(替换为自己的 API Key):
bash
spring:
application:
name: spring-ai-alibaba-demo
# Spring AI Alibaba 配置(阿里云灵积服务)
cloud:
ai:
tongyi: # 通义千问模型配置
chat:
options:
api-key: 你的阿里云API Key
model: qwen-max # 模型名称(qwen-max/qwen-plus/qwen-turbo,根据需求选择)
temperature: 0.7 # 生成温度,0~1,值越大越随机
max-tokens: 2048 # 最大生成token数,避免输出过长
# 可选:配置流式调用(用于实时返回结果,如打字机效果)
streaming:
enabled: true
# 日志配置(便于调试,查看AI调用细节)
logging:
level:
org.springframework.ai: INFO
com.alibaba.cloud.ai: INFO
root: WARN
1.2.5 启动验证
创建 Spring Boot 启动类,无需额外配置,启动项目,若控制台无报错,且出现「DashScopeChatAutoConfiguration」相关日志,说明环境搭建成功:
java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringAiAlibabaDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAiAlibabaDemoApplication.class, args);
}
}
第二章:核心概念解析(Spring AI + 阿里云适配)
Spring AI Alibaba 完全遵循 Spring AI 的核心抽象,同时针对阿里云生态做了专属适配,理解以下核心概念,是灵活使用框架的基础,避免只会"抄代码"而不懂原理。
2.1 核心抽象(Spring AI 底座)
2.1.1 Model(模型根接口)
Model 是所有 AI 模型的根接口,定义了 AI 模型最基本的能力:接收 Prompt(提示)并返回 ChatResponse(响应)。Spring AI 为不同类型的 AI 模型提供了具体的子接口,如:
-
ChatModel:对话模型接口,用于与大语言模型进行对话(核心接口,如通义千问、OpenAI GPT);
-
EmbeddingModel:嵌入模型接口,用于将文本转换为向量(用于 RAG、文本相似度计算);
-
ImageModel:图像生成模型接口,用于文生图(如阿里云文生图模型);
-
SpeechModel:语音模型接口,用于语音转文本、文本转语音。
2.1.2 Prompt(提示)
Prompt 是发送给 AI 模型的输入,是引导 AI 生成特定输出的核心,由多个 Message(消息)组成,每个 Message 包含角色(Role)和内容(Content)。Spring AI 支持 4 种核心角色:
-
System Role(系统角色):指导 AI 的行为和响应方式,设置交互上下文(如"你是一个Java开发助手,只回答Java相关问题");
-
User Role(用户角色):用户的输入,是 AI 响应的基础(如"解释Spring Boot自动装配原理");
-
Assistant Role(助手角色):AI 对用户输入的响应,用于维持对话连贯性(跟踪历史对话);
-
Tool Role(工具角色):用于工具调用,返回工具执行后的附加信息。
Spring AI 提供 PromptTemplate(提示模板),支持动态填充参数,避免硬编码提示文本,提升复用性,底层使用 StringTemplate 模板引擎实现。
2.1.3 ChatClient(统一聊天客户端)
ChatClient 是 Spring AI 提供的流畅式 API,封装了 ChatModel 的调用细节,支持同步调用、流式调用,无需手动构建 Prompt 和处理响应,是开发中最常用的组件。Spring AI Alibaba 会自动注入适配阿里云模型的 ChatClient 实例,开发者可直接使用。
2.1.4 Embeddings(嵌入)
嵌入是文本、图像等内容的数值表示(浮点数数组,称为向量),核心作用是捕捉内容的语义信息,通过计算两个向量的数值距离,可判断内容的相似度。Spring AI 支持主流嵌入模型,阿里云灵积服务也提供了专属嵌入模型,用于 RAG 等场景。
2.1.5 Tokens(词条)
Tokens 是 AI 模型运作的基石,输入时模型将文本转换为 Tokens,输出时再将 Tokens 转换为文本。通常一个英文 Token 约等于 0.75 个单词,中文 Token 约等于 1-2 个汉字。模型有 Token 限制(上下文窗口),超出限制会无法处理,需注意控制输入输出长度。
2.2 Spring AI Alibaba 专属特性
-
DashScope 接入:无缝集成阿里云灵积模型服务,支持通义千问、DeepSeek 等所有阿里云主流模型,无需额外开发适配代码;
-
Agent Framework:提供 ReactAgent、多智能体编排、上下文工程等能力,支持复杂业务场景的 AI 逻辑编排;
-
Graph 工作流:支持底层工作流编排、状态流转、并行执行、条件路由等,适配企业级复杂流程;
-
Studio/Admin 可视化:支持可视化调试、观测、评估,便于企业级应用的运维和优化;
-
自动配置:遵循 Spring Boot 自动配置理念,引入依赖、配置 API Key 后,即可自动注入 ChatClient、ChatModel 等组件,无需手动配置。
第三章:基础实战(通义千问对话功能)
本章基于 Spring AI Alibaba 实现最基础的通义千问对话功能,包括同步调用、流式调用、提示模板使用,覆盖日常开发中最常见的场景,所有代码可直接复用。
3.1 同步调用(基础版)
同步调用即发送请求后,等待 AI 生成完整响应后再返回,适用于对响应速度要求不高的场景(如后台处理、批量生成)。
3.1.1 编写 Service 层
创建 ChatService,注入 Spring AI 自动配置的 ChatClient,实现同步对话方法:
java
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
public class ChatService {
// 自动注入阿里云适配的 ChatClient(无需手动创建)
private final ChatClient chatClient;
// 构造器注入(推荐,避免字段注入的弊端)
public ChatService(ChatClient chatClient) {
this.chatClient = chatClient;
}
/**
* 基础同步对话:直接发送用户消息,无系统提示
* @param userMessage 用户输入的消息
* @return AI 完整响应
*/
public String simpleChat(String userMessage) {
// 1. 构建 Prompt(仅包含用户消息)
Prompt prompt = new Prompt(userMessage);
// 2. 调用 ChatClient,获取响应
return chatClient.call(prompt).getResult().getOutput().getContent();
}
/**
* 带系统提示的同步对话:引导 AI 按照指定规则响应
* @param userMessage 用户输入的消息
* @return AI 完整响应
*/
public String chatWithSystemPrompt(String userMessage) {
// 1. 定义系统提示(引导 AI 行为)
String systemPrompt = "你是一个专业的 Java 开发助手,只回答 Java 相关问题,简洁明了,不冗余,若问题与 Java 无关,直接回复'抱歉,我只专注于 Java 相关问题'。";
// 2. 构建包含系统角色和用户角色的 Prompt
Prompt prompt = new Prompt(
List.of(
new SystemMessage(systemPrompt),
new UserMessage(userMessage)
)
);
// 3. 调用 AI 并返回结果
return chatClient.call(prompt).getResult().getOutput().getContent();
}
}
3.1.2 编写 Controller 层
创建 ChatController,暴露 HTTP 接口,供前端调用:
java
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ChatController {
private final ChatService chatService;
public ChatController(ChatService chatService) {
this.chatService = chatService;
}
// 基础对话接口
@GetMapping("/chat/simple")
public String simpleChat(@RequestParam String message) {
return chatService.simpleChat(message);
}
// 带系统提示的对话接口
@GetMapping("/chat/system")
public String chatWithSystemPrompt(@RequestParam String message) {
return chatService.chatWithSystemPrompt(message);
}
}
3.1.3 测试接口
启动项目,通过 Postman 或浏览器访问接口,测试效果:
-
基础对话:http://localhost:8080/chat/simple?message=Spring Boot 自动装配原理
预期结果:基础对话会返回完整的 Spring Boot 自动装配原理;带系统提示的接口会返回"抱歉,我只专注于 Java 相关问题"。
3.2 流式调用(进阶版)
流式调用即 AI 生成响应时,逐字/逐句返回,类似打字机效果,适用于前端实时展示(如聊天界面),提升用户体验。Spring AI Alibaba 支持两种流式调用方式:StreamingChatClient 和 ChatClient 流式方法。
3.2.1 流式调用实现(基于 StreamingChatClient)
java
import org.springframework.ai.chat.StreamingChatClient;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
@Service
public class StreamingChatService {
// 自动注入流式聊天客户端
private final StreamingChatClient streamingChatClient;
public StreamingChatService(StreamingChatClient streamingChatClient) {
this.streamingChatClient = streamingChatClient;
}
/**
* 流式对话:逐字返回 AI 响应
* @param userMessage 用户输入的消息
* @return Flux<String> 流式响应(每一个元素是一个字符/短语)
*/
public Flux<String> streamingChat(String userMessage) {
// 构建 Prompt(可添加系统提示)
String systemPrompt = "你是一个专业的 Java 开发助手,回答简洁,步骤清晰。";
Prompt prompt = new Prompt(
List.of(
new SystemMessage(systemPrompt),
new UserMessage(userMessage)
)
);
// 流式调用,返回 Flux(响应式编程)
return streamingChatClient.stream(prompt)
.map(chatResponse -> chatResponse.getResult().getOutput().getContent());
}
}
3.2.2 编写流式接口
java
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
public class StreamingChatController {
private final StreamingChatService streamingChatService;
public StreamingChatController(StreamingChatService streamingChatService) {
this.streamingChatService = streamingChatService;
}
// 流式对话接口,指定 MediaType 为 text/event-stream(SSE 协议)
@GetMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamingChat(@RequestParam String message) {
return streamingChatService.streamingChat(message);
}
}
3.2.3 测试流式接口
通过浏览器访问 http://localhost:8080/chat/stream?message=讲解 Spring AI 的核心抽象,可看到页面逐字显示 AI 响应内容;也可通过 Postman 测试,选择 GET 方法,访问该接口,查看流式返回结果。
3.3 提示模板(PromptTemplate)使用
在实际开发中,经常需要重复使用相似的提示(如"根据用户输入的问题,生成 Java 代码示例"),此时可使用 PromptTemplate 动态填充参数,提升代码复用性和可维护性。
3.3.1 模板使用示例
java
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
public class PromptTemplateService {
private final ChatClient chatClient;
public PromptTemplateService(ChatClient chatClient) {
this.chatClient = chatClient;
}
/**
* 使用提示模板,动态生成 Prompt
* 场景:根据用户输入的需求,生成指定功能的 Java 代码
* @param functionName 功能名称(如"文件上传")
* @param framework 框架(如"Spring Boot")
* @return 生成的 Java 代码
*/
public String generateCodeWithTemplate(String functionName, String framework) {
// 1. 定义提示模板(使用 {参数名} 作为占位符)
String template = "作为专业的 {framework} 开发工程师,编写一个 {functionName} 的完整代码示例,包含注释,可直接运行,无需多余解释。";
// 2. 创建 PromptTemplate 实例
PromptTemplate promptTemplate = new PromptTemplate(template);
// 3. 填充参数
Map<String, Object> parameters = new HashMap<>();
parameters.put("framework", framework);
parameters.put("functionName", functionName);
// 4. 生成 Prompt
Prompt prompt = promptTemplate.create(parameters);
// 5. 调用 AI 并返回结果
return chatClient.call(prompt).getResult().getOutput().getContent();
}
}
3.3.2 测试模板接口
在 Controller 中添加接口,测试提示模板功能:
java
@GetMapping("/chat/template/code")
public String generateCode(@RequestParam String functionName, @RequestParam String framework) {
return promptTemplateService.generateCodeWithTemplate(functionName, framework);
}
访问 http://localhost:8080/chat/template/code?functionName=文件上传\&framework=Spring Boot,可看到 AI 生成的 Spring Boot 文件上传完整代码,包含注释,可直接复用。
第四章:进阶实战(RAG + 工具调用)
基础对话只能使用 AI 模型的训练数据,无法结合企业私有数据(如内部文档、数据库数据),也无法调用外部工具(如查询天气、调用 API)。本章讲解 Spring AI Alibaba 的进阶特性:RAG(检索增强生成)和工具调用,实现企业级 AI 应用的核心能力。
4.1 RAG 实战(结合企业私有文档)
RAG(Retrieval Augmented Generation,检索增强生成)是解决 AI 模型"知识滞后""不懂私有数据"的核心方案,原理是:将企业私有文档转换为向量,存储到向量数据库中,用户提问时,先从向量数据库中检索相似的文档片段,再将片段作为提示的一部分发送给 AI,让 AI 结合私有数据生成响应。
本实战使用阿里云向量数据库(AnalyticDB for PostgreSQL)作为向量存储,结合通义千问嵌入模型,实现 RAG 功能。
4.1.1 引入 RAG 相关依赖
XML
<!-- 阿里云向量数据库依赖(AnalyticDB for PostgreSQL) -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-ai-vector-db-adbpg</artifactId>
</dependency>
<!-- 文档加载依赖(用于加载本地文档,如 txt、pdf) -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-document-reader-tika</artifactId>
</dependency>
<!-- 文本分割依赖(将长文档分割为短片段,适配模型 Token 限制) -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-text-splitter-recursive-character</artifactId>
</dependency>
4.1.2 配置向量数据库和嵌入模型
在 application.yml 中添加向量数据库和嵌入模型配置(替换为自己的阿里云向量数据库信息):
bash
spring:
cloud:
ai:
# 通义千问嵌入模型配置(用于将文本转换为向量)
tongyi:
embedding:
api-key: 你的阿里云API Key
model: text-embedding-v1
# 阿里云向量数据库(AnalyticDB for PostgreSQL)配置
vector-db:
adbpg:
url: jdbc:postgresql://xxx.ads.aliyuncs.com:5432/xxx
username: xxx
password: xxx
table-name: doc_vector # 存储向量的表名(自动创建)
dimension: 1536 # 嵌入向量维度(text-embedding-v1 模型维度为 1536)
4.1.3 实现 RAG 核心逻辑
RAG 核心分为两步:1. 文档加载、分割、转换为向量并存储到向量数据库;2. 用户提问时,检索相似向量,结合提示生成响应。
java
import org.springframework.ai.document.Document;
import org.springframework.ai.embedding.EmbeddingClient;
import org.springframework.ai.retrieval.RetrievalAugmentor;
import org.springframework.ai.retrieval.RetrievalAugmentorBuilder;
import org.springframework.ai.text.splitter.RecursiveCharacterTextSplitter;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
@Service
public class RagService {
// 向量存储(自动注入阿里云向量数据库实现)
private final VectorStore vectorStore;
// 嵌入客户端(自动注入通义千问嵌入模型)
private final EmbeddingClient embeddingClient;
// 聊天客户端
private final ChatClient chatClient;
// 文本分割器(将长文档分割为短片段)
private final RecursiveCharacterTextSplitter textSplitter;
public RagService(VectorStore vectorStore, EmbeddingClient embeddingClient, ChatClient chatClient) {
this.vectorStore = vectorStore;
this.embeddingClient = embeddingClient;
this.chatClient = chatClient;
// 初始化文本分割器:每个片段最大 500 字符,重叠 50 字符(避免片段断裂)
this.textSplitter = new RecursiveCharacterTextSplitter(500, 50);
// 初始化:加载本地文档,存储到向量数据库(仅首次执行,后续可注释)
try {
loadDocumentToVectorDb();
} catch (IOException e) {
throw new RuntimeException("文档加载失败", e);
}
}
/**
* 第一步:加载本地文档(如企业内部文档),分割后存储到向量数据库
*/
private void loadDocumentToVectorDb() throws IOException {
// 1. 加载本地文档(放在 resources 目录下,如 doc/company-doc.txt)
ClassPathResource resource = new ClassPathResource("doc/company-doc.txt");
String content = new String(resource.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
// 2. 分割文档(避免单个片段超出模型 Token 限制)
List<Document> documents = textSplitter.split(new Document(content));
// 3. 将文档转换为向量并存储到向量数据库
vectorStore.add(documents);
}
/**
* 第二步:RAG 对话,结合向量数据库中的私有文档生成响应
* @param userMessage 用户提问
* @return 结合私有文档的 AI 响应
*/
public String ragChat(String userMessage) {
// 1. 构建检索增强器(结合向量存储和嵌入模型)
RetrievalAugmentor retrievalAugmentor = new RetrievalAugmentorBuilder()
.withVectorStore(vectorStore)
.withEmbeddingClient(embeddingClient)
.withTopK(3) // 检索前 3 个最相似的文档片段
.build();
// 2. 检索与用户提问最相似的文档片段
List<Document> similarDocuments = retrievalAugmentor.retrieve(userMessage);
// 3. 构建提示模板,将相似文档片段作为上下文
String template = "结合以下上下文信息,回答用户的问题:\n" +
"上下文:{context}\n" +
"用户问题:{userMessage}\n" +
"要求:仅基于上下文信息回答,不添加额外内容,若上下文无相关信息,回复'抱歉,未找到相关信息'。";
PromptTemplate promptTemplate = new PromptTemplate(template);
// 4. 填充参数(上下文为检索到的文档内容)
String context = similarDocuments.stream()
.map(Document::getContent)
.reduce("", (a, b) -> a + "\n" + b);
Prompt prompt = promptTemplate.create(Map.of(
"context", context,
"userMessage", userMessage
));
// 5. 调用 AI 并返回结果
return chatClient.call(prompt).getResult().getOutput().getContent();
}
}
4.1.4 测试 RAG 功能
-
在 resources 目录下创建 doc 文件夹,新建 company-doc.txt 文件,写入企业私有数据(如"本公司成立于2020年,主营Java开发、AI应用落地,核心产品为智能办公系统");
-
在 Controller 中添加 RAG 接口:
java
@GetMapping("/chat/rag")
public String ragChat(@RequestParam String message) {
return ragService.ragChat(message);
}
- 访问 http://localhost:8080/chat/rag?message=本公司成立时间,AI 会结合 company-doc.txt 中的内容,返回"本公司成立于2020年";若提问"本公司主营什么",会返回"主营Java开发、AI应用落地,核心产品为智能办公系统",实现结合私有数据的对话。
4.2 工具调用实战(调用外部 API)
工具调用是让 AI 能够调用外部工具(如查询天气、调用第三方 API、操作数据库),解决 AI 模型"无法获取实时数据""无法执行外部操作"的问题。Spring AI Alibaba 支持工具调用的自动编排,AI 会根据用户问题,自动判断是否需要调用工具,以及调用哪个工具。
本实战实现"查询天气"工具调用,调用阿里云天气 API,让 AI 能够回答用户的天气查询问题。
4.2.1 引入工具调用依赖
XML
<!-- Spring AI 工具调用核心依赖 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-tools</artifactId>
</dependency>
<!-- HTTP 客户端依赖(用于调用外部 API) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
4.2.2 定义工具类(天气查询工具)
创建工具类,实现 Tool 接口,定义工具名称、描述和执行逻辑(调用阿里云天气 API):
java
import org.springframework.ai.chat.messages.ToolMessage;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.tools.Tool;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import java.util.Map;
@Component
public class WeatherTool implements Tool {
// 阿里云天气 API(替换为自己的 API 地址和密钥)
private static final String WEATHER_API_URL = "https://api.aliyun.com/weather?city={city}&appkey={appkey}";
private static final String APP_KEY = "你的阿里云天气 API Key";
private final WebClient webClient;
public WeatherTool(WebClient.Builder webClientBuilder) {
this.webClient = webClientBuilder.build();
}
/**
* 工具名称(唯一,用于 AI 识别工具)
*/
@Override
public String getName() {
return "weather_query_tool";
}
/**
* 工具描述(详细说明工具功能、参数,让 AI 知道何时调用、如何传参)
*/
@Override
public String getDescription() {
return "用于查询指定城市的实时天气,参数为 city(城市名称,如北京、上海),返回该城市的温度、天气状况(晴、雨等)。";
}
/**
* 工具执行逻辑(调用外部 API,返回结果)
*/
@Override
public ToolMessage call(Map<String, Object> parameters) {
// 获取参数(AI 会自动传入 city 参数)
String city = parameters.get("city").toString();
// 调用阿里云天气 API
String weatherInfo = webClient.get()
.uri(WEATHER_API_URL, city, APP_KEY)
.retrieve()
.bodyToMono(String.class)
.block(); // 同步调用(实际开发可使用异步)
// 返回工具执行结果(AI 会结合该结果生成最终响应)
return new ToolMessage(weatherInfo, this.getName());
}
}
4.2.3 实现工具调用对话
创建工具调用服务,结合 ChatModel 和工具,实现 AI 自动判断是否调用工具:
java
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.tools.Tool;
import org.springframework.ai.tools.ToolExecutor;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ToolCallService {
private final ChatClient chatClient;
private final ToolExecutor toolExecutor;
// 注入所有工具(Spring 会自动扫描 Tool 接口实现类)
public ToolCallService(ChatClient chatClient, List<Tool> tools) {
this.chatClient = chatClient;
// 工具执行器,用于执行 AI 指定的工具
this.toolExecutor = new ToolExecutor(tools);
}
/**
* 工具调用对话:AI 自动判断是否需要调用工具
* @param userMessage 用户提问
* @return 结合工具执行结果的 AI 响应
*/
public String toolCallChat(String userMessage) {
// 1. 构建用户消息
Message message = new UserMessage(userMessage);
// 2. 调用 AI,判断是否需要调用工具
ChatResponse response = chatClient.call(new Prompt(List.of(message)));
// 3. 循环判断:若 AI 需要调用工具,则执行工具,再将工具结果传入 AI,直到不需要调用工具
while (response.getResult().getOutput().getToolCalls() != null && !response.getResult().getOutput().getToolCalls().isEmpty()) {
// 执行 AI 指定的工具
List<Message> toolMessages = toolExecutor.execute(response.getResult().getOutput().getToolCalls());
// 将工具执行结果作为消息,再次调用 AI
message = toolMessages.get(0);
response = chatClient.call(new Prompt(List.of(new UserMessage(userMessage), message)));
}
// 4. 返回 AI 最终响应(无需调用工具或工具调用完成后)
return response.getResult().getOutput().getContent();
}
}
4.2.4 测试工具调用功能
在 Controller 中添加工具调用接口:
java
@GetMapping("/chat/tool")
public String toolCallChat(@RequestParam String message) {
return toolCallService.toolCallChat(message);
}
访问 http://localhost:8080/chat/tool?message=北京今天天气怎么样,AI 会自动识别需要调用天气查询工具,调用阿里云天气 API 获取实时天气,然后返回格式化的天气信息;若提问"Spring AI 是什么",AI 会直接返回答案,不调用工具。
第五章:企业级优化(容错、限流、可观测性)
基础和进阶实战完成后,需进行企业级优化,解决生产环境中的常见问题(如 API 调用失败、并发过高、无法排查问题),让应用更稳定、可维护。
5.1 容错处理(避免 AI 调用失败导致服务异常)
阿里云灵积服务可能出现网络异常、API Key 失效、模型调用频率超限等问题,需添加容错机制(重试、降级),使用 Spring Retry 实现重试,结合自定义异常处理降级。
5.1.1 引入重试依赖
XML
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
5.1.2 配置重试和异常处理
java
import org.springframework.ai.chat.client.ChatClientException;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
@Service
public class FaultTolerantChatService {
private final ChatClient chatClient;
public FaultTolerantChatService(ChatClient chatClient) {
this.chatClient = chatClient;
}
/**
* 带重试的同步对话:调用失败时,重试 3 次,每次间隔 1 秒
*/
@Retryable(
value = {ChatClientException.class}, // 重试的异常类型(Spring AI 调用异常)
maxAttempts = 3, // 最大重试次数
backoff = @Backoff(delay = 1000) // 重试间隔(1 秒)
)
public String faultTolerantChat(String userMessage) {
try {
return chatClient.call(new Prompt(userMessage)).getResult().getOutput().getContent();
} catch (ChatClientException e) {
// 重试失败后,降级处理(返回友好提示)
return "抱歉,当前 AI 服务暂时不可用,请稍后再试!";
}
}
}
在启动类上添加 @EnableRetry 注解,开启重试功能。
5.2 限流处理(避免并发过高导致 API 超限)
阿里云灵积服务对 API 调用频率有限制(免费额度通常为每秒 1-5 次),高并发场景下需添加限流,避免触发限流机制导致调用失败,使用 Spring Cloud Gateway 或 Resilience4j 实现限流。
此处以 Resilience4j 为例,实现接口级限流:
5.2.1 引入限流依赖
XML
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-ratelimiter</artifactId>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot3</artifactId>
</dependency>
5.2.2 配置限流规则
bash
resilience4j:
ratelimiter:
instances:
aiChatLimiter: # 限流实例名称
limitRefreshPeriod: 1s # 限流刷新周期(1 秒)
limitForPeriod: 3 # 每个周期最大调用次数(3 次/秒)
timeoutDuration: 1000ms # 超过限流后,等待超时时间
registerHealthIndicator: true # 注册健康指标
5.2.3 接口添加限流注解
java
import io.github.resilience4j.ratelimiter.annotation.RateLimiter;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class LimiterChatController {
private final FaultTolerantChatService faultTolerantChatService;
public LimiterChatController(FaultTolerantChatService faultTolerantChatService) {
this.faultTolerantChatService = faultTolerantChatService;
}
// 添加限流注解,指定限流实例
@RateLimiter(name = "aiChatLimiter", fallbackMethod = "chatFallback")
@GetMapping("/chat/limiter")
public String limiterChat(@RequestParam String message) {
return faultTolerantChatService.faultTolerantChat(message);
}
// 限流降级方法(参数、返回值需与原方法一致)
public String chatFallback(String message, Exception e) {
return "抱歉,当前请求过于频繁,请稍后再试!";
}
}
5.3 可观测性(日志、监控)
企业级应用需具备可观测性,便于排查问题、优化性能,主要包括日志记录、指标监控、链路追踪。
5.3.1 日志优化
添加详细的日志记录,包括 AI 调用的请求、响应、耗时、错误信息,便于排查问题,修改 application.yml 日志配置:
bash
logging:
level:
org.springframework.ai: DEBUG # 打印 Spring AI 详细调用日志
com.alibaba.cloud.ai: DEBUG # 打印阿里云 AI 调用日志
com.example.springaialibabademo: DEBUG # 打印自定义服务日志
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"
file:
name: logs/spring-ai-alibaba.log # 日志文件路径
5.3.2 指标监控(Prometheus + Grafana)
引入 Spring Boot Actuator 和 Prometheus 依赖,暴露监控指标,结合 Grafana 可视化监控 AI 调用次数、耗时、失败率等。
XML
<!-- 监控核心依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Prometheus 依赖 -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
配置监控指标暴露:
bash
management:
endpoints:
web:
exposure:
include: prometheus,health,info # 暴露的监控端点
metrics:
export:
prometheus:
enabled: true
endpoint:
health:
show-details: always
启动项目后,访问 http://localhost:8080/actuator/prometheus,可看到 AI 调用相关的监控指标(如 spring_ai_chat_calls_total、spring_ai_chat_duration_seconds),结合 Grafana 配置仪表盘,实现可视化监控。
第六章:常见问题与避坑指南
6.1 常见问题
-
API Key 无效/过期:检查阿里云灵积服务的 API Key 是否正确,是否开通对应模型权限,若过期需重新创建;
-
模型调用失败(Token 超限):减少单次输入的文本长度,使用文本分割器分割长文档,调整 max-tokens 配置;