函式呼叫

此頁面描述了舊版的函式呼叫 API,該 API 已被棄用,並將在下一版本中移除。當前版本請參閱 工具呼叫。更多資訊請參閱 遷移指南

您可以在 OpenAiChatModel 中註冊自定義 Java 函式,然後 OpenAI 模型將智慧地選擇輸出一個 JSON 物件,其中包含呼叫一個或多個已註冊函式的引數。這使您能夠將 LLM 功能與外部工具和 API 連線起來。OpenAI 模型經過訓練,可以檢測何時應呼叫某個函式,並響應符合函式簽名的 JSON。

OpenAI API 不直接呼叫函式;相反,模型會生成 JSON,您可以在程式碼中使用該 JSON 呼叫函式,並將結果返回給模型以完成對話。

Spring AI 提供了靈活且使用者友好的方式來註冊和呼叫自定義函式。一般來說,自定義函式需要提供函式 namedescription 以及函式呼叫 signature(作為 JSON schema),以便模型知道函式期望哪些引數。description 有助於模型理解何時呼叫該函式。

作為開發人員,您需要實現一個函式,該函式接收從 AI 模型傳送的函式呼叫引數,並將結果響應回模型。您的函式可以轉而呼叫其他第三方服務來提供結果。

Spring AI 使這一過程變得簡單,只需定義一個返回 java.util.Function@Bean 定義,並在呼叫 ChatModel 時將 bean 名稱作為選項提供即可。

底層實現中,Spring 會使用適當的介面卡程式碼封裝您的 POJO(即函式),從而實現與 AI 模型的互動,省去了編寫繁瑣的樣板程式碼。底層基礎設施的基礎是 ToolCallback.java 介面以及配套的 Builder 工具類,用於簡化 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

透過 OpenAiChatModel 自動配置,您可以透過多種方式在 Spring 上下文中將自定義函式註冊為 bean。

我們首先介紹最對 POJO 友好的選項。

普通 Java 函式

在這種方法中,您可以在應用程式上下文中像定義其他任何 Spring 管理物件一樣定義一個 @Bean

在內部,Spring AI ChatModel 將建立一個 ToolCallback 例項,該例項添加了透過 AI 模型呼叫它的邏輯。@Bean 的名稱將作為 ChatOption 傳遞。

@Configuration
static class Config {

	@Bean
	@Description("Get the weather in location") // function description
	public Function<MockWeatherService.Request, MockWeatherService.Response> currentWeather() {
		return new MockWeatherService();
	}

}

@Description 註解是可選的,它提供了一個函式描述,幫助模型理解何時呼叫該函式。這是一個重要的屬性,有助於 AI 模型確定呼叫哪個客戶端函式。

提供函式描述的另一種選擇是在 MockWeatherService.Request 上使用 @JsonClassDescription 註解

@Configuration
static class Config {

	@Bean
	public Function<Request, Response> currentWeather() { // bean name as function name
		return new MockWeatherService();
	}

}

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

最佳實踐是使用資訊註解請求物件,以便該函式生成的 JSON schema 儘可能具有描述性,從而幫助 AI 模型選擇正確的函式進行呼叫。

FunctionCallbackWithPlainFunctionBeanIT.java 演示了這種方法。

FunctionToolCallback 包裝器

註冊函式的另一種方法是建立一個 toolCallback,如下所示

@Configuration
static class Config {

	@Bean
	public FunctionToolCallback weatherFunctionInfo() {

	    return FunctionToolCallback.builder("CurrentWeather", new MockWeatherService()) // (1) function name and instance
	        .description("Get the weather in location") // (2) function description
	        .inputType(MockWeatherService.Request.class) // (3) function input type
	        .build();
	}

}

它包裝了第三方 MockWeatherService 函式,並將其註冊為 OpenAiChatModel 的 CurrentWeather 函式。它還提供了一個描述 (2) 和一個輸入型別 (3),用於生成函式呼叫的 JSON schema。

預設情況下,響應轉換器執行 Response 物件的 JSON 序列化。
FunctionToolCallback 在內部根據 MockWeatherService.Request 類解析函式呼叫簽名。

