LangChain4j系列:LangChain4j 工具(函数调用)原理详解与API实践

LLMs 除了生成文本外,还可以触发操作。这个触发操作我们称之为工具(或函数调用),本篇文章将介绍LangChain4j框架是如何实现函数调用能力的。

什么是函数调用

函数调用功能可以增强模型推理效果或进行其他外部操作,包括信息检索、数据库操作、知识图谱搜索与推理、操作系统、触发外部操作等工具调用场景。

函数调用可以让大模型根据提示词输出一个请求调用函数的消息,其中包含所有需要调用的函数的信息、以及调用函数时所携带的参数信息。这是将大模型(LLM)能力与外部工具/API连接起来的方式。

例如,我们知道LLMs自己不太擅长数学。如果您的用例涉及数学计算,您可能希望提供LLM一个"数学工具"。通过在请求中声明一个或多个工具LLM,它可以决定在它认为合适的情况下调用其中一个工具。给定一个数学问题以及一组数学工具,LLM可能会决定要正确回答问题,它应该首先调用提供的数学工具之一。

哪些大模型支持函数调用,请参照# LangChain4j系列:LangChain4j LLM API 详解并接入OpenAI实现聊天 支持的LLMs中的Tools一栏。

函数调用解决问题

  • 触发外部操作
  • 解决大模型数据滞后性问题,可以将大模型链接到外部获取相关数据。比如搜索、数据库查询等
  • 解决大模型无真逻辑问题,比如复杂的数据计算问题。

如何定义一个函数

为了增加使用正确参数LLM调用正确工具的机会,我们应该提供一个清晰明确的:

  • 工具的名称
  • 描述该工具的作用以及何时应使用该工具
  • 每个工具参数的说明

一个好的经验法则:如果一个人能够理解工具的用途以及如何使用它,那么LLM也可以。如果定义不清晰则会导致函数调用失败。

LLMs经过专门微调,以检测何时调用工具以及如何调用它们。有些模型甚至可以同时调用多个工具,例如 OpenAI。

LangChain4j Tools API

LangChain4j 为使用工具提供了两个抽象级别:

  • Low-level, 使用 ChatLanguageModel API。 「灵活度高
  • High-level, 使用 AiService 和 @Tool 带注释的 Java 方法。 「使用方便

一般情况下推荐使用High-level的方式进行开发,使用方便,可以让开发者集中在业务开发。但是如果想追求开发的灵活性,那Low-level是你的选择。

定义Tools方式

  • 第一种方式:ToolSpecification 使用ToolSpecification 定义工具的名称、描述、参数的名称以及描述。

    java 复制代码
    ToolSpecification.builder()
                .name("add")
                .description("Calculates the sum of two numbers")
                .addParameter("a", type("integer"), description("The first number"))
                .addParameter("b", type("integer"), description("The second number"))
                .build();

注意:定义一个ToolSpecification,必须有一个 ToolExecutor 执行工具/函数调用。

  • 第二种方式:ToolSpecifications, 需要使用@Tools配合使用

    • ToolSpecifications.toolSpecificationsFrom(Class)
    • ToolSpecifications.toolSpecificationsFrom(Object)
    • ToolSpecifications.toolSpecificationFrom(Method)
  • 第三种方式:@Tool@P@ToolMemoryId

    • @Tool:定义工具/函数
      • name:名称,要具体
      • value:描述,要清晰
    • @P:对工具/方法的参数进行说明
      • value :参数说明。必填项。
      • required :参数是否为必填项,默认为 true 。可选字段。
    • @ToolMemoryId:AI Service 方法上具有@MemoryId 注释的参数。则可以使用@ToolMemoryId注释 @Tool 方法的参数。

    作用:提供给AI Service 方法的值将自动传递给该 @Tool 方法。如果聊天记忆按照用户维护隔离时,并且希望在@Tool方法中区分,那么需要使用@ToolMemoryId

Tools的执行

有了Tools的定义,那么就可以直接将其传给大模型,进行调用了。

使用 ChatLanguageModel (low-level)

java 复制代码
public interface ChatLanguageModel {
    // toolSpecifications 参数为工具定义,将使用上述两种方式之一创建工具定义
    default Response<AiMessage> generate(List<ChatMessage> messages, List<ToolSpecification> toolSpecifications) {
        throw new IllegalArgumentException("Tools are currently not supported by this model");
    }
    // 单个工具定义
    default Response<AiMessage> generate(List<ChatMessage> messages, ToolSpecification toolSpecification) {
        throw new IllegalArgumentException("Tools are currently not supported by this model");
    }
}

