関数呼び出し API

このページでは、関数呼び出し API の以前のバージョンについて説明します。このバージョンは非推奨となり、次のリリースで削除される予定です。現在のバージョンはツール呼び出しで入手できます。詳細については、移行ガイドを参照してください。

AI モデルに機能サポートを統合すると、モデルがクライアント側機能の実行をリクエストできるようになり、必要な情報にアクセスしたり、必要に応じてタスクを動的に実行したりできるようになります。

Spring AI は現在、次の AI モデルのツール / 関数呼び出しをサポートしています。

Function calling

ChatClient にカスタム Java 関数を登録し、AI モデルが登録された関数の 1 つまたは複数を呼び出すための引数を含む JSON オブジェクトをインテリジェントに出力するように選択できます。これにより、LLM 機能を外部ツールや API に接続できます。AI モデルは、関数を呼び出すタイミングを検出し、関数シグネチャーに準拠した JSON で応答するようにトレーニングされています。

API は関数を直接呼び出すのではなく、代わりにモデルが JSON を生成し、それを使用してコード内で関数を呼び出し、結果をモデルに返して会話を完了します。

Spring AI は、カスタム関数を登録して呼び出すための柔軟でユーザーフレンドリーな方法を提供します。一般に、カスタム関数は、関数 namedescription、関数呼び出し signature (JSON スキーマとして) を提供して、関数が期待する引数をモデルに知らせる必要があります。description は、モデルが関数を呼び出すタイミングを理解できます。

開発者は、AI モデルから送信された関数呼び出し引数を受け取り、その結果をモデルに返す関数を実装する必要があります。関数は、結果を提供するために他のサードパーティサービスを呼び出します。

Spring AI を使用すると、java.util.Function を返す @Bean 定義を定義し、ChatClient を呼び出すとき、またはプロンプトリクエストで関数を動的に登録するときにオプションとして Bean 名を指定するだけで、これが簡単になります。

内部的には、Spring は POJO (関数) を適切なアダプターコードでラップし、AI モデルとのやり取りを可能にするため、面倒な定型コードを記述する必要がなくなります。基盤となるインフラストラクチャの基礎は、FunctionCallback.java [GitHub] (英語) インターフェースと、Java コールバック関数の実装と登録を簡素化するコンパニオンビルダーユーティリティクラスです。

使い方

たとえば、特定の場所の現在の気温など、AI モデルが持っていない情報を応答するようにしたいとします。

AI モデルに、プロンプトを処理するときにその情報を取得するために使用できる独自の関数に関するメタデータを提供できます。

たとえば、プロンプトの処理中に、AI モデルが特定の場所の温度に関する追加情報が必要であると判断した場合、サーバー側で生成されたリクエスト / レスポンスのやり取りを開始します。AI モデルは、最終的なレスポンスメッセージを返す代わりに、関数名と引数 (JSON 形式) を指定して、特別なツール呼び出しリクエストを返します。このメッセージを処理し、指定された関数を実行して、レスポンスをツールレスポンスメッセージとして AI モデルに返すのは、クライアントの責任です。

Spring AI は、関数呼び出しをサポートするために記述する必要があるコードを大幅に簡素化します。関数呼び出しの会話を仲介します。関数定義を @Bean として提供し、プロンプトオプションで関数の Bean 名を提供するか、プロンプトリクエストオプションで関数をパラメーターとして直接渡すだけです。

プロンプトで複数の関数 Bean 名を参照することもできます。

使用例

関数呼び出しがどのように機能するかを説明する例として使用できる簡単なユースケースを定義しましょう。独自の関数を呼び出して質問に答えるチャットボットを作成しましょう。チャットボットのレスポンスをサポートするために、場所を取得してその場所の現在の天気を返す独自の関数を登録します。

モデルが "What ’ s the weather like in Boston?" などの質問に答える必要がある場合、AI モデルは関数に渡される引数として場所の値を提供するクライアントを呼び出します。この RPC のようなデータは JSON として渡されます。

関数は、SaaS ベースの天気予報サービス API を呼び出し、天気予報のレスポンスをモデルに返して会話を完了します。この例では、さまざまな場所の気温をハードコードする MockWeatherService というシンプルな実装を使用します。

次の MockWeatherService クラスは天気予報サービス API を表します。

  • Java

  • Kotlin

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);
	}
}
class MockWeatherService : Function1<Request, Response> {
	override fun invoke(request: Request) = Response(30.0, Unit.C)
}

enum class Unit { C, F }
data class Request(val location: String, val unit: Unit) {}
data class Response(val temp: Double, val unit: Unit) {}

サーバー側登録

Bean としての機能

Spring AI は、Spring コンテキストでカスタム関数を Bean として登録する複数の方法を提供します。

