在Spring AI 1.x框架中,ToolCallbackProvider是一个强大的动态工具集成机制。作为框架的核心扩展点之一,它允许开发者在不修改主流程代码的情况下,灵活地注入各种功能工具。这种设计模式在现代AI应用开发中尤为重要,特别是在需要快速集成第三方服务或自定义功能的场景。
我在实际项目中发现,合理使用ToolCallbackProvider可以显著降低系统耦合度。比如在一个智能客服项目中,我们通过这个机制动态集成了天气查询、订单状态检索和知识库搜索三个独立工具,每个工具的开发和维护都可以独立进行,而主流程代码始终保持简洁。
ToolCallbackProvider采用了典型的策略模式(Strategy Pattern)设计。其核心接口定义通常包含以下关键方法:
java复制public interface ToolCallbackProvider {
List<ToolSpecification> getToolSpecifications();
ToolCallback getToolCallback();
}
这种设计有三大优势:
组件间的协作流程如下:
重要提示:工具规格中的name属性必须全局唯一,否则会导致工具冲突。建议采用"领域_功能"的命名规范,如"weather_query"、"order_status_check"。
下面是一个完整的天气查询工具实现:
java复制@Component
public class WeatherToolCallbackProvider implements ToolCallbackProvider {
@Override
public List<ToolSpecification> getToolSpecifications() {
return List.of(
new ToolSpecification(
"weather_query",
"查询指定城市的天气情况",
Map.of(
"location", new ParameterSpecification(
"string",
"城市名称,如'北京'",
true
)
)
)
);
}
@Override
public ToolCallback getToolCallback() {
return (request, context) -> {
String location = (String) request.getParameters().get("location");
// 调用天气API获取数据
WeatherData data = weatherService.getCurrentWeather(location);
return new ToolResponse(data.toMap());
};
}
}
java复制@Override
public ToolCallback getToolCallback() {
return (request, context) -> {
try {
// 参数校验
if (!context.hasPermission("weather:query")) {
throw new SecurityException("无权限访问该工具");
}
String location = validateLocation(request.getParameters());
// 业务逻辑...
} catch (Exception e) {
return ToolResponse.error(e.getMessage());
}
};
}
通过组合多个工具实现复杂业务流程:
java复制@Component
public class TravelAssistantToolProvider implements ToolCallbackProvider {
@Autowired
private FlightTool flightTool;
@Autowired
private HotelTool hotelTool;
@Override
public ToolCallback getToolCallback() {
return (request, context) -> {
// 并行查询航班和酒店
CompletableFuture<FlightResult> flights = flightTool.searchAsync(...);
CompletableFuture<HotelResult> hotels = hotelTool.searchAsync(...);
// 组合结果
return CompletableFuture.allOf(flights, hotels)
.thenApply(v -> new TravelPlan(
flights.join(),
hotels.join()
));
};
}
}
实现工具的多版本支持:
java复制@Override
public List<ToolSpecification> getToolSpecifications() {
return List.of(
createSpec("v1", ...),
createSpec("v2", ...)
);
}
@Override
public ToolCallback getToolCallback() {
return (request, context) -> {
String version = context.getToolVersion();
switch(version) {
case "v1": return handleV1(request);
case "v2": return handleV2(request);
default: throw new UnsupportedVersionException(version);
}
};
}
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 工具未出现在可用列表中 | 1. 未正确注册为Spring Bean 2. getToolSpecifications返回空列表 |
1. 检查@Component注解 2. 调试getToolSpecifications方法 |
| 工具执行超时 | 1. 网络延迟 2. 未设置合理超时 |
1. 添加异步调用 2. 配置@Timeout注解 |
| 参数解析失败 | 1. 类型不匹配 2. 必填参数缺失 |
1. 检查ParameterSpecification定义 2. 添加参数校验逻辑 |
properties复制logging.level.org.springframework.ai.tool=DEBUG
java复制@SpringBootTest
class WeatherToolTests {
@Autowired
private ToolCaller toolCaller;
@Test
void testWeatherQuery() {
ToolRequest request = new ToolRequest(
"weather_query",
Map.of("location", "北京")
);
ToolResponse response = toolCaller.call(request);
assertNotNull(response.getResult());
}
}
java复制@Bean
public ToolCallbackInterceptor loggingInterceptor() {
return (request, context, next) -> {
log.info("Tool call: {}", request.getToolName());
try {
return next.execute(request, context);
} finally {
log.info("Tool completed in {} ms",
System.currentTimeMillis() - startTime);
}
};
}
建议为每个工具添加以下监控指标:
使用Micrometer实现示例:
java复制@Override
public ToolCallback getToolCallback() {
Counter counter = Metrics.counter("tool.calls", "name", "weather_query");
Timer timer = Metrics.timer("tool.duration", "name", "weather_query");
return (request, context) -> timer.record(() -> {
counter.increment();
try {
// 实际工具逻辑...
} catch (Exception e) {
Metrics.counter("tool.errors", "name", "weather_query").increment();
throw e;
}
});
}
在Grafana中可以创建如下监控面板:
在电商客服系统中,我们通过ToolCallbackProvider集成了以下核心工具:
订单查询工具:
库存检查工具:
推荐引擎工具:
遇到的典型问题及解决方案:
问题1:工具间存在循环依赖
问题2:部分工具响应慢影响整体体验
问题3:工具版本升级导致兼容性问题
扩展ToolSpecification以支持更多属性:
java复制public class EnhancedToolSpec extends ToolSpecification {
private String category;
private String iconUrl;
private int minPermissionLevel;
// 其他自定义字段...
}
@Override
public List<ToolSpecification> getToolSpecifications() {
EnhancedToolSpec spec = new EnhancedToolSpec();
spec.setCategory("utility");
spec.setIconUrl("/icons/weather.png");
spec.setMinPermissionLevel(2);
// 设置基础属性...
return List.of(spec);
}
声明工具间的依赖关系:
java复制@ToolDependency({"geo_service", "user_profile"})
public class LocationAwareTool implements ToolCallbackProvider {
// 实现方法...
}
构建动态工具注册中心:
java复制@RestController
@RequestMapping("/api/tools")
public class ToolRegistryController {
@PostMapping
public void registerTool(@RequestBody DynamicTool tool) {
// 验证并注册工具
toolRegistry.register(tool);
}
@GetMapping
public List<ToolInfo> listTools() {
return toolRegistry.getAllTools();
}
}
java复制@Test
void testSpecifications() {
List<ToolSpecification> specs = provider.getToolSpecifications();
assertEquals(1, specs.size());
assertEquals("weather_query", specs.get(0).getName());
}
java复制@Test
void testWeatherQuery() {
ToolRequest request = new ToolRequest(
"weather_query",
Map.of("location", "上海")
);
ToolResponse response = provider.getToolCallback()
.execute(request, ToolContext.EMPTY);
assertTrue(response.getResult().containsKey("temperature"));
}
创建测试专用的Mock工具:
java复制@Primary
@Bean
public ToolCallbackProvider mockWeatherTool() {
return new ToolCallbackProvider() {
@Override
public ToolCallback getToolCallback() {
return (req, ctx) -> new ToolResponse(
Map.of("temperature", 25, "condition", "晴天")
);
}
};
}
在最近的项目中,我们通过自定义注解实现了工具权限的声明式配置:
java复制@ToolPermission("order:read")
public class OrderQueryTool implements ToolCallbackProvider {
// 工具实现...
}
这种设计使得权限管理更加集中和直观,系统管理员可以通过注解值直接了解各工具的安全要求。