实战:Spring AI + Tool Calling :大模型轻松接内、外服务,打造智能助手的 Hello World(附全部代码)
在现代应用开发中,如何高效地整合内部和外部服务是开发者面临的一个重要挑战。Spring AI 的功能提供了一种优雅的解决方案,通过将工具和服务抽象化,开发者可以轻松地调用外部 API 或内部服务,而无需复杂的集成逻辑。简化开发流程:通过统一的工具调用接口,开发者可以专注于业务逻辑,而无需处理繁琐的 HTTP 请求和响应解析。增强扩展性:工具可以动态注册和调用,支持快速集成新服务。提升用户体验:通过
一、概述
在现代应用开发中,如何高效地整合内部和外部服务是开发者面临的一个重要挑战。Spring AI 的 Tool Calling 功能提供了一种优雅的解决方案,通过将工具和服务抽象化,开发者可以轻松地调用外部 API 或内部服务,而无需复杂的集成逻辑。
Tool Calling 的核心优势在于:
- 简化开发流程:通过统一的工具调用接口,开发者可以专注于业务逻辑,而无需处理繁琐的 HTTP 请求和响应解析。
- 增强扩展性:工具可以动态注册和调用,支持快速集成新服务。
- 提升用户体验:通过工具调用,应用可以实时获取外部数据(如天气、假期等),为用户提供了一个更智能、更全面的服务体验。
在本篇文章中,我们将通过一个具体的案例,展示如何使用 Spring AI 的 Tool Calling 功能来调用天气服务和 OA 服务,帮助开发者快速上手这一强大功能。
二、功能介绍
Spring AI 的 Tool Calling 提供了以下核心功能:
- 工具注册与管理:支持动态注册工具,并通过注解或配置文件定义工具的描述和参数。
- 工具调用链:通过方法链式调用,开发者可以轻松组合多个工具,实现复杂业务逻辑。
在本案例中,我们将实现以下功能:
- 调用外部天气 API,获取指定城市的天气预报。
- 调用内部 OA 服务,查询员工剩余假期和提交请假申请。
- 对比两种实现方式(无工具调用 vs 工具调用),展示 Tool Calling 的优势。
三、环境准备
在开始之前,请确保你的环境满足以下要求:
- 操作系统:Windows 11
- Java 版本:JDK 17+(请注意 Spring Boot 3.4.4 的兼容性)
- 依赖管理:Maven 3.8.3+
- 天气服务:需要注册天气服务 API,可以免费用一段时间,确保我们的验证是没问题的 天气服务账号申请
- 阿里云百炼平台:账号申请 后,可以查看到以下模型的选择
四、Spring AI 集成:完整代码实现
1. 代码结构
│─src
│ └─main
│ ├─java
│ │ └─chat
│ │ │ ChatApplication.java
│ │ ├─common
│ │ │ ChatInit.java
│ │ ├─controller
│ │ │ ToolCallController.java
│ │ │
│ │ └─service
│ │ OaService.java
│ │ WeatherService.java
│ │
│ └─resources
│ application.yml
└─pom.xml
说明:项目结构遵循标准的 Spring Boot 项目布局,分为控制器、服务和配置等模块,便于代码的组织和维护。
2. 初始化类(ChatInit.java)
@Slf4j
@Configuration
@RequiredArgsConstructor
public class ChatInit {
private final ChatModel chatModel;
@Value("${spring.ai.toolcalling.weather.api-key}")
private String WEATHER_API_KEY;
@Bean
public WebClient webClient() {
log.info("WebClient Bean 已经被创建!");
return WebClient.builder()
.defaultHeader(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded")
.defaultHeader("key", WEATHER_API_KEY)
.build();
}
@Bean
public ChatClient chatClient() {
return ChatClient.builder(chatModel)
.defaultSystem("你是一位专业且细致的助手。在遇到不确定或不明确的信息时," +
"会主动询问用户以获取更多信息。回答问题时," +
"你倾向于使用简洁、条理清晰的语言。如果信息复杂或包含多个部分," +
"请确保每个部分都有适当的标题或编号,以创建分层结构。")
.build();
}
}
说明:
@Configuration
注解表明这是一个配置类,用于定义 Spring 容器中的 Bean。WebClient
是 Spring 提供的用于 HTTP 请求的客户端,这里配置了默认的请求头,包括天气 API 的密钥。ChatClient
是 Spring AI 提供的聊天客户端,用于处理与用户的对话逻辑。
3. 控制器(ToolCallController.java)
@Slf4j
@RestController
@RequestMapping("/spring/ai/")
public class ToolCallController {
@Autowired
private ChatClient chatClient;
@Autowired
private WebClient webClient;
/**
* 无工具版聊天接口
*/
@GetMapping("/chat")
public String simpleChat(@RequestParam String message) {
log.info("simpleChat input message --> [{}]", message);
return chatClient.prompt(message).call().content();
}
/**
* 调用工具版聊天接口 - 使用方法链
*/
@GetMapping("/chat-tool-method")
public String chatToolMethod(@RequestParam String message) {
log.info("chatToolMethod input message --> [{}]", message);
return chatClient.prompt(message)
.tools(
new WeatherService(webClient),
new OaService())
.call()
.content();
}
}
说明:
@RestController
注解表明这是一个 RESTful 控制器,用于处理 HTTP 请求。simpleChat
方法展示了不使用工具调用的简单聊天接口,直接通过ChatClient
处理用户消息。chatToolMethod
方法展示了如何通过工具调用链调用多个工具(天气服务和 OA 服务),实现更复杂的业务逻辑。
4. Tool Calling 的 OA 服务和天气服务
OaService.java
@Slf4j
public class OaService {
private static int LEFT_DAYS = 5;
@Tool(description = "员工剩余假期查询:查询员工还有几天的假期可以请")
public String getCurrentRemainingHoliday() {
return "目前,你还有 【" + LEFT_DAYS + "】 天的假期可以使用";
}
@Tool(description = "员工请假,需要传用户id(userId),和需要请假的天数 (days)")
public String askForLeave(@ToolParam(description = "用户的工号") String userId,
@ToolParam(description = "需要请假的天数") String days) {
if (!StringUtils.isNumeric(days)) {
throw new IllegalArgumentException("days参数必须是数字");
}
int dayInt = Integer.parseInt(days);
if (dayInt >= LEFT_DAYS) {
return "你的假期不足,无法请假";
}
return "好的,员工【" + userId + "】, 已经请假【" + days + "】天,请好好享受假期";
}
}
说明:
@Tool
注解用于定义工具的描述,方便在调用时识别工具的功能。getCurrentRemainingHoliday
方法用于查询员工剩余假期。askForLeave
方法用于处理员工请假申请,包括参数验证和业务逻辑处理。
WeatherService.java
@Slf4j
@Service
public class WeatherService {
private static final String WEATHER_API_URL = "https://api.weatherapi.com/v1/forecast.json";
private final ObjectMapper objectMapper = new ObjectMapper();
private final WebClient webClient;
public WeatherService(WebClient webClient) {
this.webClient = webClient;
}
@Tool(description = "使用 api.weather 获取天的预报和天气情况.")
public String getWeatherServiceMethod(@ToolParam(description = "城市名称") String city,
@ToolParam(description = "天气预报的天数。值的范围为1到14") int days) {
if (StringUtils.isBlank(city)) {
log.error("无效请求,必须传城市名称");
return null;
}
String location = this.preprocessLocation(city);
String url = UriComponentsBuilder.fromHttpUrl(WEATHER_API_URL)
.queryParam("q", location)
.queryParam("days", days)
.toUriString();
log.info("url : {}", url);
String result = "获取天气数据失败";
try {
result = webClient.get()
.uri(url)
.retrieve()
.bodyToMono(String.class)
.block();
log.info("获取天气数据成功: {}", result);
} catch (Exception e) {
log.error("获取天气数据失败: {}", e.getMessage(), e);
}
return result;
}
/**
* 处理中文地名
* @param location
* @return
*/
public String preprocessLocation(String location) {
if (containsChinese(location)) {
return PinyinUtil.getPinyin(location, "");
}
return location;
}
/**
* 判断是否包含中文
* @param str
* @return
*/
public boolean containsChinese(String str) {
return str.matches(".*[\u4e00-\u9fa5].*");
}
}
说明:
@Service
注解表明这是一个服务类,用于处理业务逻辑。getWeatherServiceMethod
方法通过调用外部天气 API 获取指定城市的天气预报,包括参数验证和错误处理。preprocessLocation
方法用于处理中文地名,将其转换为拼音以适应 API 的要求。containsChinese
方法用于判断输入字符串是否包含中文。
5. 启动类(ChatApplication.java)
@SpringBootApplication
public class ChatApplication {
public static void main(String[] args) {
SpringApplication.run(ChatApplication.class, args);
}
}
说明:
@SpringBootApplication
是 Spring Boot 的主注解,用于标记应用的入口类。main
方法是应用的启动入口,通过SpringApplication.run
启动 Spring 容器。
6. 配置文件(application.yml)
server:
port: 8080
logging:
level:
org:
springframework: info
spring:
application:
name: Hello-Spring-AI
ai:
dashscope:
# 注意这个是使用阿里云百炼平台的API-KEY
api-key: sk-xxxxxxxxxxxxxxxxxx
model: qwq-plus
toolcalling:
time:
enabled: true
weather:
enabled: true
# 注意这个是weather.api的key
api-key: xxxxxxxxxxxxxxxxxxxxxx
说明:
- 配置文件定义了应用的基本设置,包括服务器端口、日志级别和 Spring AI 的相关配置。
spring.ai
配置块中包含了阿里云百炼平台的 API 密钥和模型选择。toolcalling
配置块中启用了天气工具,并提供了天气 API 的密钥。
7. POM 文件
<properties>
<java.version>23</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>3.4.4</spring-boot.version>
<spring-ai.version>1.0.0-M6</spring-ai.version>
<spring-alibaba.version>1.0.0-M6.1</spring-alibaba.version>
<hutool.version>5.8.35</hutool.version>
<pinyin4j.version>2.5.1</pinyin4j.version>
<maven.compiler.version>3.11.0</maven.compiler.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter</artifactId>
<version>${spring-alibaba.version}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<dependency>
<groupId>com.belerweb</groupId>
<artifactId>pinyin4j</artifactId>
<version>${pinyin4j.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.17.0</version>
</dependency>
</dependencies>
<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>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.version}</version>
<configuration>
<release>${java.version}</release>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<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>
<repositories>
<repository>
<id>alimaven</id>
<name>aliyun maven</name>
<url>https://maven.aliyun.com/repository/public</url>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
说明:
- POM 文件定义了项目的依赖和构建配置。
- 依赖部分包括 Spring Boot、Spring AI、工具库(如 Hutool 和 Pinyin4j)等。
dependencyManagement
用于管理依赖的版本,确保一致性。build
部分配置了编译器插件和 Spring Boot 打包插件。repositories
配置了 Maven 的仓库地址,以便获取所需的依赖。
五、验证与效果对比
为了验证 Spring AI 的 Tool Calling 功能是否能够正确解析用户需求并调用多个工具,我们设计了一个具体的测试场景,并对比了两种实现方式的效果。
验证目标
- 工具调用的准确性:验证工具调用接口是否能够正确解析用户问题,并调用天气服务和 OA 服务。
- 对比两种实现方式:对比无工具调用接口和工具调用接口在处理复杂需求时的表现。
- AI 生成的稳定性:提醒读者注意 AI 生成内容的潜在不稳定性,并提供应对策略。
测试场景
我们向两个接口发送相同的请求:
我计划明天从广州到北京,帮我看下这2个地方的天气?再帮我看下我还剩几天假期?
验证过程
1. 无工具调用接口
请求地址:http://127.0.0.1:8080/spring/ai/chat
请求参数:
message
: 我计划明天从广州到北京,帮我看下这2个地方的天气?再帮我看下我还剩几天假期?
响应结果:
说明:
- 无工具调用接口只能基于预设的逻辑进行简单回复,无法调用外部服务获取实时数据。
- 这种方式适合处理简单的聊天逻辑,但对于复杂需求(如实时天气查询和假期管理)显得力不从心。
2. 工具调用接口
请求地址:http://127.0.0.1:8080/spring/ai/chat-tool-method
请求参数:
message
: 我计划明天从广州到北京,帮我看下这2个地方的天气?再帮我看下我还剩几天假期?
理想响应结果:
说明:
- 工具调用接口通过调用天气服务和 OA 服务,能够提供更全面和准确的响应。
- 这种方式适合处理复杂的业务逻辑,但需要确保工具的稳定性和正确性。
验证效果总结
对比维度 | 无工具调用接口 | 工具调用接口 |
---|---|---|
实时数据支持 | 不支持 | 支持(通过调用外部服务获取实时数据) |
复杂需求处理 | 仅能提供简单回复 | 能够解析复杂需求并调用多个工具 |
扩展性 | 低(无法动态集成新服务) | 高(支持动态注册和调用新工具) |
用户体验 | 有限(无法提供全面信息) | 更佳(提供全面且准确的信息) |
通过对比可以看出,工具调用接口在处理复杂需求时具有明显优势,但需要开发者注意 AI 生成的不稳定性,并采取相应的应对措施。
六、再扯几句
Spring AI 的 Tool Calling 功能为开发者提供了一种高效、灵活的方式来整合内部和外部服务。通过工具调用,开发者可以:
- 快速集成多种服务,减少重复开发。
- 提升应用的智能化水平,为用户提供了一个更全面的服务体验。
- 简化复杂业务逻辑的实现,降低开发和维护成本。
更多推荐
所有评论(0)