这一节我们将进入工具体系让 AI 从“聊天机器人”进化成“能干事的 Agent”。每次给没用过工具调用的同学演示看到模型自己决定调哪个工具、拿到结果再给出答案大家都会有点惊讶——感觉像在看变魔术。原理其实不复杂这一节我们就把它讲透。一、工具的本质工具的本质很简单你提供一个 Java 方法模型在需要时决定调用它。整个流程大致如下用户问北京今天天气怎么样适合穿什么 ↓ 模型思考我需要知道北京的天气才能回答 ↓ 模型决定调用 getWeather(北京) ↓ Java 执行getWeather(北京) → 晴天18°C风力2级 ↓ 模型再思考有了天气数据可以给出穿衣建议了 ↓ 模型回答今天北京晴天18度建议穿轻薄外套……LangChain4j 通过Tool注解将 Java 方法暴露给模型模型在推理过程中自主决定是否调用、何时调用、调用哪个工具。开发者只需要定义好工具剩下的交给框架。二、Tool 基础用法2.1 定义一个天气工具package com.jichi.langchain4j.tools; import dev.langchain4j.agent.tool.P; import dev.langchain4j.agent.tool.Tool; import org.springframework.stereotype.Component; Component public class WeatherTools { Tool(查询指定城市的实时天气信息包括温度、天气状况和风力) public String getWeather( P(城市名称例如北京、上海、广州) String city) { // 实际项目调用天气 API这里用 Mock 数据演示 return String.format(%s晴天温度18°C风力2级, city); } Tool(查询未来几天的天气预报) public String getWeatherForecast( P(城市名称) String city, P(查询天数1-7之间的整数) int days) { return String.format(%s未来%d天周一晴18°C周二多云15°C……, city, days); } }2.2 定义 AI 服务接口package com.jichi.langchain4j.service; import dev.langchain4j.service.MemoryId; import dev.langchain4j.service.SystemMessage; import dev.langchain4j.service.UserMessage; import dev.langchain4j.service.spring.AiService; AiService public interface WeatherAssistant { SystemMessage( 你是一个天气助手可以查询实时天气和预报。 根据用户需求决定是查实时天气还是预报以及查几天的预报。 ) String chat(MemoryId String sessionId, UserMessage String message); }注意这里使用了MemoryId表示需要为不同会话保存对话记忆。后面会专门讲解记忆系统现在你只需知道它用于区分不同用户。2.3 手动绑定工具并注册 Bean由于我们的接口使用了MemoryId并且需要绑定工具不能再用简单的AiService自动注册而要通过AiServices.builder()手动构建。package com.jichi.langchain4j.config; import com.jichi.langchain4j.service.WeatherAssistant; import com.jichi.langchain4j.tools.WeatherTools; import dev.langchain4j.memory.chat.MessageWindowChatMemory; import dev.langchain4j.model.chat.ChatModel; import dev.langchain4j.service.AiServices; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; Configuration public class WeatherAssistantConfig { Bean Primary public WeatherAssistant weatherAssistantWithTools(ChatModel model, WeatherTools weatherTools) { return AiServices.builder(WeatherAssistant.class) .chatModel(model) .tools(weatherTools) // 绑定工具 .chatMemoryProvider(memoryId - MessageWindowChatMemory.withMaxMessages(10)) .build(); } }2.4 Controller 中使用package com.jichi.langchain4j.controller; import org.springframework.web.bind.annotation.*; RestController RequestMapping(/tool/weather) public class WeatherController { private final WeatherAssistant weatherAssistant; public WeatherController(WeatherAssistant weatherAssistant) { this.weatherAssistant weatherAssistant; } GetMapping public String chat(RequestParam String message, RequestHeader(value X-Session-Id, defaultValue default) String sessionId) { return weatherAssistant.chat(sessionId, message); } }测试# 查询实时天气 curl -H X-Session-Id: session-001 \ http://localhost:8080/tool/weather?message北京今天天气怎么样适合穿什么 # 查询预报 curl -H X-Session-Id: session-001 \ http://localhost:8080/tool/weather?message未来三天呢模型会自动判断是否需要调用工具、调用哪个工具并将工具返回的结果融入最终答案。三、工具描述怎么写非常重要Tool注解中的描述是模型决定是否调用该工具的唯一依据。描述写得好不好直接决定工具调用的准确率。我见过不少项目工具调用频频出错根源都是描述写得太随意。好描述 vs 差描述// ❌ 描述太简单模型不知道什么时候用 Tool(查询订单) public String queryOrder(String orderId) { ... } // ✅ 清晰说明做什么、需要什么、返回什么 Tool(根据订单号查询订单的当前状态和物流信息。 返回内容包括订单状态、下单时间、预计送达时间、当前物流节点。 只能查询当前登录用户自己的订单。) public String queryOrderStatus( P(订单号格式为 ORD 开头的14位字符串例如 ORD20240101001) String orderId) { ... }推荐描述格式Tool[动词][对象][补充说明]。返回[返回内容]。[限制条件如有]。P[参数含义][格式说明如有][取值范围如有]四、参数类型支持Tool方法支持多种参数类型模型会自动根据用户问题解析出对应的参数值。package com.jichi.langchain4j.tools; import dev.langchain4j.agent.tool.P; import dev.langchain4j.agent.tool.Tool; import org.springframework.stereotype.Component; import java.util.List; Component public class OrderQueryTools { // 基础类型 Tool(根据用户ID查询用户信息返回用户名和注册时间) public String getUserById(P(用户ID正整数) long userId) { return String.format({\id\:%d,\username\:\user%d\,\createdAt\:\2024-01-01\}, userId, userId); } // String Tool(根据用户名关键词模糊搜索用户返回匹配的用户名列表) public ListString searchUsers(P(用户名关键词至少2个字符) String keyword) { return List.of(user_ keyword _01, user_ keyword _02); } // 枚举 Tool(查询指定状态的订单列表返回订单ID和金额) public String getOrdersByStatus( P(订单状态PENDING待付款/PAID已付款/SHIPPED已发货/DELIVERED已完成/CANCELLED已取消) OrderStatus status) { return String.format([{\orderId\:\ORD001\,\status\:\%s\,\amount\:299.0}], status); } // 复杂对象模型会自动构造 Tool(查询指定日期范围内的订单统计返回订单数量和总金额) public String getOrderStats( P(日期范围包含 startDate 和 endDate格式 YYYY-MM-DD) DateRange dateRange) { return String.format({\startDate\:\%s\,\endDate\:\%s\,\count\:42,\totalAmount\:12800.0}, dateRange.startDate(), dateRange.endDate()); } } // 枚举定义 public enum OrderStatus { PENDING, PAID, SHIPPED, DELIVERED, CANCELLED } // Record 作为复杂参数 public record DateRange(String startDate, String endDate) {}模型看到getOrderStats需要DateRange对象会尝试从用户问题中提取startDate和endDate并构造该对象。五、工具返回值工具的返回值会作为“Observation”传回给模型模型看到后继续推理或直接回答。5.1 返回 String最常用Tool(查询商品库存状态返回库存描述) public String checkStock(P(商品ID) String productId) { int stock productId.hashCode() % 100; if (stock 0) return 商品已售罄; if (stock 10) return String.format(库存紧张仅剩 %d 件, stock); return String.format(库存充足当前 %d 件, stock); }5.2 返回对象自动序列化成 JSONrecord ProductInfo(String id, String name, double price, int stock) {} Tool(查询商品详细信息包括名称、价格和库存) public ProductInfo getProduct(P(商品ID) String productId) { return new ProductInfo(productId, 示例商品- productId, 99.9, 50); }5.3 void 返回不需要模型读取结果Tool(向用户发送邮件通知执行成功后模型会收到成功确认) public void sendEmail( P(收件人邮箱) String email, P(邮件标题) String subject, P(邮件正文) String body) { // 调用邮件服务 System.out.printf(发送邮件 → %s | 标题%s%n, email, subject); // void 返回模型会收到 Tool executed successfully }六、多工具组合一个 Agent 可以绑定多个工具类模型会根据问题自主选择调用哪个或哪几个工具。下面是一个金融助手的例子它能查股价、查汇率、做计算三者联动。6.1 定义三个工具类// StockTools.java Component public class StockTools { Tool(查询股票实时价格返回股票代码、当前价格和涨跌幅) public String getStockPrice(P(股票代码例如 AAPL、TSLA、000001.SZ) String symbol) { return String.format({\symbol\:\%s\,\price\:168.42,\change\:\1.2%%\}, symbol); } } // CurrencyTools.java Component public class CurrencyTools { Tool(查询两种货币之间的实时汇率) public String getExchangeRate( P(源货币代码例如 USD、EUR、JPY) String from, P(目标货币代码例如 CNY、USD) String to) { return String.format({\from\:\%s\,\to\:\%s\,\rate\:7.24}, from, to); } } // CalculatorTools.java Component public class CalculatorTools { Tool(计算数学表达式支持加减乘除和括号返回计算结果) public String calculate(P(数学表达式例如168.42 * 7.24) String expression) { try { ScriptEngine engine new ScriptEngineManager().getEngineByName(JavaScript); Object result engine.eval(expression); return String.valueOf(result); } catch (Exception e) { return 计算错误 e.getMessage(); } } }6.2 定义 AI 服务接口AiService public interface FinanceAssistant { SystemMessage( 你是一个金融助手可以查询股票价格、汇率并做数学计算。 遇到需要数据的问题先调工具获取数据再给出完整答案。 ) String chat(UserMessage String message); }6.3 注册 Bean同时绑定三个工具Configuration public class FinanceAssistantConfig { Bean Primary public FinanceAssistant financeAssistantWithTools(ChatModel model, StockTools stockTools, CurrencyTools currencyTools, CalculatorTools calculatorTools) { return AiServices.builder(FinanceAssistant.class) .chatModel(model) .tools(stockTools, currencyTools, calculatorTools) .build(); } }6.4 Controller 测试RestController RequestMapping(/tool/finance) public class FinanceController { private final FinanceAssistant financeAssistant; GetMapping public String chat(RequestParam String message) { return financeAssistant.chat(message); } }测试多步推理curl http://localhost:8080/tool/finance?message苹果股票现在多少钱折合人民币是多少模型内部会自动完成三次工具调用调用getStockPrice(AAPL)→ 得到 168.42 USD调用getExchangeRate(USD, CNY)→ 得到 7.24调用calculate(168.42 * 7.24)→ 得到约 1219.36综合三个结果回答“苹果公司股价为 168.42 美元按当前汇率 7.24 换算约合人民币 1219.36 元。”这种多步推理能力正是 Agent 系统的核心魅力。七、对比 Spring AI 的 Tool如果你学过 Spring AI 的工具调用会发现两者思路非常相似语法略有差异。下表总结了主要区别维度Spring AILangChain4j工具注解ToolTool参数描述ToolParam(description ...)P(...)注册方式ChatClient.defaultTools(bean)AiServices.builder().tools(bean)执行循环框架自动处理框架自动处理记忆支持需手动构建ChatMemory内置chatMemoryProvider对于已经熟悉 Spring AI 的同学切换到 LangChain4j 的工具体系几乎零成本估计半小时就能迁移完。八、小结通过Tool注解我们可以将任意 Java 方法暴露给 AI 模型让模型在推理过程中自主决定调用。这带来了几个核心价值实时数据模型不再依赖过时的训练数据可以获取当前天气、股价、库存等信息。业务操作模型可以发送邮件、创建工单、查询数据库真正成为“能干事”的 Agent。多步推理模型可以串联多个工具完成复杂任务如股价换算、订单统计。记住两个关键点工具描述要写清楚模型只有靠描述才能知道你的工具能干什么。善用多工具组合一个 Agent 绑定多个工具才能应对复杂业务场景。