ツール呼び出し

ツール呼び出し ( 関数呼び出しとも呼ばれる) は、モデルが一連の API またはツールと対話して機能を拡張できるようにする AI アプリケーションで一般的なパターンです。

ツールは主に以下の目的で使用されます。

  • 情報検索。このカテゴリのツールは、データベース、Web サービス、ファイルシステム、Web 検索エンジンなどの外部ソースから情報を取得するために使用できます。ゴールは、モデルの知識を増強し、他の方法では回答できない質問に答えられるようにすることです。そのため、これらのツールは Retrieval Augmented Generation (RAG) シナリオで使用できます。例: ツールを使用して、特定の場所の現在の天気を取得したり、最新のニュース記事を取得したり、特定のレコードをデータベースで照会したりできます。

  • 行動を起こす。このカテゴリのツールは、メールの送信、データベースへの新規レコードの作成、フォームの送信、ワークフローのトリガーなど、ソフトウェアシステムでアクションを実行するために使用できます。ゴールは、人間の介入や明示的なプログラミングが必要となるタスクを自動化することです。例: ツールを使用して、チャットボットと対話する顧客のフライトを予約したり、Web ページのフォームに入力したり、コード生成シナリオで自動テスト (TDD) に基づいて Java クラスを実装したりできます。

通常、ツール呼び出しはモデル機能と呼ばれますが、ツール呼び出しロジックを提供するのは実際にはクライアントアプリケーションです。モデルはツール呼び出しをリクエストして入力引数を提供することしかできませんが、入力引数からツール呼び出しを実行し、結果を返すのはアプリケーションのロールです。モデルはツールとして提供される API のいずれにもアクセスできません。これはセキュリティ上の重要な考慮事項です。

Spring AI は、ツールの定義、モデルからのツール呼び出しリクエストの解決、ツール呼び出しの実行に便利な API を提供します。次のセクションでは、Spring AI のツール呼び出し機能の概要を説明します。

チャットモデルの比較をチェックして、どの AI モデルがツール呼び出し呼び出しをサポートしているかを確認します。
廃止された FunctionCallback から ToolCallback への API から移行するには、ガイドに従ってください。

クイックスタート

Spring AI でツール呼び出しを使い始める方法を見てみましょう。情報取得用とアクション実行用の 2 つのシンプルなツールを実装します。情報取得ツールは、ユーザーのタイムゾーンの現在の日付と時刻を取得するために使用されます。アクションツールは、指定した時間にアラームを設定するために使用されます。

情報検索

AI モデルはリアルタイムの情報にアクセスできません。現在の日付や天気予報などの情報を知っていることを前提とした質問には、モデルは答えられません。ただし、これらの情報を取得できるツールを提供して、リアルタイムの情報にアクセスする必要があるときにモデルがこのツールを呼び出すようにすることはできます。

DateTimeTools クラスで、ユーザーのタイムゾーンの現在の日付と時刻を取得するツールを実装しましょう。このツールは引数を取りません。Spring Framework の LocaleContextHolder は、ユーザーのタイムゾーンを提供できます。このツールは、@Tool でアノテーションが付けられたメソッドとして定義されます。このツールを呼び出すかどうか、いつ呼び出すかをモデルが理解できるように、ツールの機能について詳細な説明を提供します。

import java.time.LocalDateTime;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.context.i18n.LocaleContextHolder;

class DateTimeTools {

    @Tool(description = "Get the current date and time in the user's timezone")
    String getCurrentDateTime() {
        return LocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()).toString();
    }

}

次に、ツールをモデルで利用できるようにします。この例では、ChatClient を使用してモデルと対話します。tools() メソッドを介して DateTimeTools のインスタンスを渡すことで、モデルにツールを提供します。モデルが現在の日付と時刻を知る必要がある場合は、ツールを呼び出すようにリクエストします。内部的には、ChatClient がツールを呼び出して結果をモデルに返し、モデルはツール呼び出しの結果を使用して元の質問に対する最終的なレスポンスを生成します。

ChatModel chatModel = ...

String response = ChatClient.create(chatModel)
        .prompt("What day is tomorrow?")
        .tools(new DateTimeTools())
        .call()
        .content();

System.out.println(response);

出力は次のようになります。

Tomorrow is 2015-10-21.

同じ質問をもう一度試すことができます。今回は、モデルにツールを提供しません。出力は次のようになります。

I am an AI and do not have access to real-time information. Please provide the current date so I can accurately determine what day tomorrow will be.

ツールがなければ、モデルは現在の日付と時刻を判別できないため、質問にどのように答えればよいかわかりません。

行動を起こす

AI モデルは、特定のゴールを達成するための計画を作成するために使用できます。例: モデルはデンマークへの旅行を予約するための計画を作成できます。ただし、モデルには計画を実行する機能はありません。そこでツールの出番です。ツールは、モデルが生成した計画を実行するために使用できます。

前の例では、現在の日付と時刻を決定するツールを使用しました。この例では、特定の時間にアラームを設定するための 2 番目のツールを定義します。ゴールは、今から 10 分後にアラームを設定することなので、このタスクを達成するには、両方のツールをモデルに提供する必要があります。

以前と同じ DateTimeTools クラスに新しいツールを追加します。新しいツールは、ISO-8601 形式の時間である単一のパラメーターを受け取ります。ツールは、指定された時間にアラームが設定されたことを示すメッセージをコンソールに出力します。以前と同様に、ツールは @Tool でアノテーションが付けられたメソッドとして定義され、モデルがツールをいつどのように使用するかを理解できるように詳細な説明を提供するためにも使用されます。

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.context.i18n.LocaleContextHolder;

class DateTimeTools {

    @Tool(description = "Get the current date and time in the user's timezone")
    String getCurrentDateTime() {
        return LocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()).toString();
    }

    @Tool(description = "Set a user alarm for the given time, provided in ISO-8601 format")
    void setAlarm(String time) {
        LocalDateTime alarmTime = LocalDateTime.parse(time, DateTimeFormatter.ISO_DATE_TIME);
        System.out.println("Alarm set for " + alarmTime);
    }

}

次に、両方のツールをモデルで利用できるようにしましょう。モデルとのやり取りには ChatClient を使用します。tools() メソッドを介して DateTimeTools のインスタンスを渡すことで、モデルにツールを提供します。10 分後にアラームを設定するようにリクエストすると、モデルはまず現在の日付と時刻を知る必要があります。次に、現在の日付と時刻を使用してアラーム時間を計算します。最後に、アラームツールを使用してアラームを設定します。内部的には、ChatClient がモデルからのツール呼び出しリクエストを処理し、ツール呼び出しの実行結果を返します。これにより、モデルは最終的なレスポンスを生成できます。

ChatModel chatModel = ...

String response = ChatClient.create(chatModel)
        .prompt("Can you set an alarm 10 minutes from now?")
        .tools(new DateTimeTools())
        .call()
        .content();

System.out.println(response);

アプリケーションログでは、アラームが正しい時間に設定されているかどうかを確認できます。

概要

Spring AI は、柔軟な抽象化のセットを通じてツール呼び出しをサポートしており、ツールを一貫したメソッドで定義、解決、実行できます。このセクションでは、Spring AI におけるツール呼び出しの主な概念とコンポーネントの概要を説明します。

The main sequence of actions for tool calling
  1. モデルでツールを使用できるようにするには、その定義をチャットリクエストに含めます。各ツール定義は、名前、説明、入力パラメーターのスキーマで構成されます。

  2. モデルがツールを呼び出すことを決定すると、定義されたスキーマに従ってモデル化されたツール名と入力パラメーターを含むレスポンスが送信されます。

  3. アプリケーションは、ツール名を使用して、提供された入力パラメーターでツールを識別し、実行する責任があります。

  4. ツール呼び出しの結果はアプリケーションによって処理されます。

  5. アプリケーションはツール呼び出しの結果をモデルに送り返します。

  6. モデルは、ツール呼び出しの結果を追加のコンテキストとして使用して、最終レスポンスを生成します。

