一.Tool Calling概述
随着大语言模型 (LLM) 能力的飞速发展,我们不再满足于仅仅让它们生成文本或回答问题。我们期望它们能成为真正的智能助手,能够与外部世界交互,执行具体任务,比如查询数据库、发送邮件或分析数据。为了解决这一挑战,工具调用 (Tool Calling) 应运而生。
Tool Calling (工具调用) ,是 AI 应用程序中的一种常见模式,允许大语言模型 (LLM) 根据用户请求,智能地选择、调用外部工具(如函数、API、服务)并获取结果的技术流程,从而增强其功能。
可以把 LLM 想象成为一个知识渊博、无所不知的博士,不论你问他什么理论问题,他都可以对答如流。但如果你问他 "帮我预订一下今晚公司附近那家最热门的意大利餐厅",他就会束手无策 ------ 因为他虽然能计算出公司附近最热门的意大利餐厅,但他没有连接订座系统,无法订座。
Tool Calling 就是解决这个问题的,它就像给这个博士配备了一个万能遥控器,这个遥控器有很多按钮,这些按钮可以执行一些具体任务,比如博士思考餐厅名称,按钮来执行订座。
Tool Calling 让 LLM 从一个 "无所不知的学者" 变成了一个 "无所不能的指挥家",它负责思考和规划,而具体的工作则由外部工具执行。
二.和Function Calling 什么关系?
官方介绍如下:
Tool calling (also known as function calling) is a common pattern in AI applications allowing a model to interact with a set of APIs, or tools, augmenting its capabilities.
摘自 Tool Calling :: Spring AI Reference
Function Calling 是 Tool Calling 早期更为流行的术语。它是指 LLM 请求调用一个开发者预定义的函数 (Function),这里的 "函数" 就是你代码中的一个方法。Tool Calling 是一个更通用、更广泛的概念,不仅包含了 Function Calling,还涵盖了调用其他类型的工具。
Function Calling 好比是万能遥控器上的一个具体的按钮,比如 "开机" 按钮,它非常具体,按下去就直接执行开机这个单一操作。Tool Calling 则是整个万能遥控器的概念,这个遥控器上不仅有 "开机" 按钮,还有 "调音量"、"换台"、"投屏" 等按钮。它是一个更上层、更通用的抽象,涵盖了函数、API、查询等各种工具。
简单来说:在 Spring AI 里,定义一个 Tool (工具),它通常就是一个 Function (函数),我们可以认为 Function Calling 是实现 Tool Calling 的具体技术方式。两者在日常讨论中经常混用,但 Tool Calling 这个词更全面。
三.应用场景
1. 信息检索
大模型可以借助 Tool 从外部资源(如数据库、Web 服务、文件系统或者 WEB 搜索引擎)检索信息。
如:人工智能模型无法访问实时信息,任何假设了解当前日期或天气预报等信息的问题都无法由模型回答。但是,我们可以提供一个可以检索此信息的工具,并让模型在需要访问实时信息时调用此工具。
2. 采取行动
大模型可以借助 Tool 完成一些执行操作(如发送电子邮件、在数据库中创建新记录、提交表单或触发工作流)
如:模型可以生成预订北京旅行的计划。但是,该模型不具备执行计划的能力。这就是工具的用武之地。它们可用于执行模型生成的计划。
四.快速应用
1.添加依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webflux</artifactId>
</dependency>
<!-- tool calling -->
<dependency>
<groupId>com.github.victools</groupId>
<artifactId>jsonschema-generator</artifactId>
<!-- 可根据实际情况使用最新稳定版本 -->
<version>4.37.0</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba-bom</artifactId>
<version>1.0.0.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.配置文件
spring:
ai:
dashscope:
api-key: ${DASH_SCOPE_API_KEY} #阿里百炼平台申请的api-key
3.调用工具代码与结果演示
java
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/chat")
public class ChatController {
private final ChatClient client;
public ChatController(DashScopeChatModel dashScopeChatModel) {
this.client = ChatClient.builder(dashScopeChatModel).build();
}
@RequestMapping("/call")
public String chat(String message) {
return client.prompt()
.user(message)
.call()
.content();
}
}

