Java程序员的AI之LangChain4j(一)从零到企业级AI开发

怎么起个名就那么难 2024-10-16 15:01:01 阅读 83

LangChain4j

LangChain4j什么是LangChain4j初识LangChain4j和OpenAi的第一次对话多轮对话打字机流式响应整合SpringBootModerationModelImageModel本节总结

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工程,然后添加以下依赖:

<code><?xml version="1.0" encoding="UTF-8"?>code>

<project xmlns="http://maven.apache.org/POM/4.0.0"code>

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"code>

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">code>

<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 qjc1024@aliyun.com

* @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"却也能调通,这是因为:

public OpenAiChatModel(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("请重复")

那么大模型还记得它之前的回答吗?我们先看看效果,代码如下:

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 qjc1024@aliyun.com

* @date 2024-10-12 10:00

**/

public class HelloAI {

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("你好,你是谁?"));

System.out.println("----");

System.out.println(model.generate("请重复"));

}

}

运行结果如下:

你好,我是一个聊天机器人,可以回答你的问题和进行对话。有什么可以帮助你的吗?

----

请重复

大模型重复了我第二次跟它说的,而不是重复它的第一次回答,这是因为,目前的代码中,每次调用generate()都是一次新的会话,我再举一个例子:

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 qjc1024@aliyun.com

* @date 2024-10-12 10:00

**/

public class HelloAI {

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("你好,我是小齐"));

System.out.println("----");

System.out.println(model.generate("我叫什么"));

}

}

运行结果为:

你好小齐,有什么可以帮助你的吗?

----

抱歉,我不知道您的名字。您可以告诉我您的名字吗?我可以记住它并以后称呼您。

一样的情况,因为第二次调用generate()方法是一次单独的会话,那么如何做到使得两次或多次generate()在同一个会话中呢?在LangChain4j中有一个ChatMemory组件,它就是专门用来实现会话功能的,但是它需要结合LangChain4j中的AiService来使用,我们后面再介绍,现在我们先使用笨办法来解决多轮对话的问题。

在ChatLanguageModel中有多个generate()重载方法:

default String generate(String userMessage) {

return generate(UserMessage.from(userMessage)).content().text();

}

default Response<AiMessage> generate(ChatMessage... messages) {

return generate(asList(messages));

}

Response<AiMessage> generate(List<ChatMessage> messages);

我们前面使用的就是第一个generate()方法,而第二个和第三个generate()方法都是接收一个ChatMessage集合,并且返回一个AiMessage,那么ChatMessage和AiMessage分别都表示什么意思呢?

ChatMessage是一个接口,表示聊天消息,它有以下四种实现:

UserMessage:表示用户发送给大模型的消息AiMessage:表示大模型响应给用户的消息SystemMessage:也是用户发送给大模型的消息,和UserMessage不同在于,SystemMessage一般是应用程序帮用户设置的,举个例子,假如有一个作家应用,那么“请你扮演一名作家,请帮我写一篇关于春天的作文”,其中“请你扮演一名作家”就是SystemMessage,“请帮我写一篇关于春天的作文”就是UserMessageToolExecutionResultMessage:也是用户发送给大模型的,表示工具的执行结果,关于LangChain4j的工具机制,会在后续介绍,目前可以忽略

我们先重点关注UserMessage和AiMessage,它们就相当于请求和响应,所以如果我们想要实现多轮对话,可以这么实现:

package com.qjc.demo;

import dev.langchain4j.data.message.AiMessage;

import dev.langchain4j.data.message.UserMessage;

import dev.langchain4j.memory.ChatMemory;

import dev.langchain4j.memory.chat.MessageWindowChatMemory;

import dev.langchain4j.model.chat.ChatLanguageModel;

import dev.langchain4j.model.openai.OpenAiChatModel;

import dev.langchain4j.model.output.Response;

/***

* @projectName langchain4j-project-1

* @packageName com.qjc.demo

* @author qjc

* @description TODO

* @Email qjc1024@aliyun.com

* @date 2024-10-12 10:00

**/

