基于Tools体验NLP编程的魅力

news2024/12/26 15:57:07

大模型能理解自然语言,从而能解决问题,但是就像人类大脑一样,大脑只能发送指令,实际行动得靠四肢,所以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());

这样大模型就能正确的告诉当前时间了:

今天是2024324日。

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
2024420日注册的用户有司马懿和曹操,他们的年龄都是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来看下返回结果:
image.png
第一次响应是一个ToolExecutionRequest工具执行请求,name为"now",表示要执行now()方法,也就是获取当前时间,然后会执行如下代码:

for (ToolExecutionRequest toolExecutionRequest : aiMessage.toolExecutionRequests()) {

	// 执行工具
	ToolExecutor toolExecutor = context.toolExecutors.get(toolExecutionRequest.name());
	// 工具执行结果
	String toolExecutionResult = toolExecutor.execute(toolExecutionRequest, memoryId);

	// 把工具执行请求和结果封装为ToolExecutionResultMessage
	ToolExecutionResultMessage toolExecutionResultMessage = ToolExecutionResultMessage.from(
		toolExecutionRequest,
		toolExecutionResult
	);

	// 添加到ChatMemory中
	chatMemory.add(toolExecutionResultMessage);
}

然后执行以下代码,再次请求大模型,此时ChatMemory中包含了第一次工具请求的结果:

response = context.chatModel.generate(chatMemory.messages(), context.toolSpecifications);

这一次得到的响应是:
image.png
仍然是一个工具执行请求,只不过方法时getUserInfo()方法,并且入参为上一步工具调用的结果,然后和上面类似,处理该工具执行请求,也就是执行getUserInfo()方法得到工具执行结果,同样再次把第二次的工具执行请求和结果封装为ToolExecutionResultMessage,并添加到ChatMemory中,此时ChatMemory中的内容为:
image.png
依次为:用户的问题、第一次工具执行请求和结果、第二次工具执行请求和结果。

把最终的ChatMemory发送给大模型之后,大模型就知道了今天注册的新用户信息有哪些了,就会把结果返回给你:
image.png
基于此,我们其实打通了大模型和我们系统内部数据之间的桥梁,使得大模型能够调用我们提供的工具来获取系统内部的最新数据,而我们可以更进一步让大模型基于这些数据来做更智能的事情,比如:

需求改为:“获取今天注册的新用户信息,然后基于这些用户发送一份邮件”,我们只需要再定义一个发送邮件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)的基础。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1883403.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

图像大模型中的注意力和因果掩码

AIM — 图像领域中 LLM 的对应物。尽管 iGPT 已经存在 2 年多了&#xff0c;但自回归尚未得到充分探索。在本文中&#xff0c;作者表明&#xff0c;当使用 AIM 对网络进行预训练时&#xff0c;一组图像数据集上的下游任务的平均准确率会随着数据和参数的增加而线性增加。 要运…

Android 大话binder通信

戳蓝字“牛晓伟”关注我哦&#xff01; 用心坚持输出易读、有趣、有深度、高质量、体系化的技术文章 由于 Android 大话binder通信(上) 和 Android 大话binder通信(下) 分为两篇阅读体验不好&#xff0c;顾合并为一篇。 本文摘要 用故事的方式把binder通信的整个过程都描述…

机械原理介绍

