LangChain4j 中 Tool 的定义方式与 @Tool 注解详解
在 LangChain4j 中,Tool(工具)允许大语言模型通过函数调用方式与外部系统、API 或业务逻辑进行交互,这是实现 AI Agent 功能的关键组件。
🛠️ Tool 的定义方式
方式1:使用 @Tool 注解(最常用)
import dev.langchain4j.agent.tool.Tool;
import org.springframework.stereotype.Component;
@Component
public class CalculatorTool {
@Tool(name = "calculator", description = "执行数学计算,支持加、减、乘、除、幂运算")
public double calculate(
@Tool.Memory String expression, // 从上下文获取表达式
@Tool.P("运算类型") String operation, // 指定参数描述
@Tool.P("第一个数字") double num1,
@Tool.P("第二个数字") double num2
) {
return switch (operation.toLowerCase()) {
case "add", "+" -> num1 + num2;
case "subtract", "-" -> num1 – num2;
case "multiply", "*" -> num1 * num2;
case "divide", "/" -> {
if (num2 == 0) throw new IllegalArgumentException("除数不能为零");
yield num1 / num2;
}
case "power", "^" -> Math.pow(num1, num2);
default -> throw new IllegalArgumentException("不支持的操作: " + operation);
};
}
}
方式2:实现 Tool 接口(更灵活)
import dev.langchain4j.agent.tool.Tool;
import dev.langchain4j.agent.tool.ToolExecutionRequest;
import dev.langchain4j.agent.tool.ToolSpecification;
import java.util.Optional;
public class WeatherTool implements Tool {
@Override
public String id() {
return "weather-tool";
}
@Override
public ToolSpecification specification() {
return ToolSpecification.builder()
.name("getWeather")
.description("获取指定城市的天气信息")
.addParameter("city", "string", "城市名称,如:北京、上海")
.addParameter("date", "string", "日期,格式:YYYY-MM-DD,默认为今天")
.build();
}
@Override
public Optional<String> execute(ToolExecutionRequest request, Object memoryId) {
// 解析请求参数
String city = request.arguments().get("city");
String date = request.arguments().getOrDefault("date", "today");
// 调用天气API
String weather = fetchWeatherFromAPI(city, date);
return Optional.of(weather);
}
private String fetchWeatherFromAPI(String city, String date) {
// 实际调用天气API的逻辑
return String.format("%s在%s的天气:晴,温度20-25°C", city, date);
}
}
方式3:使用 ToolBuilder(编程式构建)
import dev.langchain4j.agent.tool.ToolBuilder;
import java.util.function.Function;
public class StockTool {
public Tool createStockTool() {
return ToolBuilder.builder()
.name("getStockPrice")
.description("获取股票实时价格")
.parameter("symbol", "string", "股票代码,如:AAPL, TSLA")
.execute((request, memoryId) -> {
String symbol = request.arguments().get("symbol");
double price = fetchStockPrice(symbol);
return Optional.of(String.format("%s的当前价格是:$%.2f", symbol, price));
})
.build();
}
private double fetchStockPrice(String symbol) {
// 调用股票API
return 150.75; // 示例数据
}
}
📝 @Tool 注解的核心参数详解
1. 基本参数
| name | String | 否 | 方法名 | 工具的唯一标识符,AI 根据此名称调用 |
| value | String | 否 | 方法名 | name 的别名,与 name 作用相同 |
| description | String | 是 | 无 | 工具功能的详细描述,AI 根据描述决定是否调用 |
2. 方法参数注解
@Tool(name = "orderTool", description = "处理商品订单")
public String placeOrder(
// @P 注解:提供参数描述
@P("商品ID") String productId,
// @P 注解:可指定更详细的描述
@P(value = "购买数量", required = true) int quantity,
// @Tool.Memory:从上下文中获取值
@Tool.Memory String customerId,
// 默认值处理
@P(value = "配送地址", defaultValue = "默认地址") String address,
// 复杂参数类型
@P("订单备注") List<String> notes
) {
// 业务逻辑
return String.format("订单创建成功:%s x %d", productId, quantity);
}
3. 高级配置参数
@Tool(
name = "advanced_tool",
description = "这是一个高级工具示例",
// 执行配置
executeAsync = false, // 是否异步执行,默认false
timeout = 5000, // 超时时间(毫秒)
// 元数据
tags = {"critical", "finance"}, // 标签,用于分类和过滤
version = "1.0.0", // 版本号
// 返回类型提示
returnType = ReturnType.STRING // 返回类型提示
// 权限控制
requiredPermissions = {"READ", "WRITE"}
)
public String advancedOperation(@P("输入数据") String input) {
return "处理结果: " + input;
}
🔧 Tool 的注册与配置
1. Spring Boot 自动装配
@Configuration
public class ToolConfig {
@Bean
public ToolExecutor toolExecutor(
List<Tool> tools, // 自动收集所有 @Tool 注解的 Bean
@Value("${ai.tool.timeout:30000}") long timeout
) {
ToolExecutor executor = ToolExecutor.builder()
.tools(tools)
.timeout(timeout)
.build();
// 添加自定义工具处理器
executor.addToolExecutionListener(new ToolExecutionListener() {
@Override
public void onToolExecuted(ToolExecutionEvent event) {
log.info("工具 {} 执行完成,耗时 {}ms",
event.getToolName(),
event.getExecutionTime());
}
});
return executor;
}
}
2. 手动注册工具
public class ManualToolRegistration {
public AiServices<Assistant> createAssistant() {
CalculatorTool calculator = new CalculatorTool();
WeatherTool weather = new WeatherTool();
return AiServices.builder(Assistant.class)
.chatLanguageModel(createChatModel())
.tools(calculator, weather) // 注册多个工具
.toolExecutor(createToolExecutor())
.build();
}
private ToolExecutor createToolExecutor() {
return ToolExecutor.builder()
.toolSpecificationParser(new JsonToolSpecificationParser())
.argumentExtractor(new ReflectionArgumentExtractor())
.build();
}
}
🎯 实际应用示例
示例1:电商客服 Agent
@Service
public class CustomerServiceTools {
private final OrderRepository orderRepository;
private final ProductService productService;
@Tool(name = "查找订单",
description = "根据订单号或客户信息查找订单详情",
tags = {"order", "customer-service"})
public OrderInfo findOrder(
@P(value = "订单号", required = false) String orderId,
@P(value = "客户手机号", required = false) String phone,
@P(value = "客户邮箱", required = false) String email
) {
// 验证参数
if (orderId == null && phone == null && email == null) {
throw new IllegalArgumentException("至少提供一个查询条件");
}
// 查询逻辑
Order order = orderRepository.findByCriteria(orderId, phone, email);
return convertToOrderInfo(order);
}
@Tool(name = "检查库存",
description = "检查商品库存状态,返回库存数量",
timeout = 3000)
public StockInfo checkStock(
@P("商品SKU") String sku,
@P(value = "仓库代码", defaultValue = "WH001") String warehouseCode
) {
int quantity = productService.getStockQuantity(sku, warehouseCode);
boolean inStock = quantity > 0;
return new StockInfo(sku, warehouseCode, quantity, inStock);
}
@Tool(name = "处理退货",
description = "处理客户退货申请,生成退货单")
public ReturnResult processReturn(
@P("订单号") String orderId,
@P("退货原因") ReturnReason reason,
@P("商品列表") List<ReturnItem> items,
@Tool.Memory("customerId") String customerId // 从对话记忆获取
) {
// 验证退货资格
validateReturnEligibility(orderId, customerId);
// 创建退货单
String returnId = returnService.createReturn(orderId, reason, items);
return new ReturnResult(returnId, "退货申请已受理");
}
}
示例2:数据分析工具
@Component
public class DataAnalysisTools {
@Tool(name = "数据统计",
description = "对数据集进行统计分析,返回统计指标")
public StatisticsResult analyzeData(
@P("数据集") List<Double> dataset,
@P(value = "统计方法", defaultValue = "basic") AnalysisMethod method
) {
StatisticsResult result = new StatisticsResult();
result.setCount(dataset.size());
result.setMean(calculateMean(dataset));
result.setStdDev(calculateStdDev(dataset));
if (method == AnalysisMethod.ADVANCED) {
result.setMedian(calculateMedian(dataset));
result.setPercentiles(calculatePercentiles(dataset));
}
return result;
}
@Tool(name = "数据可视化",
description = "生成数据可视化图表",
executeAsync = true) // 异步执行,避免阻塞
public VisualizationResult visualize(
@P("数据集") Map<String, Object> data,
@P("图表类型") ChartType chartType,
@P(value = "样式配置", required = false) StyleConfig style
) {
// 异步生成图表
CompletableFuture<String> chartFuture = chartService.generateAsync(
data, chartType, style
);
// 返回任务ID,支持查询进度
String taskId = taskManager.createTask(chartFuture);
return new VisualizationResult(taskId, "图表生成任务已提交");
}
}
示例3:多步骤复杂工具
@Tool(name = "旅行规划",
description = "规划完整的旅行行程,包括交通、住宿、景点")
public TravelPlan planTravel(
@P("目的地") String destination,
@P("出发日期") LocalDate startDate,
@P("结束日期") LocalDate endDate,
@P("旅行人数") int travelers,
@P(value = "预算", defaultValue = "10000") double budget,
@P(value = "偏好", required = false) TravelPreference preference
) {
// 第一步:获取目的地信息
DestinationInfo destInfo = destinationService.getInfo(destination);
// 第二步:查找航班
List<FlightOption> flights = flightService.searchFlights(
"current_city", destination, startDate, travelers
);
// 第三步:查找酒店
List<HotelOption> hotels = hotelService.searchHotels(
destination, startDate, endDate, travelers, budget/3
);
// 第四步:生成景点推荐
List<Attraction> attractions = attractionService.recommend(
destination, preference
);
// 第五步:生成每日行程
List<DailySchedule> schedule = itineraryPlanner.createSchedule(
attractions, startDate, endDate
);
// 第六步:计算总费用
double totalCost = calculateTotalCost(flights, hotels, attractions);
return TravelPlan.builder()
.destination(destination)
.duration(Duration.between(startDate, endDate))
.flights(flights)
.hotels(hotels)
.attractions(attractions)
.dailySchedules(schedule)
.totalCost(totalCost)
.budgetStatus(totalCost <= budget ? "在预算内" : "超出预算")
.build();
}
⚙️ 配置与优化
1. 全局配置
# application.yml
langchain4j:
tools:
enabled: true
auto-registration: true
execution:
timeout: 10000 # 10秒超时
max-retries: 3
retry-delay: 1000
security:
enable-whitelist: true
allowed-tools:
– calculator
– weather
– stock
2. 性能优化配置
@Configuration
public class ToolPerformanceConfig {
@Bean
@Primary
public ToolExecutor toolExecutor() {
return ToolExecutor.builder()
.cacheManager(createCacheManager()) // 添加缓存
.rateLimiter(createRateLimiter()) // 添加限流
.circuitBreaker(createCircuitBreaker()) // 添加熔断
.monitor(createMonitor()) // 添加监控
.build();
}
private CacheManager createCacheManager() {
return CacheManager.builder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.recordStats()
.build();
}
private RateLimiter createRateLimiter() {
return RateLimiter.create(100); // 每秒100个请求
}
}
3. 错误处理
@Slf4j
@Component
public class GlobalToolErrorHandler implements ToolExecutionErrorHandler {
@Override
public Optional<String> handleError(ToolExecutionRequest request, Throwable error) {
log.error("工具执行失败: {}", request.name(), error);
// 根据错误类型返回不同的提示
if (error instanceof IllegalArgumentException) {
return Optional.of("参数错误: " + error.getMessage());
} else if (error instanceof TimeoutException) {
return Optional.of("工具执行超时,请稍后重试");
} else if (error instanceof RateLimitExceededException) {
return Optional.of("请求过于频繁,请稍后再试");
}
// 生产环境隐藏具体错误
return Optional.of("系统繁忙,请稍后重试");
}
}
🔍 最佳实践
1. 工具设计原则
public class ToolDesignPrinciples {
// 原则1:单一职责
@Tool(name = "单一功能工具", description = "每个工具只做一件事")
public String doOneThingWell(@P("输入") String input) {
// …
}
// 原则2:参数验证
@Tool(name = "带验证的工具")
public Result validatedTool(@P("用户ID") String userId) {
if (StringUtils.isBlank(userId)) {
throw new ValidationException("用户ID不能为空");
}
// …
}
// 原则3:文档完整
@Tool(
name = "well_documented",
description = """
这是一个文档完整的工具。
功能:计算两个数的和。
注意:仅支持整数运算。
示例:输入(5, 3)返回8。
""",
tags = {"math", "basic"}
)
public int add(@P("第一个加数") int a, @P("第二个加数") int b) {
return a + b;
}
}
2. 安全性考虑
@Component
public class SecureTools {
@PreAuthorize("hasRole('USER')") // Spring Security 集成
@Tool(name = "敏感操作", description = "需要权限验证的操作")
public String sensitiveOperation(@P("数据") String data) {
// 需要用户认证
return "操作成功";
}
@Tool(name = "输入净化工具")
public String sanitizedOperation(
@P("用户输入")
@Sanitize(type = SanitizeType.HTML) // 防止XSS
String userInput
) {
// 输入已自动净化
return process(userInput);
}
}
📊 调试与监控
1. 工具调用日志
@Aspect
@Component
@Slf4j
public class ToolLoggingAspect {
@Around("@annotation(dev.langchain4j.agent.tool.Tool)")
public Object logToolExecution(ProceedingJoinPoint joinPoint) throws Throwable {
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
Tool toolAnnotation = method.getAnnotation(Tool.class);
long startTime = System.currentTimeMillis();
try {
Object result = joinPoint.proceed();
long duration = System.currentTimeMillis() – startTime;
log.info("工具执行成功 – 工具: {}, 耗时: {}ms",
toolAnnotation.name(), duration);
return result;
} catch (Exception e) {
log.error("工具执行失败 – 工具: {}", toolAnnotation.name(), e);
throw e;
}
}
}
2. 监控指标
@Component
public class ToolMetrics {
private final MeterRegistry meterRegistry;
@EventListener
public void handleToolExecution(ToolExecutionEvent event) {
// 记录执行时间
meterRegistry.timer("tool.execution.time",
"tool", event.getToolName(),
"status", event.isSuccess() ? "success" : "error"
).record(event.getExecutionTime(), TimeUnit.MILLISECONDS);
// 记录调用次数
meterRegistry.counter("tool.execution.count",
"tool", event.getToolName()
).increment();
}
}
🎯 总结
@Tool 注解核心参数回顾
工具设计要点
通过合理使用 Tool 和 @Tool 注解,你可以让 AI 模型具备调用外部服务、执行业务逻辑的能力,从而构建出功能强大的 AI Agent 应用。
网硕互联帮助中心



评论前必须登录!
注册