什么时候需要 Tool Calling
与大模型对话时,你很快会遇到一个限制:它不能直接获取实时信息,比如当前时间、天气,也无法执行具体操作,比如设置闹钟。这些问题通常需要调用外部服务。Tool Calling 机制就是让模型能够'意识到'有哪些工具可用,并在需要时主动请求调用。
Spring AI 封装了这一整套流程,并且提供了两种控制模式:全自动和手动接管的。这篇文章会基于实际使用经验,梳理清楚 @Tool 定义、参数描述、执行循环和异常处理的关键点。
核心流程
用户提出问题后,模型会结合可用的工具列表作出判断。如果认为需要调用某个工具,它会返回一个 ToolCall 请求,里面包含工具名和参数。应用程序执行这个工具,再把结果交还给模型,模型整合后生成最终回答。这个过程可能循环多次,直到模型认为不需要再调用任何工具。
定义工具和参数
在 Spring AI 中,用 @Tool 注解标记一个 Spring Bean 的方法,方法的 description 就是对模型展现的工具说明。这个描述非常关键——直接决定了模型能否正确选中工具。描述应当说清楚工具做什么、返回什么,最好带上约束条件。
参数描述同样重要。Spring AI 支持两种方式:Jackson 的 @JsonPropertyDescription 注解,可以放格式示例和详细说明;以及 @ToolParam 注解,用于标记字段是否必需。通常,对必填字段用 @JsonPropertyDescription 给出详细的格式要求;可选字段再用 @ToolParam(required = false) 标记。两者可以混用在一个类上。
public class AlarmRequest {
@JsonPropertyDescription("闹钟时间,格式如 2025 年 3 月 31 日")
private String time;
@JsonPropertyDescription("闹钟位置,比如卧室、客厅")
@ToolParam(required = false)
private String address;
// getter/setter...
}
经验上,参数描述不清是调用失败的常见原因。比如只写一个'时间',模型可能不知道该传什么样的字符串,往往会出错。给出明确的示例格式后,成功率会高很多。
自动执行模式
如果你不需要在工具执行前后做额外处理(如记录日志、权限校验),那么最省事的办法就是让框架全自动跑完整个循环。代码只有一行关键配置:
return chatClient.prompt()
.user(question)
.tools(artisanTools)
.call()
.content();
这里的 .tools() 注册了工具实例,框架会识别大模型返回的 ToolCall,自动执行对应方法,并把结果送回聊天循环,直到模型给出最终文本。
优点是简单,缺点是你对过程一无所知:一旦出现工具无限循环,或者需要审计调用记录,就没办法了。这时就需要手动控制。
手动控制模式
通过禁用框架的内部自动执行,你可以介入每一步决策。首先构造 ToolCallingChatOptions,将 internalToolExecutionEnabled 设为 false,然后手动调用 ToolCallingManager 执行工具,并在每一轮之后用更新过的对话历史再次向模型请求。
基本代码结构如下:
ToolCallback[] callbacks = ToolCallbacks.from(new ArtisanTools());
ToolCallingChatOptions options = ToolCallingChatOptions.builder()
.toolCallbacks(callbacks)
.internalToolExecutionEnabled()
.build();
Prompt.builder()
.chatOptions(options)
.content(question)
.build();
chatClient.prompt(prompt).call().chatResponse();
ToolCallingManager.builder().build();
(response.hasToolCalls()) {
manager.executeToolCalls(prompt, response);
response = chatClient.prompt( (
result.conversationHistory(),
options
)).call().chatResponse();
}
response.getResult().getOutput().getText();


