使用LangChain4J实现Agent与Tool调用!

JavaEdge聊AIss 2024-09-23 22:08:41
一些LLM除了生成文本,还可触发操作。 所有支持tools的LLMs可在此处找到(参见“Tools”栏)。 有一个被称为“工具(tools)”或“函数调用(function calling)”的概念。它允许LLM在必要时调用一或多个由开发者定义的工具。工具可以是任何东西:网页搜索、外部API调用、或执行一段特定代码等。LLM本身无法实际调用这些工具;它们会在响应中表达出调用某个工具的意图(而不是直接生成文本)。我们开发者,则需要根据提供的参数来执行这些工具并报告工具执行结果。 如我们知道LLM本身并不擅长数学运算。若你的应用场景涉及偶尔的数学计算,你可能希望为LLM提供一个“math tool”。通过在请求中声明一个或多个工具,LLM可以在认为适合时调用其中一个。如遇到数学问题并拥有一组数学工具时,LLM可能会决定首先调用其中的一个来正确回答问题。 1 有无工具时的效果1.1 没有工具的消息示例Request:- messages: - UserMessage: - text: What is the square root of 475695037565?Response:- AiMessage: - text: The square root of 475695037565 is approximately 689710.接近正确,但不完全对。 1.2 使用以下工具的消息示例@Tool("Sums 2 given numbers")public double sum(double a, double b) { return a + b;}@Tool("Returns a square root of a given number")public double squareRoot(double x) { return Math.sqrt(x);}Request 1:- messages: - UserMessage: - text: What is the square root of 475695037565?- tools: - sum(double a, double b): Sums 2 given numbers - squareRoot(double x): Returns a square root of a given numberResponse 1:- AiMessage: - toolExecutionRequests: - squareRoot(475695037565)... here we are executing the squareRoot method with the "475695037565" argument and getting "689706.486532" as a result ...Request 2:- messages: - UserMessage: - text: What is the square root of 475695037565? - AiMessage: - toolExecutionRequests: - squareRoot(475695037565) - ToolExecutionResultMessage: - text: 689706.486532Response 2:- AiMessage: - text: The square root of 475695037565 is 689706.486532.如你所见,当LLM拥有工具时,它可在适当时决定调用其中的一个。 这是一个非常强大的功能。这简单例子,我们给LLM提供原始的数学工具,但可想象如提供如googleSearch和sendEmail工具,然后提供一个查询“我的朋友想知道AI领域的最新消息。请将简短的总结发送到friend@email.com”,那它可用googleSearch工具找到最新消息,然后总结并通过sendEmail工具发送总结。 经验法则为了增加LLM调用正确工具和参数的几率,我们应该提供清晰且明确的: 工具名称工具的功能描述以及何时使用每个工具参数的描述一个好的经验法则是:如果人类能理解工具的用途和如何使用,那么LLM也能理解。 LLM被专门微调,以检测何时调用工具以及如何调用它们。某些模型甚至可以一次调用多个工具,如OpenAI。 注意,工具/函数调用与JSON模式不同。 2 两个抽象层次LangChain4j 提供两个使用工具的抽象层: 底层,使用 ChatLanguageModel API高级,使用AI服务和@Tool注解的Java方法3 底层工具API3.1 generate可用ChatLanguageModel#generate(List, List): /** * 根据消息列表和工具规范列表从模型生成响应。响应可以是文本消息,也可以是执行指定工具之一的请求。通常,该列表包含按以下顺序排列的消息:System (optional) - User - AI - User - AI - User ... * messages – 消息列表 * toolSpecifications – 允许模型执行的工具列表。该模型自主决定是否使用这些工具中的任何一个 * return:模型生成的响应 * AiMessage 可以包含文本响应或执行其中一个工具的请求。 */default Response generate(List messages, List toolSpecifications) { throw new IllegalArgumentException("Tools are currently not supported by this model");}类似方法也存于StreamingChatLanguageModel。 3.2 ToolSpecificationpackage dev.langchain4j.agent.tool;// 包含工具所有信息public ToolSpecification { // 工具的`名称` private final String name; // 工具的`描述` private final String description; // 工具的`参数`及其描述 private final ToolParameters parameters;推荐尽可能提供关于工具的所有信息:清晰的名称、详尽的描述和每个参数的描述等。 3.2.1 创建ToolSpecification① 手动ToolSpecification toolSpecification = ToolSpecification.builder() .name("getWeather") .description("返回指定城市的天气预报") .addParameter("city", type("string"), description("应返回天气预报的城市")) .addParameter("temperatureUnit", enums(TemperatureUnit.class)) // 枚举 TemperatureUnit { 摄氏, 华氏 } .build();② 使用辅助方法ToolSpecifications.toolSpecificationsFrom(Class)ToolSpecifications.toolSpecificationsFrom(Object)ToolSpecifications.toolSpecificationFrom(Method)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, TemperatureUnit temperatureUnit ) { ... }}List toolSpecifications = ToolSpecifications.toolSpecificationsFrom(WeatherTools.class);一旦你拥有List,可调用模型: UserMessage userMessage = UserMessage.from("伦敦明天的天气如何?");Response response = model.generate(List.of(userMessage), toolSpecifications);AiMessage aiMessage = response.content();若LLM决定调用工具,返回的AiMessage将包含toolExecutionRequests字段中的数据。此时,AiMessage.hasToolExecutionRequests()将返回true。根据LLM不同,它可包含一或多个ToolExecutionRequest对象(某些LLM支持并行调用多个工具)。 每个ToolExecutionRequest应包含: public ToolExecutionRequest { // 工具调用的`id`(某些LLM不提供) private final String id; // 要调用的工具名称,例如:`getWeather` private final String name; // 工具的`参数`,例如:`{ "city": "London", "temperatureUnit": "CELSIUS" }` private final String arguments;你要用ToolExecutionRequest中的信息手动执行工具。 如希望将工具执行的结果发回LLM,你要为每个ToolExecutionRequest创建一个ToolExecutionResultMessage并与之前的所有消息一起发送: String result = "预计明天伦敦会下雨。";ToolExecutionResultMessage toolExecutionResultMessage = ToolExecutionResultMessage.from(toolExecutionRequest, result);List messages = List.of(userMessage, aiMessage, toolExecutionResultMessage);Response response2 = model.generate(messages, toolSpecifications);4 高级工具API高层,你可为任何Java方法添加@Tool注解,并将其与AI服务一起使用。 AI服务会自动将这些方法转换为ToolSpecification,并在每次与LLM的交互中包含它们。当LLM决定调用工具时,AI服务将自动执行相应的方法,并将方法的返回值(如果有)发送回LLM。实现细节可以在DefaultToolExecutor中找到。 @Tool("Searches Google for relevant URLs, given the query")public List searchGoogle(@P("search query") String query) { return googleSearchService.search(query);}@Tool("Returns the content of a web page, given the URL")public String getWebPageContent(@P("URL of the page") String url) { Document jsoupDocument = Jsoup.connect(url).get(); return jsoupDocument.body().text();}4.1 @Tool任何用@Tool注解并在构建AI服务时明确指定的Java方法,都可以被LLM执行 interface MathGenius { String ask(String question);}class Calculator { @Tool public double add(int a, int b) { return a + b; } @Tool public double squareRoot(double x) { return Math.sqrt(x); }}MathGenius mathGenius = AiServices.builder(MathGenius.class) .chatLanguageModel(model) .tools(new Calculator()) .build();String answer = mathGenius.ask("What is the square root of 475695037565?");System.out.println(answer); // The square root of 475695037565 is 689706.486532.调用ask方法时,会发生两次与LLM的交互,如前文所述。交互期间,会自动调用squareRoot方法。 @Tool注解有两个可选字段: name: 工具的名称。如果未提供,方法名将作为工具名称。value: 工具的描述。根据具体工具,即使不提供描述,LLM也可能理解其用途(例如,add(a, b)很明显),但通常最好提供清晰且有意义的名称和描述。这样,LLM在决定是否调用工具以及如何调用时会有更多信息。 4.2 @P方法的参数可以使用@P注解。 @P注解有两个字段: value: 参数的描述,此字段是必填的。required: 参数是否是必需的,默认值为true,此字段为可选。4.3 @ToolMemoryId如果AI服务方法的某个参数使用了@MemoryId注解,则可以在@Tool方法的参数上使用@ToolMemoryId进行注解。这样,提供给AI服务方法的值将自动传递给@Tool方法。这对于多个用户和/或每个用户有多个聊天或记忆的场景非常有用,可以在@Tool方法中区分它们。 4.4 访问已执行的工具如果你希望访问AI服务调用过程中执行的工具,可以通过将返回类型封装在Result类中轻松实现: interface Assistant { Result chat(String userMessage);}Result result = assistant.chat("取消我的预订 123-456");String answer = result.content();List toolExecutions = result.toolExecutions();4.5 以编程方式指定工具在使用AI服务时,也可以通过编程方式指定工具。这种方法非常灵活,因为工具可以从外部资源(如数据库和配置文件)加载。 工具名称、描述、参数名称和描述都可以使用ToolSpecification进行配置: ToolSpecification toolSpecification = ToolSpecification.builder() .name("get_booking_details") .description("返回预订详情") .addParameter("bookingNumber", type("string"), description("B-12345格式的预订编号")) .build();对于每个ToolSpecification,需要提供一个ToolExecutor实现来处理LLM生成的工具执行请求: ToolExecutor toolExecutor = (toolExecutionRequest, memoryId) -> { Map arguments = fromJson(toolExecutionRequest.arguments()); String bookingNumber = arguments.get("bookingNumber").toString(); Booking booking = getBooking(bookingNumber); return booking.toString();};一 旦我们拥有一个或多个(ToolSpecification,ToolExecutor)对,我们可以在创建AI服务时指定它们: Assistant assistant = AiServices.builder(Assistant.class) .chatLanguageModel(chatLanguageModel) .tools(singletonMap(toolSpecification, toolExecutor)) .build();4.6 动态指定工具在使用AI服务时,每次调用时也可以动态指定工具。可以配置一个ToolProvider,该提供者将在每次调用AI服务时被调用,并提供应包含在当前请求中的工具。ToolProvider接受一个包含UserMessage和聊天记忆ID的ToolProviderRequest,并返回包含工具的ToolProviderResult,其形式为ToolSpecification到ToolExecutor的映射。 下面是一个示例,展示如何仅在用户消息中包含“预订”一词时添加get_booking_details工具: ToolProvider toolProvider = (toolProviderRequest) -> { if (toolProviderRequest.userMessage().singleText().contains("booking")) { ToolSpecification toolSpecification = ToolSpecification.builder() .name("get_booking_details") .description("返回预订详情") .addParameter("bookingNumber", type("string")) .build(); return ToolProviderResult.builder() .add(toolSpecification, toolExecutor) .build(); } else { return null; }};Assistant assistant = AiServices.builder(Assistant.class) .chatLanguageModel(model) .toolProvider(toolProvider) .build();5 示例带工具的示例带动态工具的示例参考: 关于工具的精彩指南关注我,紧跟本系列专栏文章,咱们下篇再续! 作者简介:魔都架构师,多家大厂后端一线研发经验,在分布式系统设计、数据平台架构和AI应用开发等领域都有丰富实践经验。 各大技术社区头部专家博主。具有丰富的引领团队经验,深厚业务架构和解决方案的积累。 负责: 中央/分销预订系统性能优化 活动&券等营销中台建设 交易平台及数据中台等架构和开发设计 车联网核心平台-物联网连接平台、大数据平台架构设计及优化 LLM Agent应用开发 区块链应用开发 大数据开发挖掘经验 推荐系统项目 目前主攻市级软件项目设计、构建服务全社会的应用系统。 参考: 编程严选网
0 阅读:12