ツールはツール呼び出しの構成要素であり、ToolCallback インターフェースによってモデル化されます。Spring AI はメソッドと関数から ToolCallback を指定するための組み込みサポートを提供しますが、より多くのユースケースをサポートするために独自の ToolCallback 実装をいつでも定義できます。

ChatModel 実装は、ツール呼び出しリクエストを対応する ToolCallback 実装に透過的にディスパッチし、ツール呼び出し結果をモデルに送り返します。モデルは最終的に最終レスポンスを生成します。これは、ツール実行ライフサイクルの管理を担当する ToolCallingManager インターフェースを使用して行われます。

ChatClient と ChatModel はどちらも ToolCallback オブジェクトのリストを受け入れ、モデルと最終的に実行する ToolCallingManager でツールを使用できるようにします。

ToolCallback オブジェクトを直接渡すだけでなく、ToolCallbackResolver インターフェースを使用して動的に解決されるツール名のリストを渡すこともできます。

次のセクションでは、より多くのユースケースをサポートするためにカスタマイズおよび拡張する方法を含め、これらすべての概念と API についてさらに詳しく説明します。

ツールとしてのメソッド

Spring AI は、次の 2 つの方法でメソッドからツール (つまり ToolCallback (s)) を指定するための組み込みサポートを提供します。

  • @Tool アノテーションを使用して宣言的に

  • 低レベルの MethodToolCallback 実装を使用してプログラム的に実行します。

宣言的仕様: @Tool

@Tool でアノテーションを付けることで、メソッドをツールに変えることができます。

class DateTimeTools {

    @Tool(description = "Get the current date and time in the user's timezone")
    String getCurrentDateTime() {
        return LocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()).toString();
    }

}

@Tool アノテーションを使用すると、ツールに関する重要な情報を提供できます。

  • name: ツールの名前。指定しない場合は、メソッド名が使用されます。AI モデルは、ツールを呼び出すときにこの名前を使用してツールを識別します。同じクラスに同じ名前のツールが 2 つあることは許可されません。名前は、特定のチャットリクエストに対してモデルで使用できるすべてのツールで一意である必要があります。

  • description: ツールの説明。モデルはこれを使用して、ツールをいつどのように呼び出すかを理解できます。指定しない場合は、メソッド名がツールの説明として使用されます。ただし、モデルがツールの目的と使用方法を理解するには詳細な説明が最も重要であるため、詳細な説明を指定することを強くお勧めします。適切な説明を指定しないと、モデルがツールを必要なときに使用しなかったり、誤って使用したりする可能性があります。

  • returnDirect: ツールの結果をクライアントに直接返すか、モデルに返すかを指定します。詳細については、直接 return を参照してください。

  • resultConverter: ツール呼び出しの結果を String object に変換して AI モデルに送り返すために使用する ToolCallResultConverter 実装。詳細については、結果変換を参照してください。

メソッドは静的またはインスタンスのいずれかであり、任意の可視性 (public、protected、package-private、private) を持つことができます。メソッドを含むクラスは、トップレベルクラスまたはネストされたクラスのいずれかであり、任意の可視性を持つことができます (インスタンス化を計画している場所からアクセスできる限り)。

Spring AI は、メソッドを含むクラスが Spring Bean (例: @Component) である限り、@Tool アノテーションが付けられたメソッドの AOT コンパイルの組み込みサポートを提供します。それ以外の場合は、GraalVM コンパイラーに必要な構成を提供する必要があります。例: クラスに @RegisterReflection(memberCategories = MemberCategory.INVOKE_DECLARED_METHODS) をアノテーションします。

メソッドには、ほとんどの型 (プリミティブ、POJO、列挙型、リスト、配列、マップなど) で任意の数の引数 (引数なしも含む) を定義できます。同様に、メソッドは、void を含むほとんどの型を返すことができます。メソッドが値を返す場合、結果は直列化されてモデルに送り返されるため、戻り値の型は直列化可能な型である必要があります。

一部の型はサポートされていません。詳細については、メソッドツールの制限を参照してください。

Spring AI は、@Tool アノテーション付きメソッドの入力パラメーターの JSON スキーマを自動的に生成します。モデルは、このスキーマを使用して、ツールの呼び出し方法とツールリクエストの準備方法を理解します。@ToolParam アノテーションを使用すると、入力パラメーターに関する追加情報 (説明、パラメーターが必須かオプションかなど) を提供できます。デフォルトでは、すべての入力パラメーターは必須と見なされます。

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;

class DateTimeTools {

    @Tool(description = "Set a user alarm for the given time")
    void setAlarm(@ToolParam(description = "Time in ISO-8601 format") String time) {
        LocalDateTime alarmTime = LocalDateTime.parse(time, DateTimeFormatter.ISO_DATE_TIME);
        System.out.println("Alarm set for " + alarmTime);
    }

}

@ToolParam アノテーションを使用すると、ツールパラメーターに関する重要な情報を提供できます。

  • description: パラメーターの説明。モデルがパラメーターの使用方法をよりよく理解するために使用できます。例: パラメーターの形式、許可される値など。

  • required: パラメーターが必須かオプションか。デフォルトでは、すべてのパラメーターが必須と見なされます。

パラメーターが @Nullable としてアノテーションされている場合、@ToolParam アノテーションを使用して明示的に必須としてマークされていない限り、オプションと見なされます。

@ToolParam アノテーションの他に、Swagger の @Schema アノテーションや Jackson の @JsonProperty アノテーションも使用できます。詳細については、JSON スキーマを参照してください。

ChatClient へのツールの追加

宣言型仕様アプローチを使用する場合、ChatClient を呼び出すときに、ツールクラスインスタンスを tools() メソッドに渡すことができます。このようなツールは、追加された特定のチャットリクエストに対してのみ使用できます。

ChatClient.create(chatModel)
    .prompt("What day is tomorrow?")
    .tools(new DateTimeTools())
    .call()
    .content();

内部的には、ChatClient はツールクラスインスタンス内の各 @Tool アノテーション付きメソッドから ToolCallback を生成し、モデルに渡します。ToolCallback を自分で生成したい場合は、ToolCallbacks ユーティリティクラスを使用できます。

ToolCallback[] dateTimeTools = ToolCallbacks.from(new DateTimeTools());

ChatClient にデフォルトツールを追加する

宣言型仕様アプローチを使用する場合、ツールクラスインスタンスを defaultTools() メソッドに渡すことで、ChatClient.Builder にデフォルトツールを追加できます。デフォルトツールとランタイムツールの両方が提供されている場合、ランタイムツールはデフォルトツールを完全にオーバーライドします。

デフォルトのツールは、同じ ChatClient.Builder から構築されたすべての ChatClient インスタンスによって実行されるすべてのチャットリクエストで共有されます。これらは、さまざまなチャットリクエストでよく使用されるツールには便利ですが、慎重に使用しないと危険な場合もあり、使用すべきでないときに使用できるようになるリスクがあります。
ChatModel chatModel = ...
ChatClient chatClient = ChatClient.builder(chatModel)
    .defaultTools(new DateTimeTools())
    .build();

ChatModel へのツールの追加

宣言型仕様アプローチを使用する場合、ChatModel を呼び出すために使用する ToolCallingChatOptions の toolCallbacks() メソッドにツールクラスインスタンスを渡すことができます。このようなツールは、追加された特定のチャットリクエストでのみ使用できます。

ChatModel chatModel = ...
ToolCallback[] dateTimeTools = ToolCallbacks.from(new DateTimeTools());
ChatOptions chatOptions = ToolCallingChatOptions.builder()
    .toolCallbacks(dateTimeTools)
    .build();
Prompt prompt = new Prompt("What day is tomorrow?", chatOptions);
chatModel.call(prompt);

ChatModel にデフォルトツールを追加する

宣言的仕様アプローチを使用する場合、ChatModel の作成に使用される ToolCallingChatOptions インスタンスの toolCallbacks() メソッドにツールクラスインスタンスを渡すことにより、構築時に ChatModel にデフォルトツールを追加できます。デフォルトツールとランタイムツールの両方が提供されている場合、ランタイムツールはデフォルトツールを完全にオーバーライドします。

