首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >langchain4j中的工具链

langchain4j中的工具链

作者头像
江南一点雨
发布2026-03-26 17:14:40
发布2026-03-26 17:14:40
1890
举报
文章被收录于专栏:AI应用开发实践AI应用开发实践

咱们继续来聊 langchain4j。

最近松哥和大伙聊的基本上都是大模型应用开发的入门知识,松哥这边后面会开始做集团的知识库,到时候再和大家分享一些项目中的实践经验。

一 Function Calling

大语言模型(LLM)除了生成文本,还可以通过工具调用触发特定操作。该机制允许开发者为模型定义功能函数(如数学计算、API 调用等),当模型识别用户需求需要外部工具辅助时,会在响应中表达调用意图,开发者根据此意图执行工具并将结果返回给模型。

特别是在一些涉及到数学运算的场景,大家知道大模型并不擅长于此,如果能够直接调用我们自己的 API,就会方便很多。或者是一些涉及到内部 API 调用的场景,通过 Function Calling 就可以非常方便的实现。

1.1 三要素

  • 名称:明确的功能标识(如squareRoot
  • 描述:说明功能及适用场景(如"计算数的平方根")
  • 参数说明:每个参数的语义定义(如x表示待计算数值)

1.2 执行流程

通过工具调用机制,大模型突破了自身在实时性、准确性和功能扩展性方面的限制。

开发者需要重点掌握工具定义规范、执行流程编排和错误处理策略,结合 ReAct 等框架实现更智能的 AI 应用。最新技术如 LLMCompiler 的并行调用优化,以及 OpenManus 的任务分解机制,正在推动该领域向更高阶的自主智能演进。

二 两种方案

LangChain4j 中对于工具的调用提供了两种不同的抽象层次。

2.1 Low-Level

Low-Level 直接使用 ChatLanguageModelToolSpecification API 来实现。 Low-Level 具有如下一些特点:

  1. 完全控制:开发者需手动定义工具规格(ToolSpecification),包括工具名称、描述、参数结构(JSON Schema)等。
  2. 灵活性强:适用于复杂场景,如第三方 API 集成或需要动态配置参数。
  3. 开发成本高:需编写大量胶水代码处理消息构建、工具执行及结果反馈。

适用场景:

  • 需要精细控制工具逻辑(如参数校验、错误处理)
  • 集成非标准接口的外部服务

2.2 High-Level

High-Level 是通过 AI Services@Tool 注解的 Java 方法 High-Level 具有如下一些特点:

  1. 声明式开发:使用 @Tool 注解自动生成工具规格,无需手动定义。
  2. 自动化流程:框架自动处理工具调用、结果注入和多轮对话管理。
  3. 代码简洁:隐藏底层复杂度,类似 Spring Data JPA 的 Repository 接口。

适用场景:

  • 快速构建标准化工具(如内部业务 API)
  • 需要与 Spring Boot 集成的项目

接下来我会逐一和大家介绍这两种方案。

三 Low-Level

下面我们通过一个完整的天气查询案例,展示如何在 LangChain4j 中手动构造ToolSpecification并实现工具调用流程。

3.1 定义工具规格

3.1.1 手动定义

代码语言:javascript
复制
// 步骤1:手动构建天气查询工具规格
ToolSpecification weatherTool = ToolSpecification.builder()
    .name("getWeather")
    .description("返回指定城市明日天气预报,当用户询问未来24小时天气时调用")
    .parameters(JsonObjectSchema.builder()
        .addStringProperty("city", "需要查询的城市名称,如北京、London")
        .addEnumProperty("unit", List.of("CELSIUS", "FAHRENHEIT"), "温度单位,默认使用CELSIUS")
        .required("city")  // 显式声明必填参数
        .build())
    .build();

List<ToolSpecification> toolSpecifications = List.of(weatherTool);

关键设计原则:

  1. 命名规范:getWeather采用动词+名词结构,明确功能定位
  2. 描述清晰:包含触发条件("当用户询问未来 24 小时天气时调用")
  3. 参数约束:
    1. 城市参数添加示例值增强可理解性
    2. 温度单位使用枚举类型约束取值范围
    3. 通过 required() 显式标记必填参数

3.1.2 注解定义

也可以利用注解工具自动定义这个规格,如下:

代码语言:javascript
复制
class WeatherTools {

    @Tool("返回指定城市明日天气预报,当用户询问未来24小时天气时调用")
    String getWeather(@P("需要查询的城市名称,如北京、London") String city, String temperatureUnit) {
        return "2025-03-16深圳天气:多云转晴,气温18-25°C,湿度65%";
    }
}
List<ToolSpecification> toolSpecifications = ToolSpecifications.toolSpecificationsFrom(WeatherTools.class);

3.2 完整交互

步骤1:初始请求构建
代码语言:javascript
复制
// 步骤1:构造初始聊天请求
ChatRequest request = ChatRequest.builder()
    .messages(UserMessage.from("2025年3月16日深圳的天气如何?"))
    .toolSpecifications(toolSpecifications)
    .build();

// 发送请求并获取响应
ChatResponse response = model.chat(request);
AiMessage aiMessage = response.aiMessage();

此时若模型判断需要调用工具,aiMessage.toolExecutionRequests()将包含:

代码语言:javascript
复制
{
  "name": "getWeather",
  "arguments": {"city": "深圳", "unit": "CELSIUS"}
}
步骤2:工具执行与结果反馈
代码语言:javascript
复制
// 步骤2:执行工具逻辑(模拟实现)
if (aiMessage.hasToolExecutionRequests()) {
    ToolExecutionRequest request = aiMessage.toolExecutionRequests().get(0);
    Map<String, Object> args = parseJson(request.arguments());
    
    // 模拟工具执行(实际应调用外部API)
    String result = "2025-03-16深圳天气:多云转晴,气温18-25°C,湿度65%";
    
    // 构造结果消息
    ToolExecutionResultMessage resultMsg = ToolExecutionResultMessage.from(request, result);
    
    // 构建二次请求
    ChatRequest followupRequest = ChatRequest.builder()
        .messages(List.of(
            UserMessage.from("2025年3月16日深圳的天气如何?"),
            aiMessage,
            resultMsg
        ))
        .toolSpecifications(toolSpecifications)
        .build();
    
    // 获取最终响应
    ChatResponse finalResponse = model.chat(followupRequest);
    System.out.println(finalResponse.aiMessage().text()); 
    // 输出:"2025年3月16日深圳将有多云转晴天气,温度范围18-25摄氏度"
}

3.3 技术要点

3.3.1 参数结构

代码语言:javascript
复制
JsonObjectSchema.builder()
    .addStringProperty("city", "城市中文或英文名称,如'New York'需转换为'纽约'")
    .addEnumProperty("unit", List.of("CELSIUS", "FAHRENHEIT"), 
        "单位转换需求,如用户特别说明华氏度时使用")
    .build()
  • 类型校验:通过addStringProperty/addEnumProperty确保参数合法性
  • 语义增强:在描述中补充数据转换规则(如英文城市名转中文)

3.3.2 异常处理机制

代码语言:javascript
复制
try {
    // 工具执行逻辑
} catch (InvalidCityException e) {
    return ToolExecutionResultMessage.error(request, "城市名称无效: " + e.getMessage());
} catch (ApiTimeoutException e) {
    return ToolExecutionResultMessage.error(request, "天气接口响应超时");
}
  • 错误码规范:定义业务异常类型辅助模型理解
  • 错误信息结构化:通过error()方法返回标准错误格式

3.3.3 多工具协作模式

代码语言:javascript
复制
// 添加多个工具规格
ToolSpecification airQualityTool = ToolSpecification.builder()
    .name("getAirQuality")
    .description("获取城市空气质量指数,当用户询问 PM2.5 或空气质量时调用")
    .parameters(/* 参数定义 */)
    .build();

List<ToolSpecification> tools = List.of(weatherTool, airQualityTool);
  • 工具组合策略:天气与空气质量工具形成互补
  • 调用优先级:通过描述中的触发条件引导模型选择

四 High-Level

4.1 核心机制

@Tool 注解驱动开发,主要是通过 Java 注解自动生成工具规格,无需手动定义 JSON Schema。

代码语言:javascript
复制
@Tool("计算两个数之和")
public double add(@P("第一个数") int a, 
                  @P(value = "第二个数", required = false) Integer b) {
    return a + (b != null ? b : 0);
}

自动生成效果

代码语言:javascript
复制
{
  "name": "add",
  "description": "计算两个数之和",
  "parameters": {
    "type": "object",
    "properties": {
      "a": {"type": "integer", "description": "第一个数"},
      "b": {"type": "integer", "description": "第二个数"}
    },
    "required": ["a"]
  }
}

4.2 交互时序

4.3 代码实践

来看个具体的代码案例吧,也是官方给的案例:

代码语言:javascript
复制
public class CalculatorDemo01 {
    staticclass Calculator {

        @Tool("计算字符串的长度")
        int stringLength(String s) {
            System.out.println("计算字符串的长度 s='" + s + "'");
            return s.length();
        }

        @Tool("计算两个数的和")
        int add(int a, int b) {
            System.out.println("计算两个数的和 a=" + a + ", b=" + b);
            return a + b;
        }

        @Tool("计算一个数的平方根")
        double sqrt(int x) {
            System.out.println("计算一个数的平方根 x=" + x);
            return Math.sqrt(x);
        }
    }

    public static void main(String[] args) {

        ChatLanguageModel model = ZhipuAiChatModel.builder()
                .apiKey(API_KEY)
                .model("glm-4")
                .temperature(0.6)
                .maxToken(1024)
                .maxRetries(1)
                .callTimeout(Duration.ofSeconds(60))
                .connectTimeout(Duration.ofSeconds(60))
                .writeTimeout(Duration.ofSeconds(60))
                .readTimeout(Duration.ofSeconds(60))
                .build();

        Assistant assistant = AiServices.builder(Assistant.class)
                .chatLanguageModel(model)
                .tools(new Calculator())
                .chatMemory(MessageWindowChatMemory.withMaxMessages(10))
                .build();

        String question = "字符串 \"hello\" 和 \"world\" 长度总和的平方根是多少?";

        String answer = assistant.chat(question);

        System.out.println(answer);
    }
}

来看下执行结果:

上面代码比较简单,就不啰嗦解释了。

注意,在 @Tool 注解中,包含触发条件和数据来源。

4.4 注意事项

4.4.1 参数类型支持矩阵

参数类型

支持情况

示例

基本类型

完全支持(int, double)

void log(String message)

集合类型

List/Set/Map

List<User> findUsers(Query)

嵌套POJO

支持递归结构

Order create(OrderRequest)

枚举

自动识别取值范围

Status update(Status status)

复杂参数处理:

代码语言:javascript
复制
@Description("用户查询条件")
class Query {
    @Description("筛选状态:ACTIVE/INACTIVE")
    Status status;
    
    @Description("返回结果数量限制")
    @P(required = false) 
    Integer limit;
}

@Tool("查找用户")
List<User> findUsers(Query query) { ... }

4.4.2 动态工具配置

根据上下文动态加载工具。举个例子。

下面是一个使用 ToolProvider 实现动态工具加载的完整示例,该示例模拟酒店预订场景,根据用户消息内容动态选择工具:

场景描述
  • 当用户消息包含 "预定" 关键词时,动态加载 预定查询工具
  • 当用户消息包含 "天气" 关键词时,动态加载 天气查询工具
  • 其他情况不加载任何工具
工具定义
代码语言:javascript
复制
// 预定查询工具(静态定义)
publicclass BookingTools {

    @Tool("根据预定号查询预定详情,预定号格式为B-后接5位数字")
    public String getBookingDetails(
            @P("预定号,例如B-12345") String bookingNumber) {
        // 模拟数据库查询
        return"预定号: " + bookingNumber + ", 状态: 已确认, 房型: 豪华套房";
    }
}
publicclass WeatherTools {

    @Tool("返回指定城市明日天气预报,当用户询问未来24小时天气时调用")
    String getWeather(@P("需要查询的城市名称,如北京、London") String city, String temperatureUnit) {
        return"2025-03-16深圳天气:多云转晴,气温18-25°C,湿度65%";
    }

    public static ToolExecutor getWeatherExecutor() {
        return (request, memoryId) -> {
            return"2023-10-15  天气: 晴, 温度20-25°C ";
        };
    }
}
动态工具提供者
代码语言:javascript
复制
public class DynamicToolProvider implements ToolProvider {

    @Override
    public ToolProviderResult provideTools(ToolProviderRequest request) {
        String userMessage = request.userMessage().singleText().toLowerCase();

        Map<ToolSpecification, ToolExecutor> tools = new HashMap<>();

        // 预定相关工具
        if (userMessage.contains("预定")) {
            // 通过反射获取@Tool注解的方法规格
            ToolSpecification bookingSpec = null;
            try {
                bookingSpec = ToolSpecifications.toolSpecificationFrom(
                        BookingTools.class.getDeclaredMethod("getBookingDetails", String.class)
                );
            } catch (NoSuchMethodException e) {
                thrownew RuntimeException(e);
            }

            // 方法引用执行器
            ToolExecutor bookingExecutor = (req, memId) -> {
                return"bookingExecutor";
            };

            tools.put(bookingSpec, bookingExecutor);
        }

        // 天气相关工具
        if (userMessage.contains("天气")) {
            tools.put(
                    ToolSpecifications.toolSpecificationsFrom(WeatherTools.class).get(0),
                    WeatherTools.getWeatherExecutor()
            );
        }

        return ToolProviderResult.builder()
                .addAll(tools)
                .build();
    }
}
AI 服务配置与使用
代码语言:javascript
复制
public class Demo01 {
    // 1. 定义AI服务接口
    interface TravelAssistant {
        Result<String> handleRequest(String userMessage);
    }

    // 2. 配置AI服务
    TravelAssistant assistant = AiServices.builder(TravelAssistant.class)
            .chatLanguageModel(createOpenAiModel()) // 创建模型实例
            .toolProvider(new DynamicToolProvider())
            .build();

    private ChatLanguageModel createOpenAiModel() {
        ChatLanguageModel chatModel = ZhipuAiChatModel.builder()
                .apiKey(API_KEY)
                .model("glm-4")
                .temperature(0.6)
                .maxToken(1024)
                .maxRetries(1)
                .callTimeout(Duration.ofSeconds(60))
                .connectTimeout(Duration.ofSeconds(60))
                .writeTimeout(Duration.ofSeconds(60))
                .readTimeout(Duration.ofSeconds(60))
                .build();
        return chatModel;
    }

    // 3. 测试不同场景
    public static void main(String[] args) {
        Demo01 demo01 = new Demo01();
        // 案例1: 预定查询
        demo01.testRequest("我的预定号B-12345的状态是什么?");

        // 案例2: 天气查询
        demo01.testRequest("今天巴黎的天气如何?");

        // 案例3: 普通咨询
        demo01.testRequest("酒店的早餐时间是几点?");
    }

    private void testRequest(String message) {
        Result<String> result = assistant.handleRequest(message);
        System.out.println("用户问题: " + message);
        System.out.println("最终回答: " + result.content());
        result.toolExecutions().forEach(exec ->
                System.out.println("调用工具: " + exec.request().name() + ", 参数: " + exec.request().arguments())
        );
        System.out.println("-------------------");
    }
}
执行结果输出
代码语言:javascript
复制
用户问题: 我的预定号B-12345的状态是什么?
最终回答: 根据查询结果,您的预定号B-12345的状态是bookingExecutor。如果您需要更详细的预定详情,请提供更多信息,我将尽力帮助您。
调用工具: getBookingDetails, 参数: {"arg0":"B-12345"}
-------------------
用户问题: 今天巴黎的天气如何?
最终回答: 根据我获取的信息,巴黎今天的天气是晴天,温度在20-25°C之间。
调用工具: getWeather, 参数: {"arg0":"Paris","arg1":"today"}
-------------------
用户问题: 酒店的早餐时间是几点?
最终回答: 酒店的早餐时间通常根据酒店的规定而有所不同,但一般而言,大多数酒店的早餐时间可能在以下范围内:

- 早上6:00至上午10:00
- 早上7:00至上午11:00

有些酒店可能提供更早或更晚的早餐服务,以适应不同客人的需求。如果您正在计划前往某家酒店并想了解具体的早餐时间,建议您直接联系酒店的前台或查看官方网站上的信息,以获取最准确的时间安排。
-------------------

4.4.3 执行结果追踪

获取工具执行记录:

代码语言:javascript
复制
Result<String> result = assistant.chat("查看订单123状态");
List<ToolExecution> executions = result.toolExecutions();

executions.forEach(exec -> {
    System.out.println("调用工具: " + exec.request().name());
    System.out.println("参数: " + exec.request().arguments());
    System.out.println("结果: " + exec.result());
});

4.4.4 异常处理

结构化错误反馈:

代码语言:javascript
复制
@Tool
String getStockPrice(String symbol) {
    try {
        return apiClient.fetchPrice(symbol);
    } catch (InvalidSymbolException e) {
        throw new ToolExecutionException("STOCK_SYMBOL_INVALID", e.getMessage());
    }
}

错误类型定义:

代码语言:javascript
复制
class ToolExecutionException extends RuntimeException {
    private String errorCode;
    
    public ToolExecutionException(String code, String message) {
        super(message);
        this.errorCode = code;
    }
    
    // Getters
}

4.4.5 性能优化

缓存机制

代码语言:javascript
复制
@Tool("查询城市信息")
@Cacheable(value = "cityCache", key = "#cityName")
public CityInfo getCityInfo(String cityName) { ... }

批量处理

代码语言:javascript
复制
@Tool("批量查询温度")
public Map<String, Double> batchGetTemperatures(List<String> cities) { ... }

4.5 VS REST API

维度

传统REST API

LangChain4j工具API

接口定义

Swagger/OpenAPI

Java注解自动生成

参数校验

手动实现

自动类型转换+JSON Schema

调用方式

显式HTTP调用

LLM动态决策调用

错误处理

HTTP状态码

结构化异常消息反馈

协议耦合

强依赖HTTP

与协议解耦

通过高阶API,开发者可以快速将现有业务能力转化为大语言模型可用的工具,显著提升开发效率。结合动态工具配置和结果追踪功能,能够构建出高度智能化的对话系统。最新实践显示,采用该模式可使工具集成开发时间缩短约 60%。

五 方案对比

维度

低阶API

高阶API

灵活性

高(完全自定义)

中(依赖框架自动生成逻辑)

开发效率

低(需手动处理所有细节)

高(注解驱动,减少重复代码)

适用阶段

探索性开发、复杂集成

成熟业务场景、标准化工具

学习成本

高(需掌握 JSON Schema 等细节)

低(通过注解简化)

六 最佳实践

  • 优先使用高阶API快速验证功能
  • 需深度定制时切换至低阶API
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-05-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 江南一点雨 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一 Function Calling
    • 1.1 三要素
    • 1.2 执行流程
  • 二 两种方案
    • 2.1 Low-Level
    • 2.2 High-Level
  • 三 Low-Level
    • 3.1 定义工具规格
      • 3.1.1 手动定义
      • 3.1.2 注解定义
    • 3.2 完整交互
    • 3.3 技术要点
      • 3.3.1 参数结构
      • 3.3.2 异常处理机制
      • 3.3.3 多工具协作模式
  • 四 High-Level
    • 4.1 核心机制
    • 4.2 交互时序
    • 4.3 代码实践
    • 4.4 注意事项
      • 4.4.1 参数类型支持矩阵
      • 4.4.2 动态工具配置
      • 4.4.3 执行结果追踪
      • 4.4.4 异常处理
      • 4.4.5 性能优化
    • 4.5 VS REST API
  • 五 方案对比
  • 六 最佳实践
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档