在聊天選項中指定函式

要讓模型知道並呼叫您的 CurrentWeather 函式,您需要在提示請求中啟用它

OpenAiChatModel chatModel = ...

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

ChatResponse response = this.chatModel.call(new Prompt(this.userMessage,
		OpenAiChatOptions.builder().tools("CurrentWeather").build())); // Enable the function

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

上述使用者問題將觸發 3 次對 CurrentWeather 函式的呼叫(每個城市一次),最終響應將類似如下內容

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

OpenAiFunctionCallbackIT.java 測試演示了這種方法。

使用提示選項註冊/呼叫函式

除了自動配置外,您還可以使用您的 Prompt 請求動態註冊回撥函式

OpenAiChatModel chatModel = ...

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

var promptOptions = OpenAiChatOptions.builder()
	.toolCallbacks(List.of(FunctionToolCallback.builder("CurrentWeather", new MockWeatherService()) // (1) function name and instance
        .description("Get the weather in location") // (2) function description
        .inputType(MockWeatherService.Request.class) // (3) function input type
        .build())) // function code
	.build();

ChatResponse response = this.chatModel.call(new Prompt(this.userMessage, this.promptOptions));
透過提示詞註冊的函式在此請求期間預設啟用。

這種方法允許根據使用者輸入動態選擇要呼叫的不同函式。

FunctionCallbackInPromptIT.java 整合測試提供了一個完整的示例,演示如何在 OpenAiChatModel 中註冊函式並在提示請求中使用它。

工具上下文支援

Spring AI 現在支援透過工具上下文將附加的上下文資訊傳遞給函式回撥。此功能允許您提供可在函式執行期間使用的額外資料,從而增強了函式呼叫的靈活性和強大性。

上下文資訊作為 java.util.BiFunction 的第二個引數傳入。ToolContext 包含一個不可變的 Map<String,Object>,允許您訪問鍵值對。

如何使用工具上下文

您可以在構建聊天選項時設定工具上下文,併為您的回撥使用 BiFunction

BiFunction<MockWeatherService.Request, ToolContext, MockWeatherService.Response> weatherFunction =
    (request, toolContext) -> {
        String sessionId = (String) toolContext.getContext().get("sessionId");
        String userId = (String) toolContext.getContext().get("userId");

        // Use sessionId and userId in your function logic
        double temperature = 0;
        if (request.location().contains("Paris")) {
            temperature = 15;
        }
        else if (request.location().contains("Tokyo")) {
            temperature = 10;
        }
        else if (request.location().contains("San Francisco")) {
            temperature = 30;
        }

        return new MockWeatherService.Response(temperature, 15, 20, 2, 53, 45, MockWeatherService.Unit.C);
    };

OpenAiChatOptions options = OpenAiChatOptions.builder()
    .model(OpenAiApi.ChatModel.GPT_4_O.getValue())
    .toolCallbacks(List.of(FunctionToolCallback.builder("getCurrentWeather", this.weatherFunction)
        .description("Get the weather in location")
        .inputType(MockWeatherService.Request.class)
        .build()))
    .toolContext(Map.of("sessionId", "123", "userId", "user456"))
    .build();

在此示例中,weatherFunction 被定義為一個 BiFunction,它將請求和工具上下文都作為引數。這允許您直接在函式邏輯中訪問上下文。

然後,在呼叫聊天模型時可以使用這些選項

UserMessage userMessage = new UserMessage("What's the weather like in San Francisco, Tokyo, and Paris?");
ChatResponse response = chatModel.call(new Prompt(List.of(this.userMessage), options));

這種方法允許您將特定於會話或特定於使用者的資訊傳遞給函式,從而實現更具上下文感知和個性化的響應。

附錄

Spring AI 函式呼叫流程

下圖展示了 OpenAiChatModel 函式呼叫的流程

openai chatclient function call

OpenAI API 函式呼叫流程

下圖展示了 OpenAI API 函式呼叫的流程

openai function calling flow

OpenAiApiToolFunctionCallIT.java 提供了一個完整的示例,演示如何使用 OpenAI API 函式呼叫。它基於 OpenAI 函式呼叫教程