デフォルトのツールは、その ChatModel インスタンスによって実行されるすべてのチャットリクエストで共有されます。これらは、さまざまなチャットリクエストでよく使用されるツールとして便利ですが、注意して使用しないと、使用すべきでないときに使用できるようになる危険性があり、危険でもあります。
ToolCallback[] dateTimeTools = ToolCallbacks.from(new DateTimeTools());
ChatModel chatModel = OllamaChatModel.builder()
    .ollamaApi(OllamaApi.builder().build())
    .defaultOptions(ToolCallingChatOptions.builder()
            .toolCallbacks(dateTimeTools)
            .build())
    .build();

プログラム仕様: MethodToolCallback

MethodToolCallback をプログラムで構築することで、メソッドをツールに変えることができます。

class DateTimeTools {

    String getCurrentDateTime() {
        return LocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()).toString();
    }

}

MethodToolCallback.Builder を使用すると、MethodToolCallback インスタンスを構築し、ツールに関する重要な情報を提供できます。

  • toolDefinition: ツール名、説明、入力スキーマを定義する ToolDefinition インスタンス。ToolDefinition.Builder クラスを使用して構築できます。必須。

  • toolMetadata: 結果をクライアントに直接返すかどうか、使用する結果コンバーターなどの追加設定を定義する ToolMetadata インスタンス。ToolMetadata.Builder クラスを使用して構築できます。

  • toolMethod: ツールメソッドを表す Method インスタンス。必須。

  • toolObject: ツールメソッドを含むオブジェクトインスタンス。メソッドが静的である場合は、このパラメーターを省略できます。

  • toolCallResultConverter: ツール呼び出しの結果を String オブジェクトに変換して AI モデルに送り返すために使用する ToolCallResultConverter インスタンス。指定しない場合は、デフォルトのコンバーター (DefaultToolCallResultConverter) が使用されます。

ToolDefinition.Builder を使用すると、ToolDefinition インスタンスを構築し、ツール名、説明、入力スキーマを定義できます。

  • name: ツールの名前。指定しない場合は、メソッド名が使用されます。AI モデルは、ツールを呼び出すときにこの名前を使用してツールを識別します。同じクラスに同じ名前のツールが 2 つあることは許可されません。名前は、特定のチャットリクエストに対してモデルで使用できるすべてのツールで一意である必要があります。

  • description: ツールの説明。モデルはこれを使用して、ツールをいつどのように呼び出すかを理解できます。指定しない場合は、メソッド名がツールの説明として使用されます。ただし、モデルがツールの目的と使用方法を理解するには詳細な説明が最も重要であるため、詳細な説明を指定することを強くお勧めします。適切な説明を指定しないと、モデルがツールを必要なときに使用しなかったり、誤って使用したりする可能性があります。

  • inputSchema: ツールの入力パラメーターの JSON スキーマ。指定しない場合は、メソッドパラメーターに基づいてスキーマが自動的に生成されます。@ToolParam アノテーションを使用して、入力パラメーターに関する追加情報 (説明、パラメーターが必須かオプションかなど) を提供できます。デフォルトでは、すべての入力パラメーターは必須と見なされます。詳細については、JSON スキーマを参照してください。

ToolMetadata.Builder を使用すると、ToolMetadata インスタンスを構築し、ツールの追加設定を定義できます。

  • returnDirect: ツールの結果をクライアントに直接返すか、モデルに返すかを指定します。詳細については、直接 return を参照してください。

Method method = ReflectionUtils.findMethod(DateTimeTools.class, "getCurrentDateTime");
ToolCallback toolCallback = MethodToolCallback.builder()
    .toolDefinition(ToolDefinitions.builder(method)
            .description("Get the current date and time in the user's timezone")
            .build())
    .toolMethod(method)
    .toolObject(new DateTimeTools())
    .build();

メソッドは静的またはインスタンスのいずれかであり、任意の可視性 (public、protected、package-private、private) を持つことができます。メソッドを含むクラスは、トップレベルクラスまたはネストされたクラスのいずれかであり、任意の可視性を持つことができます (インスタンス化を計画している場所からアクセスできる限り)。

Spring AI は、メソッドを含むクラスが Spring Bean (例: @Component) である限り、ツールメソッドの AOT コンパイルの組み込みサポートを提供します。それ以外の場合は、GraalVM コンパイラーに必要な構成を提供する必要があります。例: クラスに @RegisterReflection(memberCategories = MemberCategory.INVOKE_DECLARED_METHODS) をアノテーションします。

メソッドには、ほとんどの型 (プリミティブ、POJO、列挙型、リスト、配列、マップなど) で任意の数の引数 (引数なしも含む) を定義できます。同様に、メソッドは、void を含むほとんどの型を返すことができます。メソッドが値を返す場合、結果は直列化されてモデルに送り返されるため、戻り値の型は直列化可能な型である必要があります。

一部の型はサポートされていません。詳細については、メソッドツールの制限を参照してください。

メソッドが静的である場合、toolObject() メソッドは必要ないため省略できます。

class DateTimeTools {

    static String getCurrentDateTime() {
        return LocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()).toString();
    }

}
Method method = ReflectionUtils.findMethod(DateTimeTools.class, "getCurrentDateTime");
ToolCallback toolCallback = MethodToolCallback.builder()
    .toolDefinition(ToolDefinitions.builder(method)
            .description("Get the current date and time in the user's timezone")
            .build())
    .toolMethod(method)
    .build();

Spring AI は、メソッドの入力パラメーターの JSON スキーマを自動的に生成します。モデルは、このスキーマを使用して、ツールの呼び出し方法とツールリクエストの準備方法を理解します。@ToolParam アノテーションを使用すると、入力パラメーターに関する追加情報 (説明、パラメーターが必須かオプションかなど) を提供できます。デフォルトでは、すべての入力パラメーターは必須と見なされます。

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import org.springframework.ai.tool.annotation.ToolParam;

class DateTimeTools {

    void setAlarm(@ToolParam(description = "Time in ISO-8601 format") String time) {
        LocalDateTime alarmTime = LocalDateTime.parse(time, DateTimeFormatter.ISO_DATE_TIME);
        System.out.println("Alarm set for " + alarmTime);
    }

}

@ToolParam アノテーションを使用すると、ツールパラメーターに関する重要な情報を提供できます。

  • description: パラメーターの説明。モデルがパラメーターの使用方法をよりよく理解するために使用できます。例: パラメーターの形式、許可される値など。

  • required: パラメーターが必須かオプションか。デフォルトでは、すべてのパラメーターが必須と見なされます。

パラメーターが @Nullable としてアノテーションされている場合、@ToolParam アノテーションを使用して明示的に必須としてマークされていない限り、オプションと見なされます。

@ToolParam アノテーションの他に、Swagger の @Schema アノテーションや Jackson の @JsonProperty アノテーションも使用できます。詳細については、JSON スキーマを参照してください。

ChatClient および ChatModel へのツールの追加

プログラムによる仕様アプローチを使用する場合、MethodToolCallback インスタンスを ChatClient の toolCallbacks() メソッドに渡すことができます。このツールは、追加された特定のチャットリクエストに対してのみ使用できます。

ToolCallback toolCallback = ...
ChatClient.create(chatModel)
    .prompt("What day is tomorrow?")
    .toolCallbacks(toolCallback)
    .call()
    .content();

ChatClient にデフォルトツールを追加する

プログラムによる仕様アプローチを使用する場合、MethodToolCallback インスタンスを defaultToolCallbacks() メソッドに渡すことで、ChatClient.Builder にデフォルトツールを追加できます。デフォルトツールとランタイムツールの両方が提供されている場合、ランタイムツールはデフォルトツールを完全にオーバーライドします。

デフォルトのツールは、同じ ChatClient.Builder から構築されたすべての ChatClient インスタンスによって実行されるすべてのチャットリクエストで共有されます。これらは、さまざまなチャットリクエストでよく使用されるツールには便利ですが、慎重に使用しないと危険な場合もあり、使用すべきでないときに使用できるようになるリスクがあります。
ChatModel chatModel = ...
ToolCallback toolCallback = ...
ChatClient chatClient = ChatClient.builder(chatModel)
    .defaultToolCallbacks(toolCallback)
    .build();

