提示:如果不能访问 OpenAI,请点击 AiCode API 注册账号,通过代理访问。
Spring AI 简化了工具调用的函数定义,支持两种配置方式:一是通过 FunctionToolCallback 编程式配置,精细定制工具逻辑;二是用 @Bean 注解将工具类 / 方法注册为 Bean,框架运行时自动解析并动态注册。这既满足复杂定制需求,又简化常规集成,方便开发者灵活选择。
FunctionToolCallback 是处理底层 AI 模型调用工具函数时执行具体逻辑的核心回调接口,负责将 AI 的工具调用请求转换为实际业务操作,并将执行结果返回给 AI 模型。
FunctionToolCallback 的核心作用:
桥接 AI 与业务逻辑:接收 AI 生成的工具调用参数(如函数名、参数列表),触发对应的业务方法执行。
处理参数与结果:负责解析 AI 传来的参数格式,适配业务方法的输入要求;同时将方法执行结果转换为 AI 可理解的格式。
支撑工具调用生命周期:在工具调用的整个过程中(参数解析、方法执行、结果处理)提供标准化的执行逻辑。
你可以通过编程方式构建 FunctionToolCallback 实例,将函数式类型(Function、Supplier、Consumer 或 BiFunction)转换为工具。
📢注意:Function、Supplier、Consumer 或 BiFunction 是在 Java 8 引入的函数式接口,作用如下:
Supplier<T> 无参数,返回一个 T 类型的结果(“供给者”,只提供数据)
Consumer<T> 接收一个 T 类型的参数,无返回值(“消费者”,只消费数据)
Function<T, R> 接收一个 T 类型的参数,返回一个 R 类型的结果(“函数”,输入转输出)
BiFunction<T, U, R> 接收两个参数(T 类型和 U 类型),返回一个 R 类型的结果(“双参数函数”)
以下是后续要用到的函数类,实现了 Function 接口:
// 天气服务类,实现 Function 接口,用于处理天气查询请求并返回结果
// Function<WeatherRequest, WeatherResponse> 表示接收 WeatherRequest 类型参数,
// 返回 WeatherResponse 类型结果
public class WeatherService implements Function<WeatherRequest, WeatherResponse> {
// 实现Function接口的apply方法,处理天气查询业务逻辑
// 参数request:包含查询地点和温度单位的请求对象
// 返回值:包含温度和单位的天气响应对象
public WeatherResponse apply(WeatherRequest request) {
// 此处简化实现,实际应用中应包含真实的天气查询逻辑
// 示例返回固定温度30.0,单位使用请求中指定的单位
return new WeatherResponse(30.0, request.unit());
}
}
// 温度单位枚举:摄氏度(C)和华氏度(F)
public enum Unit { C, F }
// 天气查询请求记录(Record),包含查询地点和温度单位
// location:查询的地点名称
// unit:期望返回的温度单位(C或F)
public record WeatherRequest(String location, Unit unit) {}
// 天气查询响应记录(Record),包含温度值和对应的单位
// temp:温度数值
// unit:温度单位(与请求中一致或转换后的值)
public record WeatherResponse(double temp, Unit unit) {}在正式通过编程方式构建 FunctionToolCallback 之前,先了解几个便捷的 Builder 类:
FunctionToolCallback.Builder 是用于构建 FunctionToolCallback 实例的建造类,提供了一种链式编程的方式来灵活配置工具回调的核心参数。它封装了 FunctionToolCallback 所需的关键信息(如目标业务对象、方法、元数据等),让开发者无需直接通过复杂的构造器参数传递,就能快速创建可被 AI 调用的工具回调实例。
FunctionToolCallback.Builder 定义如下:

字段说明:
name:工具名称。AI 模型通过此名称识别调用工具,因此同一上下文中不允许存在同名工具。对于特定聊天请求,模型可用的所有工具名称必须保持全局唯一。(必需项)
toolFunction:表示工具方法的函数式对象(Function、Supplier、Consumer 或 BiFunction)。(必需项)
description:工具描述,用于帮助模型判断何时及如何调用该工具。若未提供,将使用方法名称作为工具描述。但强烈建议提供详细描述,这对模型理解工具用途及使用方法至关重要。若描述不充分,可能导致模型在该调用工具时未调用,或错误调用工具。
inputType:函数输入类型。(必需项)
inputSchema:工具输入参数的 JSON Schema。若未提供,将基于 inputType 自动生成 Schema。你可使用 @ToolParam 注解提供输入参数的额外信息(如描述、是否必需等),默认情况下所有输入参数均为必需参数。
toolMetadata:定义额外设置的 ToolMetadata 实例(如是否将结果直接返回客户端、使用的结果转换器等),可通过 ToolMetadata.Builder 类构建。
toolCallResultConverter:用于将工具调用结果转换为 String 对象并返回 AI 模型的 ToolCallResultConverter 实例(未配置时默认使用 DefaultToolCallResultConverter)。
ToolMetadata.Builder 是用于构建 ToolMetadata 实例的建造者类,主要用于定义工具的元数据信息。ToolMetadata 包含了工具的核心描述信息,帮助 AI 模型理解工具的用途、参数要求和调用方式,是工具调用(Tool Calling)机制中连接 AI 模型与实际工具逻辑的重要桥梁。
ToolMetadata.Builder 定义如下:

