工具呼叫
工具呼叫(也稱為函式呼叫)是一種在 AI 應用中常見的模式,允許模型與一組 API(或工具)互動,從而增強其能力。
工具主要用於
-
資訊檢索。此類工具可用於從外部來源檢索資訊,例如資料庫、Web 服務、檔案系統或 Web 搜尋引擎。其目標是增強模型的知識,使其能夠回答原本無法回答的問題。因此,它們可用於檢索增強生成(RAG)場景。例如,工具可用於檢索給定位置的當前天氣、檢索最新新聞文章或查詢資料庫以獲取特定記錄。
-
執行操作。此類工具可用於在軟體系統中執行操作,例如傳送電子郵件、在資料庫中建立新記錄、提交表單或觸發工作流。其目標是自動化原本需要人工干預或顯式程式設計的任務。例如,工具可用於為與聊天機器人互動的客戶預訂航班、填寫網頁上的表單,或在程式碼生成場景中根據自動化測試 (TDD) 實現 Java 類。
儘管我們通常將工具呼叫稱為模型的能力,但實際上是由客戶端應用提供工具呼叫邏輯。模型只能請求工具呼叫並提供輸入引數,而應用負責根據輸入引數執行工具呼叫並返回結果。模型永遠無法訪問作為工具提供的任何 API,這是一個重要的安全考慮因素。
Spring AI 提供了方便的 API 來定義工具、解析來自模型的工具呼叫請求並執行工具呼叫。以下部分概述了 Spring AI 中的工具呼叫能力。
檢視Chat 模型對比,以檢視哪些 AI 模型支援工具呼叫。 |
快速入門
讓我們看看如何在 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 中工具呼叫的主要概念和元件。

