工具呼叫

工具呼叫(也稱為函式呼叫)是 AI 應用程式中常見的模式,它允許模型與一組 API 或工具互動,從而增強其能力。

工具主要用於:

  • 資訊檢索。此類別中的工具可用於從外部源(例如資料庫、Web 服務、檔案系統或 Web 搜尋引擎)檢索資訊。目標是增強模型的知識,使其能夠回答否則無法回答的問題。因此,它們可用於檢索增強生成 (RAG) 場景。例如,工具可用於檢索給定位置的當前天氣、檢索最新新聞文章或查詢資料庫以獲取特定記錄。

  • 執行操作。此類別中的工具可用於在軟體系統中執行操作,例如傳送電子郵件、在資料庫中建立新記錄、提交表單或觸發工作流。目標是自動化否則需要人工干預或顯式程式設計的任務。例如,工具可用於為與聊天機器人互動的客戶預訂航班、填寫網頁上的表單,或在程式碼生成場景中根據自動化測試 (TDD) 實現 Java 類。

儘管我們通常將工具呼叫稱為模型能力,但實際提供工具呼叫邏輯的是客戶端應用程式。模型只能請求工具呼叫並提供輸入引數,而應用程式負責根據輸入引數執行工具呼叫並返回結果。模型永遠無法訪問作為工具提供的任何 API,這是一項關鍵的安全考慮。

Spring AI 提供便捷的 API 來定義工具、解析模型的工具呼叫請求並執行工具呼叫。以下部分概述了 Spring AI 中的工具呼叫功能。

檢視聊天模型比較,瞭解哪些 AI 模型支援工具呼叫。
按照指南從已棄用的FunctionCallback 遷移到 ToolCallback API

快速入門

讓我們看看如何在 Spring AI 中開始使用工具呼叫。我們將實現兩個簡單的工具:一個用於資訊檢索,一個用於執行操作。資訊檢索工具將用於獲取使用者時區的當前日期和時間。操作工具將用於設定指定時間的鬧鐘。

資訊檢索

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 模型可用於生成實現某些目標的計劃。例如,模型可以生成預訂丹麥旅行的計劃。然而,模型不具備執行計劃的能力。這就是工具的用武之地:它們可以用於執行模型生成的計劃。

在前面的示例中,我們使用了一個工具來確定當前日期和時間。在此示例中,我們將定義第二個工具,用於在特定時間設定鬧鐘。目標是設定一個從現在起 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 介面來實現這一點,該介面負責管理工具執行生命週期。

ChatClientChatModel 都接受 ToolCallback 物件列表,以使工具可供模型和最終執行它們的 ToolCallingManager 使用。

除了直接傳遞 ToolCallback 物件之外,您還可以傳遞工具名稱列表,這些名稱將使用 ToolCallbackResolver 介面動態解析。

以下部分將更詳細地介紹所有這些概念和 API,包括如何自定義和擴充套件它們以支援更多用例。

方法作為工具

Spring AI 提供內建支援,以兩種方式從方法指定工具(即 ToolCallback):

  • 宣告式地,使用 @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 模型使用此名稱在呼叫工具時識別它。因此,同一類中不允許有兩個同名的工具。名稱在特定聊天請求可用的所有工具中必須是唯一的。

  • description:工具的描述,模型可以使用它來理解何時以及如何呼叫工具。如果未提供,方法名稱將用作工具描述。但是,強烈建議提供詳細描述,因為這對模型理解工具的目的和如何使用至關重要。未能提供好的描述可能導致模型在應該使用工具時沒有使用,或者使用不正確。

  • returnDirect:工具結果是直接返回給客戶端還是傳遞迴模型。有關更多詳細資訊,請參閱直接返回

  • resultConverter:用於將工具呼叫結果轉換為 String 物件以傳送回 AI 模型的 ToolCallResultConverter 實現。有關更多詳細資訊,請參閱結果轉換

該方法可以是靜態方法或例項方法,並且可以具有任何可見性(公共、受保護、包私有或私有)。包含該方法的類可以是頂級類或巢狀類,並且也可以具有任何可見性(只要它在您打算例項化它的地方可訪問)。

只要包含方法的類是 Spring bean(例如 @Component),Spring AI 就為 @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

當使用宣告式規範方法時,您可以將工具類例項傳遞給用於呼叫 ChatModelToolCallingChatOptionstoolCallbacks() 方法。此類工具將僅對它們所新增的特定聊天請求可用。

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 新增預設工具

