当前位置: 首页 > news >正文

聊聊langchain4j的Tools(Function Calling)

本文主要研究一下langchain4j的Tools(Function Calling)

示例

tool

@Slf4j
public class WeatherTools {

    @Tool("Returns the weather forecast for tomorrow for a given city")
    String getWeather(@P("The city for which the weather forecast should be returned") String city) {
        log.info("getWeather called");
        return "The weather tomorrow in " + city + " is 25°C";
    }

    @Tool("Returns the date for tomorrow")
    LocalDate getTomorrow() {
        log.info("getTomorrow called");
        return LocalDate.now().plusDays(1);
    }

    @Tool("Transforms Celsius degrees into Fahrenheit")
    double celsiusToFahrenheit(@P("The celsius degree to be transformed into fahrenheit") double celsius) {
        log.info("celsiusToFahrenheit called");
        return (celsius * 1.8) + 32;
    }

    String iAmNotATool() {
        log.info("iAmNotATool called");
        return "I am not a method annotated with @Tool";
    }
}

这里用@Tool注解来描述这个方法的用途,用@P注解来描述参数

Low-level

        public static void main(String[] args) {

            // STEP 1: User specify tools and query
            // Tools
            WeatherTools weatherTools = new WeatherTools();
            List<ToolSpecification> toolSpecifications = ToolSpecifications.toolSpecificationsFrom(weatherTools);
            // User query
            List<ChatMessage> chatMessages = new ArrayList<>();
            UserMessage userMessage = userMessage("What will the weather be like in London tomorrow?");
            chatMessages.add(userMessage);
            // Chat request
            ChatRequest chatRequest = ChatRequest.builder()
                    .messages(chatMessages)
                    .parameters(ChatRequestParameters.builder()
                            .toolSpecifications(toolSpecifications)
                            .build())
                    .build();


            // STEP 2: Model generates tool execution request
            ChatResponse chatResponse = openAiModel.chat(chatRequest);
            AiMessage aiMessage = chatResponse.aiMessage();
            List<ToolExecutionRequest> toolExecutionRequests = aiMessage.toolExecutionRequests();
            System.out.println("Out of the " + toolSpecifications.size() + " tools declared in WeatherTools, " + toolExecutionRequests.size() + " will be invoked:");
            toolExecutionRequests.forEach(toolExecutionRequest -> {
                System.out.println("Tool name: " + toolExecutionRequest.name());
                System.out.println("Tool args:" + toolExecutionRequest.arguments());
            });
            chatMessages.add(aiMessage);


            // STEP 3: User executes tool(s) to obtain tool results
            toolExecutionRequests.forEach(toolExecutionRequest -> {
                ToolExecutor toolExecutor = new DefaultToolExecutor(weatherTools, toolExecutionRequest);
                System.out.println("Now let's execute the tool " + toolExecutionRequest.name());
                String result = toolExecutor.execute(toolExecutionRequest, UUID.randomUUID().toString());
                ToolExecutionResultMessage toolExecutionResultMessages = ToolExecutionResultMessage.from(toolExecutionRequest, result);
                chatMessages.add(toolExecutionResultMessages);
            });


            // STEP 4: Model generates final response
            ChatRequest chatRequest2 = ChatRequest.builder()
                    .messages(chatMessages)
                    .parameters(ChatRequestParameters.builder()
                            .toolSpecifications(toolSpecifications)
                            .build())
                    .build();
            ChatResponse finalChatResponse = openAiModel.chat(chatRequest2);
            System.out.println(finalChatResponse.aiMessage().text()); //According to the payment data, the payment status of transaction T1005 is Pending.
        }

Low-level步骤比较多:

  • 1.将toolSpecifications添加到chatRequest的parameter跟userMessage一起发起chat请求
  • 2.接着判断response是否有需要toolExecutionRequests,需要的话将response的aiMessage添加到chatMessages中(包含了userMessage)
  • 3.遍历toolExecutionRequests挨个使用toolExecutor去执行获取toolExecutionResultMessages,添加到chatMessages
  • 4.最后根据汇总的chatMessages及toolSpecifications再发起chat请求得到最终的结果

High-level

非spring环境

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

        String question = "What will the weather be like in London tomorrow?";

        String answer = assistant.chat(question);
        System.out.println(answer);

使用AiServices的话,就设置tools就行了,之后它的实现类自动管理tool的调用,输出如下

2025-03-12T16:54:04.314+08:00 DEBUG 17601 --- [           main] d.l.service.tool.DefaultToolExecutor     : About to execute ToolExecutionRequest { id = null, name = "getWeather", arguments = "{
  "arg0" : "London"
}" } for memoryId default
2025-03-12T16:54:04.330+08:00  INFO 17601 --- [           main] c.example.langchain4j.tool.WeatherTools  : getWeather called
2025-03-12T16:54:04.330+08:00 DEBUG 17601 --- [           main] d.l.service.tool.DefaultToolExecutor     : Tool execution result: The weather tomorrow in London is 25°C
<think>
Okay, so the user asked about the weather in London tomorrow. I first needed to figure out which tool to use. The available tools are getTomorrow, celsiusToFahrenheit, and getWeather. Since the user specifically mentioned the weather for a city (London), the getWeather function is the right choice.

