LangChain4j
LangChain4j
现在很多公司,都偏向于AI,应用智能化相结合,达到智能化,可自定义化AI,这样让AI可以帮助我们更快的使用应用
什么是LangChain4j
它是Java版本的LangChain,随着大模型的不断发展,如何在程序中更好的利用大模型的能力来提高编程效率是一种趋势,LangChain是这么自己介绍自己的:
LangChain gives developers a framework to construct LLM‑powered apps
easily.
意思是:LangChain提供了一个开发框架,使得开发者可以很容易的用来构建具有LLM能力的应用程序。
LLM就是Large Language Model,也就是常说的大语言模型,简称大模型。
个人认为:大模型时代,如何将大模型能力和传统应用相结合,使得传统应用更加智能,是人工智能时代的趋势。以前一个应用要获得智能,需要企业自己投入资源训练模型,而现在只需要接入大模型即可,这种便利性将使得大模型会应用得更为广泛,而如何将大模型能力和Java编程语言相结合,这就是LangChain4j所做的。
注意,大模型的能力远远不止聊天的能力,而LangChain4j就在帮助我们更好的利用大模型的能力,从而帮我们打造出更加智能的应用。
初识LangChain4j
接下来,让我们与LangChain4j初识一下,新建一个Maven工程,然后添加以下依赖:
<?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.qjc</groupId><artifactId>langchain4j-project-1</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><langchain4j.version>0.27.1</langchain4j.version></properties><dependencies><!--核心--><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j</artifactId><version>${langchain4j.version}</version></dependency><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-open-ai</artifactId><version>${langchain4j.version}</version></dependency><!--日志--><dependency><groupId>org.tinylog</groupId><artifactId>tinylog-impl</artifactId><version>2.6.2</version></dependency><dependency><groupId>org.tinylog</groupId><artifactId>slf4j-tinylog</artifactId><version>2.6.2</version></dependency></dependencies></project>
引入了langchain4j的核心依赖、langchain4j集成OpenAi各个模型的依赖、轻量级实现了slf4j接口的tinylog日志依赖。
和OpenAi的第一次对话
package com.qjc.demo;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
/***
* @projectName langchain4j-project-1
* @packageName com.qjc.demo
* @author qjc
* @description TODO
* @Email [email protected]
* @date 2024-10-12 10:00
**/
public HelloAI {
public static void main(String[] args) {
ChatLanguageModel model = OpenAiChatModel.withApiKey("demo");
String answer = model.generate("你好,你是谁?");
System.out.println(answer);
}
}
运行代码结果为:
你好,我是一个人工智能助手。我可以回答你的问题和提供帮助。有什么可以帮到你的吗?
这样,我们使用LangChain4j第一次成功的和OpenAi的GPT模型进行了对话,正常来说,调用OpenAi的API接口需要在OpenAi的官网去申请ApiKey才能调用成功,而我这里传入的ApiKey为"demo"却也能调通,这是因为:
publicOpenAiChatModel(String baseUrl,String apiKey,String organizationId,String modelName,Double temperature,Double topP,List<String> stop,Integer maxTokens,Double presencePenalty,Double frequencyPenalty,Map<String,Integer> logitBias,String responseFormat,Integer seed,String user,Duration timeout,Integer maxRetries,Proxy proxy,Boolean logRequests,Boolean logResponses,Tokenizer tokenizer){
baseUrl =(String)Utils.getOrDefault(baseUrl,"https://api.openai.com/v1");if("demo".equals(apiKey)){
baseUrl ="http://langchain4j.dev/demo/openai/v1";}//其他代码}
在底层在构造OpenAiChatModel时,会判断传入的ApiKey是否等于"demo",如果等于会将OpenAi的原始API地址"https://api.openai.com/v1"改为"http://langchain4j.dev/demo/openai/v1",这个地址是langchain4j专门为我们准备的一个体验地址,实际上这个地址相当于是"https://api.openai.com/v1"的代理,我们请求代理时,代理会去调用真正的OpenAi接口,只不过代理会将自己的ApiKey传过去,从而拿到结果返回给我们。
所以,真正开发时,需要大家设置自己的apiKey或baseUrl,可以这么设置:
ChatLanguageModel model =OpenAiChatModel.builder().baseUrl("http://langchain4j.dev/demo/openai/v1").apiKey("demo").build();
多轮对话
前面的例子中,我们通过ChatLanguageModel的generate()方法向大模型提出问题:
String answer = model.generate("你好,你是谁?");
那如果我继续向大模型说:
model.generate("请重复")
那么大模型还记得它之前的回答吗?我们先看看效果,代码如下:
packagecom.qjc.demo;importdev.langchain4j.model.chat.ChatLanguageModel;importdev.langchain4j.model.openai.OpenAiChatModel;/***
* @projectName langchain4j-project-1
* @packageName com.qjc.demo
* @author qjc
* @description TODO
* @Email [email protected]
* @date 2024-10-12 10:00
**/publicclassHelloAI{publicstaticvoidmain(String[] args){ChatLanguageModel model =OpenAiChatModel.builder().baseUrl("http://langchain4j.dev/demo/openai/v1").apiKey("demo").build();System.out.println(model.generate("你好,你是谁?"));System.out.println("----");System.out.println(model.generate("请重复"));}}
运行结果如下:
你好,我是一个聊天机器人,可以回答你的问题和进行对话。有什么可以帮助你的吗?
----
请重复
大模型重复了我第二次跟它说的,而不是重复它的第一次回答,这是因为,目前的代码中,每次调用generate()都是一次新的会话,我再举一个例子:
packagecom.qjc.demo;importdev.langchain4j.model.chat.ChatLanguageModel;importdev.langchain4j.model.openai.OpenAiChatModel;/***
* @projectName langchain4j-project-1
* @packageName com.qjc.demo
* @author qjc
* @description TODO
* @Email [email protected]
* @date 2024-10-12 10:00
**/publicclassHelloAI{publicstaticvoidmain(String[] args){ChatLanguageModel model =OpenAiChatModel.builder().baseUrl("http://langchain4j.dev/demo/openai/v1").apiKey("demo").build();System.out.println(model.generate("你好,我是小齐"));System.out.println("----");System.out.println(model.generate("我叫什么"));}}
运行结果为:
你好小齐,有什么可以帮助你的吗?
----
抱歉,我不知道您的名字。您可以告诉我您的名字吗?我可以记住它并以后称呼您。
一样的情况,因为第二次调用generate()方法是一次单独的会话,那么如何做到使得两次或多次generate()在同一个会话中呢?在LangChain4j中有一个ChatMemory组件,它就是专门用来实现会话功能的,但是它需要结合LangChain4j中的AiService来使用,我们后面再介绍,现在我们先使用笨办法来解决多轮对话的问题。
在ChatLanguageModel中有多个generate()重载方法:
defaultStringgenerate(String userMessage){returngenerate(UserMessage.from(userMessage)).content().text();}defaultResponse<AiMessage>generate(ChatMessage... messages){returngenerate(asList(messages));}Response<AiMessage>generate(List<ChatMessage> messages);
我们前面使用的就是第一个generate()方法,而第二个和第三个generate()方法都是接收一个ChatMessage集合,并且返回一个AiMessage,那么ChatMessage和AiMessage分别都表示什么意思呢?
ChatMessage是一个接口,表示聊天消息,它有以下四种实现:
- UserMessage:表示用户发送给大模型的消息
- AiMessage:表示大模型响应给用户的消息
- SystemMessage:也是用户发送给大模型的消息,和UserMessage不同在于,SystemMessage一般是应用程序帮用户设置的,举个例子,假如有一个作家应用,那么“请你扮演一名作家,请帮我写一篇关于春天的作文”,其中“请你扮演一名作家”就是SystemMessage,“请帮我写一篇关于春天的作文”就是UserMessage
- ToolExecutionResultMessage:也是用户发送给大模型的,表示工具的执行结果,关于LangChain4j的工具机制,会在后续介绍,目前可以忽略
我们先重点关注UserMessage和AiMessage,它们就相当于请求和响应,所以如果我们想要实现多轮对话,可以这么实现:
packagecom.qjc.demo;importdev.langchain4j.data.message.AiMessage;importdev.langchain4j.data.message.UserMessage;importdev.langchain4j.memory.ChatMemory;importdev.langchain4j.memory.chat.MessageWindowChatMemory;importdev.langchain4j.model.chat.ChatLanguageModel;importdev.langchain4j.model.openai.OpenAiChatModel;importdev.langchain4j.model.output.Response;/***
* @projectName langchain4j-project-1
* @packageName com.qjc.demo
* @author qjc
* @description TODO
* @Email [email protected]
* @date 2024-10-12 10:00
**/publicclassHelloAI{publicstaticvoidmain(String[] args){ChatLanguageModel model =OpenAiChatModel.builder().baseUrl("http://langchain4j.dev/demo/openai/v1").apiKey("demo").build();UserMessage userMessage1 =UserMessage.userMessage("你好,我是小齐");Response<AiMessage> response1 = model.generate(userMessage1);AiMessage aiMessage1 = response1.content();// 大模型的第一次响应System.out.println(aiMessage1.text());System.out.println("----");// 下面一行代码是重点Response<AiMessage> response2 = model.generate(userMessage1, aiMessage1,UserMessage.userMessage("我叫什么"));AiMessage aiMessage2 = response2.content();// 大模型的第二次响应System.out.println(aiMessage2.text());}}
代码执行结果为:
你好,小齐。有什么可以帮助你的吗?
----
您的名字是周瑜。有什么其他问题我可以帮忙解答吗?
其中重点代码为:
Response<AiMessage> response2 = model.generate(userMessage1, aiMessage1,UserMessage.userMessage("我叫什么"));
同样是问"我叫什么",但是这里我把第一次的问题和答案,也就是我和大模型的历史对话传给了大模型,只有这样,大模型才能结合历史对话知道"我叫什么"。事实上,我们在使用ChatGPT时也是一样的原理,因为ChatGPT需要结合历史对话才更能理解你最新一句话的真正意思。
打字机流式响应
在前面的例子中,当我们通过ChatLanguageModel的generate()方法向大模型提问时,ChatLanguageModel一次性给了整段响应结果,而不是一个字一个字打字机式的回答,不过我们可以使用OpenAiStreamingChatModel来实现打字机效果,代码如下:
packagecom.qjc.demo;importdev.langchain4j.data.message.AiMessage;importdev.langchain4j.memory.ChatMemory;importdev.langchain4j.memory.chat.MessageWindowChatMemory;importdev.langchain4j.model.StreamingResponseHandler;importdev.langchain4j.model.chat.ChatLanguageModel;importdev.langchain4j.model.chat.StreamingChatLanguageModel;importdev.langchain4j.model.openai.OpenAiChatModel;importdev.langchain4j.model.openai.OpenAiStreamingChatModel;importjava.util.concurrent.TimeUnit;/***
* @projectName langchain4j-project-1
* @packageName com.qjc.demo
* @author qjc
* @description TODO
* @Email [email protected]
* @date 2024-10-12 10:00
**/publicclassHelloAI{publicstaticvoidmain(String[] args){StreamingChatLanguageModel model =OpenAiStreamingChatModel.builder().baseUrl("http://langchain4j.dev/demo/openai/v1").apiKey("demo").build();
model.generate("你好,你是谁?",newStreamingResponseHandler<AiMessage>(){@OverridepublicvoidonNext(String token){System.out.println(token);try{TimeUnit.SECONDS.sleep(1);}catch(InterruptedException e){thrownewRuntimeException(e);}}@OverridepublicvoidonError(Throwable error){System.out.println(error);}});}}
这样就能实现打字机效果了。
整合SpringBoot
先引入SpringBoot:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.1</version>
</parent>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-open-ai-spring-boot-starter</artifactId><version>0.27.1</version></dependency>
然后定义SpringBoot启动类:
packagecom.qjc.demo;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;/***
* @projectName langchain4j-project-1
* @packageName com.qjc.demo
* @author qjc
* @description TODO
* @Email [email protected]
* @date 2024-10-12 10:00
**/@SpringBootApplicationpublicclassMain{publicstaticvoidmain(String[] args){SpringApplication.run(Main.class, args);}}
然后定义HelloAIController:
packagecom.qjc.demo.controller;importdev.langchain4j.model.chat.ChatLanguageModel;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RestController;/***
* @projectName langchain4j-project-1
* @packageName com.qjc.demo.controller
* @author qjc
* @description TODO
* @Email [email protected]
* @date 2024-10-12 10:00
**/@RestControllerpublicclassHelloAIController{@AutowiredprivateChatLanguageModel chatLanguageModel;@GetMapping("/hello")publicStringhello(){return chatLanguageModel.generate("你好啊");}}
配置api-key:
langchain4j.open-ai.chat-model.api-key=demo
启动SpringBoot并访问:
ModerationModel
packagecom.qjc.demo;importdev.langchain4j.model.chat.ChatLanguageModel;importdev.langchain4j.model.moderation.Moderation;importdev.langchain4j.model.moderation.ModerationModel;importdev.langchain4j.model.openai.OpenAiChatModel;importdev.langchain4j.model.openai.OpenAiModerationModel;importdev.langchain4j.model.output.Response;/***
* @projectName langchain4j-project-1
* @packageName com.qjc.demo
* @author qjc
* @description TODO
* @Email [email protected]
* @date 2024-10-12 10:00
**/publicclassHelloAI{publicstaticvoidmain(String[] args){ModerationModel moderationModel =OpenAiModerationModel.withApiKey("demo");Response<Moderation> response = moderationModel.moderate("我要灭了你");System.out.println(response.content().flaggedText());}}
ModerationModel能够校验输入中是否存在敏感内容。
ImageModel
packagecom.qjc.demo;importdev.langchain4j.data.image.Image;importdev.langchain4j.model.chat.ChatLanguageModel;importdev.langchain4j.model.image.ImageModel;importdev.langchain4j.model.moderation.Moderation;importdev.langchain4j.model.moderation.ModerationModel;importdev.langchain4j.model.openai.OpenAiChatModel;importdev.langchain4j.model.openai.OpenAiImageModel;importdev.langchain4j.model.openai.OpenAiModerationModel;importdev.langchain4j.model.output.Response;/***
* @projectName langchain4j-project-1
* @packageName com.qjc.demo
* @author qjc
* @description TODO
* @Email [email protected]
* @date 2024-10-12 10:00
**/publicclassHelloAI{publicstaticvoidmain(String[] args){ImageModel imageModel =OpenAiImageModel.builder().baseUrl("http://localhost:3000/v1").apiKey("sk-xxxxxxx").build();Response<Image> response = imageModel.generate("一只兔子");System.out.println(response.content().url());}}
ImageModel可以根据提示词来生成图片,默认提供的“demo”key不能用来生成图片,需要大家自己获取apiKey,也可以用智普的AI。
本节总结
本节我介绍了LangChain4j的基本使用,以及多轮对话、流式响应的实现,其中我们提到了LangChain4j中的工具机制、AiService机制、ChatMemory机制。
如果你觉得我写的不错,请留下评论,或者点个赞收藏,一下是我的动力。
版权归原作者 怎么起个名就那么难 所有, 如有侵权,请联系我们删除。