當使用宣告式規範方法時,您可以透過將工具類例項傳遞給用於建立 ChatModelToolCallingChatOptions 例項的 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 模型使用此名稱在呼叫工具時識別它。因此,同一類中不允許有兩個同名的工具。名稱在特定聊天請求可用的所有工具中必須是唯一的。

  • description:工具的描述,模型可以使用它來理解何時以及如何呼叫工具。如果未提供,方法名稱將用作工具描述。但是,強烈建議提供詳細描述,因為這對模型理解工具的目的和如何使用至關重要。未能提供好的描述可能導致模型在應該使用工具時沒有使用,或者使用不正確。

  • inputSchema:工具輸入引數的 JSON 模式。如果未提供,模式將根據方法引數自動生成。您可以使用 @ToolParam 註解提供有關輸入引數的額外資訊,例如描述或引數是必需還是可選。預設情況下,所有輸入引數都被認為是必需的。有關更多詳細資訊,請參閱JSON 模式

ToolMetadata.Builder 允許您構建 ToolMetadata 例項並定義工具的其他設定:

  • returnDirect:工具結果是直接返回給客戶端還是傳遞迴模型。有關更多詳細資訊,請參閱直接返回

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();

該方法可以是靜態方法或例項方法,並且可以具有任何可見性(公共、受保護、包私有或私有)。包含該方法的類可以是頂級類或巢狀類,並且也可以具有任何可見性(只要它在您打算例項化它的地方可訪問)。

只要包含方法的類是 Spring bean(例如 @Component),Spring AI 就為工具方法提供了內建的 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 模式

將工具新增到 ChatClientChatModel

當使用程式設計規範方法時,您可以將 MethodToolCallback 例項傳遞給 ChatClienttoolCallbacks() 方法。該工具將僅對它所新增的特定聊天請求可用。

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

當使用程式設計規範方法時,您可以將 MethodToolCallback 例項傳遞給用於呼叫 ChatModelToolCallingChatOptionstoolCallbacks() 方法。該工具將僅對它所新增的特定聊天請求可用。

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

ChatModel 新增預設工具

當使用程式設計規範方法時,您可以透過將 MethodToolCallback 例項傳遞給用於建立 ChatModelToolCallingChatOptions 例項的 toolCallbacks() 方法來在構建時向 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 將函式型別(FunctionSupplierConsumerBiFunction)轉換為工具。

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 模型使用此名稱在呼叫工具時識別它。因此,同一上下文中不允許有兩個同名的工具。名稱在特定聊天請求可用的所有工具中必須是唯一的。必需。

  • toolFunction:表示工具方法的函式物件(FunctionSupplierConsumerBiFunction)。必需。

  • description:工具的描述,模型可以使用它來理解何時以及如何呼叫工具。如果未提供,方法名稱將用作工具描述。但是,強烈建議提供詳細描述,因為這對模型理解工具的目的和如何使用至關重要。未能提供好的描述可能導致模型在應該使用工具時沒有使用,或者使用不正確。

  • inputType:函式輸入的型別。必需。

  • inputSchema:工具輸入引數的 JSON 模式。如果未提供,模式將根據 inputType 自動生成。您可以使用 @ToolParam 註解提供有關輸入引數的額外資訊,例如描述或引數是必需還是可選。預設情況下,所有輸入引數都被認為是必需的。有關更多詳細資訊,請參閱JSON 模式

  • toolMetadata:定義附加設定的 ToolMetadata 例項,例如結果是否應直接返回給客戶端,以及要使用的結果轉換器。您可以使用 ToolMetadata.Builder 類構建它。

  • toolCallResultConverter:用於將工具呼叫結果轉換為 String 物件以傳送回 AI 模型的 ToolCallResultConverter 例項。如果未提供,將使用預設轉換器(DefaultToolCallResultConverter)。

ToolMetadata.Builder 允許您構建 ToolMetadata 例項並定義工具的其他設定:

  • returnDirect:工具結果是直接返回給客戶端還是傳遞迴模型。有關更多詳細資訊,請參閱直接返回

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

函式輸入和輸出可以是 Void 或 POJO。輸入和輸出 POJO 必須是可序列化的,因為結果將被序列化併發送回模型。函式以及輸入和輸出型別必須是公共的。