4.定义工具
tool calling依赖上面已经添加
java
import org.springframework.ai.tool.annotation.Tool;
import java.time.LocalDateTime;
public class DateTimeTools {
@Tool(description = "A tool to get the current date and time")
public String getCurrentDateTime() {
return LocalDateTime.now().atZone(java.time.ZoneId.systemDefault()).toString();
}
}
5.应用工具
java
@RequestMapping("/call")
public String chat(String message) {
return client.prompt()
.user(message)
.tools(new DateTimeTools()) //添加工具
.call()
.content();
}
6.运行结果

五.Tool Calling 原理
1.Tool Calling 请求响应流程

a) 注册工具: 在向 AI 模型发送请求时,应用程序需要提前告诉模型有哪些工具可用,包括工具的名称 (name)、描述 (description) 和参数格式 (input schema)
描述 (Description) 是灵魂:这是模型决定是否调用工具的核心依据,描述必须清晰准确。一个模糊的描述 (如 "处理数据") 会导致模型无法正确调用。一个清晰的描述 (如 "根据城市名称查询该城市的当前天气温度") 则能极大提高调用的准确率
b) 模型决策: AI 模型根据对话内容判断是否需要调用工具,当模型决定调用工具时,它会发送一个工具调用请求,其中包含工具名称和符合预定义的输入参数。
c) 查找并执行: 应用程序接收到这个 "工具调用请求" 后,会根据工具名称找到对应的工具,并用模型提供的参数来执行它。
d) 获取结果: 工具执行后会产生一个结果 (比如,查询到了今天的温度是 25 度),应用程序会处理返回的结果。
e) 返回结果: 应用程序将这个工具执行的结果再次发送给 AI 模型。
f) 生成最终回复: AI 模型结合工具返回的结果,组织语言,生成最终的用户回复。
2.Tool Calling 部分源码解释
Spring AI 为方法转工具(即
ToolCallback)提供了两种内置方式:
- 声明式:通过
@Tool注解实现- 编程式:通过底层的
MethodToolCallback实现
a) 声明式定义工具
Tool注解
java
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Tool {
String name() default "";
String description() default "";
boolean returnDirect() default false;
Class<? extends ToolCallResultConverter> resultConverter() default DefaultToolCallResultConverter.class;
}
-
Name:指定工具的名称,如果不指定则默认使用方法名称。AI 模型在调用工具时使用此名称来标识工具。因此,不允许在同一类中有两个同名的工具。
-
Description:工具的描述,模型可以使用它来了解何时以及如何调用工具,如果不指定则使用方法名。建议详细清晰地描述工具的功能,这对于工具的使用至关重要,直接影响大模型的使用效果。
-
returnDirect:指定工具执行的结果是直接返回,还是要发给大模型,默认发送给大模型。如果为true 上述流程直接从4-->6不会在发给大模型处理之后返回用户了。
-
resultConverter:指定工具执行结果转换器。Spring AI 内置了一个,默认将工具调用结果转换为 String,如果有特殊业务需求可以自行实现。
input schema是什么?
JSON Schema 是一种用于描述和验证 JSON 数据结构的规范。它定义了 JSON 数据应具备的格式、类型、字段、约束条件等,就像是一份数据模板 或数据说明书,用来确保 JSON 数据符合预期的结构。
我们可以把 JSON Schema 想象成一个「表格填写指南」:
- 表格 = 要接收的 JSON 数据
- 填写指南 = JSON Schema(说明哪些项必填、是什么类型、有什么限制)
例如
java
{
"name": "Alice",
"age": 28,
"email": "alice@example.com",
"hobbies": ["reading", "swimming"]
}
{
"name": "Alice",
"age": 28,
"email": "alice@example.com",
"hobbies": ["reading", "swimming"]
}
对应的 JSON Schema
java
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/user.schema.json",
"title": "User",
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "用户姓名"
},
"age": {
"type": "integer",
"minimum": 0,
"description": "用户年龄"
},
"email": {
"type": "string",
"format": "email",
"description": "电子邮箱地址"
},
"hobbies": {
"type": "array",
"items": {
"type": "string"
},
"optional": true
}
},
"required": ["name", "age", "email"]
}
在工具调用流程中,JSON Schema 用于定义工具的输入参数格式。
开发人员可以通过
@ToolParam注解为输入参数提供额外信息(如描述、是否必需等),默认情况下所有输入参数均为必需参数。
java
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 注解源码定义
java
@Target({ElementType.PARAMETER, ElementType.FIELD,
ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ToolParam {
/**
* 指定参数是否为必填,默认必填。如果参数被标记为@Nullable,则该参数将被视为可选,
* 除非使用@ToolParam注解显式标记为必需
*/
boolean required() default true;
/**
* 工具参数的描述,模型通过描述可以更好的理解如何使用它,例如,参数应采用什么格式,
* 允许使用哪些值等。
*/
String description() default "";
}
工具分类
工具分为运行时工具(特定接口调用时加载)和默认工具(所有请求都会加载的)
可以在创建chatclient的时候加上defaultTools
java
private final ChatClient client;
public ChatController(DashScopeChatModel dashScopeChatModel) {
this.client = ChatClient.builder(dashScopeChatModel)
.defaultTools(new DateTimeTools())
.build();
}
b) 编程式定义工具
定义工具
java
public class WeatherTools {
String getCurrentWeatherByCityName(String cityName) {
switch (cityName) {
case "北京":
return "北京今天天气:晴空万里";
case "上海":
return "上海今天天气:电闪雷鸣";
case "广州":
return "广州今天天气:细雨蒙蒙";
default:
return "没有该城市的天气信息";
}
}
}
上述这个还只是一个方法 还不是工具
在代码中将该方法转化为可调用的工具
@RequestMapping("/call2")
public String chat2(String message) {
//获取天气工具方法
Method method = ReflectionUtils.findMethod(WeatherTools.class,
"getCurrentWeatherByCityName", String.class);
//创建工具回调
ToolCallback toolCallback = MethodToolCallback.builder()
.toolDefinition(ToolDefinitions.builder(method)
.description("Get current weather by city name")
.build())
.toolMethod(method)
.toolObject(new WeatherTools())
.build();
return client.prompt()
.user(message)
.toolCallbacks(toolCallback) //添加工具
.call()
.content();
}.测试结果

MethodToolCallback和ToolDefinitions介绍
一下内容截取自spring ai官方 链接: https://springdoc.cn/spring-ai/api/tools.html

添加默认工具
直接在构造方法初始化client的时候添加
java
public ChatController(DashScopeChatModel dashScopeChatModel) {
//获取天气工具方法
Method method = ReflectionUtils.findMethod(WeatherTools.class,
"getCurrentWeatherByCityName", String.class);
//创建工具回调
ToolCallback toolCallback = MethodToolCallback.builder()
.toolDefinition(ToolDefinitions.builder(method)
.description("Get current weather by city name")
.build())
.toolMethod(method)
.toolObject(new WeatherTools())
.build();
this.client = ChatClient.builder(dashScopeChatModel)
.defaultToolCallbacks(toolCallback) //添加默认工具
.build();
}
如果臃肿不美观 可以运用IOC思想提取创建ToolCallBack类的方法 交给spring容器管理 然后再构造方法注入
3.工具规范
a) ToolCallback
ToolCallback 是一个用于与 AI 模型交互的回调接口,允许开发者定义可被 AI 调用的外部工具,在 AI 应用(比如大语言模型)中集成"工具"(Tools),让 AI 模型可以在需要时调用外部功能。
java
public interface ToolCallback {
/**
* 返回该工具的元信息描述
* ToolDefinition 包含工具名称、描述、参数列表等结构化信息,供 AI 模型理解"什么情况下该调用这个工具"以及"需要传什么参数"。
* 必须实现,是函数调用机制的核心部分
* @return
*/
ToolDefinition getToolDefinition();
/**
* 提供额外的非功能性元数据,主要用于框架层面的扩展性支持,不影响核心逻辑。
* 使用了 default 方法,意味着子类可以不重写,默认返回空的元数据对象。
* @return
*/
default ToolMetadata getToolMetadata() {
return ToolMetadata.builder().build();
}
/**
* 抽象方法,必须由实现类提供具体逻辑。
* 接收一个字符串输入(通常是 JSON 格式的参数),执行业务逻辑,并返回结果字符串。
* 结果会传回给 AI 模型,供其继续推理或生成回复。
*/
String call(String toolInput);
/**
* 带上下文的调用方法,也是默认实现。
* 如果toolContext有值,则不支持,主要用于被子类重写支持上下文功能
*/
default String call(String toolInput, @Nullable ToolContext toolContext) {
if (toolContext != null && !toolContext.getContext().isEmpty()) {
throw new UnsupportedOperationException("Tool context is not supported!");
} else {
return this.call(toolInput);
}
}
}
b) MethodToolCallback
MethodToolCallback 是 Spring AI 框架中实现 Tool Calling的核心机制之一。它用于将普通 Java 方法包装成 AI 模型可调用的"工具"(Tool)。
java
public final class MethodToolCallback implements ToolCallback {
//工具默认执行的转换器
private static final ToolCallResultConverter DEFAULT_RESULT_CONVERTER = new DefaultToolCallResultConverter();
//工具默认元数据
private static final ToolMetadata DEFAULT_TOOL_METADATA = ToolMetadata.builder().build();
//工具定义,也称为工具的元信息:名字、描述、参数列表等,告诉 AI"我能干啥"
private final ToolDefinition toolDefinition;
//工具额外元数据(如权限,分类)
private final ToolMetadata toolMetadata;
//真正要执行的那个 Java 方法对象(通过反射获取)
private final Method toolMethod;
//工具对象:如果方法不是静态的,需要这个对象来调用(即 object.method())
@Nullable
private final Object toolObject;
//工具转换器:把方法返回值转成字符串,返回给 AI(默认用 JSON)
private final ToolCallResultConverter toolCallResultConverter;
}
c) ToolDefinition
java
public interface ToolDefinition {
/**
* 工具名称。在提供给模型的工具集中是唯一的
*/
String name();
/**
* AI模型使用工具描述来确定工具的功能
*/
String description();
/**
* 用于调用工具的参数的模式
*/
String inputSchema();
}
ToolDefinition 实例
java
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();
d) ToolContext
Spring AI 支持通过 ToolContext API 向工具传递额外的上下文信息。此功能允许开发人员提供用户自定义的额外数据,这些数据可与 AI 模型传递的工具参数一起在工具执行过程中使用。

