Tool Calling(工具调用),也称为函数调用,是让大语言模型(LLM)在对话中"调用"外部定义的工具或 API 的机制,通过这个机制,模型可以在生成回答前提出需要执行的操作(如获取实时天气、设置数据库记录、触发业务流程等),然后由应用端执行工具,结果再反馈回模型,最终给用户完整回复。
Tool Calling主要应用场景如下:
* 信息检索:从外部源(如数据库、Web 服务、文件系统或 Web 搜索引擎)检索信息,增强模型的知识,使其能够回答其他方式无法回答的问题。
* 执行操作:在软件系统中执行特定操作,如发送电子邮件、在数据库中创建新记录、提交表单或触发工作流。目标是自动执行原本需要人工干预或显式编程的任务。
Spring AI 提供了方便的 API 来定义工具、解析来自模型的工具调用请求以及执行工具调用。
工具定义与使用
Spring AI中工具调用流程示意图如下:

-
开发者定义工具,并将其注册到 Spring 容器中。
-
模型在生成响应时,识别到需要调用工具,会生成包含工具调用信息的响应。
-
应用程序接收到模型的响应后,解析其中的工具调用信息,执行相应的工具。
-
工具执行完成后,将结果返回给应用程序。
-
应用程序将工具执行结果作为上下文信息,传递给模型。
-
模型使用工具执行结果,生成最终的响应。
工具定义:
开发者可以通过在方法上添加@Tool注解定义工具,该注解允许提供工具的名称、描述和输入参数等信息,模型在调用工具时,会根据这些信息生成相应的调用请求。
定义工具示例如下:

Tool Calling 使用案例


- 配置项目pom.xml
html
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>SpringAIToolCalling</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SpringAIToolCalling</name>
<description>SpringAIToolCalling</description>
<properties>
<java.version>17</java.version>
</properties>
<!-- 导入 Spring AI BOM,用于统一管理 Spring AI 依赖的版本,
引用每个 Spring AI 模块时不用再写 <version>,只要依赖什么模块 Mavens 自动使用 BOM 推荐的版本 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-deepseek</artifactId>
</dependency>
</dependencies>
<!-- 声明仓库, 用于获取 Spring AI 以及相关预发布版本-->
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
<repository>
<name>Central Portal Snapshots</name>
<id>central-portal-snapshots</id>
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
</project>

- 创建tools包并创建MyTools.java类
java
package com.example.springaitoolcalling.tools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
@Component
public class MyTools {
Logger log = LoggerFactory.getLogger(MyTools.class);
@Tool(description = "返回当前系统时间")
public String getCurrentTime() {
log.info("调用 getCurrentTime 工具,当前时间:"+java.time.LocalDateTime.now().toString());
return java.time.LocalDateTime.now().toString();
}
@Tool(description = "对两个数字执行加、减、乘、除运算")
public double calculate(
@ToolParam(description = "第一个数字") double a,
@ToolParam(description = "第二个数字") double b,
@ToolParam(description = "运算类型") String operation) {
log.info("调用 calculate 工具,第一个数字:"+a+",第二个数字:"+b+",运算类型:"+operation);
double result = 0;
switch (operation) {
case "add":
result = a + b;
break;
case "subtract":
result = a - b;
break;
case "multiply":
result = a * b;
break;
case "divide":
if (b != 0) {
result = a / b;
}
}
return result;
}
private RestTemplate restTemplate = new RestTemplate();
private
String baseUrl = "http://shanhe.kim/api/youjia/youjia.php";
@Tool(description = "查询指定省份的油价,直接返回完整 JSON 字符串")
public String getOilPriceJson(@ToolParam(description = "省份名称") String province) {
log.info("调用 getOilPriceJson 工具,查询省份:"+province);
String url = String.format("%s?province=%s", baseUrl, province);
return restTemplate.getForObject(url, String.class);
}
}