ChatModel へのツールの追加

プログラムによる仕様アプローチを使用する場合、ChatModel を呼び出すために使用する ToolCallingChatOptions の toolCallbacks() メソッドに MethodToolCallback インスタンスを渡すことができます。このツールは、追加された特定のチャットリクエストに対してのみ使用できます。

ChatModel chatModel = ...
ToolCallback toolCallback = ...
ChatOptions chatOptions = ToolCallingChatOptions.builder()
    .toolCallbacks(toolCallback)
    .build():
Prompt prompt = new Prompt("What day is tomorrow?", chatOptions);
chatModel.call(prompt);

ChatModel にデフォルトツールを追加する

プログラムによる仕様アプローチを使用する場合、ChatModel の作成に使用される ToolCallingChatOptions インスタンスの toolCallbacks() メソッドに MethodToolCallback インスタンスを渡すことにより、構築時に ChatModel にデフォルトツールを追加できます。デフォルトツールとランタイムツールの両方が提供されている場合、ランタイムツールはデフォルトツールを完全にオーバーライドします。

デフォルトのツールは、その ChatModel インスタンスによって実行されるすべてのチャットリクエストで共有されます。これらは、さまざまなチャットリクエストでよく使用されるツールとして便利ですが、注意して使用しないと、使用すべきでないときに使用できるようになる危険性があり、危険でもあります。
ToolCallback toolCallback = ...
ChatModel chatModel = OllamaChatModel.builder()
    .ollamaApi(OllamaApi.builder().build())
    .defaultOptions(ToolCallingChatOptions.builder()
            .toolCallbacks(toolCallback)
            .build())
    .build();

メソッドツールの制限

次の型は、ツールとして使用されるメソッドのパラメーターまたは戻り値の型として現在サポートされていません。

  • Optional

  • 非同期型 (たとえば CompletableFutureFuture)

  • リアクティブ型 (例: FlowMonoFlux)

  • 機能型(例: FunctionSupplierConsumer)。

機能型は、機能ベースのツール仕様アプローチを使用してサポートされます。詳細については、ツールとしての機能を参照してください。

ツールとしての機能

Spring AI は、低レベルの FunctionToolCallback 実装を使用してプログラム的に、または実行時に解決される @Bean として動的に、関数からツールを指定するための組み込みサポートを提供します。

プログラム仕様: FunctionToolCallback

プログラムで FunctionToolCallback を構築することで、関数型 (FunctionSupplierConsumer または BiFunction) をツールに変換できます。

public class WeatherService implements Function<WeatherRequest, WeatherResponse> {
    public WeatherResponse apply(WeatherRequest request) {
        return new WeatherResponse(30.0, Unit.C);
    }
}

public enum Unit { C, F }
public record WeatherRequest(String location, Unit unit) {}
public record WeatherResponse(double temp, Unit unit) {}

FunctionToolCallback.Builder を使用すると、FunctionToolCallback インスタンスを構築し、ツールに関する重要な情報を提供できます。

  • name: ツールの名前。AI モデルは、ツールを呼び出すときにこの名前を使用してツールを識別します。同じコンテキストで同じ名前のツールを 2 つ使用することはできません。名前は、特定のチャットリクエストに対してモデルで使用できるすべてのツールで一意である必要があります。必須。

  • toolFunction: ツールメソッド (FunctionSupplierConsumer または BiFunction) を表す機能オブジェクト。必須。

  • description: ツールの説明。モデルはこれを使用して、ツールをいつどのように呼び出すかを理解できます。指定しない場合は、メソッド名がツールの説明として使用されます。ただし、モデルがツールの目的と使用方法を理解するには詳細な説明が最も重要であるため、詳細な説明を指定することを強くお勧めします。適切な説明を指定しないと、モデルがツールを必要なときに使用しなかったり、誤って使用したりする可能性があります。

  • inputType: 関数入力の型。必須。

  • inputSchema: ツールの入力パラメーターの JSON スキーマ。指定しない場合は、inputType に基づいてスキーマが自動的に生成されます。@ToolParam アノテーションを使用して、入力パラメーターに関する追加情報 (説明、パラメーターが必須かオプションかなど) を指定できます。デフォルトでは、すべての入力パラメーターが必須とみなされます。詳細については、JSON スキーマを参照してください。

  • toolMetadata: 結果をクライアントに直接返すかどうか、使用する結果コンバーターなどの追加設定を定義する ToolMetadata インスタンス。ToolMetadata.Builder クラスを使用して構築できます。

  • toolCallResultConverter: ツール呼び出しの結果を String オブジェクトに変換して AI モデルに送り返すために使用する ToolCallResultConverter インスタンス。指定しない場合は、デフォルトのコンバーター (DefaultToolCallResultConverter) が使用されます。

ToolMetadata.Builder を使用すると、ToolMetadata インスタンスを構築し、ツールの追加設定を定義できます。

  • returnDirect: ツールの結果をクライアントに直接返すか、モデルに返すかを指定します。詳細については、直接 return を参照してください。

ToolCallback toolCallback = FunctionToolCallback
    .builder("currentWeather", new WeatherService())
    .description("Get the weather in location")
    .inputType(WeatherRequest.class)
    .build();

関数の入力と出力は、Void または POJO のいずれかになります。結果は直列化されてモデルに送り返されるため、入力と出力の POJO は直列化可能である必要があります。関数と入力および出力の型はパブリックである必要があります。

一部の型はサポートされていません。詳細については、関数ツールの制限を参照してください。

ChatClient へのツールの追加

プログラムによる仕様アプローチを使用する場合、FunctionToolCallback インスタンスを ChatClient の toolCallbacks() メソッドに渡すことができます。このツールは、追加された特定のチャットリクエストに対してのみ使用できます。

ToolCallback toolCallback = ...
ChatClient.create(chatModel)
    .prompt("What's the weather like in Copenhagen?")
    .toolCallbacks(toolCallback)
    .call()
    .content();

ChatClient にデフォルトツールを追加する

プログラムによる仕様アプローチを使用する場合、FunctionToolCallback インスタンスを defaultToolCallbacks() メソッドに渡すことで、ChatClient.Builder にデフォルトツールを追加できます。デフォルトツールとランタイムツールの両方が提供されている場合、ランタイムツールはデフォルトツールを完全にオーバーライドします。

デフォルトのツールは、同じ ChatClient.Builder から構築されたすべての ChatClient インスタンスによって実行されるすべてのチャットリクエストで共有されます。これらは、さまざまなチャットリクエストでよく使用されるツールには便利ですが、慎重に使用しないと危険な場合もあり、使用すべきでないときに使用できるようになるリスクがあります。
ChatModel chatModel = ...
ToolCallback toolCallback = ...
ChatClient chatClient = ChatClient.builder(chatModel)
    .defaultToolCallbacks(toolCallback)
    .build();

ChatModel へのツールの追加

プログラムによる仕様アプローチを使用する場合、FunctionToolCallback インスタンスを ToolCallingChatOptions の toolCallbacks() メソッドに渡すことができます。このツールは、追加された特定のチャットリクエストに対してのみ使用できます。

ChatModel chatModel = ...
ToolCallback toolCallback = ...
ChatOptions chatOptions = ToolCallingChatOptions.builder()
    .toolCallbacks(toolCallback)
    .build():
Prompt prompt = new Prompt("What's the weather like in Copenhagen?", chatOptions);
chatModel.call(prompt);

ChatModel にデフォルトツールを追加する

プログラムによる仕様アプローチを使用する場合、ChatModel の作成に使用される ToolCallingChatOptions インスタンスの toolCallbacks() メソッドに FunctionToolCallback インスタンスを渡すことにより、構築時に ChatModel にデフォルトツールを追加できます。デフォルトツールとランタイムツールの両方が提供されている場合、ランタイムツールはデフォルトツールを完全にオーバーライドします。

