功能调用

您可以使用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名称。

# 快速入门 ## 简介 在本节中,我们将介绍如何快速开始使用Spring AI。以下是一些快速入门步骤。 ## 步骤 1. 下载并安装Spring AI框架。 2. 配置您的开发环境。 3. 创建一个新的Spring AI项目。 4. 编写和运行您的第一个Spring AI程序。 ## 结论 完成这些步骤后,您将能够快速开始使用Spring AI,并开始构建自己的人工智能解决方案。

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

当对模型的提示需要回答诸如"波士顿的天气如何?"这样的问题时,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类解析函数调用签名。
Amal needs help translating HTML documents from English to Chinese.Here is the translated HTML snippet:

在聊天选项中指定函数

要让模型知道并调用你的 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 并在提示请求中使用的完整示例。

Let me know if you need any further assistance!

附录:

Spring AI函数调用流程

下图说明了OpenAiChatClient函数调用的流程:

openai chatclient function call

OpenAI API函数调用流程

下图说明了OpenAI API函数调用的流程:

openai function calling flow

OpenAiApiToolFunctionCallIT.java提供了如何使用OpenAI API函数调用的完整示例。它基于OpenAI函数调用教程