Gemini 函數呼叫

警告:顯然 Gemini Pro 無法再正確處理函數名稱。平行函數呼叫也已消失。

函數呼叫讓開發人員可以在程式碼中建立函數的描述,然後將該描述傳遞給請求中的語言模型。來自模型的響應包括函數的名稱,該名稱與描述相符,以及調用它時的參數。

您可以向 VertexAiGeminiChatModel 註冊自訂 Java 函數,並讓 Gemini Pro 模型智慧地選擇輸出一個 JSON 物件,其中包含調用一個或多個已註冊函數的參數。這讓您可以將 LLM 功能與外部工具和 API 連接起來。VertexAI Gemini Pro 模型經過訓練,可以偵測何時應該調用函數,並以符合函數簽章的 JSON 回應。

VertexAI Gemini API 不會直接調用函數;相反地,模型會產生 JSON,您可以使用它在程式碼中調用函數,並將結果返回給模型以完成對話。

Spring AI 提供了彈性且使用者友善的方式來註冊和調用自訂函數。一般而言,自訂函數需要提供函數名稱描述和函數調用簽章(作為 Open API 綱要),以讓模型知道函數預期的參數。描述有助於模型理解何時調用函數。

作為開發人員,您需要實作一個函數,該函數接收從 AI 模型傳送的函數調用參數,並以結果回應模型。您的函數可以進而調用其他第三方服務以提供結果。

Spring AI 使這一切變得非常容易,只需定義一個 @Bean 定義,該定義返回 java.util.Function,並在調用 ChatModel 時提供 Bean 名稱作為選項即可。

在底層,Spring 會使用適當的配接器程式碼包裝您的 POJO(函數),以實現與 AI 模型的互動,從而讓您免於編寫繁瑣的樣板程式碼。底層基礎架構的基礎是 FunctionCallback.java 介面和配套的 Builder 实用程式類別,以簡化 Java 回調函數的實作和註冊。

運作方式

假設我們希望 AI 模型回應它沒有的資訊,例如給定位置的目前溫度。

我們可以向 AI 模型提供有關我們自己的函數的元數據,它可以利用這些元數據在處理您的提示詞時檢索該資訊。

例如,如果在處理提示詞期間,AI 模型判斷它需要有關給定位置溫度的額外資訊,它將啟動伺服器端產生的請求/回應互動。AI 模型調用用戶端函數。AI 模型以 JSON 格式提供方法調用詳細資訊,而用戶端有責任執行該函數並傳回回應。

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

透過 VertexAiGeminiChatModel 自動配置,您有多種方法可以在 Spring 上下文中將自訂函數註冊為 Bean。

我們先從描述最 POJO 友善的選項開始。

純 Java 函數

在這種方法中,您可以在應用程式上下文中定義 @Beans,就像您對任何其他 Spring 管理的物件所做的那樣。

在內部,Spring AI ChatModel 將建立一個 FunctionCallback 實例,該實例新增了透過 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 上使用 @JsonClassDescription 註解以提供函數描述

@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 模型選擇正確的函數來調用。

FunctionCallWithFunctionBeanIT.java 示範了這種方法。

FunctionCallback 包裝器

註冊函數的另一種方法是建立 FunctionCallback 實例,如下所示

@Configuration
static class Config {

	@Bean
	public FunctionCallback weatherFunctionInfo() {

		return FunctionCallback.builder()
			.description("Get the current weather in a given location") // (2) function description
			.schemaType(SchemaType.OPEN_API_SCHEMA) // (3) schema type. Compulsory for Gemini function calling.
			.function("CurrentWeather", new MockWeatherService()) // (1) function name and instance
			.inputType(MockWeatherService.Request.class) // (4) input type
			.build();
	}
	...
}

它包裝了第三方 MockWeatherService 函數,並將其註冊為 VertexAiGeminiChatModelCurrentWeather 函數。它還提供了描述 (2)、要轉換為 Open API 類型的綱要類型 (3) 和用於產生函數調用的 Open API 綱要的輸入類型 (4)。

預設回應轉換器對 Response 物件進行 JSON 序列化。
FunctionCallback 在內部基於 MockWeatherService.Request 類別解析函數調用簽章,並在內部產生函數調用的 Open API 綱要。

在聊天選項中指定函數

為了讓模型知道並調用您的 CurrentWeather 函數,您需要在提示詞請求中啟用它

VertexAiGeminiChatModel chatModel = ...

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

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

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

上面的使用者問題將觸發對 CurrentWeather 函數的 3 次調用(每個城市一次),最終回應將類似於以下內容

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

FunctionCallWithFunctionWrapperIT.java 測試示範了這種方法。

使用提示詞選項註冊/調用函數

除了自動配置之外,您還可以使用提示詞請求動態註冊回調函數

VertexAiGeminiChatModel chatModel = ...

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

var promptOptions = VertexAiGeminiChatOptions.builder()
	.withFunctionCallbacks(List.of(FunctionCallback.builder()
		.schemaType(SchemaType.OPEN_API_SCHEMA) // IMPORTANT!!
		.description("Get the weather in location")
		.function("CurrentWeather", new MockWeatherService())
		.inputType(MockWeatherService.Request.class)
		.build()))
	.build();

ChatResponse response = this.chatModel.call(new Prompt(List.of(this.userMessage), this.promptOptions));
對於此請求的持續時間,預設情況下會啟用提示詞中註冊的函數。

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

FunctionCallWithPromptFunctionIT.java 整合測試提供了一個完整的範例,說明如何使用 VertexAiGeminiChatModel 註冊函數並在提示詞請求中使用它。