LangChain4j的Tools

参考文档和资源

LangChain4j官方文档

Tools (Function Calling) | LangChain4j

YouTuBe介绍视频

https://www.youtube.com/watch?v=cjI_6Siry-s&t=1s

工具的概念

工具可以是任何东西,比如网络搜索、调用外部 API 或执行特定代码片段。

使用工具的原因

因为LLM对于一些复杂的任务和数学计算能力薄弱。所以要自己创建一些工具来弥补LLM对于这些薄弱点的处理能力。

LLM如何使用工具

  1. LLM在接收到请求后,根据需求自己判断是否使用Tools。
  2. 如果决定使用,就会生成工具调用请求(但是LLM没有权利调用Tools,只是告诉开发者"我决定调用工具,不去执行AI搜索了"),开发者负责接收此请求,在本地或指定服务中执行对应的工具方法,开发者手动执行工具并生成执行结果返回LLM。
  3. LLM仅仅根据Tools返回的结果生成最终结果(默认情况下不执行本地或者远端搜索),将最终结果返回。(可以理解为AI润色一下返回结果)。
  • 我的理解:你相当于一个项目小组长,你给Model分配了一个任务。结果Model分析完任务后跟你说,我不适合做这个任务,Tools擅长干这个任务,你找他干这活,我不干

官网的使用举例:如果我们给它提供了例如 googleSearchsendEmail 工具, 以及一个查询,如"我的朋友想知道 AI 领域的最新消息。将简短摘要发送到 friend@email.com", 那么它可以使用 googleSearch 工具查找最新消息, 然后总结并通过 sendEmail 工具发送摘要。

声明工具的注意事项

  • 显式提供工具名称。
  • 提供好清晰尽量简短的工具作用描述。
  • 明确工具参数。
  • 并不是所有模型都支持工具调用,详见官网文档https://docs.langchain4j.dev/integrations/language-models/

下面我们结合代码理解工具的实现

低级实现

使用ChatLanguageModel的chat(ChatRequest)方法和ToolSpecification的API。

  • 注意:默认情况下,低级实现在选择调用工具后不会返回LLM,如果要返回需要手动实现,只有高级实现才会自动返回LLM。

创建工具

  • 创建工具类,使用@Tool声明工具方法。
public class DashScopeTools {
    @Tool("根据城市名称查询天气")
    public static String getWeather(@P("城市名称") String city) {
        //具体实现可以调用查询天气的API
        Map<String, String> weatherData = Map.of(
            "北京", "晴,25℃",
            "上海", "多云,28℃",
            "广州", "阵雨,30℃"
        );
        return weatherData.getOrDefault(city, "未知城市");
    }

    @Tool("计算两个数的乘积")
    public static double multiply(
        @P("第一个乘数") double a,
        @P("第二个乘数") double b
    ) {
        return a * b;
    }
}

以第一个工具为例:

  • 工具参数:@P(“城市名称”) String city 。
  • 工具名称:getWeather。
  • 工具描述:@Tool(“根据城市名称查询天气”)。

创建聊天模型

  ChatLanguageModel model = OpenAiChatModel.builder()
            .apiKey("your-openai-key")
            .modelName("gpt-3.5-turbo")
            .temperature(0.3)
            .build();

创建工具规范

  • 会自动识别出@Tool的工具方法并加载。
 List<ToolSpecification> toolSpecs = ToolSpecifications.toolSpecificationsFrom(CustomTools.class);

其他识别@Tool的方法。

  • ToolSpecifications.toolSpecificationsFrom(Class)
  • ToolSpecifications.toolSpecificationsFrom(Object)
  • ToolSpecifications.toolSpecificationFrom(Method)

调用模型

	// 用户提问
    UserMessage userMessage = UserMessage.from("计算3.5和7.2的和,并告诉我当前时间。");

    // 调用模型,传入工具规范
	//实现一:将工具绑定到模型
    Response<AiMessage> response = model.generate(
        singletonList(userMessage),
        toolSpecs
    );

	//实现二:将工具绑定到聊天请求(官网示例)
	ChatRequest request = ChatRequest.builder()
    .messages(UserMessage.from("明天伦敦的天气会怎样?"))
    .toolSpecifications(toolSpecifications)
    .build();
    ChatResponse response = model.chat(request);
    AiMessage aiMessage = response.aiMessage();
	//如果 LLM 决定调用工具,返回的 AiMessage 将在 toolExecutionRequests 字段中包含数据。 在这种情况下,			AiMessage.hasToolExecutionRequests() 将返回 true。 根据 LLM 的不同,它可以包含一个或多个 ToolExecutionRequest 对象 (一些 LLM 支持并行调用多个工具)。

模型就会根据内容自行判断是否调用工具。

  • 如果 LLM 决定调用工具,返回的 AiMessage 将在 toolExecutionRequests 字段中包含数据。 在这种情况下,AiMessage.hasToolExecutionRequests() 将返回 true
  • toolExecutionRequest中包含工具调用的ID(某些LLM不支持),工具名称,调用工具的参数。

如果需要再次调用大模型

  • 每个ToolExecutionResult对应一个ToolExecutionResultMessage
String result = "Tools的生成结果";
//将传入数据转化为ToolExecutionResultMessage
ToolExecutionResultMessage toolExecutionResultMessage = ToolExecutionResultMessage.from(toolExecutionRequest, result);
//构建大模型请求
ChatRequest request2 = ChatRequest.builder()
        .messages(List.of(userMessage, aiMessage, toolExecutionResultMessage))
        .toolSpecifications(toolSpecifications)
        .build();
