関数呼び出し

Ollama 0.2.8 以降が必要です。
ツールをサポートするには、事前トレーニング済みのモデル (英語) が必要です。通常、このようなモデルには Tools タグが付けられます。たとえば、mistralfirefunction-v2llama3.1:70b などです。
現在、Ollama API (0.2.8) はストリーミングモードでの関数呼び出しをサポートしていません。

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

Ollama API は関数を直接呼び出すのではなく、モデルが JSON を生成します。この JSON を使用してコード内で関数を呼び出し、結果をモデルに返して会話を完了します。Spring AI は、カスタム関数を登録して呼び出すための柔軟でユーザーフレンドリーな方法を提供します。一般に、カスタム関数は関数 namedescription、関数呼び出し signature (JSON スキーマとして) を提供して、関数が期待する引数をモデルに知らせる必要があります。description は、モデルが関数を呼び出すタイミングを理解できます。

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

Spring AI を使用すると、java.util.Function を返す @Bean 定義を定義し、ChatModel を呼び出すときにオプションとして Bean 名を指定するだけで、これが簡単になります。

内部では、Spring は、AI モデルとの対話を可能にする適切なアダプターコードで POJO (関数) をラップし、退屈な定型コードの作成を省略します。基盤となるインフラストラクチャの基礎は、Java コールバック関数の実装と登録を簡素化するための FunctionCallback.java [GitHub] (英語) インターフェースと、それに付随する FunctionCallbackWrapper.java [GitHub] (英語) ユーティリティクラスです。

使い方

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

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

例: プロンプトの処理中に、特定の場所の温度に関する追加情報が必要であると AI モデルが判断した場合、サーバー側で生成されたリクエスト / レスポンスの対話が開始されます。AI モデルはクライアント側の関数を呼び出します。AI モデルはメソッド呼び出しの詳細を JSON として提供し、その関数を実行してレスポンスを返すのはクライアントの責任です。

モデルとクライアントの対話は、Spring AI 関数呼び出しフロー図に示されています。

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 として登録する

OllamaChatModel 自動構成を使用すると、カスタム関数を Spring コンテキストの Bean として登録する複数の方法があります。

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

プレーンな Java 関数

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

内部的には、Spring AI ChatModel は、AI モデル経由で呼び出されるロジックを追加する FunctionCallbackWrapper ラッパーのインスタンスを作成します。@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) {}

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

FunctionCallback ラッパー

関数を登録する別の方法は、次のように FunctionCallbackWrapper ラッパーを作成することです。

@Configuration
static class Config {

	@Bean
	public FunctionCallback weatherFunctionInfo() {

    return FunctionCallbackWrapper.builder(new MockWeatherService())
        .withName("CurrentWeather") // (1) function name
        .withDescription("Get the weather in location") // (2) function description
        .build();
	}
	...
}

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

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

チャットオプションで機能を指定する

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

OllamaChatModel chatModel = ...

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

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

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

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

プロンプトオプションを使用した関数の登録 / 呼び出し

自動構成に加えて、プロンプトリクエストを使用してコールバック関数を動的に登録できます。

OllamaChatModel chatModel = ...

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

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

ChatResponse response = chatModel.call(new Prompt(List.of(userMessage), promptOptions));
プロンプト内に登録された機能は、このリクエストの間、デフォルトで有効になります。

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

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

付録:

Spring AI 関数呼び出しフロー

次の図は、OllamaChatModel 関数呼び出しのフローを示しています。

ollama chatmodel function call

OllamaAPI 関数呼び出しフロー

次の図は、Ollama API のフローを示しています。

ollama function calling flow

OllamaApiToolFunctionCallIT.java [GitHub] (英語) は、Ollama API 関数呼び出しの使用方法の完全な例を提供します。