Spring AI Tool Calling
1. Tool Calling?
Tool Calling(也称为函数调用)是 AI 应用程序中的一种常见模式,允许模型与一组 API 或工具进行交互,从而增强其功能。
工具主要用于:
-
信息检索:此类别中的工具可用于从外部源(如数据库、Web 服务、文件系统或 Web 搜索引擎)检索信息。目标是增强模型的知识,使其能够回答其他方式无法回答的问题。因此,它们可用于检索增强生成 (RAG) 方案。例如,工具可用于检索给定位置的当前天气、检索最新的新闻文章或查询数据库以获取特定记录。
-
采取行动:此类别中的工具可用于在软件系统中执行作,例如发送电子邮件、在数据库中创建新记录、提交表单或触发工作流。目标是自动执行原本需要人工干预或显式编程的任务。例如,可以使用工具为与聊天机器人交互的客户预订航班,在网页上填写表单,或在代码生成场景中实现基于自动测试 (TDD) 的 Java 类。
在 AI 应用中,Tool Calling 指的是让 AI 模型能够调用外部工具(如数据库查询、天气 API、搜索功能等)来增强其能力。例如:
- 当用户询问天气时,AI 可以调用天气 API 获取实时数据。
- 当用户需要查找信息时,AI 可以调用搜索引擎。
2. Tool Calling 支持
Spring AI 提供了对 Tool Calling 的支持,尤其是在与大语言模型(LLM)结合使用时。它允许定义工具,并让 LLM 在推理过程中决定是否调用这些工具。
示例场景
假设你有一个天气查询工具,你可以将其注册到 Spring AI 中,当用户提问“今天北京天气如何?”时,Spring AI 会自动调用该工具并返回结果。
3. 实现 Tool Calling
- 当想让某个工具可供模型使用时,会在 chat 请求中包含其定义。每个工具定义都包含输入参数的名称、描述和方案。
- 当模型决定调用工具时,它会发送一个响应,其中包含工具名称和输入参数,这些参数在定义的架构之后建模。
- 应用程序负责使用工具名称来识别和执行具有提供的输入参数的工具。
- 工具调用的结果由应用程序处理。
- 应用程序将工具调用结果发送回模型。
- 该模型使用 tool call result 作为附加上下文生成最终响应。
信息检索
(1) 定义工具接口
需要创建一个 Java 接口或类来表示调用的工具。例如:
public interface WeatherService {String getWeather(String location);
}
(2) 实现工具逻辑
实现接口的具体功能:
@Service
public class OpenWeatherMapService implements WeatherService {@Overridepublic String getWeather(String location) {// 调用实际的天气 APIreturn "Sunny, 25°C in " + location;}
}
(3) 注册为 Spring AI 工具
使用 Spring AI 提供的注解将工具注册为可调用的组件:
@Configuration
public class AiConfig {@Beanpublic Tool weatherTool(WeatherService weatherService) {return Tool.from(weatherService::getWeather).name("weather").description("Get the current weather for a given location.");}
}
(4) 使用工具调用
在 AI 服务中,可以通过 AiClient 来调用这些工具:
@Service
public class AiAssistantService {private final AiClient aiClient;public AiAssistantService(AiClient aiClient) {this.aiClient = aiClient;}public String respondToUser(String userQuery) {return aiClient.call(userQuery).content();}
}
采取行动
AI 模型可用于生成实现某些目标的计划。例如,模型可以生成预订丹麦旅行的计划。但是,该模型无法执行该计划。这就是工具的用武之地:它们可用于执行模型生成的计划。
在此示例中,定义第一个工具,用于在特定时间设置闹钟。目标是从现在开始设置 10 分钟的闹钟,因此需要为模型提供工具来完成此任务。
(1) 定义工具接口
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.context.i18n.LocaleContextHolder;
class DateTimeTools {
@Tool(description = "Get the current date and time in the user's timezone")
String getCurrentDateTime() {return LocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()).toString();
}@Tool(description = "Set a user alarm for the given time, provided in ISO-8601 format")
void setAlarm(String time) {LocalDateTime alarmTime = LocalDateTime.parse(time, DateTimeFormatter.ISO_DATE_TIME);System.out.println("Alarm set for " + alarmTime);
}
}
(2) 使用工具调用
ChatModel chatModel = ...String response = ChatClient.create(chatModel).prompt("Can you set an alarm 10 minutes from now?").tools(new DateTimeTools()).call().content();System.out.println(response);
4. 工作流程
- 用户输入问题。
- LLM 分析问题,判断是否需要调用工具。
- 如果需要,调用相应的工具获取数据。
- 将工具返回的数据整合到最终的回答中。
工具
Spring AI 以两种方式为从方法指定工具(即(s)))提供了内置支持:ToolCallback
- 以声明方式使用注解
@Tool
- 以编程方式,实现 MethodToolCallback
声明性规范:@Tool
class DateTimeTools {@Tool(description = "Get the current date and time in the user's timezone")String getCurrentDateTime() {return LocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()).toString();}}
关键信息:@Tool
-
name:工具的名称。如果未提供,则将使用方法名称。AI 模型在调用工具时使用此名称来识别工具。因此,不允许在同一个类中有两个同名的工具。该名称在模型可用于特定聊天请求的所有工具中必须是唯一的。
-
description:工具的描述,模型可以使用它来了解何时以及如何调用工具。如果未提供,则方法名称将用作工具描述。但是,强烈建议提供详细的描述,因为这对于模型了解工具的用途以及如何使用它至关重要。未能提供良好的描述可能会导致模型在应该使用该工具时未使用该工具或错误地使用该工具。
-
returnDirect:工具结果是应直接返回给客户端还是传递回模型。有关更多详细信息,请参阅直接返回。
-
resultConverter:用于将工具调用的结果转换为发送回 AI 模型的实现。有关更多详细信息,请参阅 Result Conversion。ToolCallResultConverterString object
输入参数:@ToolParam
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
-
description:参数的描述,模型可以使用该描述来更好地了解如何使用它。例如,参数应采用什么格式、允许哪些值等。
-
required:参数是必需的还是可选的。默认情况下,所有参数都被视为必需参数。
如果参数被注释为 ,则除非使用注释明确标记为必需,否则该参数将被视为可选参数。@Nullable@ToolParam
除了注释之外,还可以使用 Swagger 或 Jackson 中的注释。有关更多详细信息,请参阅 JSON 架构。@ToolParam@Schema@JsonProperty
程序化规范:FunctionToolCallback
以通过以编程方式构建函数类型 (、 、 或 ) 转换为工具。
public class WeatherService implements Function<WeatherRequest, WeatherResponse> {public WeatherResponse apply(WeatherRequest request) {return new WeatherResponse(30.0, Unit.C);}
}public enum Unit { C, F }
public record WeatherRequest(String location, Unit unit) {}
public record WeatherResponse(double temp, Unit unit) {}
允许构建实例并提供有关该工具的关键信息:FunctionToolCallback.BuilderFunctionToolCallback
- name:工具的名称。AI 模型在调用工具时使用此名称来识别工具。因此,不允许在同一上下文中有两个同名的工具。该名称在模型可用于特定聊天请求的所有工具中必须是唯一的。必填。
- toolFunction:表示工具方法的功能对象(、、 或 )。必填。FunctionSupplierConsumerBiFunction
- description:工具的描述,模型可以使用它来了解何时以及如何调用工具。如果未提供,则方法名称将用作工具描述。但是,强烈建议提供详细的描述,因为这对于模型了解工具的用途以及如何使用它至关重要。未能提供良好的描述可能会导致模型在应该使用该工具时未使用该工具或错误地使用该工具。
- inputType:函数输入的类型。必填。
- inputSchema:工具输入参数的 JSON 架构。如果未提供,则将根据 自动生成架构。您可以使用注释提供有关输入参数的其他信息,例如描述或参数是必需的还是可选的。默认情况下,所有输入参数都被视为必需参数。有关更多详细信息,请参阅 JSON 架构。inputType@ToolParam
- toolMetadata:定义其他设置的实例,例如是否应将结果直接返回给客户端,以及要使用的结果转换器。你可以使用 class 构建它。ToolMetadataToolMetadata.Builder
- toolCallResultConverter:用于将工具调用的结果转换为要发送回 AI 模型的对象实例的实例。如果未提供,将使用默认转换器 ()。ToolCallResultConverterStringDefaultToolCallResultConverter
- 添加工具ChatClient
ToolCallback toolCallback = ...
ChatClient.create(chatModel).prompt("What's the weather like in Copenhagen?").tools(toolCallback).call().content();
- 添加默认工具ChatClient
ChatModel chatModel = ...
ToolCallback toolCallback = ...
ChatClient chatClient = ChatClient.builder(chatModel).defaultTools(toolCallback).build();
- 添加工具ChatModel
ChatModel chatModel = ...
ToolCallback toolCallback = ...
ChatOptions chatOptions = ToolCallingChatOptions.builder().toolCallbacks(toolCallback).build():
Prompt prompt = new Prompt("What's the weather like in Copenhagen?", chatOptions);
chatModel.call(prompt);
- 添加默认工具ChatModel
ToolCallback toolCallback = ...
ChatModel chatModel = OllamaChatModel.builder().ollamaApi(OllamaApi.builder().build()).defaultOptions(ToolCallingChatOptions.builder().toolCallbacks(toolCallback).build()).build();
执行
执行是使用提供的输入参数调用工具并返回结果的过程。工具执行由接口处理,该接口负责管理工具执行生命周期。ToolCallingManager
public interface ToolCallingManager {/*** Resolve the tool definitions from the model's tool calling options.*/List<ToolDefinition> resolveToolDefinitions(ToolCallingChatOptions chatOptions);/*** Execute the tool calls requested by the model.*/ToolExecutionResult executeToolCalls(Prompt prompt, ChatResponse chatResponse);}
如果使用的是Spring AI Spring Boot Starters,则是接口的自动配置实现。可以通过提供自己的 bean 来自定义工具执行行为。DefaultToolCallingManagerToolCallingManagerToolCallingManager
@Bean
ToolCallingManager toolCallingManager() {return ToolCallingManager.builder().build();
}
默认情况下, Spring AI 从每个实现中透明地为管理工具执行生命周期。但是,可以选择退出此行为并自行控制工具的执行。
框架控制的工具执行
当使用默认行为时, Spring AI 将自动拦截来自模型的任何工具调用请求,调用工具并将结果返回给模型。
-
当让一个工具可供模型使用时,在聊天请求 (Prompt) 中包含它的定义,并调用将请求发送到 AI 模型的 API。
-
当模型决定调用工具时,它会发送一个响应 (ChatResponse),其中包含工具名称和输入参数,这些参数在定义的架构之后建模。
-
将工具调用请求发送到ChatModelToolCallingManager API。
-
ToolCallingManager负责识别要调用的工具,并使用提供的输入参数执行它。
-
工具调用的结果将返回到ToolCallingManager .
-
将工具执行结果返回给ToolCallingManagerChatModel .
-
将工具执行结果发送回 AI 模型 (ChatModelToolResponseMessage)。
-
AI 模型使用工具调用结果作为附加上下文生成最终响应,并通过 将其发送回调用方 (ChatResponseChatClient)。
确定工具调用是否符合执行条件的逻辑由接口处理。默认情况下,通过检查 属性 of 是否设置为(默认值)以及是否包含任何工具调用来确定工具执行资格。
public class DefaultToolExecutionEligibilityPredicate implements ToolExecutionEligibilityPredicate {@Overridepublic boolean test(ChatOptions promptOptions, ChatResponse chatResponse) {return ToolCallingChatOptions.isInternalToolExecutionEnabled(promptOptions) && chatResponse != null&& chatResponse.hasToolCalls();}}
用户控制的工具执行
在某些情况下,自己控制工具执行生命周期可以通过将internalToolExecutionEnabled 的ToolCallingChatOptions属性设置为 false 来实现此目的。
当使用此选项调用 一个 时ChatModel,工具执行将委托给调用方,从而能够完全控制工具执行生命周期。负责检查 中的工具调用并使用ChatResponse 执行ToolCallingManager。
以下示例演示了用户控制的工具执行方法的最小实现:
ChatModel chatModel = ...
ToolCallingManager toolCallingManager = ToolCallingManager.builder().build();ChatOptions chatOptions = ToolCallingChatOptions.builder().toolCallbacks(new CustomerTools()).internalToolExecutionEnabled(false).build();
Prompt prompt = new Prompt("Tell me more about the customer with ID 42", chatOptions);ChatResponse chatResponse = chatModel.call(prompt);while (chatResponse.hasToolCalls()) {ToolExecutionResult toolExecutionResult = toolCallingManager.executeToolCalls(prompt, chatResponse);prompt = new Prompt(toolExecutionResult.conversationHistory(), chatOptions);chatResponse = chatModel.call(prompt);
}System.out.println(chatResponse.getResult().getOutput().getText());
以下示例显示了用户控制的工具执行方法的最小实现以及 API 的使用:ChatMemory API
ToolCallingManager toolCallingManager = DefaultToolCallingManager.builder().build();
ChatMemory chatMemory = MessageWindowChatMemory.builder().build();
String conversationId = UUID.randomUUID().toString();ChatOptions chatOptions = ToolCallingChatOptions.builder().toolCallbacks(ToolCallbacks.from(new MathTools())).internalToolExecutionEnabled(false).build();
Prompt prompt = new Prompt(List.of(new SystemMessage("You are a helpful assistant."), new UserMessage("What is 6 * 8?")),chatOptions);
chatMemory.add(conversationId, prompt.getInstructions());Prompt promptWithMemory = new Prompt(chatMemory.get(conversationId), chatOptions);
ChatResponse chatResponse = chatModel.call(promptWithMemory);
chatMemory.add(conversationId, chatResponse.getResult().getOutput());while (chatResponse.hasToolCalls()) {ToolExecutionResult toolExecutionResult = toolCallingManager.executeToolCalls(promptWithMemory,chatResponse);chatMemory.add(conversationId, toolExecutionResult.conversationHistory().get(toolExecutionResult.conversationHistory().size() - 1));promptWithMemory = new Prompt(chatMemory.get(conversationId), chatOptions);chatResponse = chatModel.call(promptWithMemory);chatMemory.add(conversationId, chatResponse.getResult().getOutput());
}UserMessage newUserMessage = new UserMessage("What did I ask you earlier?");
chatMemory.add(conversationId, newUserMessage);ChatResponse newResponse = chatModel.call(new Prompt(chatMemory.get(conversationId)));
异常处理
当工具调用失败时,异常将传播ToolExecutionExceptionTool可以捕获以处理错误。 ExecutionExceptionProcessor 可用于ToolExecutionException处理具有两种结果:生成要发送回 AI 模型的错误消息,或引发由调用方处理的异常。
@FunctionalInterface
public interface ToolExecutionExceptionProcessor {/*** Convert an exception thrown by a tool to a String that can be sent back to the AI* model or throw an exception to be handled by the caller.*/String process(ToolExecutionException exception);}
如果使用的是任何 Spring AI Spring Boot Starters,则DefaultToolExecutionExceptionProcessor 自动配置实现。默认情况下,错误消息将发送回模型。DefaultToolExecutionExceptionProcessor构造函数允许您将alwaysThrow属性设置为true 或false 。如果 true ,将引发异常,而不是将错误消息发送回模型。
@Bean
ToolExecutionExceptionProcessor toolExecutionExceptionProcessor() {return new DefaultToolExecutionExceptionProcessor(true);
}
5. Tool Calling 优势
- 自动化决策:LLM 自动决定何时调用工具。
- 灵活扩展:可以轻松添加新的工具。
- 无缝集成:与 Spring 生态系统(如 Spring Boot、Spring Data)无缝集成。
6. 应用场景Demo
天气查询
- 工具类需用@Tool注解声明,方法参数用@Param标注描述
- 通过ToolRegistry集中注册工具实例
- AI模型配置需关联ToolRegistry实现工具调用能力
- 实际项目应替换伪代码为真实气象API调用(如中国天气网API
WeatherTool.java
import org.springframework.ai.tool.annotation.*;
import org.springframework.stereotype.Component;@Component
@Tool(name = "get_weather", description = "获取指定城市天气信息")
public class WeatherTool {@ToolMethod(description = "查询城市天气")public String getWeather(@Param(name = "city", description = "城市名称") String city,@Param(name = "unit", description = "温度单位") String unit) {// 实际应调用气象API(示例伪代码)return String.format("%s当前天气: 25°%s, 晴", city, unit);}
}
AiConfig.java
org.springframework.ai.model.Model;
import org.springframework.ai.tool.ToolRegistry;
import org.springframework.context.annotation.*;@Configuration
public class AiConfig {@Beanpublic ToolRegistry toolRegistry(WeatherTool weatherTool) {return new ToolRegistry().register(weatherTool);}@Beanpublic Model aiModel(ToolRegistry registry) {return new SpringAiModel().withTools(registry);}
}
WeatherController.java
import org.springframework.ai.client.AiClient;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/api/weather")
public class WeatherController {private final AiClient aiClient;public WeatherController(AiClient aiClient) {this.aiClient = aiClient;}@GetMapping("/query")public String queryWeather(@RequestParam String city,@RequestParam(defaultValue = "C") String unit) {String prompt = String.format("请查询%s的天气,温度单位使用%s", city, unit);return aiClient.generate(prompt);}
}
新闻检索
- 工具类需使用@Tool注解声明功能范围,方法参数用@Param标注语义描述
- 通过ToolRegistry集中管理工具实例,支持动态注册与卸载
- 实际项目应替换伪代码为真实新闻API调用(如NewsAPI或头条开放平台)
- 建议集成阿里云MCP服务增强检索能力
NewsTool.java
import org.springframework.ai.tool.annotation.*;
import org.springframework.web.client.RestTemplate;@Component
@Tool(name = "news_retriever", description = "新闻检索工具")
public class NewsTool {private final RestTemplate restTemplate;public NewsTool(RestTemplateBuilder builder) {this.restTemplate = builder.build();}@ToolMethod(description = "按关键词检索新闻")public String searchNews(@Param(name = "keyword", description = "搜索关键词") String keyword,@Param(name = "limit", description = "返回结果数") int limit) {// 示例调用新闻API(需替换为真实API)String apiUrl = "https://news-api.com/search?q={keyword}&limit={limit}";return restTemplate.getForObject(apiUrl, String.class, keyword, limit);}
}
AiConfig.java
org.springframework.ai.tool.ToolRegistry;
import org.springframework.context.annotation.*;@Configuration
public class AiConfig {@Beanpublic ToolRegistry toolRegistry(NewsTool newsTool) {return new ToolRegistry().register(newsTool);}@Beanpublic Model aiModel(ToolRegistry registry) {return new SpringAiModel().withTools(registry).withTemperature(0.7);}
}
NewsController.java
org.springframework.ai.client.AiClient;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/api/news")
public class NewsController {private final AiClient aiClient;public NewsController(AiClient aiClient) {this.aiClient = aiClient;}@GetMapping("/search")public String search(@RequestParam String query,@RequestParam(defaultValue = "5") int limit) {String prompt = String.format("请检索关于'%s'的最新新闻,返回%d条结果", query, limit);return aiClient.generate(prompt);}
}
数据库查询
- 通过@Tool注解声明工具类,@Param标注参数语义
- 使用JdbcTemplate实现安全SQL查询
- 温度参数设为0.3保证SQL生成稳定性
- 支持自然语言转SQL的交互方式
DatabaseTool.java
import org.springframework.ai.tool.annotation.*;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;@Component
@Tool(name = "db_query", description = "数据库查询工具")
public class DatabaseTool {private final JdbcTemplate jdbcTemplate;public DatabaseTool(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;}@ToolMethod(description = "执行SQL查询")public List<Map<String, Object>> executeQuery(@Param(name = "sql", description = "SQL查询语句") String sql) {return jdbcTemplate.queryForList(sql);}
}
AiConfig.java
org.springframework.ai.tool.ToolRegistry;
import org.springframework.context.annotation.*;@Configuration
public class AiConfig {@Beanpublic ToolRegistry toolRegistry(DatabaseTool dbTool) {return new ToolRegistry().register(dbTool);}@Beanpublic Model aiModel(ToolRegistry registry) {return new SpringAiModel().withTools(registry).withTemperature(0.3);}
}
DbController.java
org.springframework.ai.client.AiClient;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/api/db")
public class DbController {private final AiClient aiClient;public DbController(AiClient aiClient) {this.aiClient = aiClient;}@PostMapping("/query")public String query(@RequestBody String naturalLanguageQuery) {String prompt = "将以下自然语言转换为SQL并执行: " + naturalLanguageQuery;return aiClient.generate(prompt);}
}
计算器功能
该实现包含四则运算基础功能,通过@ToolMethod注解声明每个数学运算方法,温度参数设为0.1保证计算精确性。需添加spring-ai-alibaba-starter和spring-boot-starter-web依赖
CalcualtorTool.java
import org.springframework.ai.tool.annotation.*;
import org.springframework.stereotype.Component;@Component
@Tool(name = "calculator", description = "数学计算工具")
public class CalculatorTool {@ToolMethod(description = "加法运算")public double add(@Param(name = "a", description = "第一个加数") double a,@Param(name = "b", description = "第二个加数") double b) {return a + b;}@ToolMethod(description = "减法运算")public double subtract(@Param(name = "a", description = "被减数") double a,@Param(name = "b", description = "减数") double b) {return a - b;}@ToolMethod(description = "乘法运算")public double multiply(@Param(name = "a", description = "第一个乘数") double a,@Param(name = "b", description = "第二个乘数") double b) {return a * b;}@ToolMethod(description = "除法运算")public double divide(@Param(name = "a", description = "被除数") double a,@Param(name = "b", description = "除数") double b) {if (b == 0) throw new IllegalArgumentException("除数不能为零");return a / b;}
}
AiConfig.java
org.springframework.ai.tool.ToolRegistry;
import org.springframework.context.annotation.*;@Configuration
public class AiConfig {@Beanpublic ToolRegistry toolRegistry(CalculatorTool calculatorTool) {return new ToolRegistry().register(calculatorTool);}@Beanpublic Model aiModel(ToolRegistry registry) {return new SpringAiModel().withTools(registry).withTemperature(0.1);}
}
MathController.java
org.springframework.ai.client.AiClient;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/api/math")
public class MathController {private final AiClient aiClient;public MathController(AiClient aiClient) {this.aiClient = aiClient;}@PostMapping("/calculate")public String calculate(@RequestBody String expression) {return aiClient.generate("计算表达式: " + expression);}
}
日历/提醒功能
实现包含提醒设置、事件管理、定时检查等完整功能,通过@Scheduled实现每分钟自动检查提醒,使用ConcurrentHashMap保证线程安全。需要添加spring-boot-starter-web和spring-ai-core依赖。
ReminderTool.java
import org.springframework.ai.tool.annotation.*;
import org.springframework.scheduling.annotation.Scheduled;
import java.time.LocalDateTime;
import java.util.concurrent.ConcurrentHashMap;@Component
@Tool(name = "reminder_system", description = "日历提醒工具")
public class ReminderTool {private final ConcurrentHashMap<String, LocalDateTime> reminders = new ConcurrentHashMap<>();@ToolMethod(description = "设置提醒")public String setReminder(@Param(name = "content", description = "提醒内容") String content,@Param(name = "time", description = "提醒时间") LocalDateTime time) {reminders.put(content, time);return "已设置提醒: " + content + " - " + time;}@Scheduled(fixedRate = 60000)public void checkReminders() {LocalDateTime now = LocalDateTime.now();reminders.forEach((content, time) -> {if (now.isAfter(time)) {System.out.println("提醒: " + content);reminders.remove(content);}});}
}
CalendarTool.java
org.springframework.ai.tool.annotation.*;
import java.time.LocalDate;
import java.util.*;@Component
@Tool(name = "calendar_manager", description = "日历管理工具")
public class CalendarTool {private final Map<LocalDate, List<String>> events = new HashMap<>();@ToolMethod(description = "添加日历事件")public String addEvent(@Param(name = "date", description = "事件日期") LocalDate date,@Param(name = "event", description = "事件内容") String event) {events.computeIfAbsent(date, k -> new ArrayList<>()).add(event);return date + " 的事件已添加: " + event;}@ToolMethod(description = "查询某日事件")public List<String> getEvents(@Param(name = "date", description = "查询日期") LocalDate date) {return events.getOrDefault(date, Collections.emptyList());}
}
AiConfig.java
import org.springframework.ai.tool.ToolRegistry;
import org.springframework.context.annotation.*;@Configuration
public class AiConfig {@Beanpublic ToolRegistry toolRegistry(ReminderTool reminderTool, CalendarTool calendarTool) {return new ToolRegistry().register(reminderTool).register(calendarTool);}@Beanpublic Model aiModel(ToolRegistry registry) {return new SpringAiModel().withTools(registry).withTemperature(0.5);}
}
CalendarController.java
import org.springframework.ai.client.AiClient;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/api/calendar")
public class CalendarController {private final AiClient aiClient;public CalendarController(AiClient aiClient) {this.aiClient = aiClient;}@PostMapping("/command")public String handleCommand(@RequestBody String command) {return aiClient.generate("处理日历命令: " + command);}
}
注意事项
- 安全性:确保工具调用不会暴露敏感信息。
- 性能:避免频繁调用外部 API,影响响应速度。
- 错误处理:处理工具调用失败的情况,提供备用方案。