使用 Quarkus、Langchain4j 和 Jlama 创建纯 Java LLM 注入式应用程序
目前,绝大多数基于 LLM 的应用程序都依赖于专业公司提供的外部服务。这些服务通常提供对巨大、通用模型的访问,这意味着能源消耗和随之而来的成本与这些模型的规模成正比。
更糟糕的是,这种使用模式还带来了隐私和安全方面的担忧,因为几乎不可能确定这些服务提供商最终将如何重新使用其客户的提示,而在某些情况下,这些提示可能还包含敏感信息。
出于这些原因,许多公司正在探索训练或微调小型模型的选项,这些模型并不声称能在通用场景中使用,而是针对特定的业务需求进行了定制,并随后在本地或私有云上运行(在 LLM 术语中称为“服务”)这些模型。
这些专用模型提供的功能需要集成到现有的软件基础设施中,而在企业世界中,这些基础设施通常是用 Java 编写的。这可以通过遵循传统的客户端-服务器架构来实现,例如通过像 Ollama 这样的外部服务器来提供模型,并通过 REST 调用进行查询。虽然这对 Java 开发人员来说不应该有任何特殊问题,但如果他们可以直接在 Java 中使用模型而无需安装其他工具,他们会工作得更有效率。最后,将 LLM 交互直接嵌入到运行应用程序的同一个 Java 进程中,将更容易从本地开发迁移到部署,从而减轻 IT 部门管理外部服务器的负担,从而绕过对更成熟平台工程策略的需求。这就是 Jlama 发挥作用的地方。
如何以及为何使用 Jlama 在纯 Java 中执行 LLM 推理
Jlama 是一个允许在纯 Java 中执行 LLM 推理的库。它支持 Llama、Mistral、Qwen2 和 Granite 等许多 LLM 模型系列。它还开箱即用地实现了许多有用的 LLM 相关功能,例如函数调用、模型量化、专家混合甚至分布式推理。
Jlama 通过 专用的基于 lanchain4j 的扩展 与 Quarkus 良好集成。请注意,出于性能原因,Jlama 使用 Vector API,该 API 在 Java 23 中仍处于预览状态,并很可能在 Java 25 中发布为受支持的功能。
本质上,Jlama 使得在 Java 中服务 LLM 成为可能,直接嵌入到运行 Java 应用程序的同一个 JVM 中,但为什么这会有用呢?实际上,这在许多用例中是可取的,并且具有许多相关优势,例如以下几点:
-
模型与应用程序生命周期相似:有些用例中,模型和使用它的应用程序具有相同的生命周期,因此应用程序新功能的开发也需要模型更改。同样,由于提示高度依赖于模型,当模型更新(即使是通过微调)时,您的提示可能需要被替换。在这些情况下,将模型嵌入应用程序将有助于简化开发周期的版本控制和可追溯性。
-
快速开发/原型制作:无需安装、配置和与外部服务器交互,可以使 LLM 驱动的 Java 应用程序的开发更加容易。
-
轻松测试模型:在 JVM 中嵌入运行 LLM 推理也使得在开发阶段更容易测试不同的模型及其集成。
-
安全性:在运行使用它的应用程序的同一个 JVM 实例中执行模型推理,消除了仅通过 REST 调用与 LLM 交互的需要,从而防止了私有数据泄露,并允许在更细粒度级别上强制执行用户授权。
-
支持单体应用程序:上述优点也将有利于仍然运行单体应用程序的用户,他们可以通过这种方式在不更改其架构或平台的情况下,将 LLM 功能集成到这些应用程序中。
-
监控和可观测性:在纯 Java 中运行 LLM 推理还将简化监控和可观测性,收集关于 LLM 响应可靠性和速度的统计数据。
-
开发者体验:可调试性将以同样的方式得到简化,允许 Java 开发人员在必要时导航和调试 Jlama 代码。
-
分发:能够将 LLM 推理嵌入到同一个 Java 进程中,还可以将模型本身包含到使用它的应用程序的同一个应用程序包中(尽管这可能只在非常特殊的情况下才建议)。
-
边缘友好性:实现和部署自包含的、具备 LLM 功能的 Java 应用程序的能力,使其比客户端/服务器架构更适合边缘环境。
-
嵌入辅助 LLM:许多应用程序,特别是依赖于代理 AI 模式的应用程序,同时使用许多不同的 LLM。例如,一个较小的 LLM 可以用来验证和批准一个较大的 LLM 的响应。在这种情况下,混合方法可能很方便,嵌入较小的辅助 LLM,同时通过专用服务器保持主 LLM 的服务。
网站摘要器:一个纯 Java LLM 驱动的应用程序
为了演示 Quarkus、Langchain4j 和 Jlama 如何轻松创建纯 Java LLM 注入式应用程序,其中 LLM 推理直接嵌入到运行应用程序的同一个 JVM 中,我创建了一个 简单项目,该项目使用 LLM 自动生成维基百科页面或更广泛地来自任何网站的博客文章的摘要。
此视频演示了该应用程序的工作原理。
开箱即用,该项目使用了一个 经过 4 位量化的 Llama-3.2 小型模型。首次编译应用程序时,Jlama 会从 Huggingface 存储库自动将模型下载到本地。但是,您可以通过简单地编辑 application.properties 文件中的 quarkus.langchain4j.jlama.chat-model.model-name 属性来替换此模型并尝试其他模型。
使用 `mvn clean package` 编译并打包项目后,使用它的最简单方法是运行 jar 文件,并将要摘要的文章所在网页的 URL 作为参数传递,例如
java -jar --enable-preview --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.vector target/quarkus-app/quarkus-run.jar https://www.infoq.com/articles/native-java-quarkus/
这将生成如下输出:
$ java -jar --enable-preview --enable-native-access=ALL-UNNAMED --add-modules jdk.incubator.vector target/quarkus-app/quarkus-run.jar https://www.infoq.com/articles/native-java-quarkus/
WARNING: Using incubator modules: jdk.incubator.vector
__ ____ __ _____ ___ __ ____ ______
--/ __ \/ / / / _ | / _ \/ //_/ / / / __/
-/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2024-11-28 11:01:09,295 INFO [io.quarkus] (main) site-summarizer 1.0.0-SNAPSHOT on JVM (powered by Quarkus 3.16.4) started in 0.402s. Listening on: http://0.0.0.0:8080
2024-11-28 11:01:09,299 INFO [io.quarkus] (main) Profile prod activated.
2024-11-28 11:01:09,300 INFO [io.quarkus] (main) Installed features: [cdi, langchain4j, langchain4j-jlama, qute, rest, smallrye-context-propagation, smallrye-openapi, vertx]
2024-11-28 11:01:11,006 INFO [org.mfu.sit.SiteSummarizer] (main) Site crawl took 1701 ms
2024-11-28 11:01:11,010 INFO [org.mfu.sit.SiteSummarizer] (main) Text extraction took 3 ms
2024-11-28 11:01:11,010 INFO [org.mfu.sit.SiteSummarizer] (main) Summarizing content 17749 characters long
2024-11-28 11:01:11,640 INFO [com.git.tja.jla.ten.ope.TensorOperationsProvider] (main) Using Native SIMD Operations (OffHeap)
2024-11-28 11:01:11,647 INFO [com.git.tja.jla.mod.AbstractModel] (main) Model type = Q4, Working memory type = F32, Quantized memory type = I8
The text you provided is a summary of the Kubernetes Native Java series, which is part of the "Native Compilations Boosts Java" article series. The series aims to provide answers to questions about native compilation, such as how to use native Java, when to switch to native Java, and what framework to use.
The text also mentions the following key points:
* Native compilation with GraalVM makes Java in the cloud cheaper.
* Native compilation raises many questions for all Java users, such as how to use native Java, when to switch to native Java, and what framework to use.
* The series will provide answers to these questions.
Overall, the text provides an overview of the Kubernetes Native Java series and its goals, highlighting the importance of native compilation in the cloud and the need for answers to specific questions about native Java.
Here is a summary of the key points:
* Native compilation with GraalVM makes Java in the cloud cheaper.
* Native compilation raises many questions for all Java users, such as how to use native Java, when to switch to native Java, and what framework to use.
* The series will provide answers to these questions.
I hope this summary is helpful. Let me know if you have any further questions or if there's anything else I can help with.
---
Site summarization done in 53851 ms
2024-11-28 11:02:03,164 INFO [io.quarkus] (main) site-summarizer stopped in 0.012s
请注意,有必要使用一些附加参数启动 JVM,以启用对 Vector API 的访问,该 API 仍然是 Java 的预览功能,但 Jlama 在内部使用它来加速计算。
正如项目 README 中更详细解释的那样,有一个专门的操作系统模式来处理维基百科页面,并且还可以通过 REST 端点公开此服务。
该项目的内部实现相对简单:在以编程方式从包含它的 HTML 页面提取要摘要的文本后,该文本通过通常的 Langchain4j AiService 发送给 Jlama 进行处理。
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import io.quarkiverse.langchain4j.RegisterAiService;
import io.smallrye.mutiny.Multi;
import jakarta.inject.Singleton;
@RegisterAiService
@Singleton
public interface SummarizerAiService {
@SystemMessage("""
You are an assistant that receives the content of a web page and sums up
the text on that page. Add key takeaways to the end of the sum-up.
""")
@UserMessage("Here's the text: '{text}'")
Multi<String> summarize(String text);
}
如前所述,尽管此实现看起来与任何其他 LLM 推理引擎集成相同,但在这种情况下,没有对外部服务的远程调用,而是 LLM 推理直接在运行应用程序的同一个 JVM 中执行。
小型定制模型的日益普及以及这些模型在企业软件开发世界的采用这两个趋势的结合,很可能在不久的将来推动类似解决方案的使用。