某些型別不支援。有關更多詳細資訊,請參閱函式工具限制

ChatClient 新增工具

當使用程式設計規範方法時,您可以將 FunctionToolCallback 例項傳遞給 ChatClienttoolCallbacks() 方法。該工具將僅對它所新增的特定聊天請求可用。

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 例項傳遞給 ToolCallingChatOptionstoolCallbacks() 方法。該工具將僅對它所新增的特定聊天請求可用。

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 新增預設工具

當使用程式設計規範方法時,您可以透過將 FunctionToolCallback 例項傳遞給用於建立 ChatModelToolCallingChatOptions 例項的 toolCallbacks() 方法來在構建時向 ChatModel 新增預設工具。如果同時提供了預設工具和執行時工具,則執行時工具將完全覆蓋預設工具。

預設工具在由該 ChatModel 例項執行的所有聊天請求中共享。它們對於跨不同聊天請求常用工具很有用,但如果不小心使用也可能很危險,冒著在不應該可用時使其可用的風險。
ToolCallback toolCallback = ...
ChatModel chatModel = OllamaChatModel.builder()
    .ollamaApi(OllamaApi.builder().build())
    .defaultOptions(ToolCallingChatOptions.builder()
            .toolCallbacks(toolCallback)
            .build())
    .build();

動態規範:@Bean

您可以透過將工具定義為 Spring bean,而不是以程式設計方式指定工具,並讓 Spring AI 使用 ToolCallbackResolver 介面(透過 SpringBeanToolCallbackResolver 實現)在執行時動態解析它們。此選項使您能夠使用任何 FunctionSupplierConsumerBiFunction 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 名稱)傳遞給 ChatClienttoolNames() 方法。該工具將僅對它所新增的特定聊天請求可用。

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

當使用動態規範方法時,您可以將工具名稱傳遞給用於呼叫 ChatModelToolCallingChatOptionstoolNames() 方法。該工具將僅對它所新增的特定聊天請求可用。

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 新增預設工具

當使用動態規範方法時,您可以透過將工具名稱傳遞給用於建立 ChatModelToolCallingChatOptions 例項的 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 = ToolDefinitions.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 模式時可以自定義的兩個主要選項:描述和必需狀態。

描述

除了為工具本身提供描述外,您還可以為工具的輸入引數提供描述。描述可用於提供有關輸入引數的關鍵資訊,例如引數應採用何種格式,允許哪些值等等。這有助於模型理解輸入模式以及如何使用它。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 序列化為 JSON(DefaultToolCallResultConverter),但您可以透過提供自己的 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.BuilderresultConverter() 屬性來為工具提供自定義的 ToolCallResultConverter

有關更多詳細資訊,請參閱方法作為工具

函式工具呼叫結果轉換

當使用程式設計方法從函式構建工具時,您可以透過設定 FunctionToolCallback.BuilderresultConverter() 屬性來為工具提供自定義的 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 將是兩者的合併,其中執行時選項優先於預設選項。

直接返回

預設情況下,工具呼叫的結果作為響應傳送回模型。然後,模型可以使用結果繼續對話。

在某些情況下,您寧願將結果直接返回給呼叫者,而不是將其傳送回模型。例如,如果您構建了一個依賴 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,DefaultToolCallingManagerToolCallingManager 介面的自動配置實現。您可以透過提供自己的 ToolCallingManager bean 來定製工具執行行為。

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

預設情況下,Spring AI 在每個 ChatModel 實現內部透明地為您管理工具執行生命週期。但您可以選擇退出此行為並自行控制工具執行。本節描述了這兩種情況。

框架控制的工具執行

當使用預設行為時,Spring AI 將自動攔截來自模型的任何工具呼叫請求,呼叫工具並將結果返回給模型。所有這些都由每個 ChatModel 實現使用 ToolCallingManager 透明地為您完成。

Framework-controlled tool execution lifecycle
  1. 當我們需要使工具可供模型使用時,我們會在聊天請求 (Prompt) 中包含其定義並呼叫 ChatModel API,該 API 將請求傳送到 AI 模型。

  2. 當模型決定呼叫工具時,它會發送一個包含工具名稱和根據定義模式建模的輸入引數的響應 (ChatResponse)。

  3. ChatModel 將工具呼叫請求傳送到 ToolCallingManager API。

  4. ToolCallingManager 負責識別要呼叫的工具並使用提供的輸入引數執行它。

  5. 工具呼叫的結果返回給 ToolCallingManager

  6. ToolCallingManager 將工具執行結果返回給 ChatModel

  7. ChatModel 將工具執行結果發回給 AI 模型 (ToolResponseMessage)。

  8. AI 模型使用工具呼叫結果作為附加上下文生成最終響應,並透過 ChatClient 將其發回給呼叫者 (ChatResponse)。

