【Spring AI】聊天API-OpenAI-Function Call

文章目录

  • [Function Calling](#Function Calling)
    • 工作原理
    • 快速上手
      • [将函数注册为 Bean](#将函数注册为 Bean)
        • [纯 Java 函数实现(Plain Java Functions)](#纯 Java 函数实现(Plain Java Functions))
        • [FunctionCallback Wrapper](#FunctionCallback Wrapper)
      • [Specifying functions in Chat Options](#Specifying functions in Chat Options)
      • [Register/Call Functions with Prompt Options](#Register/Call Functions with Prompt Options)
    • 附录:
      • [Spring AI 函数调用流程](#Spring AI 函数调用流程)
      • [OpenAI API 函数调用流程](#OpenAI API 函数调用流程)

Function Calling

您可以向 OpenAiChatClient 注册自定义 Java 函数,并让 OpenAI 模型输出包含参数的 JSON 对象,智能地选择调用一个或多个已注册函数。这允许您将LLM功能与外部工具和 API 连接起来。OpenAI 模型经过训练,可以检测何时应调用函数,并使用符合函数协议的 JSON 进行响应。

OpenAI API 不直接调用函数 ;相反,模型会生成符合函数协议的 JSON,您可以使用该 JSON 在代码中调用函数并将结果返回给模型以完成对话。

Spring AI 提供了灵活且用户友好的注册和调用自定义函数的方式。通常,自定义函数需要提供函数name、description和函数调用signature(作为 JSON 架构),以便让模型知道函数需要哪些参数。description有助于模型了解何时调用函数。

作为开发人员,您需要实现一个函数,该函数接收从 AI 模型返回的函数调用参数,并将函数的结果再返回给模型。您的函数也可以反过来调用其他第三方服务来提供结果。

Spring AI 使这变得简单,只需定义一个返回java.util.Function的@Bean定义,在调用ChatClient的时候,设置这个 Bean 的名字到ChatClient的参数中。

当下,Spring 使用适配器包装您的 POJO(函数),该代理支持与 AI 模型进行交互,从而避免编写繁琐的代码。底层基础结构的基础是 FunctionCallback.java 接口和配套的 FunctionCallbackWrapper.java实用程序类,以简化 Java 回调函数的实现和注册。

工作原理


假设我们希望 AI 模型处理它没有的信息进行响应,例如让大模型提供某个位置的实时温度。

我们可以为 AI 模型提供有关我们自己函数的元数据,它可以结合您的提示词和这些元数据来判断是否调用用户定义的函数检索该信息。

例如,如果在处理提示词期间,AI 模型确定它需要给定地域的实时温度,它将启动一个请求/响应交互的服务器端。AI 模型调用客户端。AI 模型以 JSON 格式提供方法调用详细信息给客户端,客户端负责执行该函数并将函数返回的数据返回给 AI模型。

模型与客户端的交互在下面的 Spring AI 函数调用流程图 中进行了说明。

Spring AI 大大简化了支持函数调用所需的代码。它为您代理函数调用对话。您只需将函数定义为@Bean,然后在提示词参数(options)中提供函数的 Bean 名称即可。您还可以在提示词中引用多个函数 Bean 名称。


快速上手

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

当模型对提示词的返回信息中有需要回答问题如"What's the weather like in Boston?",AI模型将调用客户端,提供位置值作为要传递给函数的参数。此类似 RPC 的数据以 JSON 形式传递。

我们的函数可以基于一些 SaaS 的天气服务 API 实现,将天气响应返回给模型以完成对话。在此示例中,我们将使用一个名为" MockWeatherService " 的类进行硬编码来简单实现,该实现对不同位置的温度进行硬编码。

天气服务 API MockWeatherService.java 如下所示:

java 复制代码
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

OpenAI 章节中自动装配 机制,可以让您通过多种方式在 Spring 上下文中将自定义函数注册为 bean。

我们需要对POJO的参数做友好的描述。

纯 Java 函数实现(Plain Java Functions)

在这种方法中,您可以在应用程序上下文中定义@Beans,就像定义任何其他 Spring 托管对象一样。

在内部,Spring AI ChatClient 将创建一个 FunctionCallbackWrapper 包装类实例,该实例添加通过 AI 模型调用它的逻辑。@Bean的名称将转换成一个ChatOption 。

java 复制代码
@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 模型确定要调用的客户端函数。

提供函数描述的另一个注解是 @JacksonDescription , 这个注解可以作用在MockWeatherService.Request 上面:

java 复制代码
@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 模型可以正确选择要调用的函数。
FunctionCallbackWithPlainFunctionBeanIT.java 演示了这种方法。

FunctionCallback Wrapper

注册函数的另一种方法是创建 FunctionCallbackWrapper 包装器,如下所示:

java 复制代码
@Configuration
static class Config {

	@Bean
	public FunctionCallback weatherFunctionInfo() {

		return new FunctionCallbackWrapper<>("CurrentWeather", // (1) function name
				"Get the weather in location", // (2) function description
				(response) -> "" + response.temp() + response.unit(), // (3) Response Converter
				new MockWeatherService()); // function code
	}
	...
}

它包装第 3 方函数MockWeatherService并将其注册为 OpenAiChatClient 的 CurrentWeather 函数 。它还提供了函数描述 (2)参数 和可选的响应转换器 (3)参数(用于将响应转换为模型预期的文本)。

默认情况下,响应转换器是对 Response 对象执行 JSON 序列化操作。
FunctionCallbackWrapper 基于 MockWeatherService.Request 类解析函数调用签名。

Specifying functions in Chat Options

要让模型知道并调用您的 CurrentWeather 函数,您需要在提示词中启用它:

java 复制代码
OpenAiChatClient chatClient = ...

UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?");

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

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

上面的用户问题将触发 3 次CurrentWeather函数调用(每个城市一个),最终响应如下:

text 复制代码
Here is the current weather for the requested cities:
- San Francisco, CA: 30.0°C
- Tokyo, Japan: 10.0°C
- Paris, France: 15.0°C

FunctionCallbackWrapperIT.java 测试演示了这种方法。

Register/Call Functions with Prompt Options

除了自动配置之外,您还可以在提示词中动态注册回调函数:

java 复制代码
OpenAiChatClient chatClient = ...

UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?");

var promptOptions = OpenAiChatOptions.builder()
	.withFunctionCallbacks(List.of(new FunctionCallbackWrapper<>(
		"CurrentWeather", // name
		"Get the weather in location", // function description
		new MockWeatherService()))) // function code
	.build();

ChatResponse response = chatClient.call(new Prompt(List.of(userMessage), promptOptions));

默认情况下,会启用提示词内注册的函数。

这种方法允许用户动态输入想要调用的函数。
FunctionCallbackInPromptIT.java 集成测试提供了一个完整的示例,说明如何向 OpenAiChatClient 注册函数并在提示词中使用它。


附录:


Spring AI 函数调用流程

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

OpenAI API 函数调用流程

下图说明了 OpenAI API Function Calling 的流程:

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


相关推荐
余炜yw3 分钟前
【LSTM实战】跨越千年,赋诗成文:用LSTM重现唐诗的韵律与情感
人工智能·rnn·深度学习
乌啼霜满天24913 分钟前
Spring 与 Spring MVC 与 Spring Boot三者之间的区别与联系
java·spring boot·spring·mvc
莫叫石榴姐19 分钟前
数据科学与SQL:组距分组分析 | 区间分布问题
大数据·人工智能·sql·深度学习·算法·机器学习·数据挖掘
Elaine20239137 分钟前
零碎04 MybatisPlus自定义模版生成代码
java·spring·mybatis
如若12341 分钟前
利用 `OpenCV` 和 `Matplotlib` 库进行图像读取、颜色空间转换、掩膜创建、颜色替换
人工智能·opencv·matplotlib
YRr YRr1 小时前
深度学习:神经网络中的损失函数的使用
人工智能·深度学习·神经网络
ChaseDreamRunner1 小时前
迁移学习理论与应用
人工智能·机器学习·迁移学习
Guofu_Liao1 小时前
大语言模型---梯度的简单介绍;梯度的定义;梯度计算的方法
人工智能·语言模型·矩阵·llama
我爱学Python!1 小时前
大语言模型与图结构的融合: 推荐系统中的新兴范式
人工智能·语言模型·自然语言处理·langchain·llm·大语言模型·推荐系统
果冻人工智能1 小时前
OpenAI 是怎么“压力测试”大型语言模型的?
人工智能·语言模型·压力测试