デフォルトのツールは、その ChatModel インスタンスによって実行されるすべてのチャットリクエストで共有されます。これらは、さまざまなチャットリクエストでよく使用されるツールとして便利ですが、注意して使用しないと、使用すべきでないときに使用できるようになる危険性があり、危険でもあります。
ToolCallback toolCallback = ...
ChatModel chatModel = OllamaChatModel.builder()
    .ollamaApi(OllamaApi.builder().build())
    .defaultOptions(ToolCallingChatOptions.builder()
            .toolCallbacks(toolCallback)
            .build())
    .build();

動的仕様: @Bean

プログラムでツールを指定する代わりに、ツールを Spring Bean として定義し、ToolCallbackResolver インターフェース (SpringBeanToolCallbackResolver 実装経由) を使用して実行時に Spring AI が動的に解決できるようにすることができます。このオプションにより、任意の FunctionSupplierConsumer、または BiFunction Bean をツールとして使用できるようになります。Bean 名はツール名として使用され、Spring Framework の @Description アノテーションはツールの説明を提供するために使用できます。モデルはこれを使用して、いつどのようにツールを呼び出すかを理解します。説明を指定しない場合は、メソッド名がツールの説明として使用されます。ただし、モデルがツールの目的と使用方法を理解するために、詳細な説明を指定することが最も重要であるため、詳細な説明を指定することを強くお勧めします。適切な説明を指定しないと、モデルがツールを使用すべきときに使用しなかったり、誤って使用したりする機能があります。

@Configuration(proxyBeanMethods = false)
class WeatherTools {

    WeatherService weatherService = new WeatherService();

	@Bean
	@Description("Get the weather in location")
	Function<WeatherRequest, WeatherResponse> currentWeather() {
		return weatherService;
	}

}
一部の型はサポートされていません。詳細については、関数ツールの制限を参照してください。

ツールの入力パラメーターの JSON スキーマは自動的に生成されます。@ToolParam アノテーションを使用して、入力パラメーターに関する追加情報 (説明、パラメーターが必須かオプションかなど) を提供できます。デフォルトでは、すべての入力パラメーターは必須と見なされます。詳細については、JSON スキーマを参照してください。

record WeatherRequest(@ToolParam(description = "The name of a city or a country") String location, Unit unit) {}

このツール仕様アプローチには、ツール解決が実行時に行われるため、型の安全性が保証されないという欠点があります。これを軽減するには、@Bean アノテーションを使用してツール名を明示的に指定し、値を定数に格納して、ツール名をハードコードする代わりにチャットリクエストで使用できるようにします。

@Configuration(proxyBeanMethods = false)
class WeatherTools {

    public static final String CURRENT_WEATHER_TOOL = "currentWeather";

	@Bean(CURRENT_WEATHER_TOOL)
	@Description("Get the weather in location")
	Function<WeatherRequest, WeatherResponse> currentWeather() {
		...
	}

}

ChatClient へのツールの追加

動的仕様アプローチを使用する場合、ツール名(つまり、関数 Bean 名)を ChatClient の tools() メソッドに渡すことができます。ツールは、追加された特定のチャットリクエストでのみ使用できます。

ChatClient.create(chatModel)
    .prompt("What's the weather like in Copenhagen?")
    .toolNames("currentWeather")
    .call()
    .content();

ChatClient にデフォルトツールを追加する

動的仕様アプローチを使用する場合、ツール名を defaultToolNames() メソッドに渡すことで、ChatClient.Builder にデフォルトツールを追加できます。デフォルトツールとランタイムツールの両方が指定されている場合、ランタイムツールはデフォルトツールを完全にオーバーライドします。

デフォルトのツールは、同じ ChatClient.Builder から構築されたすべての ChatClient インスタンスによって実行されるすべてのチャットリクエストで共有されます。これらは、さまざまなチャットリクエストでよく使用されるツールには便利ですが、慎重に使用しないと危険な場合もあり、使用すべきでないときに使用できるようになるリスクがあります。
ChatModel chatModel = ...
ChatClient chatClient = ChatClient.builder(chatModel)
    .defaultToolNames("currentWeather")
    .build();

ChatModel へのツールの追加

動的仕様アプローチを使用する場合、ChatModel を呼び出すために使用する ToolCallingChatOptions の toolNames() メソッドにツール名を渡すことができます。ツールは、追加された特定のチャットリクエストでのみ使用できます。

ChatModel chatModel = ...
ChatOptions chatOptions = ToolCallingChatOptions.builder()
    .toolNames("currentWeather")
    .build();
Prompt prompt = new Prompt("What's the weather like in Copenhagen?", chatOptions);
chatModel.call(prompt);

ChatModel にデフォルトツールを追加する

動的仕様アプローチを使用する場合、ChatModel の作成に使用される ToolCallingChatOptions インスタンスの toolNames() メソッドにツール名を渡すことにより、構築時に ChatModel にデフォルトツールを追加できます。デフォルトツールとランタイムツールの両方が提供されている場合、ランタイムツールはデフォルトツールを完全にオーバーライドします。

デフォルトのツールは、その ChatModel インスタンスによって実行されるすべてのチャットリクエストで共有されます。これらは、さまざまなチャットリクエストでよく使用されるツールとして便利ですが、注意して使用しないと、使用すべきでないときに使用できるようになる危険性があり、危険でもあります。
ChatModel chatModel = OllamaChatModel.builder()
    .ollamaApi(OllamaApi.builder().build())
    .defaultOptions(ToolCallingChatOptions.builder()
            .toolNames("currentWeather")
            .build())
    .build();

関数ツールの制限

次の型は現在、ツールとして使用される関数の入力型または出力型としてサポートされていません。

  • 基本タイプ

  • Optional

  • コレクション型 (例: ListMapArraySet)

  • 非同期型 (たとえば CompletableFutureFuture)

  • リアクティブ型(例: FlowMonoFlux)。

プリミティブ型とコレクションは、メソッドベースのツール仕様アプローチを使用してサポートされます。詳細については、ツールとしてのメソッドを参照してください。

ツール仕様

Spring AI では、ツールは ToolCallback インターフェースを介してモデル化されます。前のセクションでは、Spring AI によって提供される組み込みサポートを使用して、メソッドと関数からツールを定義する方法について説明しました ( ツールとしてのメソッドおよびツールとしての機能を参照)。このセクションでは、ツール仕様の詳細と、より多くのユースケースをサポートするためにツールをカスタマイズおよび拡張する方法について説明します。

ツールコールバック

ToolCallback インターフェースは、定義と実行ロジックの両方を含む、AI モデルから呼び出すことができるツールを定義する方法を提供します。これは、ツールを最初から定義する場合に実装する主要なインターフェースです。例: MCP クライアント (モデルコンテキストプロトコルを使用) または ChatClient (モジュラーエージェントアプリケーションを構築するため) から ToolCallback を定義できます。

インターフェースは次のメソッドを提供します。

public interface ToolCallback {

	/**
	 * Definition used by the AI model to determine when and how to call the tool.
	 */
	ToolDefinition getToolDefinition();

	/**
	 * Metadata providing additional information on how to handle the tool.
	 */
	ToolMetadata getToolMetadata();

    /**
	 * Execute tool with the given input and return the result to send back to the AI model.
	 */
	String call(String toolInput);

    /**
	 * Execute tool with the given input and context, and return the result to send back to the AI model.
	 */
	String call(String toolInput, ToolContext tooContext);

}

Spring AI は、ツールメソッド (MethodToolCallback) とツール関数 (FunctionToolCallback) の組み込み実装を提供します。

ツールの定義

ToolDefinition インターフェースは、ツール名、説明、入力スキーマなど、ツールの可用性について AI モデルが認識するために必要な情報を提供します。各 ToolCallback 実装では、ツールを定義するために ToolDefinition インスタンスを提供する必要があります。

インターフェースは次のメソッドを提供します。

public interface ToolDefinition {

	/**
	 * The tool name. Unique within the tool set provided to a model.
	 */
	String name();

	/**
	 * The tool description, used by the AI model to determine what the tool does.
	 */
	String description();