目前,與模型交換的關於工具執行的內部訊息不會暴露給使用者。如果您需要訪問這些訊息,您應該使用使用者控制的工具執行方法。

確定工具呼叫是否符合執行條件的邏輯由 ToolExecutionEligibilityPredicate 介面處理。預設情況下,工具執行資格由檢查 ToolCallingChatOptionsinternalToolExecutionEnabled 屬性是否設定為 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 實現。

使用者控制的工具執行

在某些情況下,您寧願自己控制工具執行生命週期。您可以透過將 ToolCallingChatOptionsinternalToolExecutionEnabled 屬性設定為 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,並有兩種結果:生成要傳送回 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,DefaultToolExecutionExceptionProcessorToolExecutionExceptionProcessor 介面的自動配置實現。預設情況下,RuntimeException 的錯誤訊息會發送回模型,而檢查異常和錯誤(例如 IOExceptionOutOfMemoryError)總是丟擲。DefaultToolExecutionExceptionProcessor 建構函式允許您將 alwaysThrow 屬性設定為 truefalse。如果為 true,則會丟擲異常,而不是將錯誤訊息傳送回模型。

您可以使用 spring.ai.tools.throw-exception-on-error 屬性控制 DefaultToolExecutionExceptionProcessor bean 的行為:

財產 描述 預設值

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

如果為 true,則工具呼叫錯誤將作為異常丟擲,由呼叫者處理。如果為 false,則錯誤將轉換為訊息併發送回 AI 模型,允許其處理並響應錯誤。

@Bean
ToolExecutionExceptionProcessor toolExecutionExceptionProcessor() {
    return new DefaultToolExecutionExceptionProcessor(true);
}
如果您定義了自己的 ToolCallback 實現,請確保在 call() 方法的工具執行邏輯中發生錯誤時丟擲 ToolExecutionException

ToolExecutionExceptionProcessor 由預設的 ToolCallingManagerDefaultToolCallingManager)內部使用,用於在工具執行期間處理異常。有關工具執行生命週期的更多詳細資訊,請參閱工具執行

工具解析

將工具傳遞給模型的主要方法是在呼叫 ChatClientChatModel 時提供 ToolCallback,使用方法作為工具函式作為工具中描述的策略之一。

但是,Spring AI 還支援使用 ToolCallbackResolver 介面在執行時動態解析工具。

public interface ToolCallbackResolver {

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

}

當使用此方法時:

  • 在客戶端,您將工具名稱而不是 ToolCallback 傳遞給 ChatClientChatModel

  • 在伺服器端,ToolCallbackResolver 實現負責將工具名稱解析為相應的 ToolCallback 例項。

預設情況下,Spring AI 依賴於一個 DelegatingToolCallbackResolver,它將工具解析委託給一個 ToolCallbackResolver 例項列表:

  • SpringBeanToolCallbackResolver 從型別為 FunctionSupplierConsumerBiFunction 的 Spring bean 解析工具。有關更多詳細資訊,請參閱動態規範:@Bean

  • StaticToolCallbackResolverToolCallback 例項的靜態列表解析工具。當使用 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));
}

ToolCallbackResolverToolCallingManager 內部使用,以在執行時動態解析工具,支援框架控制的工具執行使用者控制的工具執行

可觀察性

工具呼叫包括對 spring.ai.tool 觀測的可觀察性支援,該觀測測量完成時間並傳播跟蹤資訊。請參閱工具呼叫可觀察性

此外,Spring AI 可以將工具呼叫引數和結果作為跨度屬性匯出,預設情況下出于敏感性原因停用。詳細資訊:工具呼叫引數和結果資料

日誌記錄

工具呼叫功能的所有主要操作都以 DEBUG 級別記錄。您可以透過將 org.springframework.ai 包的日誌級別設定為 DEBUG 來啟用日誌記錄。

© . This site is unofficial and not affiliated with VMware.