//传入大模型
ChatResponse response2 = model.chat(request2);
  • userMessage:提供原始问题,方便模型理解上下文。
  • AiMessage:包含工具调用请求,告知模型当前处于工具调用中(上一段代码的AiMessage)。
  • ToolExecutionResultMessage:向模型传递工具执行结果,作为最终回答的依据。

高级实现

工具方法限制

  • 只要带有@Tool,无论静态非静态,公共私有。

工具方法参数

  • 默认情况下,工具方法中的参数都是必须的(不能为空)

  • 基本类型:intdouble 等。

  • 对象类型:StringIntegerDouble 等。

  • 自定义 POJO(可以包含嵌套 POJO)。

  • enum(枚举)。

  • List<T>/Set<T>,其中 T 是上述类型之一。

  • Map<K,V>(您需要在参数描述中使用 @P 手动指定 KV 的类型)。

指定参数可选方法

@Tool
//表明unit参数不是必须的
void getTemperature(String location, @P(required = false) Unit unit) {
    ...
}
//复杂参数的字段和子字段默认也是必须的,可以由此设置为非必须
record User(String name, @JsonProperty(required = false) String email) {}
@Tool
void add(User user) {
    ...
}
  • 当与结构化输出一起使用时, 所有字段和子字段默认都被视为可选的

工具方法返回类型

  • 可以返回任何类型包括void。
  • String的返回类型会原样发给LLM不做任何转换。
  • 其他返回类型会转化为Json字符串再发给LLM。

工具方法异常处理

  • 如果带有 @Tool 注解的方法抛出 Exception, 异常的消息(e.getMessage())将作为工具执行的结果发送给 LLM。 这允许 LLM 纠正其错误并在认为必要时重试。

工具注解

@Tool

  • name:工具名称。如果未提供,方法名将作为工具名称。
  • value:工具描述。
//请求问题数据
interface MathGenius { 
    String ask(String question);
}

//工具类
class Calculator { 
    @Tool
    double add(int a, int b) {
        return a + b;
    }
    @Tool
    double squareRoot(double x) {
        return Math.sqrt(x);
    }
}

//构建AI服务
MathGenius mathGenius = AiServices.builder(MathGenius.class)
    .chatLanguageModel(model)
    .tools(new Calculator())
    .build();

String answer = mathGenius.ask("475695037565 的平方根是多少?");

System.out.println(answer); // 475695037565 的平方根是 689706.486532。

@P

  • value:参数描述。必填字段。
  • required:参数是否必需,默认为 true。可选字段。

@Description

  • 类和字段的描述可以使用 @Description 注解指定。
@Description("要执行的查询")
class Query {
  @Description("要选择的字段")
  private List<String> select;
}
  • 注意:放在 enum 值上的 @Description 没有效果,并且不会包含在生成的 JSON schema。

@ToolMemoryId

  • 如果AI 服务方法有一个带有 @MemoryId 注解的参数, 可以使用 @ToolMemoryId 注解 @Tool 方法的参数。 提供给 AI 服务方法的值将自动传递给 @Tool 方法。 如果有多个用户或每个用户有多个聊天/记忆, 可以在 @Tool 方法内区分它们。

访问已执行的工具

interface Assistant {

    Result<String> chat(String userMessage);
}

Result<String> result = assistant.chat("取消我的预订 123-456");

String answer = result.content();
//获取到执行过的工具
List<ToolExecution> toolExecutions = result.toolExecutions();

//流式模式下,通过onToolExecuted实现
tokenStream
    .onToolExecuted((ToolExecution toolExecution) -> System.out.println(toolExecution))
    .onPartialResponse(...)
    .onCompleteResponse(...)
    .onError(...)
    .start();

以编程方式指定工具

  • 更加灵活。
  • 可以从外部源(数据库或者配置文件)加载。
ToolSpecification toolSpecification = ToolSpecification.builder()
    .name("get_booking_details")            // 工具名称(LLM通过此名称调用工具)
    .description("返回预订详情")             // 工具描述(LLM根据此描述决定是否调用)
    .parameters(JsonObjectSchema.builder()  // 定义工具参数结构(JSON Schema格式)
        .properties(Map.of(
            "bookingNumber", JsonStringSchema.builder()
                .description("B-12345 格式的预订号")  // 参数描述
                .build()
        ))
        .build())
    .build();

工具幻觉策略

  • 可能会发生 LLM 在工具调用上产生幻觉的情况,或者换句话说,它要求使用一个不存在的名称的工具。在这种情况下,默认情况下 LangChain4j 将抛出一个异常报告问题,但可以通过为 AI 服务提供在这种情况下使用的策略来配置不同的行为。

  • 这个策略是 Function<ToolExecutionRequest, ToolExecutionResultMessage> 的实现,定义了对于包含请求调用不可用工具的 ToolExecutionRequest 应该产生什么 ToolExecutionResultMessage 作为结果。例如,可以使用一个策略配置 AI 服务,该策略向 LLM 返回一个响应,希望推动它尝试调用不同的工具,知道之前请求的工具不存在。

AssistantHallucinatedTool assistant = AiServices.builder(AssistantHallucinatedTool.class)
        .chatLanguageModel(chatLanguageModel)
        .tools(new HelloWorld())
        .hallucinatedToolNameStrategy(toolExecutionRequest -> ToolExecutionResultMessage.from(
                toolExecutionRequest, "错误:没有名为 " + toolExecutionRequest.name() + " 的工具"))
        .build();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值