	/**
	 * The schema of the parameters used to call the tool.
	 */
	String inputSchema();

}
入力スキーマの詳細については、JSON スキーマを参照してください。

ToolDefinition.Builder を使用すると、デフォルトの実装 (DefaultToolDefinition) を使用して ToolDefinition インスタンスを構築できます。

ToolDefinition toolDefinition = ToolDefinition.builder()
    .name("currentWeather")
    .description("Get the weather in location")
    .inputSchema("""
        {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string"
                },
                "unit": {
                    "type": "string",
                    "enum": ["C", "F"]
                }
            },
            "required": ["location", "unit"]
        }
    """)
    .build();

方法ツールの定義

メソッドからツールを構築すると、ToolDefinition が自動的に生成されます。ToolDefinition を自分で生成したい場合は、この便利なビルダーを使用できます。

Method method = ReflectionUtils.findMethod(DateTimeTools.class, "getCurrentDateTime");
ToolDefinition toolDefinition = ToolDefinition.from(method);

メソッドから生成された ToolDefinition には、ツール名としてのメソッド名、ツールの説明としてのメソッド名、メソッド入力パラメーターの JSON スキーマが含まれます。メソッドに @Tool のアノテーションが付けられている場合、ツール名と説明はアノテーションから取得されます (設定されている場合)。

詳細については、ツールとしてのメソッドを参照してください。

一部またはすべての属性を明示的に指定したい場合は、ToolDefinition.Builder を使用してカスタム ToolDefinition インスタンスを構築できます。

Method method = ReflectionUtils.findMethod(DateTimeTools.class, "getCurrentDateTime");
ToolDefinition toolDefinition = ToolDefinitions.builder(method)
    .name("currentDateTime")
    .description("Get the current date and time in the user's timezone")
    .inputSchema(JsonSchemaGenerator.generateForMethodInput(method))
    .build();

機能ツールの定義

関数からツールを構築すると、ToolDefinition が自動的に生成されます。FunctionToolCallback.Builder を使用して FunctionToolCallback インスタンスを構築する場合、ToolDefinition の生成に使用されるツール名、説明、入力スキーマを指定できます。詳細については、ツールとしての機能を参照してください。

JSON スキーマ

AI モデルにツールを提供する場合、モデルはツールを呼び出すための入力型のスキーマを認識する必要があります。スキーマは、ツールの呼び出し方法とツールリクエストの準備方法を理解するために使用されます。Spring AI は、JsonSchemaGenerator クラスを介してツールの入力型の JSON スキーマを生成するための組み込みサポートを提供します。スキーマは、ToolDefinition の一部として提供されます。

ToolDefinition の詳細と、それに入力スキーマを渡す方法については、ツールの定義を参照してください。

JsonSchemaGenerator クラスは、ツールとしてのメソッドおよびツールとしての機能で説明されているいずれかの戦略を使用して、メソッドまたは関数の入力パラメーターの JSON スキーマを生成するために内部的に使用されます。JSON スキーマ生成ロジックは、メソッドおよび関数の入力パラメーターで使用して結果のスキーマをカスタマイズできる一連のアノテーションをサポートしています。

このセクションでは、ツールの入力パラメーターの JSON スキーマを生成するときにカスタマイズできる 2 つの主なオプション (説明と必須ステータス) について説明します。

説明

ツール自体の説明を提供するだけでなく、ツールの入力パラメーターの説明も提供できます。説明は、パラメーターの形式や許可される値など、入力パラメーターに関する重要な情報を提供するために使用できます。これは、モデルが入力スキーマとその使用方法を理解できます。Spring AI は、次のいずれかのアノテーションを使用して入力パラメーターの説明を生成するための組み込みサポートを提供します。

  • Spring AI からの @ToolParam(description = "…​") 

  • Jackson からの @JsonClassDescription(description = "…​") 

  • Jackson からの @JsonPropertyDescription(description = "…​") 

  • Swagger の @Schema(description = "…​")

このアプローチはメソッドと関数の両方に機能し、ネストされた型に対して再帰的に使用できます。

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.context.i18n.LocaleContextHolder;

class DateTimeTools {

    @Tool(description = "Set a user alarm for the given time")
    void setAlarm(@ToolParam(description = "Time in ISO-8601 format") String time) {
        LocalDateTime alarmTime = LocalDateTime.parse(time, DateTimeFormatter.ISO_DATE_TIME);
        System.out.println("Alarm set for " + alarmTime);
    }

}

必須 / オプション

デフォルトでは、各入力パラメーターは必須とみなされ、ツールを呼び出す際に AI モデルが値を提供するよう強制されます。ただし、以下のいずれかのアノテーション(優先順位順)を使用することで、入力パラメーターをオプションにすることができます。

  • Spring AI からの @ToolParam(required = false) 

  • Jackson からの @JsonProperty(required = false) 

  • Swagger の @Schema(required = false) 

  • Spring Framework から @Nullable

このアプローチはメソッドと関数の両方に機能し、ネストされた型に対して再帰的に使用できます。

class CustomerTools {

    @Tool(description = "Update customer information")
    void updateCustomerInfo(Long id, String name, @ToolParam(required = false) String email) {
        System.out.println("Updated info for customer with id: " + id);
    }

}
入力パラメーターの必須ステータスを正しく定義することは、幻覚のリスクを軽減し、ツールを呼び出す際にモデルが正しい入力を提供することを保証するために不可欠です。前の例では、email パラメーターはオプションであるため、モデルは値を指定せずにツールを呼び出すことができます。もしこのパラメーターが必須であれば、モデルはツールを呼び出す際に値を指定する必要があります。そして、値が存在しない場合は、モデルが勝手に値を作成し、幻覚を引き起こす可能性があります。

結果変換

ツール呼び出しの結果は ToolCallResultConverter を使用して直列化され、AI モデルに送り返されます。ToolCallResultConverter インターフェースは、ツール呼び出しの結果を String オブジェクトに変換する方法を提供します。

インターフェースは次のメソッドを提供します。

@FunctionalInterface
public interface ToolCallResultConverter {

	/**
	 * Given an Object returned by a tool, convert it to a String compatible with the
	 * given class type.
	 */
	String convert(@Nullable Object result, @Nullable Type returnType);

}

結果は直列化可能な型である必要があります。デフォルトでは、結果は Jackson (DefaultToolCallResultConverter) を使用して JSON に直列化されますが、独自の ToolCallResultConverter 実装を提供することで直列化プロセスをカスタマイズできます。

Spring AI は、メソッドと関数ツールの両方で ToolCallResultConverter に依存しています。

メソッドツール呼び出し結果変換

宣言的なアプローチを使用してメソッドからツールを構築する場合、@Tool アノテーションの resultConverter() 属性を設定することで、ツールに使用するカスタム ToolCallResultConverter を提供できます。

class CustomerTools {

    @Tool(description = "Retrieve customer information", resultConverter = CustomToolCallResultConverter.class)
    Customer getCustomerInfo(Long id) {
        return customerRepository.findById(id);
    }

}

プログラムによるアプローチを使用する場合は、MethodToolCallback.Builder の resultConverter() 属性を設定することで、ツールに使用するカスタム ToolCallResultConverter を提供できます。

詳細については、ツールとしてのメソッドを参照してください。

関数ツール呼び出し結果変換

プログラムによるアプローチを使用して関数からツールを構築する場合、FunctionToolCallback.Builder の resultConverter() 属性を設定することで、ツールに使用するカスタム ToolCallResultConverter を提供できます。

詳細については、ツールとしての機能を参照してください。

ツールコンテキスト

Spring AI は、ToolContext API を介してツールに追加のコンテキスト情報を渡すことをサポートしています。この機能により、AI モデルから渡されるツール引数に加えて、ツール実行時に使用できるユーザー提供の追加データを提供できます。

Providing additional contextual info to tools
class CustomerTools {

    @Tool(description = "Retrieve customer information")
    Customer getCustomerInfo(Long id, ToolContext toolContext) {
        return customerRepository.findById(id, toolContext.getContext().get("tenantId"));
    }

}