まず、POJO に最も適したオプションについて説明します。

単純な関数

このアプローチでは、他の Spring 管理対象オブジェクトと同様に、アプリケーションコンテキストで @Bean を定義します。

内部的には、Spring AI ChatModel は、AI モデルを介して呼び出されるロジックを追加する FunctionCallback のインスタンスを作成します。@Bean の名前は関数名として使用されます。

  • Java

  • Kotlin

@Configuration
static class Config {

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

}
@Configuration
class Config {

	@Bean
	@Description("Get the weather in location") // function description
	fun currentWeather(): (Request) -> Response = MockWeatherService()

}

@Description アノテーションはオプションであり、モデルが関数をいつ呼び出すかを理解するのに役立つ関数の説明を提供します。これは、AI モデルがどのクライアント側関数を呼び出すかを決定するのに役立つ重要なプロパティです。

関数の説明を提供する別のオプションは、MockWeatherService.Request で @JsonClassDescription アノテーションを使用することです。

  • Java

  • Kotlin

@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) {}
@Configuration
class Config {

	@Bean
	fun currentWeather(): (Request) -> Response  { // bean name as function name
		return MockWeatherService()
	}
}

@JsonClassDescription("Get the weather in location") // function description
data class Request(val location: String, val unit: Unit)

AI モデルが呼び出す正しい関数を選択できるように、その関数の生成された JSON スキーマが可能な限り説明的になるように、リクエストオブジェクトに情報をアノテーション付けすることがベストプラクティスです。

FunctionCallback

関数を登録する別の方法は、次のように FunctionCallback を作成することです。

  • Java

  • Kotlin

@Configuration
static class Config {

	@Bean
	public FunctionCallback weatherFunctionInfo() {

        return FunctionCallback.builder()
            .function("CurrentWeather", new MockWeatherService()) // (1) function name and instance
            .description("Get the weather in location") // (2) function description
            .inputType(MockWeatherService.Request.class) // (3) input type to build the JSON schema
            .build();
	}
}
import org.springframework.ai.model.function.withInputType

@Configuration
class Config {

	@Bean
	fun weatherFunctionInfo(): FunctionCallback {

        return FunctionCallback.builder()
            .function("CurrentWeather", MockWeatherService()) // (1) function name and instance
            .description("Get the weather in location") // (2) function description
            // (3) Required due to Kotlin SAM conversion being an opaque lambda
            .inputType<MockWeatherService.Request>()
            .build();
	}
}

サードパーティの MockWeatherService 関数をラップし、ChatClient とともに CurrentWeather 関数として登録します。また、説明 (2) と、モデルが期待するとおりにレスポンスをテキストに変換するためのオプションのレスポンスコンバーターも提供します。

デフォルトでは、レスポンスコンバーターは Response オブジェクトの JSON 直列化を実行します。
FunctionCallback.Builder は、MockWeatherService.Request クラスに基づいて関数呼び出しシグネチャーを内部的に解決します。

Bean 名で機能を有効にする

モデルに CurrentWeather 関数を知らせて呼び出すには、プロンプトリクエストでそれを有効にする必要があります。

ChatClient chatClient = ...

ChatResponse response = this.chatClient.prompt("What's the weather like in San Francisco, Tokyo, and Paris?")
    .functions("CurrentWeather") // Enable the function
    .call().
    chatResponse();

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

上記のユーザーの質問により、CurrentWeather 関数が 3 回呼び出され (各都市に 1 回ずつ)、最終的なレスポンスは次のようになります。

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

FunctionCallbackWithPlainFunctionBeanIT.java [GitHub] (英語) テストでは、このアプローチをデモしています。

クライアント側登録

自動構成に加えて、コールバック関数を動的に登録できます。関数呼び出しまたはメソッド呼び出しのいずれかのアプローチを使用して、ChatClient または ChatModel リクエストで関数を登録できます。

クライアント側の登録により、デフォルトで関数を登録できるようになります。

関数呼び出し

ChatClient chatClient = ...

ChatResponse response = this.chatClient.prompt("What's the weather like in San Francisco, Tokyo, and Paris?")
    .functions(FunctionCallback.builder()
            .function("currentWeather", (Request request) -> new Response(30.0, Unit.C)) // (1) function name and instance
            .description("Get the weather in location") // (2) function description
            .inputType(MockWeatherService.Request.class) // (3) input type to build the JSON schema
            .build())
    .call()
    .chatResponse();
このリクエストの期間中、オンザフライ機能はデフォルトで有効になります。

このアプローチにより、ユーザー入力に基づいて呼び出すさまざまな関数を動的に選択できます。

FunctionCallbackInPromptIT.java [GitHub] (英語) 統合テストは、関数を ChatClient に登録し、それをプロンプトリクエストで使用する方法の完全な例を提供します。

