news 2026/10/17 7:21:29

Tool Calling

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Tool Calling

目录

1. Tool Calling 是什么?

2. Tool的 构建规则和调用路线

2.1 构建规则

2.1.1 声明式调用工具

2.1.2 编程式定义工具

2.2 Tool的调用路线

3. 工具规范

3.1 ToolCallback

3.2 MethodToolCallback

3.3 ToolDefinition


以下所有代码:巨蛇神/JavaEE进阶 - 码云 - 开源中国

1. Tool Calling 是什么?

Tool Calling(工具调用) 是大语言模型(LLM) 的四肢!

Tool Calling(工具调用),是AI应用程序中一种常见模式,允许大语言模型(LLM)根据用户请求,智能地选择、调用外部工具(如API、函数等)并获取结果的技术流程,从而增强其功能。


我们一般向AI询问问题,获取答案,但是后面真正的事情还得自己做。例如领导让你定餐厅,你执行顺序会是:

  1. 打开APP,查看附近推荐的热门餐厅
  2. 预定餐厅
  3. 提醒大家
  4. 等待出发

如果你叫AI帮你预定,它仅能利用现有的知识(不是实时的)给你推荐餐厅,2,3都无法做到,但我们真正想的是,它能根据实时数据推荐餐厅,并且自动预定餐厅,甚至呼叫好车辆。

那么AI怎么从只会动脑,变成智能且联网的决策者?Yes!Tool Calling闪亮登场!


现在许多模型厂商提供的网页模型服务是经过联网操作的,你询问“今天天气”之类的实时信息它是可以给你准确返回的,但是问他“今天或明天某站上海到北京的车次还有哪些”这它是无法办到的,因为它没有配备响应的工具(tool)!


2. Tool的 构建规则和调用路线

2.1 构建规则

2.1.1 声明式调用工具

使用@Tool注解定义的工具就是 “声明式定义工具”。

(ChatClient调用)先看一个工具调用案例,代码之后介绍写和调工具的规则:

//控制器 public class ChatClientController { private ChatClient chatClient; public ChatClientController(ChatClient.Builder builder) { this.chatClient = builder // .defaultTools(new CustomerTool()) //添加默认工具 .build(); } @RequestMapping("/getDateTime") public String getDateTime(String message){ return chatClient.prompt() .user(message) .tools(new DateTimeTool()) //添加工具类,在调用模型之前 .call() .content(); } } //工具类 public class DateTimeTool { //查询时间的工具 @Tool(description = "获取用户时区的当前日期和时间") String getCurrentDateTime() { //返回当前时间 return LocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()).toString(); } }

@Tool注解有四个参数:

  • name——工具的名字,不传该参数则默认tool名为函数名,如上述默认名为“getDateTime”
  • description——强烈建议写;描述工具是做什么的,什么时候使用它,模型就是靠这个描述选择和调用工具。
  • returnDirect——将结果直接返回给调用者,而不将其反馈给模型;默认值为false,经过模型。
  • resultConverter——方法返回的数据,默认请况下使用转化成json,但你也可以自定义转化器。

resultConverter的使用需要自定义转化器并实现ToolCallResultConverter(函数式接口),重写convert方法,下面演示:

从此可看出AI会根据@Tool的描述选择符合用户输入内容的工具,接着根据@ToolParam的描述传入对应的实参。


(ChatModel调用)比较底层,套路比较多:

package com.jusheshen.springai.controller; import com.jusheshen.springai.tools.DateTimeTool; import org.springframework.ai.chat.model.ChatModel; import org.springframework.ai.chat.model.ChatResponse; import org.springframework.ai.chat.prompt.ChatOptions; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.deepseek.DeepSeekChatOptions; import org.springframework.ai.model.tool.ToolCallingManager; import org.springframework.ai.model.tool.ToolExecutionResult; import org.springframework.ai.support.ToolCallbacks; import org.springframework.ai.tool.ToolCallback; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RequestMapping("/model") @RestController public class ChatModelController { private ChatModel chatModel; private ToolCallingManager toolCallingManager = ToolCallingManager.builder().build(); public ChatModelController(ChatModel chatModel) { this.chatModel = chatModel; } @RequestMapping("/getDateTime") public String getDateTime(String message){ ToolCallback[] tools = ToolCallbacks.from(new DateTimeTool()); ChatOptions chatOptions = DeepSeekChatOptions.builder() .toolCallbacks(tools) .build(); Prompt prompt = new Prompt(message,chatOptions); ChatResponse response = chatModel.call(prompt); //使用ChatModel,开发者可以实现当returnDirect=true 时的现象 while (response.hasToolCalls()) { ToolExecutionResult result = toolCallingManager.executeToolCalls(prompt, response); //看情况实现 if (result.returnDirect()) { // Tool's returnDirect=true — break out without sending back to the model // return result.conversationHistory(); } prompt = new Prompt(result.conversationHistory(), chatOptions); response = chatModel.call(prompt); } String finalAnswer = response.getResult().getOutput().getText(); return finalAnswer; } }

2.1.2 编程式定义工具

(ChatClient调用)利用MethodToolCallback 手动构建工具

//通过反射获得工具方法。第一个参数是对象类型,第二是方法名称,后面是方法的形参类型 Method method = ReflectionUtils.findMethod(WeatherTools.class,"getCurrentWeatherByCityName",String.class); MethodToolCallback callback = MethodToolCallback.builder() .toolDefinition(ToolDefinitions.builder(method) .description("根据给定的城市名称, 获取当前城市的天⽓") //工具描述,不写的话默认工具方法名作为描述 // .name() //工具名,不起默认为方法名 // .inputSchema("") //参数输入格式,不写模型自动识别匹配 .build()) .toolMethod(method) //工具 .toolObject(new WeatherTools()) //工具所在对象;若是tool是静态方法不用写 // .toolMetadata() //元信息 .build();

调用时添加工具:

@RequestMapping("/getWeather") public String getWeather(String message){ //通过反射获得工具方法 Method method = ReflectionUtils.findMethod(WeatherTools.class,"getCurrentWeatherByCityName",String.class); MethodToolCallback callback = MethodToolCallback.builder() .toolDefinition(ToolDefinitions.builder(method) .description("根据给定的城市名称, 获取当前城市的天⽓") //工具描述,不写的话默认工具方法名作为描述 // .name() //工具名,不起默认为方法名 // .inputSchema("") //参数输入格式,不写模型自动识别匹配 .build()) .toolMethod(method) //工具 .toolObject(new WeatherTools()) //工具所在对象 // .toolMetadata() //元信息 .build(); return chatClient.prompt() .user(message) .tools(callback) .call() .content(); }

添加默认工具:

@Configuration public class ToolConfig { @Bean public MethodToolCallback callback(){ //通过反射获得工具方法 Method method = ReflectionUtils.findMethod(WeatherTools.class,"getCurrentWeatherByCityName",String.class); MethodToolCallback callback = MethodToolCallback.builder() .toolDefinition(ToolDefinitions.builder(method) .description("根据给定的城市名称, 获取当前城市的天⽓") //工具描述,不写的话默认工具方法名作为描述 // .name() //工具名,不起默认为方法名 // .inputSchema("") //参数输入格式,不写模型自动识别匹配 .build()) .toolMethod(method) //工具 .toolObject(new WeatherTools()) //工具所在对象 // .toolMetadata() //元信息 .build(); return callback; } } //======================================================================= public ChatClientController(ChatClient.Builder builder,MethodToolCallback toolCallback) { this.chatClient = builder .defaultTools(new CustomerTool(),toolCallback) //添加默认工具 .build(); }

(ChatModel调用)与声明式调用的ChatModel调用几乎一模一样:

@Autowired private MethodToolCallback toolCallback; //这句换掉就行了 // ToolCallback[] tools = ToolCallbacks.from(new DateTimeTool()); ChatOptions chatOptions = DeepSeekChatOptions.builder() .toolCallbacks(toolCallback) //注意! .build(); Prompt prompt = new Prompt(message,chatOptions); ChatResponse response = chatModel.call(prompt);

2.2 Tool的调用路线

(默认情况下)

  1. 用户提示词与后端Tool定义 经过 SpringAI框架传递给模型
  2. 模型整合完数据之后,把数据发给Spring AI 框架
  3. 框架再把数据给到Tool
  4. Tool处理完,再发给框架
  5. 框架给到模型
  6. 模型把Tool生成的数据经过润色后,再经过Spring AI响应给用户

(自定义的特殊情况)

开发者可在ChatClient的call()或stream()之前使用toolContext()直接传入Map数据给工具(下图红圈部分),这个数据不会经过模型,而直接给工具,也就是上图的第3"步。当然如果没设置为returnDirect=true那么Tool层的数据还是会经过模型,也就是第四、五步。


如果returnDirect=true,则经历下图:

3. 工具规范

3.1 ToolCallback

ToolCallback是一个用于与AI交互的回调接口,允许开发者定义可被AI调用的外部工具。它携带AI模型看到的定义和模型调用它时调用的执行逻辑。

public interface ToolCallback {

/**
* 返回该⼯具的元信息描述
* ToolDefinition 包含⼯具名称、描述、参数列表等结构化信息,供AI模型理解“什么情况
*下该调⽤这个⼯具”以及“需要传什么参数”。
* 必须实现,是函数调⽤机制的核⼼部分
*/


ToolDefinition getToolDefinition();

/**
* 提供额外的⾮功能性元数据,主要⽤于框架层⾯的扩展性⽀持,不影响核⼼逻辑。
* 使⽤了default⽅法,意味着⼦类可以不重写,默认返回空的元数据对象。
*/

default ToolMetadata getToolMetadata();

/**
* 抽象⽅法,必须由实现类提供具体逻辑。
* 接收⼀个字符串输⼊(通常是JSON格式的参数),执⾏业务逻辑,并返回结果字符串。
* 结果会传回给AI模型,供其继续推理或⽣成回复。
*/


String call(String toolInput);

/**
* 带上下⽂的调⽤⽅法,也是默认实现。
* 如果toolContext有值,则不⽀持,主要⽤于被⼦类重写⽀持上下⽂功能
*/


default String call(String toolInput, ToolContext toolContext);
}

3.2 MethodToolCallback

MethodToolCallback是Spring AI框架中ToolCallback的核心实现之一,它用于将普通Java方法包装成AI模型可调用的”工具“。

public final classMethodToolCallbackimplementsToolCallback{
//⼯具默认执行的转换器
private static finalToolCallResultConverterDEFAULT_RESULT_CONVERTER=new
DefaultToolCallResultConverter();
//⼯具默认元数据
private static finalToolMetadataDEFAULT_TOOL_METADATA=
ToolMetadata.builder().build();
//⼯具定义,也称为⼯具的元信息:名字、描述、参数列表等,告诉AI “我能干啥”
private finalToolDefinition toolDefinition;
//⼯具额外元数据(如权限,分类)
private finalToolMetadata toolMetadata;
//真正要执行的那个Java 方法对象(通过反射获取)
private finalMethod toolMethod;
//⼯具对象:如果方法不是静态的,需要这个对象来调用(即object.method()
@Nullable
private finalObject toolObject;
//⼯具转换器:把方法返回值转成字符串,返回给AI(默认用JSON)
private finalToolCallResultConverter toolCallResultConverter;
}

3.3 ToolDefinition

ToolDefinition 接口提供AI模型识别工具所需要的信息,包含工具名称、描述及输入模式。每个ToolCallback 都必须提供ToolDefinition示例来定义工具。

public interfaceToolDefinition{
/**
*⼯具名称.在提供给模型的工具集中是唯⼀的
*/
Stringname();
/**
* AI模型使用工具描述来确定工具的功能
*/
Stringdescription();
/**
*⽤于调用工具的参数的模式
*/
StringinputSchema();
}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 7:21:26

AI智能体如何重构传统行业?从对话工具到数字同事的实战指南

先说一个我观察了很久的现象:很多传统企业买了各种AI工具,甚至是花大价钱搞了大模型私有化部署,但半年之后再去看,它们大多数都变成了“高级聊天玩具”。员工偶尔用来写个周报、做两张图,领导拿去演讲充门面&#xff0…

作者头像 李华
网站建设 2026/10/17 7:21:20

Cesium克里金插值:前端三维温度场可视化渲染

简介:这是一份基于Cesium库与克里金插值算法实现温度数据三维渲染的工程资源,适合WebGIS开发者、三维可视化学习者以及从事气象、海洋等空间数据分析的技术人员。资源以离散温度测站数据为起点,演示了变异函数分析、半方差拟合、未知点位估值…

作者头像 李华
网站建设 2026/10/17 7:20:44

彩色点云配准原理与Open3D实操:弥补传统ICP不足

1. 为什么需要Colored Point Cloud Registration1.1 传统ICP的软肋做三维重建的同行应该都有这种感受:数据采回来,点云密度、精度都还行,但就是“对不齐”。两帧点云明明看着轮廓吻合,细看却有半厘米到几厘米的错位。这时候第一反…

作者头像 李华
网站建设 2026/10/17 7:18:51

代码图谱如何重构Claude Code的上下文供给方式

1. 项目概述:这不是插件,而是一次代码理解范式的迁移“30K Star 神器,给 Claude Code 装上代码地图,Token 中位数省 65 倍”——这个标题里藏着三个被严重低估的信号:Star 数不是热度指标,而是工程共识&…

作者头像 李华
网站建设 2026/10/17 7:18:34

朴素贝叶斯实现评论情感分类:从小样本到上线全流程

如果你处理过用户评论,一定知道那种感觉:后台每几分钟多几百条,靠人一条条看,看到后来眼睛发花。我最早做评论分类的时候,卡在了一个很基础的问题上:怎么在标注数据很少、算力有限的情况下,先把…

作者头像 李华
网站建设 2026/10/17 7:18:28

Workflow-DSL:让业务人员掌控AI工作流的零代码契约

1. 为什么“AI工作流”正在从概念走向产线——一个被低估的工程拐点我去年在给一家做智能客服SaaS的客户做系统重构时,遇到过一个典型场景:他们的对话路由逻辑最初是用Java硬编码写的,规则嵌套三层if-else,加上状态机跳转&#xf…

作者头像 李华