ToolContext には、ChatClient を呼び出すときにユーザーが提供するデータが入力されます。

ChatModel chatModel = ...

String response = ChatClient.create(chatModel)
        .prompt("Tell me more about the customer with ID 42")
        .tools(new CustomerTools())
        .toolContext(Map.of("tenantId", "acme"))
        .call()
        .content();

System.out.println(response);
ToolContext で提供されるデータは AI モデルに送信されません。

同様に、ChatModel を直接呼び出すときにツールコンテキストデータを定義できます。

ChatModel chatModel = ...
ToolCallback[] customerTools = ToolCallbacks.from(new CustomerTools());
ChatOptions chatOptions = ToolCallingChatOptions.builder()
    .toolCallbacks(customerTools)
    .toolContext(Map.of("tenantId", "acme"))
    .build();
Prompt prompt = new Prompt("Tell me more about the customer with ID 42", chatOptions);
chatModel.call(prompt);

toolContext オプションがデフォルトオプションとランタイムオプションの両方で設定されている場合、結果の ToolContext は 2 つのオプションをマージしたものになり、ランタイムオプションがデフォルトオプションよりも優先されます。

直接 return

デフォルトでは、ツール呼び出しの結果はレスポンスとしてモデルに返されます。その後、モデルはその結果を使用して会話を続行できます。

結果をモデルに返すのではなく、呼び出し元に直接返す方が望ましい場合があります。たとえば、RAG ツールに依存するエージェントを構築する場合、不要な後処理のためにモデルに結果を返すのではなく、呼び出し元に直接返す方が望ましい場合があります。あるいは、エージェントの推論ループを終了させる必要がある特定のツールがあるかもしれません。

各 ToolCallback 実装では、ツール呼び出しの結果を呼び出し元に直接返すか、モデルに送り返すかを定義できます。デフォルトでは、結果はモデルに送り返されますが、この動作はツールごとに変更できます。

ツール実行ライフサイクルの管理を担う ToolCallingManager は、ツールに関連付けられた returnDirect 属性の処理を担当します。この属性が true に設定されている場合、ツール呼び出しの結果は呼び出し元に直接返されます。それ以外の場合は、結果はモデルに返されます。

複数のツール呼び出しが一度にリクエストされた場合、すべてのツールが呼び出し元に直接結果を返すように、returnDirect 属性を true に設定する必要があります。そうでない場合、結果はモデルに返されます。
Returning tool call results directly to the caller
  1. モデルでツールを使用できるようにするには、チャットリクエストにその定義を含めます。ツールの実行結果を呼び出し元に直接返したい場合は、returnDirect 属性を true に設定します。

  2. モデルがツールを呼び出すことを決定すると、定義されたスキーマに従ってモデル化されたツール名と入力パラメーターを含むレスポンスが送信されます。

  3. アプリケーションは、ツール名を使用して、提供された入力パラメーターでツールを識別し、実行する責任があります。

  4. ツール呼び出しの結果はアプリケーションによって処理されます。

  5. アプリケーションは、ツール呼び出しの結果をモデルに送り返すのではなく、呼び出し元に直接送信します。

メソッドリターンダイレクト

宣言的なアプローチを使用してメソッドからツールを構築する場合、@Tool アノテーションの returnDirect 属性を true に設定することで、ツールが結果を呼び出し元に直接返すようにマークできます。

class CustomerTools {

    @Tool(description = "Retrieve customer information", returnDirect = true)
    Customer getCustomerInfo(Long id) {
        return customerRepository.findById(id);
    }

}

プログラムによるアプローチを使用する場合は、ToolMetadata インターフェースを介して returnDirect 属性を設定し、それを MethodToolCallback.Builder に渡すことができます。

ToolMetadata toolMetadata = ToolMetadata.builder()
    .returnDirect(true)
    .build();

詳細については、ツールとしてのメソッドを参照してください。

関数戻り値直接

プログラムによるアプローチを使用して関数からツールを構築する場合、ToolMetadata インターフェースを介して returnDirect 属性を設定し、それを FunctionToolCallback.Builder に渡すことができます。

ToolMetadata toolMetadata = ToolMetadata.builder()
    .returnDirect(true)
    .build();

詳細については、ツールとしての機能を参照してください。

ツールの実行

ツール実行とは、指定された入力引数を用いてツールを呼び出し、結果を返すプロセスです。ツール実行は、ツール実行ライフサイクルの管理を担う ToolCallingManager インターフェースによって処理されます。

public interface ToolCallingManager {

	/**
	 * Resolve the tool definitions from the model's tool calling options.
	 */
	List<ToolDefinition> resolveToolDefinitions(ToolCallingChatOptions chatOptions);

	/**
	 * Execute the tool calls requested by the model.
	 */
	ToolExecutionResult executeToolCalls(Prompt prompt, ChatResponse chatResponse);

}

Spring AI Spring Boot Starter のいずれかをご利用の場合、DefaultToolCallingManager は ToolCallingManager インターフェースの自動構成実装です。独自の ToolCallingManager Bean を提供することで、ツールの実行動作をカスタマイズできます。

@Bean
ToolCallingManager toolCallingManager() {
    return ToolCallingManager.builder().build();
}

デフォルトでは、Spring AI は各 ChatModel 実装内からツール実行ライフサイクルを透過的に管理します。ただし、この動作をオプトアウトし、ツール実行を独自に制御することも可能です。このセクションでは、これら 2 つのシナリオについて説明します。

フレームワーク制御ツール実行

デフォルトの動作を使用する場合、Spring AI はモデルからのツール呼び出しリクエストを自動的にインターセプトし、ツールを呼び出し、結果をモデルに返します。これらはすべて、ToolCallingManager を使用した各 ChatModel 実装によって透過的に実行されます。

Framework-controlled tool execution lifecycle
  1. ツールをモデルで使用できるようにするには、その定義をチャットリクエスト (Prompt) に含め、リクエストを AI モデルに送信する ChatModel API を呼び出します。

  2. モデルがツールを呼び出すことを決定すると、定義されたスキーマに従ってモデル化されたツール名と入力パラメーターを含むレスポンス (ChatResponse) が送信されます。

  3. ChatModel はツール呼び出しリクエストを ToolCallingManager API に送信します。

  4. ToolCallingManager は、呼び出すツールを識別し、提供された入力パラメーターを使用してそれを実行するロールを担います。

  5. ツール呼び出しの結果は ToolCallingManager に返されます。

  6. ToolCallingManager はツール実行結果を ChatModel に返します。

  7. ChatModel はツール実行結果を AI モデル (ToolResponseMessage) に送り返します。

  8. AI モデルは、ツール呼び出しの結果を追加のコンテキストとして使用して最終レスポンスを生成し、ChatClient を介して呼び出し元 (ChatResponse) に返します。

現在、ツール実行に関してモデルと交換される内部メッセージはユーザーに公開されていません。これらのメッセージにアクセスする必要がある場合は、ユーザー制御のツール実行アプローチを使用する必要があります。

ツール呼び出しが実行可能かどうかを判断するロジックは、ToolExecutionEligibilityPredicate インターフェースによって処理されます。デフォルトでは、ツール実行の適格性は、ToolCallingChatOptions の internalToolExecutionEnabled 属性が true (デフォルト値)に設定されているかどうか、および ChatResponse にツール呼び出しが含まれているかどうかによって判断されます。

public class DefaultToolExecutionEligibilityPredicate implements ToolExecutionEligibilityPredicate {

	@Override
	public boolean test(ChatOptions promptOptions, ChatResponse chatResponse) {
		return ToolCallingChatOptions.isInternalToolExecutionEnabled(promptOptions) && chatResponse != null
				&& chatResponse.hasToolCalls();
	}

}

ChatModel Bean を作成するときに、ToolExecutionEligibilityPredicate のカスタム実装を提供できます。

ユーザー制御ツールの実行

ツール実行ライフサイクルを自分で制御したい場合もあります。その場合は、ToolCallingChatOptions の internalToolExecutionEnabled 属性を false に設定してください。