public class HelloAI {

public static void main(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来实现打字机效果,代码如下:

package com.qjc.demo;

import dev.langchain4j.data.message.AiMessage;

import dev.langchain4j.memory.ChatMemory;

import dev.langchain4j.memory.chat.MessageWindowChatMemory;

import dev.langchain4j.model.StreamingResponseHandler;

import dev.langchain4j.model.chat.ChatLanguageModel;

import dev.langchain4j.model.chat.StreamingChatLanguageModel;

import dev.langchain4j.model.openai.OpenAiChatModel;

import dev.langchain4j.model.openai.OpenAiStreamingChatModel;

import java.util.concurrent.TimeUnit;

/***

* @projectName langchain4j-project-1

* @packageName com.qjc.demo

* @author qjc

* @description TODO

* @Email qjc1024@aliyun.com

* @date 2024-10-12 10:00

**/

public class HelloAI {

public static void main(String[] args) {

StreamingChatLanguageModel model = OpenAiStreamingChatModel.builder()

.baseUrl("http://langchain4j.dev/demo/openai/v1")

.apiKey("demo")

.build();

model.generate("你好,你是谁?", new StreamingResponseHandler<AiMessage>() {

@Override

public void onNext(String token) {

System.out.println(token);

try {

TimeUnit.SECONDS.sleep(1);

} catch (InterruptedException e) {

throw new RuntimeException(e);

}

}

@Override

public void onError(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启动类:

package com.qjc.demo;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

/***

* @projectName langchain4j-project-1

* @packageName com.qjc.demo

* @author qjc

* @description TODO

* @Email qjc1024@aliyun.com

* @date 2024-10-12 10:00

**/

@SpringBootApplication

public class Main {

public static void main(String[] args) {

SpringApplication.run(Main.class, args);

}

}

然后定义HelloAIController:

package com.qjc.demo.controller;

import dev.langchain4j.model.chat.ChatLanguageModel;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RestController;

/***

* @projectName langchain4j-project-1

* @packageName com.qjc.demo.controller

* @author qjc

* @description TODO

* @Email qjc1024@aliyun.com

* @date 2024-10-12 10:00

**/

@RestController

public class HelloAIController {

@Autowired

private ChatLanguageModel chatLanguageModel;

@GetMapping("/hello")

public String hello(){

return chatLanguageModel.generate("你好啊");

}

}

配置api-key:

langchain4j.open-ai.chat-model.api-key=demo

启动SpringBoot并访问:

在这里插入图片描述

ModerationModel

<code>package com.qjc.demo;

import dev.langchain4j.model.chat.ChatLanguageModel;

import dev.langchain4j.model.moderation.Moderation;

import dev.langchain4j.model.moderation.ModerationModel;

import dev.langchain4j.model.openai.OpenAiChatModel;

import dev.langchain4j.model.openai.OpenAiModerationModel;

import dev.langchain4j.model.output.Response;

/***

* @projectName langchain4j-project-1

* @packageName com.qjc.demo

* @author qjc

* @description TODO

* @Email qjc1024@aliyun.com

* @date 2024-10-12 10:00

**/

public class HelloAI { -- -->

public static void main(String[] args) {

ModerationModel moderationModel = OpenAiModerationModel.withApiKey("demo");

Response<Moderation> response = moderationModel.moderate("我要灭了你");

System.out.println(response.content().flaggedText());

}

}

ModerationModel能够校验输入中是否存在敏感内容。

ImageModel

package com.qjc.demo;

import dev.langchain4j.data.image.Image;

import dev.langchain4j.model.chat.ChatLanguageModel;

import dev.langchain4j.model.image.ImageModel;

import dev.langchain4j.model.moderation.Moderation;

import dev.langchain4j.model.moderation.ModerationModel;

import dev.langchain4j.model.openai.OpenAiChatModel;

import dev.langchain4j.model.openai.OpenAiImageModel;

import dev.langchain4j.model.openai.OpenAiModerationModel;

import dev.langchain4j.model.output.Response;

/***

* @projectName langchain4j-project-1

* @packageName com.qjc.demo

* @author qjc

* @description TODO

* @Email qjc1024@aliyun.com

* @date 2024-10-12 10:00

**/

public class HelloAI {

public static void main(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机制。

如果你觉得我写的不错,请留下评论,或者点个赞收藏,一下是我的动力。



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。