你可以使用 MistralAiChatClient 注册自定义的 Java 函数,并让 Mistral AI 模型智能地选择输出一个包含调用一个或多个已注册函数参数的 JSON 对象。这使您能够将 LLM 的能力与外部工具和 API 连接起来。mistral_small_latestmistral_large_latest 模型经过训练,能够检测何时应该调用函数,并以遵循函数签名的 JSON 响应。

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

Note
截至2024年3月13日,Mistral AI已将并行函数调用的支持集成到他们的`mistral_large_latest`模型中,这是首个春季AI Mistral AI时所缺少的功能。

Spring 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

通过链接:../mistralai-chat.html#_auto_configuration[MistralAiChatClient 自动配置],你可以通过多种方式在Spring上下文中注册自定义函数作为bean。

我们首先描述最适合POJO的选项。

普通Java函数

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

在内部,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/mistralai/tool/PaymentStatusBeanIT.java 中的 [PaymentStatusBeanIT.java] 展示了这种方法。

Tip
链接:https://github.com/spring-projects/spring-ai/blob/main/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/mistralai/tool/PaymentStatusBeanOpenAiIT[PaymentStatusBeanOpenAiIT]使用OpenAI API实现了相同的功能。在这方面,MistralAI与OpenAI几乎相同。

函数回调包装器

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

@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
	}
	...
}

它包装了第三方的`MockWeatherService`函数,并将其作为`CurrentWeather`函数注册到`MistralAiChatClient`中。它还提供了一个描述(2)和一个可选的响应转换器(3),以将响应转换成模型所期望的文本格式。

Note
默认情况下,响应转换器会对Response对象进行JSON序列化。
Note
FunctionCallbackWrapper`内部基于`MockWeatherService.Request`类解析函数调用签名。

在聊天选项中指定功能

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

MistralAiChatClient chatClient = ...

UserMessage userMessage = new UserMessage("巴黎的天气怎么样?");

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

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

根据用户的问题,将触发3次对`CurrentWeather`函数的调用(每个城市一次),并生成最终的响应。

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

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

MistralAiChatClient chatClient = ...

UserMessage userMessage = new UserMessage("巴黎的天气怎么样?");

var promptOptions = MistralAiChatOptions.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));
Note
在提示中注册的函数默认在此请求期间启用。

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

'''The PaymentStatusPromptIT.java 集成测试提供了一个注册函数至 MistralAiChatClient 并在提示请求中使用它的完整示例。'''

附录

使用最新的Mistral AI API在Java和Spring AI中调用函数[(博客) 使用最新的Mistral AI API在Java和Spring AI中调用函数]

米斯特拉尔人工智能API函数调用流程

以下图表展示了Mistral AI低级API的流程,用于链接:https://docs.mistral.ai/guides/function-calling[函数调用]:

mistral ai function calling flow

链接:https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-mistral-ai/src/test/java/org/springframework/ai/mistralai/api/tool/PaymentStatusFunctionCallingIT.java [PaymentStatusFunctionCallingIT.java] 提供了一个关于如何使用 Mistral AI API 函数调用的完整示例。它基于 https://docs.mistral.ai/guides/function-calling [Mistral AI 函数调用教程]。