函数调用允许开发者在代码中创建一个函数的描述,然后在请求中将该描述传递给一个语言模型。模型的响应包括匹配该描述的函数名称和调用它所需的参数。

您可以使用`AzureOpenAiChatClient`注册自定义Java函数,并让模型智能地选择输出一个包含调用一个或多个已注册函数参数的JSON对象。这使您能够将LLM(大型语言模型)的能力与外部工具和APIs连接。Azure模型经过训练,能够检测何时应调用函数,并以遵循函数签名的JSON响应。

Note
并行函数调用仅支持gpt-35-turbo (1106) 和 gpt-4 (1106-preview),也被称为GPT-4 Turbo Preview。

Azure OpenAI API不会直接调用函数;相反,模型会生成你可以在代码中用于调用函数的JSON,并将结果返回给模型以完成对话。

春季AI提供灵活且用户友好的方式来注册和调用自定义函数。一般而言,自定义函数需要提供函数的`名称`、描述`以及函数调用的`签名(作为JSON模式),以让模型知道该函数期望什么参数。`描述`帮助模型理解何时调用该函数。

作为一名开发者,你需要实现一个函数,该函数接收来自AI模型发送的函数调用参数,并将结果返回给模型。你的函数可以进一步调用其他第三方服务来提供结果。

Spring AI使得这变得很简单,只需定义一个返回`java.util.Function`的`@Bean`定义,并在调用`ChatClient`时提供bean的名称作为选项。

在底层,Spring会用适当的适配器代码包装你的POJO(函数),使其能与AI模型交互,避免了你编写繁琐的样板代码。底层基础设施的核心是[FunctionCallback.java](FunctionCallbackWrapper.java(https://github.com/spring-projects/spring-ai/blob/main/spring-ai-core/src/main/java/org/springframework/ai/model/function/FunctionCallbackWrapper.java)。

它是如何工作的

假设我们希望AI模型能够回答其所不具备的信息,例如给定位置的当前温度。

我们可以为AI模型提供关于我们自己功能的元数据,它可以在处理你的提示时使用这些信息来检索该信息。

例如,如果在处理一个提示时,AI模型确定它需要关于特定位置的温度的额外信息,它将启动一个服务器端生成的请求/响应交互。AI模型调用一个客户端函数。AI模型提供方法调用的详细信息,以JSON格式,客户端有责任执行该函数并返回响应。

Spring AI大大简化了你需要编写以支持函数调用的代码。它为你代理了函数调用的对话。你可以简单地提供你的函数定义作为一个`@Bean`,然后在你的提示选项中提供该函数的bean名称。你还可以在你的提示中引用多个函数bean名称。

快速开始

让我们创建一个通过调用我们自己的函数来回答问题的聊天机器人。为了支持聊天机器人的回答,我们将注册我们自己的函数,该函数接受一个地点并返回该地点的当前天气。

当模型需要回答如`"What’s the weather like in Boston?"`这样的问题时,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

通过链接:../azure-openai-chat.html#_auto_configuration[AzureOpenAiChatClient Auto-Configuration],你可以有多种方式在Spring上下文中注册自定义函数作为bean。

我们从描述最符合POJO友好性的选项开始。

简单的Java函数

在这种方法中,你可以像管理其他Spring管理的对象一样,在你的应用程序上下文中定义`@Beans`。

在内部,Spring AI 的 ChatClient 会创建一个 FunctionCallbackWrapper 包装器的实例,该包装器添加了通过 AI 模型调用它的逻辑。@Bean 的名称作为 ChatOption 传递。

@Configuration
static class Config {

@Bean
@Description("Get the weather in location") // function 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 name as function name.
	return new MockWeatherService();
}
...
}

@JsonClassDescription("Get the weather in location") // (2) function description
public record Request(String location, Unit unit) {}

将请求对象注释得尽可能描述性,以便生成的该函数的JSON模式尽可能描述性,这是一个最佳实践,有助于AI模型选择调用正确的函数。

链接:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/tool/FunctionCallWithFunctionBeanIT.java[FunctionCallWithFunctionBeanIT.java] 展示了这种方法。

函数回调包装器

另一种注册函数的方式是创建`FunctionCallbackWrapper`包装器,像这样:

@Configuration
static class Config {

@Bean
public FunctionCallback weatherFunctionInfo() {

return FunctionCallbackWrapper.builder(new MockWeatherService())
			.withName("CurrentWeather") // (1) 函数名
			.withDescription("Get the current weather in a given location") // (2) 函数描述
			.build();
	}
	...
}

它封装了第三方的`MockWeatherService`函数,并将其作为`CurrentWeather`函数注册到`ChatClient`中,并提供了描述(2)。

Note
默认的响应转换器会对Response对象进行JSON序列化。
Note
FunctionCallbackWrapper`内部根据`MockWeatherService.Request`类解析函数调用签名,并内部生成函数调用的JSON模式。

在聊天选项中指定功能

要让模型知道并调用你的`CurrentWeather`函数,你需要在你的提示请求中启用它:

AzureOpenAiChatClient chatClient = ...

UserMessage userMessage = new UserMessage("旧金山、东京和巴黎的天气怎么样?");

ChatResponse response = chatClient.call(new Prompt(List.of(userMessage),
		AzureOpenAiChatOptions.builder().withFunction("CurrentWeather").build())); // (1) Enable the function

logger.info("Response: {}", response);

上述用户问题将触发对`CurrentWeather`函数的3次调用(每个城市一次),最终的回应将类似于此:

以下是所请求城市的当前天气情况:
- 旧金山, CA(加利福尼亚): 30.0°C
- 东京, 日本: 10.0°C
- 巴黎, 法国: 15.0°C

链接:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/tool/FunctionCallWithFunctionWrapperIT.java[FunctionCallWithFunctionWrapperIT.java] 测试展示了这种方法。

注册/调用具有提示选项的函数

除了自动配置外,您还可以动态地为您的提示请求注册回调函数:

AzureOpenAiChatClient chatClient = ...

UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?  Use Multi-turn function calling.");

var promptOptions = AzureOpenAiChatOptions.builder()
	.withFunctionCallbacks(List.of(FunctionCallbackWrapper.builder(new MockWeatherService())
		.withName("CurrentWeather")
		.withDescription("Get the weather in location")
		.build()))
	.build();

ChatResponse response = chatClient.call(new Prompt(List.of(userMessage), promptOptions));
Note
在提示中注册的函数默认在此请求期间启用。

这种方法允许根据用户输入动态选择要调用的不同函数。

'''该https://github.com/spring-projects/spring-ai/blob/main/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/azure/tool/FunctionCallWithPromptFunctionIT.java[FunctionCallWithPromptFunctionIT.java]集成测试提供了如何使用`AzureOpenAiChatClient`注册函数并在提示请求中使用它的完整示例。'''