基于Tools体验NLP编程的魅力
大模型能理解自然语言,从而能解决问题,但是就像人类大脑一样,大脑只能发送指令,实际行动得靠四肢,所以LangChain4j提供的Tools机制就是大模型的四肢。
大模型的不足
大模型在解决问题时,是基于互联网上很多历史资料进行预测的,而且答案具有一定的随机性,那如果我问"今天是几月几号?",大模型是大概率答错的,因为大模型肯定还没有来得及学习今天所产生的最新资料。
比如:
package com.timi;import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;public class _04_Toos {public static void main(String[] args) {ChatLanguageModel model = OpenAiChatModel.builder().baseUrl("http://langchain4j.dev/demo/openai/v1").apiKey("demo").build();System.out.println(model.generate("今天是几月几号?"));}
}
代码执行结果为:
今天是十二月十九号。
多执行几次,每次执行结果很有可能不一样,所以如果要求大模型处理时间相关的问题,它就无能为力了。
因此,我们扩展一下,出现这种情况的原因是ChatGPT是基于历史数据来进行预测的,它没办法拿到当前最新的数据,比如说时间,从而限制了它的进一步使用,那么LangChain4j的Tools机制就能够帮助大模型来获取当前最新的数据,从而解决上述时间相关的问题。
由于LangChain4j提供的"demo"不支持Tools机制,需要大家自行获取OpenAI的ApiKey,或者找一些OpenAI的代理来间接的调用OpenAI的API。
ToolSpecification
首先需要定义一个工具,其实就是一个方法,用来返回当前日期,并且通过@Tool注解来描述该工具,从而使得大模型在需要获取当前时间时能够调用该工具方法得到当前时间:
@Tool("获取当前日期")
public static String dateUtil(){return LocalDateTime.now().toString();
}
然后将工具方法转成ToolSpecification对象,并传递给大模型:
package com.timi;import dev.langchain4j.agent.tool.Tool;
import dev.langchain4j.agent.tool.ToolSpecification;
import dev.langchain4j.agent.tool.ToolSpecifications;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.model.output.Response;import java.time.LocalDateTime;
import java.util.Collections;public class _04_Tools {@Tool("获取当前日期")public static String dateUtil(){return LocalDateTime.now().toString();}public static void main(String[] args) throws NoSuchMethodException {ChatLanguageModel model = OpenAiChatModel.builder().baseUrl("http://localhost:3000/v1").apiKey("sk-peszVtFXoLnWK45bB15370Df6f344cAa9a088eF50f9c7302").build();ToolSpecification toolSpecification = ToolSpecifications.toolSpecificationFrom(_04_Tools.class.getMethod("dateUtil"));UserMessage userMessage = UserMessage.from("今天是几月几号?");Response<AiMessage> response = model.generate(Collections.singletonList(userMessage), toolSpecification);System.out.println(response.content());}
}
所以,一个ToolSpecification对象就代表一个工具,当用户把要问题UserMessage和工具ToolSpecification一起传递给大模型,大模型就知道要结合工具描述来解决用户的问题,此时大模型响应的AiMessage不再是一串文本,而是:
AiMessage { text = null toolExecutionRequests = [ToolExecutionRequest { id = "call_IPiiRjIM5PmVdDWjpXcUN5c7", name = "dateUtil", arguments = "{}" }] }
一个ToolExecutionRequest,表示一个工具执行请求,表示大模型在解决问题时,需要调用工具来解决用户的问题,由于我们可能传了多个工具给大模型,所以toolExecutionRequests是一个List,表示为了解决用户的问题需要调用哪些工具。
所以,我们在得到了ToolExecutionRequest后,就需要取执行对应的工具方法了,其中ToolExecutionRequest的name属性就是方法名,arguments就表示要传递给方法的参数值:
Response<AiMessage> response = model.generate(Collections.singletonList(userMessage), toolSpecification);AiMessage aiMessage = response.content();
if (aiMessage.hasToolExecutionRequests()) {for (ToolExecutionRequest toolExecutionRequest : aiMessage.toolExecutionRequests()) {String methodName = toolExecutionRequest.name();Method method = _04_Tools.class.getMethod(methodName);// result就是当前时间String result = (String) method.invoke(null);System.out.println(result);}
}
此时的输出结果为:
2024-03-24T11:37:02.618942
这就是大模型想要的当前时间,相当于是ToolExecutionRequest的响应结果,那我们该如何把这个响应结果告诉给大模型,从而让大模型告诉我“今天是几月几号?”呢?
前面在介绍ChatMessage类型时,除开有UserMessage、AiMessage、SystemMessage之外,还有一种类型就是ToolExecutionResultMessage,因此ToolExecutionResultMessage就表示工具执行结果,所以我们把工具的执行结果封装为ToolExecutionResultMessage即可:
ToolExecutionResultMessage toolExecutionResultMessage = ToolExecutionResultMessage.from(toolExecutionRequest.id(), toolExecutionRequest.name(), result);
然后使用历史对话的思想,把以上用户和大模型之间涉及到的ChatMessage按顺序添加到List中发送给大模型即可:
ToolExecutionResultMessage toolExecutionResultMessage = ToolExecutionResultMessage.from(toolExecutionRequest.id(), toolExecutionRequest.name(), result);AiMessage message = model.generate(Lists.newArrayList(userMessage, aiMessage, toolExecutionResultMessage)).content();
System.out.println(message.text());
这样大模型就能正确的告诉当前时间了:
今天是2024年3月24日。
AiServices整合Tools
以上使用Tools的方式有点复杂,如果大模型要解决一个复杂问题需要调用多个工具或多轮工具调用,以上代码就更不合适了,而AiServices能简化这个过程。
假如有这么一个需求:获取今天注册的所有新用户信息,对于这个需求我们可以这么来实现。
首先定义一个User对象:
static class User {private String username;private Integer age;public User(String username, Integer age) {this.username = username;this.age = age;}
}
然后定义两个Tools:
static class MyTools {@Tool("获取当前日期")public static String dateUtil(String onUse) {return LocalDateTime.now().toString();}@Tool("获取指定日期注册的用户信息")public static List<User> getUserInfo(String date) {System.out.println("接收到的date参数的值:" + date);User user1 = new User("司马懿", 18);User user2 = new User("曹操", 18);return Lists.newArrayList(user1, user2);}
}
一个用来获取当前时间,一个接收当前时间并返回用户信息。
再定义一个UserService接口:
interface UserService {@SystemMessage("先获取具体日期,然后再解决用户问题")String getUserInfo(String desc);
}
然后利用AiServices创建UserService接口的代理对象:
public static void main(String[] args) {ChatLanguageModel model = ZhipuAiChatModel.builder().apiKey("0f4d2b0e8d95f48e6e1f138b881d0a53.UkIov25cJBSvjFDo").build();UserService userService = AiServices.builder(UserService.class).chatLanguageModel(model).tools(new MyTools()).chatMemory(MessageWindowChatMemory.withMaxMessages(10)).build();String userInfo = userService.getUserInfo("获取今天的注册的新用户信息");System.out.println(userInfo);}
并执行getUserInfo()方法,传入你的描述信息就可以获取到User信息了。比如以上代码的执行结果为:
接收到的date参数的值:2024-04-21
2024年4月20日注册的用户有司马懿和曹操,他们的年龄都是18岁。
源码分析
在代理对象的invoke()方法中,以下代码会去调用大模型的底层API:
Response<AiMessage> response = context.toolSpecifications == null? context.chatModel.generate(messages): context.chatModel.generate(messages, context.toolSpecifications);
当指定了Tools时,就会调用context.chatModel.generate(messages, context.toolSpecifications)
,我们debug来看下返回结果:
第一次响应是一个ToolExecutionRequest工具执行请求,name为"now",表示要执行now()方法,也就是获取当前时间,然后会执行如下代码:
for (ToolExecutionRequest toolExecutionRequest : aiMessage.toolExecutionRequests()) {// 执行工具ToolExecutor toolExecutor = context.toolExecutors.get(toolExecutionRequest.name());// 工具执行结果String toolExecutionResult = toolExecutor.execute(toolExecutionRequest, memoryId);// 把工具执行请求和结果封装为ToolExecutionResultMessageToolExecutionResultMessage toolExecutionResultMessage = ToolExecutionResultMessage.from(toolExecutionRequest,toolExecutionResult);// 添加到ChatMemory中chatMemory.add(toolExecutionResultMessage);
}
然后执行以下代码,再次请求大模型,此时ChatMemory中包含了第一次工具请求的结果:
response = context.chatModel.generate(chatMemory.messages(), context.toolSpecifications);
这一次得到的响应是:
仍然是一个工具执行请求,只不过方法时getUserInfo()方法,并且入参为上一步工具调用的结果,然后和上面类似,处理该工具执行请求,也就是执行getUserInfo()方法得到工具执行结果,同样再次把第二次的工具执行请求和结果封装为ToolExecutionResultMessage,并添加到ChatMemory中,此时ChatMemory中的内容为:
依次为:用户的问题、第一次工具执行请求和结果、第二次工具执行请求和结果。
把最终的ChatMemory发送给大模型之后,大模型就知道了今天注册的新用户信息有哪些了,就会把结果返回给你:
基于此,我们其实打通了大模型和我们系统内部数据之间的桥梁,使得大模型能够调用我们提供的工具来获取系统内部的最新数据,而我们可以更进一步让大模型基于这些数据来做更智能的事情,比如:
需求改为:“获取今天注册的新用户信息,然后基于这些用户发送一份邮件”,我们只需要再定义一个发送邮件Tool就可以了:
@Tool("给指定用户发送邮件")
public void email(String user) {System.out.println("发送邮件:" + user);
}
然后:
List<User> users = userService.getUserInfo("获取今天注册的新用户信息,然后基于这些用户发送一份邮件");
代码执行结果为:
接收到的date参数的值:2024-04-21
发送邮件:司马懿,曹操
今天注册的用户有司马懿和曹操,已经给他们发送了一份邮件。
通过这个Demo,我们发现,我们可以利用自然语言来整合各项系统功能,这将是一种新的编程模式:自然语言编程。
本节总结
本节我们学习了LangChain4j中的Tools机制,通过Tools机制可以通过自然语言整合大模型和系统内部功能,使得大模型这个智能大脑拥有了灵活的四肢,从而可以处理更复杂的场景,同时也感受到了自然语言编程离我们越来越近了,下一节我们将学习文本向量化以及向量模型、向量数据库,这是检索增强生成(RAG)的基础。
相关文章:

基于Tools体验NLP编程的魅力
大模型能理解自然语言,从而能解决问题,但是就像人类大脑一样,大脑只能发送指令,实际行动得靠四肢,所以LangChain4j提供的Tools机制就是大模型的四肢。 大模型的不足 大模型在解决问题时,是基于互联网上很…...
强化学习-3深度学习基础
文章目录 1 强化学习与深度学习的关系2 线性回归3 梯度下降4 逻辑回归5 全连接网络6 更高级的神经网络6.1 卷积神经网络6.2 循环神经网络6.3 transformer 将深度学习和强化学习结合起来,利用深度学习网络强大的拟合能力通过将状态、动作等作为输入,来估计…...

SOC模块LoRa-STM32WLE5有哪些值得关注
SoC 是片上系统的缩写,是一种集成芯片,集成了计算机或其他电子系统的所有或大部分组件。这些组件通常包括中央处理器 (CPU)、内存、输入/输出接口和辅助存储接口。包含数字、模拟、混合信号和通常的 RF 信号处理功能,具体取决于应用。片上系统…...
CSS中的display属性:布局控制的关键
CSS的display属性是控制元素在页面上如何显示的核心属性之一。它决定了元素的显示类型,以及它在页面布局中的行为。本文将详细介绍display属性的不同值及其使用场景,帮助你更好地掌握布局控制。 display属性的基本值 block 特点:块级元素&…...
【Spring Boot AOP通知顺序】
文章目录 一、Spring Boot AOP简介二、通知顺序1. 通知类型及其顺序示例代码 2. 控制通知顺序示例代码 一、Spring Boot AOP简介 AOP(Aspect-Oriented Programming,面向切面编程)是对OOP(Object-Oriented Programming,…...

k8s是什么
1、k8s出现的背景: 随着服务器上的应用增多,需求的千奇百怪,有的应用不希望被外网访问,有的部署的时候,要求内存要达到多少G,每次都需要登录各个服务器上执行操作更新,不仅容易出错,…...
使用雪花算法(Snowflake Algorithm)在Python中生成唯一ID
使用雪花算法Snowflake Algorithm在Python中生成唯一ID 使用雪花算法(Snowflake Algorithm)在Python中生成唯一ID雪花算法简介Python实现代码解析使用示例优势注意事项适用场景结论 使用雪花算法(Snowflake Algorithm)在Python中生…...

Docker期末复习
云计算服务类型有: IaaS 基础设施及服务 PaaS 平台及服务 SaaS 软件及服务 服务类型辨析示例: IaaS 服务提供的云服务器软件到操作系统,具体应用软件自己安装,如腾讯云上申请的云服务器等;SaaS提供的服务就是具体的软件,例如微软的Office套件等。 云计算部署模式有: 私有云…...

DP:子数组问题
文章目录 引言子数组问题介绍动态规划的基本概念具体问题的解决方法动态规划解法:关于子数组问题的几个题1.最大子数组和2.环形子数组的最大和3.乘积最大子数组4.乘积为正数的最长子数组长度5.等差数列划分 总结 引言 介绍动态规划(DP)在解决…...
[Day 20] 區塊鏈與人工智能的聯動應用:理論、技術與實踐
AI在醫療領域的創新應用 隨著科技的快速發展,人工智能(AI)在各行各業的應用越來越廣泛,醫療領域也不例外。AI技術在醫療中的應用不僅提高了診斷的準確性,還改善了病患的治療效果,優化了醫療資源的配置。本…...

Handling `nil` Values in `NSDictionary` in Objective-C
Handling nil Values in NSDictionary in Objective-C When working with Objective-C, particularly when dealing with data returned from a server, it’s crucial (至关重要的) to handle nil values appropriately (适当地) to prevent unexpected crashes. Here, we ex…...

【深入浅出 】——【Python 字典】——【详解】
目录 1. 什么是 Python 字典? 1.1 字典的基本概念 1.2 字典的用途 1.3 字典的优势 2. 字典的基本特点 2.1 键的唯一性 2.2 可变性 2.3 无序性 3. 如何创建字典? 3.1 使用 {} 符号 3.2 使用 dict() 工厂方法 3.3 使用 fromkeys() 方法 4. 字…...

开发RpcProvider的发布服务(NotifyService)
1.发布服务过程 目前完成了mprpc框架项目中的以上的功能。 作为rpcprovider的使用者,也就是rpc方法的发布方 main函数如下: 首先我们init调用框架的init,然后启动一个provider,然后向provider上注册服务对象方法,即us…...
Suno: AI音乐创作的新时代
名人说:一点浩然气,千里快哉风。 ——苏轼 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 目录 一、什么是Suno?1、Suno2、应用场景二、如何使用Suno制作音乐?步骤1:注册并登录Suno平台步骤2:创建音乐项目步骤3:生成音乐片段三、Suno的影响很高兴你打开了…...

六西格玛项目实战:数据驱动,手机PCM率直线下降
在当前智能手机市场日益竞争激烈的背景下,消费者对手机质量的要求达到了前所未有的高度。PCM(可能指生产过程中的某种不良率或缺陷率)作为影响手机质量的关键因素,直接关联到消费者满意度和品牌形象。为了应对这一挑战,…...
数据结构递归(01)汉诺塔经典问题
说明:使用递归时,必须要遵守两个限制条件: 递归存在限制条件,满⾜这个限制条件时,递归不再继续; 每次递归调⽤之后越来越接近这个限制条件; 1 汉诺塔(Hanoi Tower)经典…...

计算机专业课面试常见问题-计算机网络篇
目录 1. 计算机网络分为哪 5 层? 2. TCP 协议简述? 3. TCP 和 UDP 的区别?->不同的应用场景? 4. 从浏览器输入网址到显示页…...

HarmonyOS ArkUi ArkWeb加载不出网页问题踩坑
使用 使用还是比较简单的,直接贴代码了 别忘了配置网络权限 Entry Component struct WebPage {State isAttachController: boolean falseState url: string State title: string Prop controller: web_webview.WebviewController new web_webview.WebviewCont…...

微信换手机号了怎么绑定新手机号?
微信换手机号了怎么绑定新手机号? 1、在手机上找到并打开微信; 2、打开微信后,点击底部我的,并进入微信设置; 3、在微信设置账号与安全内,找到手机号并点击进入; 4、选择更换手机号,…...

64.WEB渗透测试-信息收集- WAF、框架组件识别(4)
免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动! 内容参考于: 易锦网校会员专享课 上一个内容:63.WEB渗透测试-信息收集- WAF、框架组件识别(3)-CSDN博客 我们在…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...
生成 Git SSH 证书
🔑 1. 生成 SSH 密钥对 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 参数说明: -t rsa&#x…...
如何为服务器生成TLS证书
TLS(Transport Layer Security)证书是确保网络通信安全的重要手段,它通过加密技术保护传输的数据不被窃听和篡改。在服务器上配置TLS证书,可以使用户通过HTTPS协议安全地访问您的网站。本文将详细介绍如何在服务器上生成一个TLS证…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...

微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)
在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...