このオプションを指定して ChatModel を呼び出すと、ツールの実行は呼び出し元に委譲され、ツール実行ライフサイクルを完全に制御できるようになります。ChatResponse でツール呼び出しを確認し、ToolCallingManager を使用して実行する責任はあなたにあります。

次の例は、ユーザー制御のツール実行アプローチの最小限の実装を示しています。

ChatModel chatModel = ...
ToolCallingManager toolCallingManager = ToolCallingManager.builder().build();

ChatOptions chatOptions = ToolCallingChatOptions.builder()
    .toolCallbacks(new CustomerTools())
    .internalToolExecutionEnabled(false)
    .build();
Prompt prompt = new Prompt("Tell me more about the customer with ID 42", chatOptions);

ChatResponse chatResponse = chatModel.call(prompt);

while (chatResponse.hasToolCalls()) {
    ToolExecutionResult toolExecutionResult = toolCallingManager.executeToolCalls(prompt, chatResponse);

    prompt = new Prompt(toolExecutionResult.conversationHistory(), chatOptions);

    chatResponse = chatModel.call(prompt);
}

System.out.println(chatResponse.getResult().getOutput().getText());
ユーザー制御によるツール実行アプローチを選択する場合は、ツール呼び出し操作の管理に ToolCallingManager を使用することをお勧めします。これにより、Spring AI が提供するツール実行のための組み込みサポートを活用できます。ただし、独自のツール実行ロジックを実装することも可能です。

次の例は、ChatMemory API の使用と組み合わせた、ユーザー制御のツール実行アプローチの最小限の実装を示しています。

ToolCallingManager toolCallingManager = DefaultToolCallingManager.builder().build();
ChatMemory chatMemory = MessageWindowChatMemory.builder().build();
String conversationId = UUID.randomUUID().toString();

ChatOptions chatOptions = ToolCallingChatOptions.builder()
    .toolCallbacks(ToolCallbacks.from(new MathTools()))
    .internalToolExecutionEnabled(false)
    .build();
Prompt prompt = new Prompt(
        List.of(new SystemMessage("You are a helpful assistant."), new UserMessage("What is 6 * 8?")),
        chatOptions);
chatMemory.add(conversationId, prompt.getInstructions());

Prompt promptWithMemory = new Prompt(chatMemory.get(conversationId), chatOptions);
ChatResponse chatResponse = chatModel.call(promptWithMemory);
chatMemory.add(conversationId, chatResponse.getResult().getOutput());

while (chatResponse.hasToolCalls()) {
    ToolExecutionResult toolExecutionResult = toolCallingManager.executeToolCalls(promptWithMemory,
            chatResponse);
    chatMemory.add(conversationId, toolExecutionResult.conversationHistory()
        .get(toolExecutionResult.conversationHistory().size() - 1));
    promptWithMemory = new Prompt(chatMemory.get(conversationId), chatOptions);
    chatResponse = chatModel.call(promptWithMemory);
    chatMemory.add(conversationId, chatResponse.getResult().getOutput());
}

UserMessage newUserMessage = new UserMessage("What did I ask you earlier?");
chatMemory.add(conversationId, newUserMessage);

ChatResponse newResponse = chatModel.call(new Prompt(chatMemory.get(conversationId)));

例外処理

ツール呼び出しが失敗すると、例外は ToolExecutionException として伝播され、これをキャッチすることでエラーを処理できます。ToolExecutionExceptionProcessor は ToolExecutionException の処理に使用でき、その処理結果は 2 種類あります。AI モデルに返されるエラーメッセージを生成するか、呼び出し元で処理される例外をスローするかのいずれかです。

@FunctionalInterface
public interface ToolExecutionExceptionProcessor {

	/**
	 * Convert an exception thrown by a tool to a String that can be sent back to the AI
	 * model or throw an exception to be handled by the caller.
	 */
	String process(ToolExecutionException exception);

}

Spring AI Spring Boot Starter のいずれかを使用している場合、DefaultToolExecutionExceptionProcessor は ToolExecutionExceptionProcessor インターフェースの自動構成された実装です。デフォルトでは、エラーメッセージがモデルに返されます。DefaultToolExecutionExceptionProcessor コンストラクターを使用すると、alwaysThrow 属性を true または false に設定できます。true の場合は、モデルにエラーメッセージが返される代わりに例外がスローされます。

`spring.ai.tools.throw-exception-on-error プロパティを使用して、DefaultToolExecutionExceptionProcessor Bean の動作を制御できます。

プロパティ 説明 デフォルト

spring.ai.tools.throw-exception-on-error

true の場合、ツール呼び出しエラーは例外としてスローされ、呼び出し元が処理します。false の場合、エラーはメッセージに変換され、AI モデルに返送されます。これにより、AI モデルはエラーを処理して対応できるようになります。

false

@Bean
ToolExecutionExceptionProcessor toolExecutionExceptionProcessor() {
    return new DefaultToolExecutionExceptionProcessor(true);
}
独自の ToolCallback 実装を定義した場合は、call() メソッドのツール実行ロジックの一部としてエラーが発生したときに、必ず ToolExecutionException をスローするようにしてください。

ToolExecutionExceptionProcessor は、ツール実行中の例外を処理するために、デフォルトの ToolCallingManager (DefaultToolCallingManager)によって内部的に使用されます。ツール実行ライフサイクルの詳細については、ツールの実行を参照してください。

ツール解決

モデルにツールを渡す主なメソッドは、ツールとしてのメソッドおよびツールとしての機能で説明されている戦略のいずれかを使用して、ChatClient または ChatModel を呼び出すときに ToolCallback を提供することです。

ただし、Spring AI は、ToolCallbackResolver インターフェースを使用して実行時にツールを動的に解決することもサポートします。

public interface ToolCallbackResolver {

	/**
	 * Resolve the {@link ToolCallback} for the given tool name.
	 */
	@Nullable
	ToolCallback resolve(String toolName);

}

このアプローチを使用する場合:

  • クライアント側では、ToolCallback ではなく、ChatClient または ChatModel にツール名を指定します。

  • サーバー側では、ToolCallbackResolver 実装がツール名を対応する ToolCallback インスタンスに解決するロールを担います。

デフォルトでは、Spring AI は、ツール解決を ToolCallbackResolver インスタンスのリストに委譲する DelegatingToolCallbackResolver に依存します。

  • SpringBeanToolCallbackResolver は、FunctionSupplierConsumer 型または BiFunction 型の Spring Bean からツールを解決します。詳細については、動的仕様: @Bean を参照してください。

  • StaticToolCallbackResolver は、ToolCallback インスタンスの静的リストからツールを解決します。Spring Boot 自動構成を使用する場合、このリゾルバーは、アプリケーションコンテキストで定義されている ToolCallback 型のすべての Bean を使用して自動的に設定されます。

Spring Boot 自動構成に依存している場合は、カスタム ToolCallbackResolver Bean を提供することで解決ロジックをカスタマイズできます。

@Bean
ToolCallbackResolver toolCallbackResolver(List<FunctionCallback> toolCallbacks) {
    StaticToolCallbackResolver staticToolCallbackResolver = new StaticToolCallbackResolver(toolCallbacks);
    return new DelegatingToolCallbackResolver(List.of(staticToolCallbackResolver));
}

ToolCallbackResolver は、実行時にツールを動的に解決するために ToolCallingManager によって内部的に使用され、フレームワーク制御ツール実行ユーザー制御ツールの実行の両方をサポートします。

可観測性

ツール呼び出しには、完了時間を測定しトレース情報を伝播する spring.ai.tool の観測による可観測性サポートが含まれます。ツール呼び出しの可観測性を参照してください。

オプションで、Spring AI はツール呼び出し引数と結果をスパン属性としてエクスポートできますが、機密性上の理由からデフォルトでは無効になっています。詳細: ツール呼び出し引数と結果データ

ログ

ツール呼び出し機能のすべての主要な操作は、DEBUG レベルでログに記録されます。org.springframework.ai パッケージの場合は、ログレベルを DEBUG に設定することで、ログ記録を有効にすることができます。