功能调用
您可以使用OpenAiChatClient
注册自定义Java函数,并让OpenAI模型智能地选择输出包含调用注册函数之一或多个的参数的JSON对象。这使您能够将LLM功能与外部工具和API连接起来。OpenAI模型经过训练,能够检测到何时应该调用函数,并以符合函数签名的JSON响应。
OpenAI API不直接调用函数;相反,模型生成的JSON可以用于在您的代码中调用函数,并将结果返回给模型以完成对话。
Spring AI提供了灵活且用户友好的方法来注册和调用自定义函数。一般来说,自定义函数需要提供一个函数名称
、描述
以及函数调用签名
(作为JSON模式),以让模型知道函数需要什么参数。描述
有助于模型理解何时调用函数。
作为开发者,您需要实现一个函数,该函数接收来自AI模型的函数调用参数,并将结果返回给模型。您的函数可以反过来调用其他第三方服务来提供结果。
Spring AI使这变得非常简单,只需定义一个返回java.util.Function
的@Bean
定义,并在调用ChatClient
时提供bean名称作为选项。
在幕后,Spring使用适当的适配器代码包装您的POJO(函数),使其能够与AI模型进行交互,从而避免您编写繁琐的样板代码。基础设施的基础是FunctionCallback.java接口以及配套的FunctionCallbackWrapper.java实用类,以简化Java回调函数的实现和注册。
工作原理
假设我们希望AI模型以回答它没有的信息,例如给定位置的当前温度。
我们可以向AI模型提供关于我们自己函数的元数据,以便它在处理您的提示时使用它来检索该信息。
例如,如果在处理提示的过程中,AI模型确定需要有关给定位置温度的附加信息,它将启动一个服务器端生成的请求/响应交互。AI模型调用客户端函数。AI模型提供JSON格式的方法调用详情,客户端负责执行该函数并返回响应。
模型与客户端的交互在Spring AI功能调用流程图中有所说明。
Spring AI大大简化了您需要编写以支持函数调用的代码。它为您进行了函数调用对话的代理。您只需将函数定义提供为@Bean
,然后在提示选项中提供函数的bean名称。您还可以在提示中引用多个函数bean名称。
让我们创建一个聊天机器人,通过调用我们自己的函数来回答问题。为了支持聊天机器人的响应,我们将注册一个接受位置并返回该位置当前天气的函数。
当对模型的提示需要回答诸如"波士顿的天气如何?"
这样的问题时,AI模型将调用客户端,提供位置值作为要传递给函数的参数。这种类似RPC的数据以JSON形式传递。
我们的函数可以使用一些基于SaaS的天气服务API,并将天气响应返回给模型以完成对话。在这个例子中,我们将使用一个简单的实现,名为MockWeatherService
,它为各种位置硬编码了温度。
以下MockWeatherService.java
代表了天气服务API:
public class MockWeatherService implements Function<Request, Response> {
public enum Unit { C, F }
public record Request(String location, Unit unit) {}
public record Response(double temp, Unit unit) {}
public Response apply(Request request) {
return new Response(30.0, Unit.C);
}
}
将函数注册为Bean
通过OpenAiChatClient自动配置,您可以以多种方式将自定义函数注册为Spring上下文中的bean。
我们从描述最POJO友好的选项开始。
普通Java函数
在这种方法中,您可以像定义任何其他Spring管理对象一样在应用程序上下文中定义@Beans
。
在内部,Spring AI ChatClient
将创建一个FunctionCallbackWrapper
的实例,该实例为其通过AI模型调用添加了逻辑。 @Bean
的名称被传递为ChatOption
。
@Configuration
static class Config {
@Bean
@Description("获取位置的天气") // 函数描述
public Function<MockWeatherService.Request, MockWeatherService.Response> weatherFunction1() {
return new MockWeatherService();
}
...
}
@Description
注解是可选的,并提供一个函数描述(2),帮助模型了解何时调用函数。这是一个重要的属性,设置它可以帮助AI模型确定何时调用客户端端函数。
提供函数描述的另一种选项是在MockWeatherService.Request
上使用@JacksonDescription
注解提供函数描述:
@Configuration
static class Config {
@Bean
public Function<Request, Response> currentWeather3() { // (1) bean名称作为函数名称。
return new MockWeatherService();
}
...
}
@JsonClassDescription("获取位置的天气") // (2) 函数描述
public record Request(String location, Unit unit) {}
最佳实践是使用注解注释请求对象的信息,以使该函数生成的JSON模式尽可能描述性,以帮助AI模型选择正确的函数进行调用。
FunctionCallback包装器
另一种注册函数的方式是创建如下所示的FunctionCallbackWrapper
包装器:
@Configuration
static class Config {
@Bean
public FunctionCallback weatherFunctionInfo() {
return new FunctionCallbackWrapper<>("CurrentWeather", // (1) 函数名称
"获取位置的天气", // (2) 函数描述
(response) -> "" + response.temp() + response.unit(), // (3) 响应转换器
new MockWeatherService()); // 函数代码
}
...
}
它包装了第三方MockWeatherService
函数,并将其注册为CurrentWeather
函数与OpenAiChatClient
。 它还提供了一个描述(2)和一个可选的响应转换器(3),将响应转换为模型所期望的文本。
默认情况下,响应转换器对响应对象进行JSON序列化。 |
FunctionCallbackWrapper 在内部根据MockWeatherService.Request 类解析函数调用签名。 |
在聊天选项中指定函数
要让模型知道并调用你的 CurrentWeather
函数,你需要在提示请求中启用它:
OpenAiChatClient chatClient = ...
UserMessage userMessage = new UserMessage("旧金山、东京和巴黎的天气如何?");
ChatResponse response = chatClient.call(new Prompt(List.of(userMessage),
OpenAiChatOptions.builder().withFunction("CurrentWeather").build())); // (1) 启用函数
logger.info("响应: {}", response);
上述用户问题将触发 3 次对 CurrentWeather
函数的调用(每个城市一次),最终响应将类似于这样:
以下是请求城市的当前天气: - 加利福尼亚州旧金山:30.0°C - 日本东京:10.0°C - 法国巴黎:15.0°C
该 FunctionCallbackWrapperIT.java 测试演示了这种方法。
使用提示选项注册/调用函数
除了自动配置外,你还可以在提示请求中动态地注册回调函数:
OpenAiChatClient chatClient = ...
UserMessage userMessage = new UserMessage("旧金山、东京和巴黎的天气如何?");
var promptOptions = OpenAiChatOptions.builder()
.withFunctionCallbacks(List.of(new FunctionCallbackWrapper<>(
"CurrentWeather", // 名称
"获取位置的天气", // 函数描述
new MockWeatherService()))) // 函数代码
.build();
ChatResponse response = chatClient.call(new Prompt(List.of(userMessage), promptOptions));
在提示中注册的函数将在请求期间默认启用。 |
这种方法允许基于用户输入动态选择要调用的不同函数。
集成测试 FunctionCallbackInPromptIT.java 提供了如何注册函数到 OpenAiChatClient
并在提示请求中使用的完整示例。
附录:
OpenAI API函数调用流程
下图说明了OpenAI API函数调用的流程:
OpenAiApiToolFunctionCallIT.java提供了如何使用OpenAI API函数调用的完整示例。它基于OpenAI函数调用教程。