字段说明:
returnDirect:控制是否将工具结果直接返回客户端(true)还是传回模型(false)。
下面是一个通过编程方式定义 FunctionToolCallback 的完整例子:
ToolCallback toolCallback = FunctionToolCallback
.builder("currentWeather", new WeatherService()) // 前面的 WeatherService 服务
.description("获取指定地点的天气情况")
.inputType(WeatherRequest.class)
.build();注意,函数输入和输出可以是 Void 或 POJO。输入和输出的 POJO 必须是可序列化的,因为结果将被序列化并发送回模型。函数及输入输出类型必须是 public 的。
可以将 FunctionToolCallback 实例传递给 ChatClient 的 toolCallbacks() 方法,该工具仅在添加它的特定聊天请求中可用。例如:
@RestController
class MyController {
@Autowired
private ChatModel chatModel;
// 模型使用 gpt-4-turbo
// GPT-4 全面支持工具调用功能,且在处理复杂度、准确性和灵活性上显著优于 GPT-3.5-turbo
// http://localhost:8080/ai
@GetMapping(value = "/ai",produces = "text/html; charset=UTF-8")
public String ai() {
ToolCallback toolCallback = FunctionToolCallback
.builder("currentWeather", new WeatherService())
.description("获取指定地点的天气情况")
.inputType(WeatherRequest.class)
.build();
return ChatClient.create(chatModel)
.prompt("哥本哈根的天气怎么样?")
.toolCallbacks(toolCallback) // 将 ToolCallback 设置给 ChatClient
.call()
.content();
}
}可以通过将 FunctionToolCallback 实例传递给 ChatClient.Builder 的 defaultToolCallbacks() 方法来添加默认工具。若同时提供默认工具和运行时工具,运行时工具将完全覆盖默认工具。
默认工具在所有基于同一 ChatClient.Builder 构建的 ChatClient 实例执行的聊天请求间共享。此类工具适用于跨聊天请求的通用功能,但若使用不当,可能导致工具在不该出现的场景中被调用。例如:
@RestController
class MyController {
@Autowired
private ChatModel chatModel;
// 模型使用 gpt-4-turbo
// GPT-4 全面支持工具调用功能,且在处理复杂度、准确性和灵活性上显著优于 GPT-3.5-turbo
// http://localhost:8080/ai
@GetMapping(value = "/ai",produces = "text/html; charset=UTF-8")
public String ai() {
ToolCallback toolCallback = FunctionToolCallback
.builder("currentWeather", new WeatherService())
.description("获取指定地点的天气情况")
.inputType(WeatherRequest.class)
.build();
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultToolCallbacks(toolCallback)
.build();
return chatClient.prompt("哥本哈根的天气怎么样?").call().content();
}
}可以将 FunctionToolCallback 实例传递给 ToolCallingChatOptions 的 toolCallbacks() 方法。该工具仅在添加它的特定聊天请求中可用。例如:
@RestController
class MyController {
@Autowired
private ChatModel chatModel;
// 模型使用 gpt-4-turbo
// GPT-4 全面支持工具调用功能,且在处理复杂度、准确性和灵活性上显著优于 GPT-3.5-turbo
// http://localhost:8080/ai
@GetMapping(value = "/ai",produces = "text/html; charset=UTF-8")
public String ai() {
ToolCallback toolCallback = FunctionToolCallback
.builder("currentWeather", new WeatherService())
.description("获取指定地点的天气情况")
.inputType(WeatherRequest.class)
.build();
ChatOptions chatOptions = ToolCallingChatOptions.builder()
.toolCallbacks(toolCallback)
.build();
Prompt prompt = new Prompt("哥本哈根的天气怎么样?", chatOptions);
return chatModel.call(prompt).getResult().getOutput().getText();
}
}可以在构建 ChatModel 时,通过用于创建 ChatModel 的 OpenAiChatOptions 实例的 toolCallbacks() 方法传入 FunctionToolCallback 实例来添加默认工具。若同时提供默认工具和运行时工具,运行时工具将完全覆盖默认工具。
默认工具在该 ChatModel 实例处理的所有聊天请求间共享。此类工具适用于跨聊天请求的通用功能,但若使用不当,可能导致工具在不该出现的场景中被调用。
简单示例:
@RestController
class MyController {
@Value("${spring.ai.openai.base-url}")
private String baseUrl;
@Value("${spring.ai.openai.api-key}")
private String apiKey;
// 模型使用 gpt-4-turbo
// GPT-4 全面支持工具调用功能,且在处理复杂度、准确性和灵活性上显著优于 GPT-3.5-turbo
// http://localhost:8080/ai
@GetMapping(value = "/ai",produces = "text/html; charset=UTF-8")
public String ai() {
ToolCallback toolCallback = FunctionToolCallback
.builder("currentWeather", new WeatherService())
.description("获取指定地点的天气情况")
.inputType(WeatherRequest.class)
.build();
ChatModel chatModel = OpenAiChatModel.builder()
.openAiApi(OpenAiApi.builder().baseUrl(baseUrl).apiKey(apiKey).build())
.defaultOptions(OpenAiChatOptions.builder()
.model(OpenAiApi.ChatModel.GPT_4_TURBO) // 模型名
.toolCallbacks(toolCallback)
.build()
).build();
return chatModel.call("哥本哈根的天气怎么样?");
}
}
动态规范允许我们无需编程式配置工具,你可将工具定义为 Spring Bean,由 Spring AI 通过 ToolCallbackResolver 接口(具体实现为 SpringBeanToolCallbackResolver)在运行时动态解析。
此方案支持将任意 Function、Supplier、Consumer 或 BiFunction 类型的 Bean 作为工具使用。Bean 名称将作为工具名称,可通过 Spring Framework 的 @Description 注解提供工具描述(用于指导模型判断调用时机及方式)。如果未提供描述,则使用方法名称作为工具描述。但强烈建议提供详细描述,这对模型理解工具用途及使用方法至关重要。如果描述不充分,可能导致模型在该调用工具时未调用,或错误调用工具。
例如:
@Configuration(proxyBeanMethods = false)
class WeatherTools {
WeatherService weatherService = new WeatherService();
@Bean
@Description("获取指定地点的天气情况") // 巩固描述
Function<WeatherRequest, WeatherResponse> currentWeather() {
return weatherService;
}
}工具输入参数的 JSON Schema 将自动生成。你可使用 @ToolParam 注解提供输入参数的额外信息(如描述、是否必需等),默认情况下所有输入参数均为必需参数。如下:
// 通过 @ToolParam 注解为 location 参数指定详细描述信息
record WeatherRequest(@ToolParam(description = "一个城市或一个国家的名称") String location, Unit unit) {
//...
}注意,该工具规范方案存在无法保证类型安全的缺点(因工具解析是在运行时完成,根据工具名称进行解析)。要解决此问题,你可通过 @Bean 注解显式指定工具名称,并将名称存储在常量中,以便在聊天请求中使用该常量而非硬编码工具名称。例如:
@Configuration(proxyBeanMethods = false)
class WeatherTools {
// 工具名称常量
public static final String CURRENT_WEATHER_TOOL = "currentWeather";
@Bean(CURRENT_WEATHER_TOOL) // 为工具指定名称
@Description("获取指定地点的天气情况")
Function<WeatherRequest, WeatherResponse> currentWeather() {
//...
}
}可以将工具名称(即函数 bean 名称)传递给 ChatClient 的 toolNames() 方法。该工具仅在添加它的特定聊天请求中可用。
(1)使用 @Configuration 注解创建配置类,配置一个名为“currentWeather”的 WeatherService 工具,如下:
@Configuration(proxyBeanMethods = false)
public class Config {
@Bean("currentWeather")
@Description("获取指定地点的天气情况")
Function<WeatherRequest, WeatherResponse> weatherService() {
System.out.println("->> weatherService()");
return new WeatherService();
}
}(2)创建一个名为 MyController 的控制器,如下:
@RestController
class MyController {
@Autowired
private ChatModel chatModel;
// 模型使用 gpt-4-turbo
// GPT-4 全面支持工具调用功能,且在处理复杂度、准确性和灵活性上显著优于 GPT-3.5-turbo
// http://localhost:8080/ai
@GetMapping(value = "/ai",produces = "text/html; charset=UTF-8")
public String ai() {
return ChatClient.create(chatModel)
.prompt("哥本哈根的天气怎么样?")
// 工具名称,使用 SpringBeanToolCallbackResolver 将名称解析为 ToolCallback
.toolNames("currentWeather")
.call()
.content();
}
}可以通过将工具名称传递给 ChatClient.Builder 的 defaultToolNames() 方法来添加默认工具。若同时提供默认工具和运行时工具,运行时工具将完全覆盖默认工具。
默认工具在所有基于同一 ChatClient.Builder 构建的 ChatClient 实例执行的聊天请求间共享。此类工具适用于跨聊天请求的通用功能,但若使用不当,可能导致工具在不该出现的场景中被调用。
(1)使用 @Configuration 注解创建配置类,配置一个名为“currentWeather”的 WeatherService 工具,如下:
@Configuration(proxyBeanMethods = false)
public class Config {
@Bean("currentWeather")
@Description("获取指定地点的天气情况")
Function<WeatherRequest, WeatherResponse> weatherService() {
System.out.println("->> weatherService()");
return new WeatherService();
}
}(2)创建一个名为 MyController 的控制器,如下:
@RestController
class MyController {
@Autowired
private ChatModel chatModel;
// 模型使用 gpt-4-turbo
// GPT-4 全面支持工具调用功能,且在处理复杂度、准确性和灵活性上显著优于 GPT-3.5-turbo
// http://localhost:8080/ai
@GetMapping(value = "/ai",produces = "text/html; charset=UTF-8")
public String ai() {
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultToolNames("currentWeather")
.build();
return chatClient.prompt("哥本哈根的天气怎么样?")
.call()
.content();
}
}可以通过调用 ChatModel 时使用的 ToolCallingChatOptions 中的 toolNames() 方法传入工具名称。该工具仅在添加它的特定聊天请求中可用。例如:
(1)使用 @Configuration 注解创建配置类,配置一个名为“currentWeather”的 WeatherService 工具,如下:
@Configuration(proxyBeanMethods = false)
public class Config {
@Bean("currentWeather")
@Description("获取指定地点的天气情况")
Function<WeatherRequest, WeatherResponse> weatherService() {
System.out.println("->> weatherService()");
return new WeatherService();
}
}(2)创建一个名为 MyController 的控制器,如下:
@RestController
class MyController {
@Autowired
private ChatModel chatModel;
// 模型使用 gpt-4-turbo
// GPT-4 全面支持工具调用功能,且在处理复杂度、准确性和灵活性上显著优于 GPT-3.5-turbo
// http://localhost:8080/ai
@GetMapping(value = "/ai",produces = "text/html; charset=UTF-8")
public String ai() {
ChatOptions chatOptions = ToolCallingChatOptions.builder()
.toolNames("currentWeather")
.build();
Prompt prompt = new Prompt("哥本哈根的天气怎么样?", chatOptions);
return chatModel.call(prompt).getResult().getOutput().getText();
}
}在构建 ChatModel 时,通过用于创建 ChatModel 的 OpenAiChatOptions 实例的 toolNames() 方法传入工具名称来添加默认工具。若同时提供默认工具和运行时工具,运行时工具将完全覆盖默认工具。
默认工具在该 ChatModel 实例处理的所有聊天请求间共享。此类工具适用于跨聊天请求的通用功能,但若使用不当,可能导致工具在不该出现的场景中被调用。
简单示例:
@RestController
class MyController {
@Value("${spring.ai.openai.base-url}")
private String baseUrl;
@Value("${spring.ai.openai.api-key}")
private String apiKey;
@Autowired
private ToolCallbackResolver toolCallbackResolver;
// 模型使用 gpt-4-turbo
// GPT-4 全面支持工具调用功能,且在处理复杂度、准确性和灵活性上显著优于 GPT-3.5-turbo
// http://localhost:8080/ai
@GetMapping(value = "/ai",produces = "text/html; charset=UTF-8")
public String ai() {
ChatModel chatModel = OpenAiChatModel.builder()
// 需要自己配置工具名称解析器,否则 currentWeather 不会被解析
.toolCallingManager(ToolCallingManager.builder().toolCallbackResolver(toolCallbackResolver).build())
.openAiApi(OpenAiApi.builder().baseUrl(baseUrl).apiKey(apiKey).build())
.defaultOptions(OpenAiChatOptions.builder()
.model(OpenAiApi.ChatModel.GPT_4_TURBO) // 模型名
.toolNames("currentWeather")
.build()
)
// 也可以在这里添加解析器
//.toolCallingManager(ToolCallingManager.builder().toolCallbackResolver(toolCallbackResolver).build())
.build();
return chatModel.call("哥本哈根的天气怎么样?");
}
}以下类型目前不支持作为工具函数的输入或输出类型:
基本类型
Optional
集合类型 (如 List、Map、Array、Set)
异步类型(如 CompletableFuture、Future)
响应式类型(如 Flow、Mono、Flux)
基本类型和集合在使用基于方法的工具规范方案时受支持。
提示:如果不能访问 OpenAI,请点击 AiCode API 注册账号,通过代理访问。