机械原理介绍 1 介绍1.1 概述1.2 资料书籍在线资料 2 [机械原理知识整理](https://tomm.muzing.top/) 【muzing整理编写】1 绪论2 机构的结构分析2-2 机构的组成及分类2-3 机构运动简图2-4 机构具有确定运动的条件及最小阻力定律2-5 2-6 机构自由度的计算2-7 平面机构的组成原理…

【深度学习】图生图img3img论文原理,SD EDIT

https://arxiv.org/abs/2108.01073 摘要 引导图像合成技术使普通用户能够以最小的努力创建和编辑逼真的图像。关键挑战在于平衡对用户输入&#xff08;例如&#xff0c;手绘的彩色笔画&#xff09;的忠实度和合成图像的真实感。现有的基于GAN的方法试图通过使用条件GAN或GAN反…

64.WEB渗透测试-信息收集- WAF、框架组件识别(4)

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

面经-计算机基础

1.计算机⽹络 1.1 OSI与TCP/IP各层的结构与功能,都有哪些协议? 计算机网络体系结构 应⽤层 应⽤层 (application-layer &#xff09;的任务是通过应⽤进程间的交互来完成特定⽹络应⽤。 应⽤层协议定 义的是应⽤进程&#xff08;进程&#xff1a;主机中正在运⾏的程序&…

便签 Pro(Mac 智能便签工具)专业版怎么样,值得购买吗?

使用 Mac 的小伙伴平时都是怎么记录工作生活中的碎片信息&#xff1f;用聊天软件&#xff0c;还是系统备忘录呢&#xff1f; 实际体验下来&#xff0c;其实都难以称得上好用。 赶紧来了解一下 Mac 多彩思维速记工具便签 Pro&#xff01;拥有智能边框大小、iCloud 同步、历史记…

昇思25天学习打卡营第1天|MindSpore 全流程操作指南

目录 MindSpore 库相关操作的导入指南 处理数据集 网络构建 模型训练 保存模型 加载模型 MindSpore 库相关操作的导入指南 首先&#xff0c;我们导入了 MindSpore 这个库的整个模块。然后&#xff0c;从 MindSpore 库中引入了 nn 模块&#xff0c;一般来说&#xff0c;它是…

JavaEE—什么是服务器?以及Tomcat安装到如何集成到IDEA中?

目录 ▐ 前言 ▐ JavaEE是指什么? ▐ 什么是服务器&#xff1f; ▐ Tomcat安装教程 * 修改服务端口号 ▐ 将Tomcat集成到IDEA中 ▐ 测试 ▐ 结语 ▐ 前言 至此&#xff0c;这半年来我已经完成了JavaSE&#xff0c;Mysql数据库&#xff0c;以及Web前端知识的学习了&am…

ROS2在rviz2中实时显示轨迹和点

本文是将《ROS在rviz中实时显示轨迹和点》博客中rviz轨迹显示转为ROS2环境中的rviz2显示。 ros2的工作空间创建这里就不展示了。 包的创建 ros2 pkg create --build-type ament_cmake showpath --dependencies rclcpp nav_msgs geometry_msgs tf2_geometry_msgsshowpath.cpp…

【微服务网关——Websocket代理】

1.Websocket协议与原理 1.1 连接建立协议 1.1.1 客户端发起连接请求 客户端通过 HTTP 请求发起 WebSocket 连接。以下是一个 WebSocket 握手请求的例子&#xff1a; GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key…

python 中的 下划线_ 是啥意思

在 Python 中&#xff0c;_&#xff08;下划线&#xff09;通常用作占位符&#xff0c;表示一个变量名&#xff0c;但程序中不会实际使用这个变量的值。 目录 忽略循环变量&#xff1a;忽略函数返回值&#xff1a;在解释器中使用&#xff1a;举例子1. 忽略循环变量2. 忽略不需…

APP逆向 day8 JAVA基础3

一.前言 昨天我们讲了点java基础2.0&#xff0c;发现是又臭又长&#xff0c;今天就是java基础的最后一章&#xff0c;也就是最难的&#xff0c;面向对象。上一末尾也是提到了面向对象&#xff0c;但是面向对象是最重要的&#xff0c;怎么可能只有这么短呢&#xff1f;所以今天…

怎样将word默认Microsoft Office,而不是WPS

设置——>应用——>默认应用——>选择"word"——>将doc和docx都选择Microsoft Word即可

【嵌入式DIY实例】- LCD ST7735显示DHT11传感器数据

LCD ST7735显示DHT11传感器数据 文章目录 LCD ST7735显示DHT11传感器数据1、硬件准备与接线2、代码实现本文介绍如何将 ESP8266 NodeMCU 板 (ESP-12E) 与 DHT11 (RHT01) 数字湿度和温度传感器连接。 NodeMCU 从 DHT11 传感器读取温度(以 C 为单位)和湿度(以 rH% 为单位)值,…

连锁品牌如何做宣传?短视频矩阵工具助轻松千万流量曝光!

今天给大家分享一家烘焙行业连锁品牌&#xff08;可可同学&#xff09;&#xff0c;通过小魔推获得了1031万的话题曝光&#xff0c;旗下的连锁门店登顶同城人气榜单第一名&#xff0c;​让自己的流量和销量获得双增长 01 品牌连锁店如何赋能旗下门店&#xff1f; 作为一家全国…

昇思25天学习打卡营第13天|基于MobileNetV2的垃圾分类

MobileNetv2模型原理介绍 相比于传统的卷积神经网络&#xff0c;MobileNet网络使用深度可分离卷积&#xff08;Depthwise Separable Convolution&#xff09;的思想在准确率小幅度降低的前提下&#xff0c;大大减小了模型参数与运算量。并引入宽度系数α和分辨率系数β使模型满…

【面试题】IPS(入侵防御系统)和IDS(入侵检测系统)的区别

IPS&#xff08;入侵防御系统&#xff09;和IDS&#xff08;入侵检测系统&#xff09;在网络安全领域扮演着不同的角色&#xff0c;它们之间的主要区别可以归纳如下&#xff1a; 功能差异&#xff1a; IPS&#xff1a;这是一种主动防护设备&#xff0c;不仅具备检测攻击的能力&…

利用pyecharts制作2023全国GDP分布图

完整代码&#xff1a; from pyecharts import options as opts from pyecharts.charts import Map import pandas as pddf pd.read_excel(各省份GDP.xlsx) # print(df.head())year 2023 info df[[省份,year]] # print(info)info_list info.values.tolist() print(info_lis…