基于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());

這樣大模型就能正確的告訴當前時間了:

今天是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);// 把工具執行請求和結果封裝為ToolExecutionResultMessageToolExecutionResultMessage 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.pswp.cn/diannao/38989.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/38989.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/38989.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Tomcat部署與優化

Tomcat部署與優化 Tomcat簡述 server&#xff1a; 服務器&#xff0c;Tomcat運行的進程實例&#xff0c;一個Server中可以有多個service&#xff0c;但通常就一個 service&#xff1a;服務&#xff0c;用來組織Engine&#xff08;引擎&#xff09;和Connector&#xff08;連接…

gdb及其使用

gdb調試一&#xff1a; 首先進入gdb&#xff0c;確定好進程&#xff0c;輸入進程號 確定要調試哪個文件&#xff0c;然后輸入&#xff1a;&#xff08;b為打斷點&#xff09; (gdb) b serialization_protobuffer.h:write<ros::serialization::OStream>(ros::serializat…

MySQL的limit關鍵字和聚合函數講解

目錄 一、MySQL數據庫介紹二、MySQL聚合函數三、MySQL數據排序分組四、MySQL的limit關鍵字 一、MySQL數據庫介紹 MySQL是一種廣泛使用的開源關系型數據庫管理系統&#xff0c;由瑞典MySQL AB公司開發&#xff0c;后被Sun Microsystems收購&#xff0c;最終成為Oracle公司的一部…

RANSAC空間圓擬合實現

由初中的幾何知識我們可以知道&#xff0c;確定一個三角形至少需要三個不共線的點&#xff0c;因此確定一個三角形的外接圓至少可用三個點。我們不妨假設三個點坐標為P1(x1,y1,z1),P2(x2,y2,z2),P3(x3,y3,z3)。 圓方程的標準形式為&#xff1a; (xi-x)2(yi-y)2R2 &#xff08;1…

【Annotation】SpringBoot自定義注解

1. 自定義注釋是基于SpringAOP實現的 Spring AOP&#xff08;Aspect-Oriented Programming&#xff0c;面向切面編程&#xff09;是Spring框架中的一個強大功能模塊&#xff0c;它實現了AOP編程模型&#xff0c;允許開發者將橫切關注點&#xff08;如日志記錄、事務管理、安全…

新火種AI|蘋果要將蘋果智能做成AI時代的APP Store?

作者&#xff1a;一號 編輯&#xff1a;美美 蘋果還是想要自己做AI時代的“APP Store”。 自從去年開始落了隊&#xff0c;蘋果現在AI上開始高歌猛進。今年WWDC上展示的AI產品和與OpenAI的合作只是開始。有消息稱&#xff0c;蘋果正與Meta等AI巨頭展開深入合作&#xff0c;這…

隨想錄 Day 74 Floyd / A*

隨想錄 Day 74 Floyd / A* Bellman_ford 隊列優化 97. 小明逛公園 時間限制&#xff1a;1.000S 空間限制&#xff1a;256MB 題目描述 小明喜歡去公園散步&#xff0c;公園內布置了許多的景點&#xff0c;相互之間通過小路連接&#xff0c;小明希望在觀看景點的同時&#xff…

小和問題和逆序對問題

小和問題和逆序對問題 小和問題&#xff0c; 在一個數組中&#xff0c;每一個數左邊的數中比當前數小的數累加起來&#xff0c;叫做這個數組的小和&#xff0c;求一個數組的小和 直接遍歷&#xff1a; int littleSum1(int* arr, int L, int R) {int temp 0;for (int i L; …

Spring底層原理之bean的加載方式四 @import 注解

