LangChain4j Tool(Function Call)

0 阅读3分钟

工具(函数调用)

有一种被称为“工具”或“函数调用”的概念。它允许 LLM 在必要的时候调用一个或多个工具,这些工具通常由开发者定义。

工具可以是任何功能,如:网络搜索、外部 API 调用、执行特定代码片段等。

LLM 本身不能直接调用工具,相反,它们会在响应中表达调用特定工具的意图(而不是用纯文本回复)。作为开发者,我们需要使用提供的参数执行这个工具,然后将工具执行的结果反馈给模型。

LangChain4j 为此提供了两种级别的使用方式:

  • Low-level 使用 ChatModelToolSpecification API
  • High-level 使用 AI Service@Tool 的方法

创建工具的规范

手动创建

// 工具声明
ToolSpecification toolSpecification = ToolSpecification.builder()
    .name("getWeather")
    .description("Returns the weather forecast for a given city")
    .parameters(JsonObjectSchema.builder()
        .addStringProperty("city", "The city for which the weather forecast should be returned")
        .addEnumProperty("temperatureUnit", List.of("CELSIUS", "FAHRENHEIT"))
        .required("city") // the required properties should be specified explicitly
        .build())
    .build();
// 工具业务逻辑
ToolExecutor toolExecutor = (req, memoryId) -> {
    String[] elf = { "皮卡丘", "妙蛙种子", "喷火龙", "喵喵" };
    int i = (int) (Math.random() * 100) % elf.length;
    return req.name() + " · " + elf[i];
};

JsonObjectSchema 的更多信息:docs.langchain4j.dev/tutorials/s…

使用类创建

class WeatherTools { 
    @Tool("Returns the weather forecast for a given city")
    String getWeather(@P("The city for which the weather forecast should be returned") String city,
                      @P(value = "Unit of temperature", required = false) Unit unit,
            			TemperatureUnit temperatureUnit) {
        ...
    }
}
// List<ToolSpecification> toolSpecifications = ToolSpecifications.toolSpecificationsFrom(WeatherTools.class);

@Tool 注解的方法:

  • 可以是静态的或非静态的
  • 可见性可以是任意的,如 public、private 都可以

@Tool 注解的方法可以接受任意数量和类型的参数:

  • 基本类型、对象类型、POJO、枚举、List 和 Set集合
  • Map 集合(需要再参数描述中手动指定 K 和 V 的类型,使用 @P 注解)
  • 无参

@P

  • 参数描述

返回值类型:

  • void 如果返回值是 void,方法执行成功时,会将 "Success" 字符串发送给 LLM。
  • String 返回值将原样发送给 LLM,不进行任何转换。
  • other 返回其他类型,返回值在发送给 LLM 之前会转换为 JSON 字符串。

Low-level

一旦你拥有 List<ToolSpecification> ,就可以调用模型:

ChatRequest request = ChatRequest.builder()
    .messages(UserMessage.from("What will the weather be like in London tomorrow?"))
    .toolSpecifications(toolSpecifications) // 传入工具
    .build();
ChatResponse response = model.chat(request);
AiMessage aiMessage = response.aiMessage();

如果 LLM 决定调用工具,返回的 AiMessage 将包含 toolExecutionRequests 字段中的数据。在这种情况下, AiMessage.hasToolExecutionRequests() 将返回 true 。根据 LLM 的不同,它可以包含一个或多个 ToolExecutionRequest 对象(一些 LLM 支持并行调用多个工具)。

toolExecutionRequests

  • id 根据调用的 ID。注意:某些 LLM 提供商(如 Google、Ollama)可能会省略此ID
  • name 根据将要调用的工具名称。
  • arguments 工具参数

你需要使用 ToolExecutionRequest 中的信息手动执行工具。

如果你想将工具执行的结果发送回 LLM,你需要创建一个 ToolExecutionResultMessage (每个 ToolExecutionRequest 对应一个),并将其与所有之前的消息一起发送:

String result = "It is expected to rain in London tomorrow.";
ToolExecutionResultMessage toolExecutionResultMessage = ToolExecutionResultMessage.from(toolExecutionRequest, result);
ChatRequest request2 = ChatRequest.builder()
        .messages(List.of(userMessage, aiMessage, toolExecutionResultMessage))
        .toolSpecifications(toolSpecifications)
        .build();
ChatResponse response2 = model.chat(request2);

High-level API

@Bean("elfToolAssistantV1")
    public Assistant elfToolAssistantV1(StreamingChatModel streamingChatModel) {
    // 工具说明
    ToolSpecification toolSpecification = ToolSpecification.builder()
            .name("精灵匹配助手")
            .description("根据姓名和爱好匹配精灵")
            .parameters(
                    JsonObjectSchema.builder()
                            .addStringProperty("name", "姓名")
                            .addStringProperty("hobby", "爱好")
                            .build()
            )
            .build();

    // 工具业务逻辑
    ToolExecutor toolExecutor = (req, memoryId) -> {
        String[] elf = { "皮卡丘", "妙蛙种子", "喷火龙", "喵喵" };
        int i = (int) (Math.random() * 100) % elf.length;
        return req.name() + " · " + elf[i];
    };

    return AiServices.builder(Assistant.class)
            .streamingChatModel(streamingChatModel)
            .tools(Map.of(toolSpecification, toolExecutor))
            .build();
}

High-level API

定义类

@Component
public class ElfMatchingTool {
    private String[] elf = { "皮卡丘", "妙蛙种子", "喷火龙", "喵喵" };

    @Tool("根据姓名和爱好匹配精灵")
    public String getElf(@P("姓名") String name, @P("爱好") String hobby) {
        return matching(name, hobby);
    }
    
    @Tool("查看有一共多少只精灵")
    public String getAllElfDesc() {
        return Arrays.toString(elf);
    }

    public String matching(String name, String hobby) {
        int i = (int) (Math.random() * 100) % elf.length;
        return elf[i];
    }
}

配置

@Bean("elfToolAssistantV2")
public Assistant elfToolAssistantV2(StreamingChatModel streamingChatModel, ElfMatchingTool elfMatchingTool) {
    return AiServices.builder(Assistant.class)
            .streamingChatModel(streamingChatModel)
            .tools(elfMatchingTool)
            .build();
}

image-20260213163123549