メソッド呼び出し

MethodInvokingFunctionCallback は、JSON スキーマの生成とパラメーター変換を自動的に処理しながら、リフレクションによるメソッド呼び出しを可能にします。これは、AI モデルのインタラクション内で呼び出し可能な関数として Java メソッドを統合する場合に特に便利です。

MethodInvokingFunctionCallback は FunctionCallback インターフェースを実装し、以下を提供します。

  • メソッドパラメーターの JSON スキーマの自動生成

  • 静的メソッドとインスタンスメソッドの両方をサポート

  • 任意の数のパラメーター(なしも含む)と戻り値 (無効を含む)

  • 任意のパラメーター / 戻り値の型 (プリミティブ、オブジェクト、コレクション)

  • ToolContext パラメーターの特別な処理

次のように MethodInvokingFunctionCallback を作成するには FunctionCallback.Builder が必要です。

// Create using builder pattern
FunctionCallback methodInvokingCallback = FunctionCallback.builder()
    .method("MethodName", Class<?>...argumentTypes) // The method to invoke and its argument types
    .description("Function calling description") // Hints the AI to know when to call this method
    .targetObject(targetObject)       // Required instance methods for static methods use targetClass
    .build();

以下に使用例をいくつか示します。

  • 静的メソッド呼び出し

  • ToolContext を使用したインスタンスメソッド

public class WeatherService {
    public static String getWeather(String city, TemperatureUnit unit) {
        return "Temperature in " + city + ": 20" + unit;
    }
}

// Usage
FunctionCallback callback = FunctionCallback.builder()
    .method("getWeather", String.class, TemperatureUnit.class)
    .description("Get weather information for a city")
    .targetClass(WeatherService.class)
    .build();
public class DeviceController {
    public void setDeviceState(String deviceId, boolean state, ToolContext context) {
        Map<String, Object> contextData = context.getContext();
        // Implementation using context data
    }
}

// Usage
DeviceController controller = new DeviceController();

String response = ChatClient.create(chatModel).prompt()
    .user("Turn on the living room lights")
    .functions(FunctionCallback.builder()
        .method("setDeviceState", String.class,boolean.class,ToolContext.class)
        .description("Control device state")
        .targetObject(controller)
        .build())
    .toolContext(Map.of("location", "home"))
    .call()
    .content();

OpenAiChatClient メソッド呼び出し関数コールバック IT [GitHub] (英語) 統合テストでは、FunctionCallback.Builder を使用してメソッド呼び出し FunctionCallbacks を作成する方法の追加例が提供されます。

ツールコンテキスト

Spring AI は、ツールコンテキストを介して関数コールバックに追加のコンテキスト情報を渡すことをサポートするようになりました。この機能により、AI モデルによって渡される関数引数とともに、関数実行内で使用できるユーザー提供の追加データを提供できます。

Function calling with Tool Context

ToolContext [GitHub] (英語) クラスは、追加のコンテキスト情報を渡す方法を提供します。

ツールコンテキストの使用

関数呼び出しの場合、java.util.BiFunction の 2 番目の引数として渡されるコンテキスト情報。

メソッド呼び出しの場合、コンテキスト情報は ToolContext 型のメソッド引数として渡されます。

関数呼び出し

チャットオプションを構築するときにツールコンテキストを設定し、コールバックに 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);
    };


ChatResponse response = chatClient.prompt("What's the weather like in San Francisco, Tokyo, and Paris?")
    .functions(FunctionCallback.builder()
        .function("getCurrentWeather", this.weatherFunction)
        .description("Get the weather in location")
        .inputType(MockWeatherService.Request.class)
        .build())
    .toolContext(Map.of("sessionId", "1234", "userId", "5678"))
    .call()
    .chatResponse();

この例では、weatherFunction は、リクエストとツールコンテキストの両方をパラメーターとして受け取る BiFunction として定義されています。これにより、関数ロジック内でコンテキストに直接アクセスできます。

このアプローチにより、セッション固有またはユーザー固有の情報を関数に渡すことができ、よりコンテキストに応じたパーソナライズされたレスポンスが可能になります。

メソッド呼び出し

public class DeviceController {
    public void setDeviceState(String deviceId, boolean state, ToolContext context) {
        Map<String, Object> contextData = context.getContext();
        // Implementation using context data
    }
}

// Usage
DeviceController controller = new DeviceController();

String response = ChatClient.create(chatModel).prompt()
    .user("Turn on the living room lights")
    .functions(FunctionCallback.builder()
        .method("setDeviceState", String.class,boolean.class,ToolContext.class)
        .description("Control device state")
        .targetObject(controller)
        .build())
    .toolContext(Map.of("location", "home"))
    .call()
    .content();