bean的加載方式四 import 第四種bean的導入方式 是import導入的方式 在配置類上面加上注解就行 package com.bigdata1421.config;import com.bigdata1421.bean.Dog; import org.springframework.context.annotation.Import;Import(Dog.class) public class SpringConfig4 {…

CesiumJS【Basic】- #041 繪制紋理線(Entity方式)- 需要自定義著色器

文章目錄 繪制紋理線(Entity方式)- 需要自定義著色器1 目標2 代碼2.1 main.ts3 資源文件繪制紋理線(Entity方式)- 需要自定義著色器 1 目標 使用Entity方式繪制紋理線 2 代碼 2.1 main.ts import * as Cesium from cesium;const viewer = new Cesium.Viewer

Java并發編程:最佳實踐與性能優化

Java并發編程&#xff1a;最佳實踐與性能優化 大家好&#xff0c;我是免費搭建查券返利機器人省錢賺傭金就用微賺淘客系統3.0的小編&#xff0c;也是冬天不穿秋褲&#xff0c;天冷也要風度的程序猿&#xff01; 介紹并發編程 在當今軟件開發中&#xff0c;多核處理器和分布式…

K8S學習教程(一):使用PetaExpress云服務器安裝Minikube 集群題

什么是Minikube Minikube是一款工具&#xff0c;主要用于在本地運行 Kubernetes 集群。Kubernetes 開源的平臺&#xff0c;用于自動化容器化應用的部署、擴展和管理&#xff0c;而Minikube 使得開發人員能夠在本地機器上輕松創建一個單節點的 Kubernetes 集群&#xff0c;從而…

【高級篇】第6章 Elasticsearch 高級查詢與搜索優化

在Elasticsearch的深入應用之旅中,掌握高級查詢技巧與優化搜索性能是提升數據處理效率的關鍵。本章將帶你深入探索Elasticsearch的高級查詢特性,揭示搜索性能優化的奧秘,以及如何利用高亮與建議API增強用戶體驗。 6.1 復雜查詢 6.1.1 Nested查詢 Nested基本概念與用法: …

IT設備監控模板:支持多種監控工具和平臺的集成和整合

IT設備監控模板管理在支持多種監控工具和平臺方面發揮著關鍵作用&#xff0c;它通過提供統一的配置和管理界面&#xff0c;使運維人員能夠靈活地適應和整合不同的監控工具和平臺。以下是IT設備監控模板管理如何支持多種監控工具和平臺的具體方式&#xff1a; 一、抽象化和標準…

如何使用AI學習一門編程語言?

無論你是軟件開發新手還是擁有幾十年的豐富經驗&#xff0c;總是需要學習新知識。TIOBE Index追蹤50種最受歡迎的編程語言&#xff0c;許多生態系統為職業發展和橫向轉型提供了機會。鑒于現有技術具有的廣度&#xff0c;抽空學習一項新技能并有效運用技能可能困難重重。 最近我…

ARCGIS python 裁剪柵格函數 arcpy.management.Clip

ARCGIS python 裁剪柵格函數 arcpy.management.Clip 1 功能 裁剪掉柵格數據集、鑲嵌數據集或圖像服務圖層的一部分。 2 使用情況 基于模板范圍提取部分柵格數據集&#xff0c;輸出與模板范圍相交的所有像素使用以 x 和 y 坐標的最小值和最大值確定的包絡矩形或使用輸出范圍文…

MATLAB-振動問題:單自由度阻尼振動系統受迫振動

一、基本理論 二、MATLAB實現 單自由度阻尼振動系統受迫振動&#xff0c;MATLAB代碼如下&#xff1a; clear; clc; close allA 1; psi 0; F0 10; D 20; Rm 0.5; M 1; omega 2; delta Rm / (2*M); omega0 sqrt(D / M); Omega sqrt(omega0^2 - delta^2); Zm Rm i *…

多線程的三種創建方式

繼承Thread類的方式進行實現 public class MyThread extends Thread{ Override public void run(){//多線程具體業務邏輯} }在main方法里面創建子類對象&#xff0c;開啟線程 public static void main(String[] args) {MyThread t1 new MyThread(); MyThread t2 new MyThrea…

LLM大模型工程師面試經驗寶典--基礎版(2024.7月最新)

1.簡單介紹一下大模型【LLMs】&#xff1f; 大模型&#xff1a;一般指1億以上參數的模型&#xff0c;但是這個標準一直在升級&#xff0c;目前萬億參數以上的模型也有了。大語言模型&#xff08;Large Language Model&#xff0c;LLM&#xff09;是針對語言的大模型。 2.目前主…

基于布雷格曼偏差校正技術的全變分一維時間序列信號降噪方法(MATLAB R2018A)

信號降噪是信號處理的重要步驟之一&#xff0c;目的是提高所獲得信號數據的質量&#xff0c;以達到更高的定性和定量分析精度。信號降噪能提升信號處理其他環節的性能和人們對信息識別的準確率&#xff0c;給信號處理工作提供更可靠的保證。信號降噪的難點是降低噪聲的同時也會…