工具添加上下文 还是以获取当前时间为例
java
@Tool(description = "A tool to get the current date and time")
public String getCurrentDateTime(ToolContext context) {
System.out.println("userID: " + context.getContext().get("userID"));
return LocalDateTime.now().atZone(java.time.ZoneId.systemDefault()).toString();
}
java
@RequestMapping("/call")
public String chat(String message) {
return client.prompt()
.user(message)
.tools(new DateTimeTools()) //添加工具
.toolContext(Map.of("userID", "12345")) //调用时给工具传入上下文信息
.call()
.content();
}
测试结果

e) Return Direct
默认情况下,工具调用结果将作为响应返回模型,随后模型可利用该结果继续对话。
某些场景下,开发人员可能希望将结果直接返回调用方而非传回模型。例如:当构建依赖 RAG 工具的代理时,开发人员可能希望直接将检索结果返回调用方,而非传回模型进行不必要的后处理;
默认值为false

二者对比

4.工具的执行
工具执行是指使用提供的输入参数调用工具并返回结果的过程。
这个过程由 ToolCallingManager 接口处理,该接口是 Spring AI 中负责管理 "AI 调用工具" 全过程的核心组件,负责管理工具执行的完整生命周期。
它主要管控以下核心问题:
- 哪些工具可用?
- AI 请求调用工具时,怎么执行?
- 执行结果如何返回给 AI 继续对话?
java
public interface ToolCallingManager {
/**
* 从模型的工具调用选项中解析工具定义,告诉模型:我能用哪些工具
*/
List<ToolDefinition> resolveToolDefinitions(ToolCallingChatOptions chatOptions);
/**
* 执行模型所要求的工具调用:也就是真正的去"调工具"
*/
ToolExecutionResult executeToolCalls(Prompt prompt, ChatResponse chatResponse);
}
流程图

流程图文字解释
- 当需要向模型提供工具时,我们将其定义包含在聊天请求(
Prompt)中,并调用ChatModelAPI 将请求发送至 AI 模型。 - 当模型决定调用工具时,它会发送包含工具名称及符合定义模式的输入参数的响应(
ChatResponse)。 ChatModel将工具调用请求发送至ToolCallingManagerAPI。ToolCallingManager负责识别需调用的工具并使用提供的输入参数执行该工具。- 工具调用结果返回至
ToolCallingManager。 ToolCallingManager将工具执行结果返回给ChatModel。ChatModel将工具执行结果返回 AI 模型(ToolResponseMessage)。- AI 模型利用工具调用结果作为附加上下文生成最终响应,并通过
ChatClient将其返回调用方(ChatResponse)
默认情况下,Spring AI 在每个
ChatModel实现中透明地管理工具执行生命周期。但开发人员可选择退出此行为,自行控制工具执行。