The getWeather function requires a city name as an argument, so I called it with "London" as the parameter. The response from the tool came back saying the temperature is 25°C. Now, the user's question was in English, and they might prefer the temperature in Fahrenheit since that's commonly used in some countries. But wait, do I need to convert it? Let me check the tools again. There's a celsiusToFahrenheit function available.

Even though the user didn't explicitly ask for Fahrenheit, providing both might be helpful. However, maybe I should just present the information as received and offer to convert if needed. Alternatively, since the tool response is in Celsius, perhaps converting it would make the answer more comprehensive. Let me calculate that: 25°C converted to Fahrenheit is (25 * 9/5) + 32 = 77°F. 

So I'll state the weather in both units to be thorough. Also, confirming the date using getTomorrow could add clarity, but since the user already specified "tomorrow" and the tool's response mentions tomorrow, maybe that's redundant. But just to make sure, calling getTomorrow would ensure the correct date is mentioned. Wait, the getWeather function already provided the temperature for tomorrow, so the date part is covered.

Putting it all together: inform the user about the 25°C (77°F) weather in London tomorrow. That should answer their query fully and anticipate their possible follow-up about Fahrenheit.
</think>

The weather in London tomorrow will be 25°C, which is equivalent to 77°F.

spring环境

对于springboot的话,还可以让标注@Tool方法的类托管给spring就行,之后都自动帮你配置好:

@Component
public class WeatherToolsV2 {

    @Tool("Returns the weather forecast for tomorrow for a given city")
    String getWeather(@P("The city for which the weather forecast should be returned") String city) {
        log.info("getWeather called");
        return "The weather tomorrow in " + city + " is 25°C";
    }

    @Tool("Returns the date for tomorrow")
    LocalDate getTomorrow() {
        log.info("getTomorrow called");
        return LocalDate.now().plusDays(1);
    }

    @Tool("Transforms Celsius degrees into Fahrenheit")
    double celsiusToFahrenheit(@P("The celsius degree to be transformed into fahrenheit") double celsius) {
        log.info("celsiusToFahrenheit called");
        return (celsius * 1.8) + 32;
    }

    String iAmNotATool() {
        log.info("iAmNotATool called");
        return "I am not a method annotated with @Tool";
    }
}

对于AiServices也是,托管给spring就行

@AiService
public interface AssistantV2 {

    @SystemMessage("You are a polite assistant")
    String chat(String userMessage);
}

注意事项

  • 对于tool的描述要清晰,避免模糊
  • 需要描述清楚该tool是做什么的,什么场景下使用
  • 需要描述请求每个参数的含义

一个简单的原则就是:如果开发者能通过描述文档准确理解tool的用途和使用方法,那么经过适当训练的LLM同样可以完成有效调用

小结

langchain4j针对Tools(Function Calling)提供了Low-level及High-level两层抽象。Low-level是ChatLanguageModel及ToolSpecification APIs,High-level是AI Services及@Tool注解。High-level的方式节省了很多代码,基于spring的High-level方式更为简单,只需要把标注@Tool方法的类托管给spring就行。

doc

  • tools
  • language-models

相关文章:

  • mybatis集合映射association与collection
  • 常用的遍历方法用途和运用
  • QT学习笔记1
  • 【在数轴上找最优位置,使移动距离最短】
  • 【区块链 + 商贸零售】商小萌小程序 | FISCO BCOS 应用案例
  • uniapp路由跳转导致页面堆积问题
  • 51单片机和STM32 入门分析
  • RSA后台解密报错:javax.crypto.BadPaddingException: Message is larger than modulus
  • 4.1--入门知识扫盲,ISO知识体系介绍(看一遍,协议啥的全部记住)
  • Android Zygote的进程机制
  • nginx配置txt文件点击链接后下载
  • 【ES6新特性】默认参数常见用法
  • (C语言)斐波那契数列(递归求解)
  • uniapp-x vue 特性
  • 通过 API 将Deepseek响应流式内容输出到前端
  • 论文精度:Transformers without Normalization
  • 提示词模板
  • KNN算法性能优化技巧与实战案例
  • vuex持久化存储,手动保存到localStorage,退出登录时清空vuex及localStorage
  • 【数据库】掌握MySQL事务与锁机制-数据一致性的关键
  • 陕西三原高新区违法占用土地,被自然资源局罚款10万元
  • 浙江省台州市政协原副主席林虹被“双开”
  • 互降关税后,从中国至美国的集装箱运输预订量飙升近300%
  • 小耳朵等来了春天:公益义诊筛查专家走进安徽安庆
  • “一百零一个愿望——汉字艺术展”亮相意大利威尼斯
  • 秘鲁总理辞职