-
當我們想讓工具對模型可用時,我們會將它的定義包含在聊天請求中。每個工具定義包括名稱、描述和輸入引數的模式。
-
當模型決定呼叫工具時,它會發送一個響應,其中包含工具名稱以及根據定義模式建模的輸入引數。
-
應用負責使用工具名稱來識別並執行帶有提供輸入引數的工具。
-
工具呼叫的結果由應用處理。
-
應用將工具呼叫結果傳送回模型。
-
模型使用工具呼叫結果作為附加上下文生成最終響應。
工具是工具呼叫的基本構建塊,它們由 ToolCallback
介面建模。Spring AI 提供了內建支援,可以從方法和函式中指定 ToolCallback
,但您始終可以定義自己的 ToolCallback
實現以支援更多用例。
ChatModel
實現會透明地將工具呼叫請求分派給相應的 ToolCallback
實現,並將工具呼叫結果傳送回模型,模型最終將生成最終響應。它們使用 ToolCallingManager
介面執行此操作,該介面負責管理工具執行生命週期。
ChatClient
和 ChatModel
都接受一個 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
:用於將工具呼叫結果轉換為要傳送回 AI 模型的String
物件的ToolCallResultConverter
實現。詳情請參閱結果轉換。
方法可以是靜態方法或例項方法,並且可以具有任何可見性(public、protected、package-private 或 private)。包含該方法的類可以是頂級類或巢狀類,並且也可以具有任何可見性(只要在您計劃例項化它的地方可訪問即可)。
只要包含方法的類是 Spring bean(例如 @Component ),Spring AI 就提供內建支援,對使用 @Tool 註解的方法進行 AOT 編譯。否則,您需要向 GraalVM 編譯器提供必要的配置。例如,透過使用 @RegisterReflection(memberCategories = MemberCategory.INVOKE_DECLARED_METHODS) 註解類。 |
您可以為方法定義任意數量的引數(包括無引數),引數型別可以是大多數型別(基本型別、POJO、列舉、列表、陣列、Map 等等)。類似地,方法可以返回大多數型別,包括 void
。如果方法返回一個值,則返回型別必須是可序列化型別,因為結果將被序列化併發送回模型。
某些型別當前不支援。詳情請參閱方法工具限制。 |
Spring AI 將自動為使用 @Tool
註解的方法的輸入引數生成 JSON schema。schema 由模型用於理解如何呼叫工具並準備工具請求。@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 Schema。
向 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
時,透過將工具類例項傳遞給用於建立 ChatModel
的 ToolCallingChatOptions
例項的 toolCallbacks()
方法來新增預設工具。如果同時提供了預設工具和執行時工具,執行時工具將完全覆蓋預設工具。
預設工具在該 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
:定義工具名稱、描述和輸入 schema 的ToolDefinition
例項。您可以使用ToolDefinition.Builder
類構建它。必需。 -
toolMetadata
:定義附加設定(例如結果是否應直接返回給客戶端以及要使用的結果轉換器)的ToolMetadata
例項。您可以使用ToolMetadata.Builder
類構建它。 -
toolMethod
:表示工具方法的Method
例項。必需。 -
toolObject
:包含工具方法的物件例項。如果方法是靜態方法,您可以省略此引數。 -
toolCallResultConverter
:用於將工具呼叫結果轉換為要傳送回 AI 模型的String
物件的ToolCallResultConverter
例項。如果未提供,將使用預設轉換器 (DefaultToolCallResultConverter
)。
ToolDefinition.Builder
允許您構建 ToolDefinition
例項並定義工具名稱、描述和輸入 schema
-
name
:工具的名稱。如果未提供,將使用方法名稱。AI 模型在呼叫工具時使用此名稱進行識別。因此,同一類中不允許存在名稱相同的兩個工具。對於特定聊天請求,該名稱在模型可用的所有工具中必須是唯一的。 -
description
:工具的描述,模型可以使用此描述來理解何時以及如何呼叫該工具。如果未提供,將使用方法名稱作為工具描述。但是,強烈建議提供詳細描述,這對於模型理解工具目的及使用方法至關重要。未提供良好的描述可能導致模型在應該使用時未使用工具,或者使用錯誤。 -
inputSchema
:工具輸入引數的 JSON schema。如果未提供,schema 將根據方法引數自動生成。您可以使用@ToolParam
註解提供有關輸入引數的附加資訊,例如描述或引數是必需還是可選。預設情況下,所有輸入引數都被視為必需。詳情請參閱JSON Schema。
ToolMetadata.Builder
允許您構建 ToolMetadata
例項並定義工具的附加設定
-
returnDirect
:工具結果是直接返回給客戶端,還是傳回給模型。詳情請參閱直接返回。
Method method = ReflectionUtils.findMethod(DateTimeTools.class, "getCurrentDateTime");
ToolCallback toolCallback = MethodToolCallback.builder()
.toolDefinition(ToolDefinition.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 bean(例如 @Component ),Spring AI 就提供內建支援,對工具方法進行 AOT 編譯。否則,您需要向 GraalVM 編譯器提供必要的配置。例如,透過使用 @RegisterReflection(memberCategories = MemberCategory.INVOKE_DECLARED_METHODS) 註解類。 |
您可以為方法定義任意數量的引數(包括無引數),引數型別可以是大多數型別(基本型別、POJO、列舉、列表、陣列、Map 等等)。類似地,方法可以返回大多數型別,包括 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(ToolDefinition.builder(method)
.description("Get the current date and time in the user's timezone")
.build())
.toolMethod(method)
.build();
Spring AI 將自動為方法的輸入引數生成 JSON schema。schema 由模型用於理解如何呼叫工具並準備工具請求。@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 Schema。
向 ChatClient
和 ChatModel
新增工具
當使用程式設計式規範方法時,您可以將 MethodToolCallback
例項傳遞給 ChatClient
的 tools()
方法。該工具僅對它所新增到的特定聊天請求可用。
ToolCallback toolCallback = ...
ChatClient.create(chatModel)
.prompt("What day is tomorrow?")
.tools(toolCallback)
.call()
.content();
向 ChatClient
新增預設工具
當使用程式設計式規範方法時,您可以透過將 MethodToolCallback
例項傳遞給 defaultTools()
方法來向 ChatClient.Builder
新增預設工具。如果同時提供了預設工具和執行時工具,執行時工具將完全覆蓋預設工具。
預設工具在由同一個 ChatClient.Builder 構建的所有 ChatClient 例項執行的所有聊天請求中共享。它們對於在不同聊天請求中常用的工具很有用,但如果使用不當,也可能存在危險,可能會在不應該可用的情況下使其可用。 |
ChatModel chatModel = ...
ToolCallback toolCallback = ...
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultTools(toolCallback)
.build();
向 ChatModel
新增工具
當使用程式設計式規範方法時,您可以將 MethodToolCallback
例項傳遞給您用於呼叫 ChatModel
的 ToolCallingChatOptions
的 toolCallbacks()
方法。該工具僅對它所新增到的特定聊天請求可用。
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
時,透過將 MethodToolCallback
例項傳遞給用於建立 ChatModel
的 ToolCallingChatOptions
例項的 toolCallbacks()
方法來新增預設工具。如果同時提供了預設工具和執行時工具,執行時工具將完全覆蓋預設工具。
預設工具在該 ChatModel 例項執行的所有聊天請求中共享。它們對於在不同聊天請求中常用的工具很有用,但如果使用不當,也可能存在危險,可能會在不應該可用的情況下使其可用。 |
ToolCallback toolCallback = ...
ChatModel chatModel = OllamaChatModel.builder()
.ollamaApi(OllamaApi.builder().build())
.defaultOptions(ToolCallingChatOptions.builder()
.toolCallbacks(toolCallback)
.build())
.build();
方法工具限制
以下型別當前不支援作為工具方法的引數或返回型別
-
Optional
-
非同步型別(例如
CompletableFuture
,Future
) -
響應式型別(例如
Flow
,Mono
,Flux
) -
函式式型別(例如
Function
,Supplier
,Consumer
)。
函式式型別可以使用基於函式的工具規範方法來支援。詳情請參閱函式作為工具。
函式作為工具
Spring AI 提供內建支援,可以從函式中指定工具,既可以透過程式設計式使用低階 FunctionToolCallback
實現,也可以動態地作為執行時解析的 @Bean
。
程式設計式規範:FunctionToolCallback
您可以透過程式設計式構建 FunctionToolCallback
將函式式型別(Function
、Supplier
、Consumer
或 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 模型在呼叫工具時使用此名稱進行識別。因此,在同一上下文中不允許存在名稱相同的兩個工具。對於特定聊天請求,該名稱在模型可用的所有工具中必須是唯一的。必需。 -
toolFunction
:表示工具方法的函式式物件(Function
、Supplier
、Consumer
或BiFunction
)。必需。 -
description
:工具的描述,模型可以使用此描述來理解何時以及如何呼叫該工具。如果未提供,將使用方法名稱作為工具描述。但是,強烈建議提供詳細描述,這對於模型理解工具目的及使用方法至關重要。未提供良好的描述可能導致模型在應該使用時未使用工具,或者使用錯誤。 -
inputType
:函式輸入的型別。必需。 -
inputSchema
:工具輸入引數的 JSON schema。如果未提供,schema 將根據inputType
自動生成。您可以使用@ToolParam
註解提供有關輸入引數的附加資訊,例如描述或引數是必需還是可選。預設情況下,所有輸入引數都被視為必需。詳情請參閱JSON Schema。 -
toolMetadata
:定義附加設定(例如結果是否應直接返回給客戶端以及要使用的結果轉換器)的ToolMetadata
例項。您可以使用ToolMetadata.Builder
類構建它。 -
toolCallResultConverter
:用於將工具呼叫結果轉換為要傳送回 AI 模型的String
物件的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 必須是可序列化的,因為結果將被序列化併發送回模型。函式以及輸入和輸出型別必須是 public 的。
某些型別當前不支援。詳情請參閱函式工具限制。 |
向 ChatClient
新增工具
當使用程式設計式規範方法時,您可以將 FunctionToolCallback
例項傳遞給 ChatClient
的 tools()
方法。該工具僅對它所新增到的特定聊天請求可用。
ToolCallback toolCallback = ...
ChatClient.create(chatModel)
.prompt("What's the weather like in Copenhagen?")
.tools(toolCallback)
.call()
.content();
向 ChatClient
新增預設工具
當使用程式設計式規範方法時,您可以透過將 FunctionToolCallback
例項傳遞給 defaultTools()
方法來向 ChatClient.Builder
新增預設工具。如果同時提供了預設工具和執行時工具,執行時工具將完全覆蓋預設工具。
預設工具在由同一個 ChatClient.Builder 構建的所有 ChatClient 例項執行的所有聊天請求中共享。它們對於在不同聊天請求中常用的工具很有用,但如果使用不當,也可能存在危險,可能會在不應該可用的情況下使其可用。 |
ChatModel chatModel = ...
ToolCallback toolCallback = ...
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultTools(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
時,透過將 FunctionToolCallback
例項傳遞給用於建立 ChatModel
的 ToolCallingChatOptions
例項的 toolCallbacks()
方法來新增預設工具。如果同時提供了預設工具和執行時工具,執行時工具將完全覆蓋預設工具。
預設工具在該 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
實現)動態解析它們。此選項使您可以使用任何 Function
、Supplier
、Consumer
或 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 schema 將自動生成。您可以使用 @ToolParam
註解為輸入引數提供額外資訊,例如描述或引數是否為必需或可選。預設情況下,所有輸入引數都被視為必需。有關更多詳細資訊,請參閱 JSON Schema。
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?")
.tools("currentWeather")
.call()
.content();
向 ChatClient
新增預設工具
使用動態規範方法時,您可以透過將工具名稱傳遞給 defaultTools()
方法,向 ChatClient.Builder
新增預設工具。如果同時提供了預設工具和執行時工具,則執行時工具將完全覆蓋預設工具。
預設工具在由同一個 ChatClient.Builder 構建的所有 ChatClient 例項執行的所有聊天請求中共享。它們對於在不同聊天請求中常用的工具很有用,但如果使用不當,也可能存在危險,可能會在不應該可用的情況下使其可用。 |
ChatModel chatModel = ...
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultTools("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
時,透過將工具名稱傳遞給用於建立 ChatModel
例項的 ToolCallingChatOptions
的 toolNames()
方法,向 ChatModel
新增預設工具。如果同時提供了預設工具和執行時工具,則執行時工具將完全覆蓋預設工具。
預設工具在該 ChatModel 例項執行的所有聊天請求中共享。它們對於在不同聊天請求中常用的工具很有用,但如果使用不當,也可能存在危險,可能會在不應該可用的情況下使其可用。 |
ChatModel chatModel = OllamaChatModel.builder()
.ollamaApi(OllamaApi.builder().build())
.defaultOptions(ToolCallingChatOptions.builder()
.toolNames("currentWeather")
.build())
.build();
函式工具的限制
目前,以下型別不支援用作工具函式的輸入或輸出型別:
-
基本型別
-
Optional
-
集合型別(例如
List
、Map
、Array
、Set
) -
非同步型別(例如
CompletableFuture
,Future
) -
響應式型別(例如
Flow
、Mono
、Flux
)。
基本型別和集合型別可以透過基於方法論的工具規範方法來支援。有關更多詳細資訊,請參閱 方法作為工具。
工具規範
在 Spring AI 中,工具透過 ToolCallback
介面建模。在前面的章節中,我們已經瞭解瞭如何使用 Spring AI 提供的內建支援(參見 方法作為工具 和 函式作為工具)從方法和函式定義工具。本節將深入探討工具規範以及如何對其進行自定義和擴充套件以支援更多用例。
Tool Callback
ToolCallback
介面提供了一種定義可由 AI 模型呼叫的工具的方式,包括定義和執行邏輯。它是從頭開始定義工具時需要實現的主要介面。例如,您可以從 MCP 客戶端(使用 Model Context Protocol)或 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 模型瞭解工具的可用性提供了所需資訊,包括工具名稱、描述和輸入 schema。每個 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();
}
有關輸入 schema 的更多詳細資訊,請參閱 JSON Schema。 |
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 schema。如果方法使用 @Tool
註解,則工具名稱和描述(如果已設定)將取自該註解。
有關更多詳細資訊,請參閱 方法作為工具。 |
如果您寧願顯式提供部分或全部屬性,可以使用 ToolDefinition.Builder
構建自定義的 ToolDefinition
例項。
Method method = ReflectionUtils.findMethod(DateTimeTools.class, "getCurrentDateTime");
ToolDefinition toolDefinition = ToolDefinition.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
的工具名稱、描述和輸入 schema。有關更多詳細資訊,請參閱 函式作為工具。
JSON Schema
當向 AI 模型提供工具時,模型需要知道呼叫該工具所需的輸入型別的 schema。Schema 用於理解如何呼叫工具並準備工具請求。Spring AI 透過 JsonSchemaGenerator
類提供內建支援,用於生成工具輸入型別的 JSON Schema。該 schema 作為 ToolDefinition
的一部分提供。
有關 ToolDefinition 以及如何將輸入 schema 傳遞給它的更多詳細資訊,請參閱 工具定義。 |
JsonSchemaGenerator
類在底層用於根據 方法作為工具 和 函式作為工具 中描述的任何策略,生成方法或函式輸入引數的 JSON schema。JSON schema 生成邏輯支援一系列可用於方法和函式輸入引數的註解,以自定義生成的 schema。
本節描述了在為工具輸入引數生成 JSON schema 時可以自定義的兩個主要選項:描述和必需狀態。
描述
除了為工具本身提供描述外,您還可以為工具的輸入引數提供描述。描述可用於提供有關輸入引數的關鍵資訊,例如引數應採用什麼格式、允許什麼值等。這有助於模型理解輸入 schema 以及如何使用它。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 模型傳遞的工具引數一起使用。

class CustomerTools {
@Tool(description = "Retrieve customer information")
Customer getCustomerInfo(Long id, ToolContext toolContext) {
return customerRepository.findById(id, toolContext.get("tenantId"));
}
}
在呼叫 ChatClient
時,ToolContext
會填充使用者提供的資料。
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 ,才能將結果直接返回給呼叫者。否則,結果將傳送回模型。 |

-
當我們想讓模型可以使用某個工具時,我們會將該工具的定義包含在聊天請求中。如果希望工具執行的結果直接返回給呼叫者,我們將
returnDirect
屬性設定為true
。 -
當模型決定呼叫工具時,它會發送一個響應,其中包含工具名稱以及根據定義模式建模的輸入引數。
-
應用負責使用工具名稱來識別並執行帶有提供輸入引數的工具。
-
工具呼叫的結果由應用處理。
-
應用程式直接將工具呼叫結果傳送給呼叫者,而不是將其傳送回模型。
方法直接返回
使用宣告式方法從方法構建工具時,您可以透過將 @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
實現內部透明地為您管理工具執行生命週期。但您可以選擇退出此行為並自行控制工具執行。本節描述了這兩種場景。
框架控制的工具執行
使用預設行為時,Spring AI 會自動攔截來自模型的任何工具呼叫請求,呼叫工具並將結果返回給模型。所有這一切都由每個 ChatModel
實現使用 ToolCallingManager
透明地為您完成。

-
當我們想讓模型可以使用某個工具時,我們會將該工具的定義包含在聊天請求(
Prompt
)中,並呼叫將請求傳送到 AI 模型的ChatModel
API。 -
當模型決定呼叫某個工具時,它會發送一個響應(
ChatResponse
),其中包含工具名稱和根據已定義 schema 建模的輸入引數。 -
ChatModel
將工具呼叫請求傳送到ToolCallingManager
API。 -
ToolCallingManager
負責識別要呼叫的工具,並使用提供的輸入引數執行它。 -
工具呼叫的結果返回給
ToolCallingManager
。 -
ToolCallingManager
將工具執行結果返回給ChatModel
。 -
ChatModel
將工具執行結果傳送回 AI 模型(ToolResponseMessage
)。 -
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
,它有兩種結果:生成一個錯誤訊息傳送回 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
,則會丟擲異常而不是將錯誤訊息傳送回模型。
@Bean
ToolExecutionExceptionProcessor toolExecutionExceptionProcessor() {
return new DefaultToolExecutionExceptionProcessor(true);
}
如果您定義了自己的 ToolCallback 實現,請確保在 call() 方法的工具執行邏輯中發生錯誤時丟擲 ToolExecutionException 。 |
預設的 ToolCallingManager
(DefaultToolCallingManager
)內部使用 ToolExecutionExceptionProcessor
來處理工具執行期間的異常。有關工具執行生命週期的更多詳細資訊,請參閱 工具執行。
工具解析
但是,Spring AI 還支援使用 ToolCallbackResolver
介面在執行時動態解析工具。
public interface ToolCallbackResolver {
/**
* Resolve the {@link ToolCallback} for the given tool name.
*/
@Nullable
ToolCallback resolve(String toolName);
}
使用這種方法時:
-
在客戶端,您向
ChatClient
或ChatModel
提供工具名稱,而不是ToolCallback
(s)。 -
在服務端,
ToolCallbackResolver
實現負責將工具名稱解析為相應的ToolCallback
例項。
預設情況下,Spring AI 依賴於 DelegatingToolCallbackResolver
,它將工具解析委託給 ToolCallbackResolver
例項列表:
-
SpringBeanToolCallbackResolver
從型別為Function
、Supplier
、Consumer
或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
在內部使用,以在執行時動態解析工具,支援 框架控制的工具執行 和 使用者控制的工具執行。