工具调用概述
工具调用(Tool Calling,也称为 函数调用 Function Calling)是 AI 应用中的一种常见模式,允许模型与一组 API 或”工具”交互,从而扩展模型的能力。
工具主要用于以下场景:
- 信息检索(Information Retrieval):此类工具用于从外部源(如数据库、Web 服务、文件系统或搜索引擎)检索信息,目的是增强模型的知识,使其能回答原本无法回答的问题,比如适用于 检索增强生成(RAG) 场景。工具的使用例子有“获取某地的当前天气”、“检索最新新闻文章”、“查询数据库中的特定记录”等等
- 执行操作(Taking Action):此类工具可在软件系统中执行操作,如发送邮件、在数据库中创建记录、提交表单或触发工作流,旨在自动化原需人工干预或显式编程的任务。比如“为聊天机器人用户预订航班”、“填写网页表单”、“在代码生成场景中依据自动化测试实现 Java 类(TDD)”
虽然通常称”工具调用”是模型的能力,但实际上由客户端应用程序提供工具调用逻辑。模型只能请求调用工具并提供输入参数,应用程序负责执行工具并返回结果。模型永远不会直接接触被注册为工具的 API。
工具调用示例
场景:闹钟设置(如果经常用siri来预定闹钟,那么对这个场景就不会陌生)
涉及到两个简单工具:一个用于信息检索,一个用于执行操作。
- 信息检索工具: 获取用户时区的当前日期和时间
- 操作工具: 在指定时间设置闹钟
定义 @Tool
定义 DateTimeTools 类并实现两个工具,用 @Tool 注解标记方法并给出详细描述:
1 | public class DateTimeTools { |
注册并调用
通过 tools() 方法进行工具实例注册,ChatClient 内部会自动处理工具调用循环:
1 | String answer = chatClient.prompt() |
执行结果
1 | [getCurrentDateTime] |
工具核心概念
Spring AI 通过一系列灵活的抽象层支持工具调用,使得能够以一致的方式定义、解析和执行工具。工具触发的整个过程如下:
- 应用将工具定义(名称 + 描述 + 入参 JSON Schema)随聊天请求发给模型
- 模型决定调用工具时,返回工具名称 + 入参
- 应用按工具名称来识别并根据提供的输入参数执行工具
- 应用程序会处理工具调用的结果
- 应用将工具执行结果返回给模型
- 模型使用工具调用结果作为附加上下文生成最终回复
整个过程中对工具调用的派发执行是由ToolCallingManager(DefaultToolCallingManager)来协调完成的,其中工具是工具调用的构建块,由 ToolCallback 接口建模;Spring AI 内置支持从方法(@Tool 注解)和函数(Function/Supplier 等)生成 ToolCallback;
基于方法的工具定义 — @Tool 注解(声明式)
@Tool 注解属性
| 属性 | 说明 |
|---|---|
name |
工具名称,默认取方法名。同一请求中所有工具名称必须唯一 |
description |
工具描述,强烈建议详细填写,影响模型是否及何时调用 |
returnDirect |
true = 结果直接返回调用方,不回传模型 |
resultConverter |
自定义 ToolCallResultConverter |
方法要求:
- 方法可以是实例方法或静态方法,任意访问权限(public、protected、package-private 或 private)
- 包含方法的类可以是顶层类或嵌套类,任意访问权限
- 参数数量任意(包括无参),支持大多数类型:基本类型、POJO、Enum、List、数组、Map 等
- 返回值可以是大多数类型,包括
void - 如果方法有返回值,返回类型必须是可序列化类型
@ToolParam 注解属性
| 属性 | 说明 |
|---|---|
description |
参数描述,帮助模型理解如何使用该参数(格式、允许值等) |
required |
是否必填,默认 true |
如果参数标注了
@Nullable,除非使用@ToolParam(required = true)显式标记,否则视为可选。
除了 @ToolParam,还可以使用 Swagger 的 @Schema 或 Jackson 的 @JsonProperty/@JsonClassDescription。
示例:
1 | (description = "根据城市名查询当前天气") |
注册方式
单次请求注册:
1 | chatClient.prompt() |
默认工具(Builder 级别):
1 | ChatClient chatClient = ChatClient.builder(model) |
通过 ChatModel 选项注册:
1 | ChatModel model = ...; |
如果同时提供了默认工具和运行时工具,运行时工具将完全覆盖默认工具。
编程式生成 ToolCallback:
1 | // 使用 ToolCallbacks 工具类 |
tools() 方法也直接接受 ToolCallback、ToolCallbackProvider 实例,以及 @Tool 注解的 POJO 实例和这些类型的集合。
基于方法的工具定义 — 编程式(MethodToolCallback)
使用 MethodToolCallback.Builder 以编程方式构建工具:
1 | MethodToolCallback callback = MethodToolCallback.builder() |
Builder 可配置项:
| 属性 | 说明 |
|---|---|
toolDefinition |
ToolDefinition 实例,定义工具名称、描述和输入 Schema(必填) |
toolMetadata |
ToolMetadata 实例,定义额外设置(如 returnDirect、resultConverter) |
toolMethod |
代表工具方法的 Method 对象(必填) |
toolObject |
包含工具方法的对象实例(静态方法可省略) |
toolCallResultConverter |
自定义结果转换器 |
注册方式同声明式:
.tools(callback)— 单次请求.defaultTools(callback)— 默认工具ToolCallingChatOptions.toolCallbacks(callback)— ChatModel 选项
❌ 不支持作为工具方法参数/返回值的类型:
Optional、异步类型(CompletableFuture、Future)、响应式类型(Flow、Mono、Flux)、函数式类型(Function、Supplier、Consumer)。函数式类型请使用函数式工具规范方式。
基于函数的工具定义 — FunctionToolCallback
支持 Function、Supplier、Consumer、BiFunction 等函数式类型。
1 | FunctionToolCallback.from( |
Builder 可配置项:
| 属性 | 说明 |
|---|---|
name |
工具名称(必填) |
toolFunction |
函数对象 Function/Supplier/Consumer/BiFunction(必填) |
description |
工具描述 |
inputType |
函数输入类型(必填) |
inputSchema |
输入参数的 JSON Schema |
toolMetadata |
额外设置(returnDirect、resultConverter) |
toolCallResultConverter |
自定义结果转换器 |
函数要求:
- 函数输入和输出可以是
Void或 POJO - 输入和输出 POJO 必须是可序列化的
- 函数及输入输出类型必须是 public
❌ 不支持作为函数入参/出参的类型:原始类型、集合类型(
List、Map、Array、Set)、异步类型、响应式类型。原始类型和集合请使用基于方法的工具规范方式。
工具规范
ToolCallback 接口
ToolCallback 是 Spring AI 中工具的核心建模接口,包含工具的定义和执行逻辑:
1 | public interface ToolCallback { |
Spring AI 提供两个内置实现:
MethodToolCallback— 基于方法的工具FunctionToolCallback— 基于函数的工具
ToolDefinition 接口
提供 AI 模型了解工具可用性所需的信息:
1 | public interface ToolDefinition { |
使用 ToolDefinition.Builder 构建:
1 | ToolDefinition toolDef = ToolDefinition.builder() |
JSON Schema 生成
Spring AI 通过 JsonSchemaGenerator 类内置支持为工具输入类型生成 JSON Schema,作为 ToolDefinition 的一部分提供给模型。
参数描述: 可通过以下注解为输入参数提供描述(按优先级排序):
| 注解 | 来源 |
|---|---|
@ToolParam(description = "...") |
Spring AI |
@JsonClassDescription(description = "...") |
Jackson |
@JsonPropertyDescription(description = "...") |
Jackson |
@Schema(description = "...") |
Swagger |
此方法对方法和函数均适用,且支持嵌套类型的递归使用。
必填/可选控制:默认每个输入参数都是必填的。可通过以下注解将参数标记为可选(按优先级排序):
| 注解 | 来源 |
|---|---|
@ToolParam(required = false) |
Spring AI |
@JsonProperty(required = false) |
Jackson |
@Schema(required = false) |
Swagger |
@Nullable |
Spring Framework |
工具结果转换(ToolCallResultConverter)
工具调用的结果通过 ToolCallResultConverter 序列化为字符串后回传给 AI 模型。
1 | public interface ToolCallResultConverter { |
- 默认使用 Jackson 序列化(
DefaultToolCallResultConverter) - 可实现自定义转换器
配置方式:
1 | // 声明式:@Tool 注解 |
工具上下文(ToolContext)
Spring AI 支持通过 ToolContext API 向工具传递额外的上下文信息。
1 | // ChatClient |
如果默认选项和运行时选项都设置了
toolContext,结果是两者的合并,运行时选项优先。
Return Direct(直接返回)
默认情况下,工具调用的结果会回传给模型继续对话。但有时你希望结果直接返回给调用方,而不是发回模型。
典型场景:
- RAG 工具的结果直接展示给用户,避免模型不必要的后处理
- 某些工具应该终止 Agent 的推理循环
每个 ToolCallback 实现都可以定义是否将结果直接返回调用方。
配置方式:
1 | // 声明式:@Tool 注解 |
ToolCallingManager 负责管理与工具关联的 returnDirect 属性。如果设为 true,工具调用结果直接返回调用方;否则结果回传给模型。
工具执行生命周期
ToolCallingManager 负责管理工具执行生命周期。如果使用 Spring AI Spring Boot Starters,DefaultToolCallingManager 是自动配置的默认实现。
Spring AI 支持三种工具执行生命周期管理模式:
| 模式 | 说明 |
|---|---|
| 框架托管(推荐) | 通过 ChatClient 自动处理工具调用循环 |
| Advisor 可控 | 自定义工具调用循环 |
| 用户手动控制 | 完全手动控制 |
框架托管(推荐 — ChatClient)
使用 ChatClient 时,Spring AI 通过 ToolCallingAdvisor 自动处理整个工具调用生命周期,该 Advisor 始终自动注册在 Advisor 链中(除非显式禁用)。
执行流程:
全局禁用自动注册:
1 | spring.ai.chat.client.tool-calling-advisor.enabled: false |
单条请求禁用:
1 | chatClient.prompt() |
Advisor 可控(自定义循环)
ToolCallingAdvisor 将工具调用循环作为 Advisor 链的一部分实现,并暴露多个扩展点:
| 配置项 | 说明 |
|---|---|
toolCallingManager |
使用的 ToolCallingManager 实例 |
advisorOrder |
Advisor 在链中的执行顺序 |
conversationHistoryEnabled |
是否在工具调用迭代期间内部维护对话历史(默认 true) |
toolExecutionEligibilityChecker |
决定模型响应是否应触发下一轮工具调用的 Function |
对话历史管理:
- 默认(
conversationHistoryEnabled = true):ToolCallingAdvisor在工具调用迭代期间内部维护完整对话历史 - 默认排序:
DEFAULT_CHAT_MEMORY_PRECEDENCE_ORDER为HIGHEST_PRECEDENCE + 200,低于ToolCallingAdvisor.DEFAULT_ORDER(HIGHEST_PRECEDENCE + 300) - 使用
.disableInternalConversationHistory()时需将 Memory Advisor 放在循环内部
Return Direct 支持:
ToolCallingAdvisor 支持”直接返回”特性。当工具执行的 returnDirect = true 时,Advisor 跳出工具调用循环并直接将工具结果返回。
用户手动控制
适用于需要自行控制工具执行生命周期的场景,例如向 UI 流式传输中间进度、添加自定义可观测性或在迭代间应用条件逻辑。
ChatClient 方式:
1 | // 禁用自动注册的 ToolCallingAdvisor |
流式 API 方式:
1 | Flux<ChatResponse> stream = chatClient.prompt() |
ChatModel 方式:
1 | // 直接调用 ChatModel,不使用 ToolCallingAdvisor |
工具执行异常处理
工具执行失败时,异常以 ToolExecutionException 传播,可被捕获以处理错误。
ToolExecutionExceptionProcessor 用于处理 ToolExecutionException,有两种结果:
- 产生一条错误消息回传给 AI 模型
- 抛出异常由调用方处理
默认行为(DefaultToolExecutionExceptionProcessor):
RuntimeException的错误消息回传给模型- 受检异常和 Error(如
IOException、OutOfMemoryError)始终抛出
配置:
1 | spring.ai.tools.throw-exception-on-error: false |
| 值 | 行为 |
|---|---|
true |
工具调用错误作为异常抛出,由调用方处理 |
false(默认) |
错误转换为消息并回传给 AI 模型,由其处理和响应 |
也可在构造函数中设置 alwaysThrow 属性。
动态工具解析(ToolCallbackResolver)
Spring AI 支持在运行时通过 ToolCallbackResolver 接口动态解析工具。
1 | // 按名称注册工具 |
ToolCallbackResolver 实现负责将工具名称解析为对应的 ToolCallback 实例。
- 默认使用
StaticToolCallbackResolver,从静态ToolCallback列表中解析 - Spring Boot 自动配置下,所有
ToolCallback类型的 Bean 自动注册到解析器 - 可提供自定义
ToolCallbackResolverBean 覆盖默认逻辑
工具入参 Schema 增强
Spring AI 提供工具输入 Schema 动态增强功能,支持在不修改底层工具实现的情况下捕获额外信息(如推理过程、元数据等)。
常见用例:
- 内部思考/推理: 在执行工具前捕获模型的逐步推理
- 记忆增强: 提取洞察存储到长期记忆
- 分析与追踪: 收集元数据、用户意图或使用模式
- 多 Agent 协调: 传递 Agent 标识符或协调信号
使用示例:
1 | // 1. 定义增强参数作为 Java Record |
LLM 会看到带有额外字段的增强 Schema,而原始工具只接收其预期的参数。
核心类:
| 类 | 说明 |
|---|---|
AugmentedToolCallbackProvider |
包装工具对象或 Provider,用指定 Record 类型增强所有工具 |
AugmentedToolCallback |
包装单个 ToolCallback 实例 |
AugmentedArgumentEvent |
包含 toolDefinition()、rawInput() 和 arguments() 供消费者使用 |
ToolInputSchemaAugmenter |
Schema 操作的底层工具类 |
removeExtraArgumentsAfterProcessing 选项控制是否将增强参数传递给原始工具:
true(默认)— 调用工具前移除增强参数false— 在输入中保留增强参数(如果工具可以忽略额外字段)
可观测性与日志
- 工具调用生成
spring.ai.tool观测 Span(耗时 + 链路追踪信息传播) - 可选导出工具入参和结果为 Span Attribute(默认关闭,出于敏感性考虑)
- 开启 DEBUG 日志:
1 | logging.level.org.springframework.ai: DEBUG |