本文是 Spring AI Agent 开发学习系列的第 5 篇
开篇
上一篇文章我们学会了用 @Tool 定义工具。但在生产环境中,你还需要面对这些问题:
• ❓ 怎么确保工具输入是有效的? —— 邮箱格式、年龄范围、必填字段 • ❓ 怎么动态创建工具? —— 不依赖 @Tool注解,运行时注册• ❓ 怎么控制谁可以调用什么工具? —— 基于角色的权限控制 • ❓ 怎么集成第三方 API? —— 把外部服务包装成 AI 工具
这些就是我们今天要解决的进阶问题。
读完本文,你将收获:
• ✅ 工具参数验证与错误处理模式 • ✅ 使用 FunctionToolCallback动态注册工具• ✅ 通过 ToolContext实现权限控制• ✅ 将第三方 API 包装为 AI 工具 • ✅ 配套代码验证通过的所有接口
一、参数验证:让工具更健壮

1.1 验证模式
在 @Tool 方法中直接进行参数验证:
@Tool(name = "send_email", description = "发送电子邮件")public String sendEmail( @ToolParam(description = "收件人邮箱") String to, @ToolParam(description = "邮件主题") String subject, @ToolParam(description = "邮件正文") String body) { // 格式验证 if (!to.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$")) { throw new IllegalArgumentException("邮箱格式无效:" + to); } // 必填验证 if (subject == null || subject.isBlank()) { throw new IllegalArgumentException("邮件主题不能为空"); } // 长度验证 if (body.length() > 1000) { throw new IllegalArgumentException("正文超过1000字限制"); } return "邮件发送成功 → " + to;}当验证失败时:
1. 工具抛出 IllegalArgumentException2. Spring AI 捕获异常,将错误信息返回给模型 3. 模型根据错误信息调整参数后重试 4. 或告知用户输入不合法
1.2 生产级验证工具
send_email | ||
create_user | ||
book_flight |
二、动态工具注册:摆脱 @Tool 束缚
@Tool 注解虽然方便,但有些场景需要运行时动态创建工具:
• 工具列表来自配置文件 • 为不同用户生成不同的工具 • 集合类操作需要统一注册
2.1 使用 FunctionToolCallback
Spring AI 提供了 FunctionToolCallback.builder() 来编程式创建工具:
// 无参数工具(Supplier)ToolCallback randomTool = FunctionToolCallback.builder("generate_random", () -> "随机数:" + ThreadLocalRandom.current().nextInt(100, 1000)) .description("生成一个随机的三位数") .build();支持的四种函数签名:
Supplier<O> | |||
Consumer<I> | |||
Function<I, O> | |||
BiFunction<I, ToolContext, O> |
2.2 实现 ToolCallbackProvider
@Componentpublic class DynamicToolProvider implements ToolCallbackProvider { @Override public ToolCallback[] getToolCallbacks() { return new ToolCallback[]{ createRandomNumberTool(), createSystemInfoTool(), createUuidTool() }; } private ToolCallback createSystemInfoTool() { return FunctionToolCallback.builder("get_system_info", () -> { Runtime rt = Runtime.getRuntime(); return "操作系统:" + System.getProperty("os.name") + ",内存:" + usedMem + "MB / " + totalMem + "MB"; }) .description("获取服务器系统信息") .build(); }}2.3 注册到 ChatClient
chatClient.prompt() .user("生成一个随机数") .toolCallbacks(dynamicToolProvider) // ← ToolCallbackProvider .call() .content();toolCallbacks() 接受三种参数:
• ToolCallback...— 单个或多个回调• List<ToolCallback>— 回调列表• ToolCallbackProvider...— 提供者(自动调用getToolCallbacks())
三、工具上下文与权限控制
3.1 ToolContext 是什么?
ToolContext 是 Spring AI 用来给工具传递上下文的机制,比如当前用户、角色、请求 ID 等:

// 在 Controller 中设置上下文chatClient.prompt() .user("查看用户 zhangsan 的信息") .tools(contextAwareToolService) .toolContext(Map.of( "current_user", "admin", "user_role", "admin" )) .call() .content();在工具方法中通过参数接收:
@Tool(name = "get_user_profile", description = "获取用户信息")public String getUserProfile( @ToolParam(description = "用户名") String username, ToolContext ctx) { // ← 自动注入 ToolContext String role = (String) ctx.getContext().get("user_role"); if (!"admin".equals(role)) { return "权限不足:需要 admin 角色"; } // ... 执行查询}⚠️ 注意:
ToolContext参数不需要@ToolParam注解。
3.2 基于角色的访问控制
用户请求 → toolContext({"role": "user"}) ↓工具检查 role → 不是 admin → 返回"权限不足" ↓模型告知用户:你需要管理员权限对比效果:
四、第三方 API 集成

4.1 在工具中调用外部 API
工具方法内部可以直接调用 HTTP API:
@Componentpublic class ExternalApiToolService { private final RestTemplate restTemplate = new RestTemplate(); @Tool(name = "get_exchange_rate", description = "查询实时汇率") public String getExchangeRate( @ToolParam(description = "源货币") String from, @ToolParam(description = "目标货币") String to) { // 调用第三方汇率 API String url = "https://api.exchangerate-api.com/v4/latest/" + from; Map<String, Object> response = restTemplate.getForObject(url, Map.class); Map<String, Double> rates = (Map<String, Double>) response.get("rates"); return from + " → " + to + " = " + rates.get(to); }}4.2 本项目的模拟 API 调用
由于教程的限制,我们用内置数据模拟了三个常用场景:
get_ip_info | ||
get_exchange_rate | ||
get_stock_price |
五、完整接口一览
本项目部署在 8084 端口,覆盖 4 个主题共 10 个端点:
参数验证
GET /api/advanced/email | ||
GET /api/advanced/create-user | ||
GET /api/advanced/book-flight |
权限控制
GET /api/advanced/profile?role=admin | ||
GET /api/advanced/sensitive?role=admin |
第三方 API(模拟)
GET /api/advanced/exchange-rate | |
GET /api/advanced/stock | |
GET /api/advanced/ip-info |
动态注册
GET /api/advanced/dynamic | |
GET /api/advanced/system-info |
📌 核心知识点总结
✅ 掌握内容
• @Tool方法中的参数验证与异常处理• FunctionToolCallback.builder()动态创建工具• ToolCallbackProvider接口的实现与注册• ToolContext传递用户上下文实现权限控制• 第三方 API 包装为 AI 工具的模式
🔑 关键要点
1. 验证要前置:在工具方法入口验证所有参数,抛出 IllegalArgumentException2. 动态 vs 注解:注解适合固定工具, FunctionToolCallback适合运行时创建3. 上下文就是安全: ToolContext不只是传参,更是实现权限控制的核心机制4. API 集成模式:工具方法 = 普通 Java 方法 = 可以调用任何 API
🎯 实战能力
读完本文,你应该能够:
• ✅ 在工具方法中实现参数校验和错误提示 • ✅ 使用 FunctionToolCallback动态注册工具• ✅ 通过 ToolContext实现角色权限控制• ✅ 将任意外部 API 封装为 AI 可调用的工具
下期预告
第6篇:聊天记忆与会话管理——让 AI 记住每一次对话
当前的 AI 每一次对话都是独立的,它不记得你刚才说了什么。下期我们将解决这个问题,让 AI 拥有"记忆"。
敬请期待!
关注本系列,系统掌握 Spring AI Agent 开发!🔔
4.Spring AI 工具调用:让 AI 模型具备行动能力
引用链接
[1] ip-api.com: https://ip-api.com[2] exchangerate-api.com: https://exchangerate-api.com
夜雨聆风