当前位置:Java -> Java中LLMs的威力:利用Quarkus和LangChain4j
自2022年11月公开发布以来,ChatGPT持续吸引着数以百万计的用户,提高了他们的创造力,同时,也引起了技术爱好者们对其可能的缺点甚至弱点的关注。
ChatGPT以及类似的聊天机器人是一种特殊类型的软件,称为大型语言模型(LLMs),它们已经在自然语言处理(NLP)领域发生了巨大的变革,以提供诸如问答、文本生成和摘要等更新且不太常见的任务。所有这些术语听起来都非常复杂,虽然这篇文章的目标远非阐述LLM的“量子飞跃”,我们试图观察它们是如何工作的,特别是它们如何在Java中使用,并突出它们引人入胜的可能性以及一些潜在问题。让我们开始吧!
NLP指的是构建能够识别、理解和生成人类语言文本的机器。对许多人来说,这可能听起来像是一种新技术,但实际上,它早已存在,就像计算机本身一样。事实上,在信息时代的开端,自动将一种人类语言转换成另一种语言是任何程序员的梦想。
1950年,艾伦·图灵发表了一篇论文,论述说如果一台机器能够产生与人类完全一致的回答,那么它可以被认为是“智能”的。这种方法被称为图灵测试,如今被认为是机器“智能”的一个不完整案例,因为现代程序很容易通过,这些程序被创建为模仿人类言语。
最初的自然语言处理程序采用了一种简单的方法,使用一套规则和启发式,以模仿对话。1966年,麻省理工学院的教授Joseph Weizenbaum发布了历史上第一个聊天机器人Eliza。基于常见语言模式匹配,这个程序通过提出开放式问题并给予通用回应,比如“请继续”,来制造一种对话的幻觉,对于自己不“理解”的句子。
在随后的几十年中,基于规则的文本解析和模式匹配仍然是最常见的NLP方法。到了20世纪90年代,NLP发生了重要的范式转变,将基于规则的方法替换为统计方法。与旧模型试图定义和构建语法不同,新模型被设计为通过“训练”来“学习”语言模式。现在,成千上万的文档被用来向NLP程序提供数据,以便“教会”它们特定的语言模型。因此,人们开始“训练”程序进行文本生成、分类或其他自然语言任务,在初期,这个过程是基于输入序列,模型将其分割成标记(通常是单词或部分单词),然后将其转换成训练算法给出的相关数学表示。最后,这个特定的表示被转回为标记,以产生可读的结果。这种来回标记处理的过程被称为编码-解码。
2014年,NLP研究人员发现了传统方法通过逐片传递序列至编码器-解码器模型的另一个替代方案。这种称为“注意力”的新方法,是让解码器搜索完整的输入序列,试图找到从语言模型角度来说最相关的片段。几年后,谷歌发布了一篇名为《注意力机制全满足你的需求》的论文。它表明,基于这种新的注意力原则的模型速度更快且可并行化。它们被称为“变压器”。
变压器标志着LLMs的开始,因为它们使得训练规模更大的模型成为可能。2018年,OpenAI推出了第一个LLM,名为生成式预训练变压器(GPT)。这个LLM是基于变压器的,它使用了大量的未标记数据进行训练,然后可以被微调到特定任务,比如机器翻译、文本分类、情感分析等。同年引入的另一个LLM,BERT(双向编码器表示变压器),由谷歌开发,使用了更庞大的训练数据,包括数十亿个词和超过1亿个参数。
与以往的NLP程序不同,这些LLMs并非特定于任务。相反,它们只是被训练来预测在模型特定的上下文中最合适的标记。它们被应用到不同的领域,并且正在成为我们日常生活的一个重要组成部分。苹果的Siri、亚马逊的Alexa或谷歌的智能音响能够监听查询,将声音转化为文本,并回答问题。它们的通用性和多功能性导致了广泛范围的自然语言任务,包括但不限于:
LLMs的任务在于以高度灵活的方式为各种情况生成文本,这使得它们在与人类对话方面非常出色。聊天机器人专门设计用于对话。ChatGPT是最知名的,但还有很多其他类似的:
嵌入企业级应用程序的对话式LLMs是教育、医疗保健、网络内容生成、化学、生物学等领域的理想解决方案。聊天机器人和虚拟助手可以通过获得对话式LLMs的能力来提供动力支持。在传统应用程序中集成LLMs需要它们提供一致的API。为了从应用程序调用这些API,需要一个工具包,能够与AI模型进行交互并促进定制创建。
自ChatGPT推出以来,人工智能领域发生了许多快速发展,其中LLM工具包经历了一场真正的爆炸。其中一些最知名的工具包,如AutoGPT、MetaGPT、AgentGPT等,都试图乘势而起。但毫无疑问,其中最现代、同时也是最受关注的是 LangChain。LangChain 作为一个开源库,最初由Harrison Chase开发,于2022年发布,支持Python、JavaScript和TypeScript,不久之后成为了人工智能领域中增长最快的项目之一。
但尽管它备受欢迎,LangChain也存在一个主要的缺点:缺乏对Java的支持。因此,为了解决这个问题,LangChain4j在2023年初应运而生,成为LangChain Python库的Java实现。在下面的演示中,我们将使用LangChain4j来实现由最主导和有影响力的LLMs支持的企业级Java服务和组件。
为了说明我们的论述,我们将使用一个执行自然语言任务的简单Java程序。我们选择用来实现这一目的的用例是实现一个能够创作俳句的AI服务。对于不了解俳句是什么的人来说,以下是Britannica对其的定义:
“由17个音节组成的无韵诗形式,分布在三行中,分别是5、7和5个音节。”
正如你所看到的,这样的任务的实用性并不是很突出,事实上,这不仅仅是一个真正的用例,更是一个用来展示LangChain4j特征的借口,同时使用一个欢乐而有希望的原创形式。
因此,我们的项目是一个maven多模块项目,具有以下结构:
我们的项目是一个Quarkus项目。因此,使用以下Bill of Material(BOM)如下:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-bom</artifactId>
<version>${quarkus.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
它使用Quarkus 3.8.3,Java 17和LangChain4j 0.25.0。
这个名为haiku的模块使用quarkus-resteasy-reactive-jackson Quarkus扩展来公开一个REST API:
@Path("/haiku")
public class HaikuResource
{
private final HaikuService haikuService;
public HaikuResource(HaikuService haikuService)
{
this.haikuService = haikuService;
}
@GET
public String makeHaiku(@DefaultValue("samurai") @RestQuery String subject)
{
return haikuService.writeHaiku(subject);
}
}
如你所见,这个API定义了一个端点,监听GET HTTP请求,接受俳句主题作为查询参数,其中包含默认值:“武士”。该模块还使用quarkus-container-image-jib Quarkus扩展来创建运行AI服务的Docker镜像。这个Docker镜像的属性在application.properties文件中定义,如下所示:
...
quarkus.container-image.build=true
quarkus.container-image.group=quarkus-llm
quarkus.container-image.name=haiku
quarkus.jib.jvm-entrypoint=/opt/jboss/container/java/run/run-java.sh
...
这些属性说明,新创建的Docker镜像名称将是quarkus-llm/haiku,它的入口将是位于容器/opt/jboss/container/java/run目录中的run-java.sh shell脚本。
这个项目使用了Quarkus扩展quarkus-langchain4j-ollama,它提供了与LangChain4j库和Ollama工具的集成。Ollama是一个先进的AI精简实用工具,允许用户在本地设置和运行大型LLMs,如openai、llama2、mistral等。在这里,我们在本地运行llama2。这是通过在application.properties中进行以下声明来配置的:
quarkus.langchain4j.ollama.chat-model.model-id=llama2:latest
这个声明说明,为了提供AI请求,这里使用的LLM将是llama2的最新版本。现在让我们来看看我们的AI服务本身:
@RegisterAiService
public interface HaikuService
{
@SystemMessage("You are a professional haiku poet")
@UserMessage("Write a haiku about {subject}.")
String writeHaiku(String subject);
}
就是这样!如你所见,我们的AI服务是一个用@RegisterAiService注解进行标注的接口。由Quarkus扩展提供的注解处理器将生成实现这个接口的类。为了能够提供服务,任何对话式LLM都需要一个定义的上下文或范围。
在我们的情况下,这个范围是一个专门创作俳句的艺术家。这就是@SystemMessage注解的作用:设置当前的范围。最后但同样重要的是@UserMessage注解允许我们定义将作为AI服务提示的特定文本。在这里,我们要求我们的AI服务在一个由类型为String的输入参数subject定义的主题上创作俳句。
在审查了我们的AI服务的实现之后,让我们看看如何设置所需的基础设施。名为infra的基础设施模块是一个使用docker-compose实用工具启动以下Docker容器的maven子项目:
以下是创建上述基础设施所需的docker-compose.yaml文件:
version: "3.7"
services:
ollama:
image: nicolasduminil/ollama:llama2
hostname: ollama
container_name: ollama
ports:
- "11434:11434"
expose:
- 11434
haiku:
image: quarkus-llm/haiku:1.0-SNAPSHOT
depends_on:
- ollama
hostname: haiku
container_name: haiku
links:
- ollama:ollama
ports:
- "8080:8080"
environment:
JAVA_DEBUG: "true"
JAVA_APP_DIR: /home/jboss
JAVA_APP_JAR: quarkus-run.jar
如你所见,ollama服务运行在拥有DNS名称ollama的节点上,并在TCP端口号11434上监听。因此,我们的AI服务需要被适当地配置为连接到相同的节点/端口。同样,它的application.properties文件用于此目的,如下所示:
quarkus.langchain4j.ollama.base-url=http://ollama:11434
这个声明意味着我们的人工智能服务将向URL发送请求:http://ollama:11434,其中ollama
会被DNS服务转换为分配给具有相同名称的Docker容器的IP地址。
为了运行和测试这个示例项目,您可以按照以下步骤进行:
克隆存储库:
$ git clone https://github.com/nicolasduminil/llm-java.git
cd
到项目目录:
$ cd llm-java
构建项目:
$ mvn clean install
检查所有必需的容器是否正在运行:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
19006601c908 quarkus-llm/haiku:1.0-SNAPSHOT "/opt/jboss/containe…" 5 seconds ago Up 4 seconds 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp, 8443/tcp haiku
602e6bb06aa9 nicolasduminil/ollama:llama2 "/bin/ollama serve" 5 seconds ago Up 4 seconds 0.0.0.0:11434->11434/tcp, :::11434->11434/tcp ollama
运行open-api接口以测试服务。在您喜欢的浏览器中访问:http://localhost:8080/q/swaggerui。在标记为Haiku API
的Swagger对话框中,单击GET
按钮,并使用Try it
功能执行测试。在标记为Subject
的文本字段中,输入您希望我们的AI服务撰写俳句的主题名称,或保留默认值samurai
。下图显示了测试结果:
您还可以通过使用curl
工具向我们的AI服务发送GET请求来测试项目,如下所示:
$ curl http://localhost:8080/haiku?subject=quarkus
Quarkus, tiny gem
In the cosmic sea of space
Glints like a star
在上面的演示中,我们探讨了LLMs的历史,并使用LangChain4J实现了由最主导和有影响力的LLMs支持的企业级Java服务和组件。
希望您喜欢本文介绍的LLMs以及如何在Java空间中实现它们的内容!
推荐阅读: 阿里巴巴面经(14)
本文链接: Java中LLMs的威力:利用Quarkus和LangChain4j