Spring AI Function Call 实践
用户问「SKU-1001 还有多少库存?」应用可以把查询库存的方法交给模型选择,再用实际查询结果回答。本文用一条内存库存记录跑通这条链路。函数调用的消息原理见 Function Call 介绍。
1. 准备项目
使用 JDK 17 或以上版本、Maven 3.6.3 或以上版本,以及支持工具调用的模型。示例固定使用 Spring Boot 3.5.15 和 Spring AI 1.1.8。新建 Maven 项目,写入 pom.xml。Spring AI 1.1.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.5.15</version>
<relativePath/>
</parent>
<groupId>example</groupId>
<artifactId>stock-tool-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.1.8</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>写入 src/main/resources/application.yml。示例是命令行程序,通过环境变量 OPENAI_API_KEY 读取模型服务密钥。
spring:
main:
web-application-type: none
ai:
openai:
api-key: ${OPENAI_API_KEY}
chat:
options:
model: gpt-4o-mini2. 声明工具并注册到请求
创建 src/main/java/example/stock/StockApplication.java。示例把 SKU-1001 的可用库存设为 37 件。商品不存在时返回 found: false,让模型能区分「查无此商品」和「库存为零」。
package example.stock;
import java.util.Map;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@SpringBootApplication
public class StockApplication {
public static void main(String[] args) {
SpringApplication.run(StockApplication.class, args).close();
}
@Bean
CommandLineRunner askStock(ChatClient.Builder builder, StockTools stockTools) {
return args -> {
String sku = args.length == 0 ? "SKU-1001" : args[0];
String answer = builder.build()
.prompt()
.user("请用库存工具查询 %s 的可用库存。如果查无此商品,请直接说明。".formatted(sku))
.tools(stockTools)
.call()
.content();
System.out.println(answer);
};
}
}
@Component
class StockTools {
private final Map<String, Integer> stock = Map.of("SKU-1001", 37);
@Tool(name = "get_stock", description = "根据商品 SKU 查询可用库存,单位为件")
public StockResult getStock(@ToolParam(description = "商品 SKU,例如 SKU-1001") String sku) {
if (sku == null || !sku.matches("SKU-[0-9]{4}")) {
throw new IllegalArgumentException("SKU 格式应为 SKU- 加四位数字");
}
System.out.printf("tool=get_stock sku=%s%n", sku);
Integer available = stock.get(sku);
return new StockResult(sku, available != null, available);
}
record StockResult(String sku, boolean found, Integer available) {
}
}@Tool 指定模型可以看到的工具名和用途,@ToolParam 补充参数说明,Spring AI 据此生成工具的输入 Schema。.tools(stockTools) 把该实例的工具加入这一次请求。若在构建客户端时用 defaultTools(),工具会作为后续请求的默认选项。单次请求显式提供工具时,该列表会覆盖默认工具。Spring AI Tool Calling
3. 运行并确认工具真的执行了
在终端设置 OPENAI_API_KEY,然后从项目根目录运行:
mvn spring-boot:run如果模型发起工具调用,控制台会先出现 Java 方法的日志,随后打印模型的回答。模型的措辞可能不同。
tool=get_stock sku=SKU-1001
SKU-1001 的可用库存为 37 件。查看未找到的分支时,可以用以下命令把 SKU 改成 SKU-9999。
mvn spring-boot:run "-Dspring-boot.run.arguments=SKU-9999"在这次请求中,应用和模型的往返过程是:
- Spring AI 把用户问题和
get_stock的定义一并发给模型。 - 模型选中工具,返回工具名和参数,例如
{"sku":"SKU-1001"}。 - Spring AI 查找回调、转换参数并执行
getStock(),把返回值序列化为工具结果。 - Spring AI 把结果交回模型,模型据此生成自然语言回答。Spring AI Tool Calling
4. 排查常见问题
- 有回答,控制台没有
tool=get_stock。 核对模型是否支持工具调用、当前请求是否传了.tools(stockTools),再检查提示是否明确要求查询库存。测试时以执行日志或断点为准。 - 模型传入了无效 SKU。 检查工具参数说明与方法校验是否一致。示例要求
SKU-加四位数字,符合格式但查不到库存的商品会返回found: false。 - 接入真实库存服务。 在工具或业务服务内保留查询权限、参数校验和错误处理。模型生成的参数需要按外部输入处理。
本例把 Java 方法注册到当前应用。需要让其他 Agent 也能连接并发现同一个工具时,可以继续阅读 Spring AI MCP 实践。