使用 AI Services (high-level)

java 复制代码
@AiService(
        wiringMode = AiServiceWiringMode.EXPLICIT,
        tools = {"calculator"}
)
public interface Assistant {
    String chat(String userMessage);
}

这个也有两种方式的使用;

  • wiringMode=AiServiceWiringMode.EXPLICIT,必须指定bean的名字。
  • wiringMode=AiServiceWiringMode.AUTOMATIC,会自动注入所有的@Tool标准的方法。但是类实例也必须被Spring Bean工厂管理。

代码实践

定义工具

java 复制代码
package org.ivy.chatmemory.tool;

import dev.langchain4j.agent.tool.Tool;
import org.springframework.stereotype.Component;

@Component
public class Calculator {
    @Tool("Calculates the sum of two numbers")
    public int add(int a, int b) {
        System.out.println("Called add with a=" + a + ", b=" + b);
        return a + b;
    }
}

AI Services

java 复制代码
package org.ivy.chatmemory.service;

import dev.langchain4j.service.spring.AiService;
import dev.langchain4j.service.spring.AiServiceWiringMode;

@AiService(
        wiringMode = AiServiceWiringMode.EXPLICIT,
        tools = {"calculator"}
)
public interface Assistant {
    String chat(String userMessage);
}

Controller

java 复制代码
package org.ivy.chatmemory.controller;

import org.ivy.chatmemory.service.Assistant;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ToolsController {
    private final Assistant assistant;

    public ToolsController(Assistant assistant) {
        this.assistant = assistant;
    }
    @GetMapping("/cal")
    public String calculate(String prompt) {
        return assistant.chat(prompt);
    }
}

测试结果

请求:[http://localhost:8805/cal?prompt=Calculates the sum of two numbers 2 and 10]

源码剖析

以实现的例子,看清函数调用的真实面目:

  • 输入提示词:Calculates the sum of two numbers 2 and 10
  • 发送给大模型的请求:
  • 大模型返回: 如果大模型决定使用函数调用,则返回的AiMessage中包含toolExecutionRequests对象(一个或者多个 ),用于函数的执行,主要包含如下字段;
    • id:调用工具的id,有些LLMs不提供。
    • name:要调用工具的名称, 例如:add 方法。
    • arguments:调用工具的参数,例如{"a":1,"b":10}。
  • 函数的真正执行
    • message中包含的信息
  • 最终返回结果 从整个过程来看,函数调用要进行两次与大模型交互;
  • 第一次:通过提示词 + 函数定义 返回要调用的函数和函数所有需要的参数。
  • 第二次:将第一次返回的函数调用结果 + 用户提示词 发送给大模型返回最终的结果。

所有源码内容都在 dev.langchain4j.service.DefaultAiServices类中实现,大家可以单步调试,看实现原理。 通过查看源码,可以清晰的理解了什么是函数调用了!!

示例源码与总结

示例源码

本文介绍了函数调用作用,详细介绍了LangChain4j实现函数调用的API和工具类,并实现了一个简单的两数相加的工具,最后对函数调用执行过程,源码进行分析。

相关推荐
吉小雨几秒前
PyTorch经典模型
人工智能·pytorch·python
无名之逆25 分钟前
计算机专业的就业方向
java·开发语言·c++·人工智能·git·考研·面试
CV-杨帆30 分钟前
大语言模型-教育方向数据集
人工智能·语言模型·自然语言处理
码拉松1 小时前
千万不要错过,优惠券设计与思考初探
后端·面试·架构
Jackilina_Stone1 小时前
【AI】简单了解AIGC与ChatGPT
人工智能·chatgpt·aigc
paixiaoxin1 小时前
学术新手进阶:Zotero插件全解锁,打造你的高效研究体验
人工智能·经验分享·笔记·机器学习·学习方法·zotero
破晓的历程1 小时前
【机器学习】:解锁数据背后的智慧宝藏——深度探索与未来展望
人工智能·机器学习
AiBoxss1 小时前
AI工具集推荐,简化工作流程!提升效率不是梦!
人工智能
crownyouyou1 小时前
最简单的一文安装Pytorch+CUDA
人工智能·pytorch·python
WenGyyyL1 小时前
变脸大师:基于OpenCV与Dlib的人脸换脸技术实现
人工智能·python·opencv