勤于奋
十年全栈开发经验,精通国外lead广告联盟与策略。

上手实践:Quarkus 集成 LangChain4j 与聊天模型

一、引言

本文将带您学习如何借助 Quarkus LangChain4j 项目,构建基于不同聊天模型的应用程序。Quarkus 提供的 AI 聊天模型接口兼具可移植性与简洁性,能让开发者与各类 AI 模型实现无缝交互。我们将搭建一个示例 Quarkus 应用,实现 OpenAI、Mistral AI 和 Ollama 这三款主流聊天模型的切换使用。

本文是 Quarkus LangChain4j 系列 AI 教程的第一篇,后续还会在博客中推出更多相关内容。本教程的设计思路与 Spring AI 系列教程相似,示例应用的功能也与对应的 Spring Boot 应用保持一致,方便您直观对比两种框架的实现方式。

若您对 Quarkus 感兴趣,可前往博客的 Quarkus 分类专栏,查找更多您关注的主题内容。

二、源码获取

如果您想亲自实践本教程的示例,可直接使用我提供的源码。操作步骤很简单:先克隆我的 GitHub 示例仓库,再按照文中的步骤指引执行即可。

三、开发背景

每次撰写 AI 相关的文章或开发示例时,我都会先明确要解决的问题。本次示例要解决的问题其实很常见:在讲解复杂技术概念时,我常会发布一些小型演示应用,而这些应用通常需要测试数据来展示输出效果。以往,我要么手动添加演示数据,要么使用 Datafaker 这类库自动生成数据;这次,我们可以借助 AI 聊天模型的 API 来实现这一需求——接下来,就让我们正式开始吧!

值得一提的是,我此前已针对 Spring Boot 框架讲解过类似主题。若您想对比两种框架在 AI 聊天模型简单交互场景下的功能差异,可参考这篇关于 Spring AI 的文章。

四、依赖配置

本示例应用使用的是当前最新版本的 Quarkus 框架,首先需要配置依赖管理与核心依赖:

4.1 依赖管理配置

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>io.quarkus.platform</groupId>
            <artifactId>quarkus-bom</artifactId>
            <version>${quarkus.platform.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

4.2 多 AI 模型依赖切换

通过激活特定的 Maven Profile,可轻松实现不同 AI 模型依赖的切换。默认情况下,open-ai Profile 处于激活状态,会在 Maven 依赖中引入 quarkus-langchain4j-openai 模块;若激活 mistral-aiollama Profile,则会分别替换为 quarkus-langchain4j-mistral-aiquarkus-langchain4j-ollama 模块。

<profiles>
    <profile>
        <id>open-ai</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <dependencies>
            <dependency>
                <groupId>io.quarkiverse.langchain4j</groupId>
                <artifactId>quarkus-langchain4j-openai</artifactId>
                <version>${quarkus-langchain4j.version}</version>
            </dependency>
        </dependencies>
    </profile>
    <profile>
        <id>mistral-ai</id>
        <dependencies>
            <dependency>
                <groupId>io.quarkiverse.langchain4j</groupId>
                <artifactId>quarkus-langchain4j-mistral-ai</artifactId>
                <version>${quarkus-langchain4j.version}</version>
            </dependency>
        </dependencies>
    </profile>
    <profile>
        <id>ollama</id>
        <dependencies>
            <dependency>
                <groupId>io.quarkiverse.langchain4j</groupId>
                <artifactId>quarkus-langchain4j-ollama</artifactId>
                <version>${quarkus-langchain4j.version}</version>
            </dependency>
        </dependencies>
    </profile>
</profiles>

4.3 核心与测试依赖

示例应用功能简洁,主要通过暴露 REST 接口与选定的 AI 模型交互,并返回 AI 生成的响应。因此,只需引入 quarkus-rest-jacksonquarkus-arc 等核心 Quarkus 模块即可。为实现 REST API 的 JUnit 测试,还需在 test 作用域中引入 quarkus-junit5rest-assured 模块。

<dependencies>
    <!-- 核心 Quarkus 依赖 -->
    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-rest-jackson</artifactId>
    </dependency>
    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-arc</artifactId>
    </dependency>

    <!-- 测试依赖 -->
    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-junit5</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>io.rest-assured</groupId>
        <artifactId>rest-assured</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

五、Quarkus LangChain4j 聊天模型集成

Quarkus 提供了一套创新的 AI 聊天模型交互方案,核心步骤如下:首先,通过 @RegisterAiService 注解标记接口,并定义面向 AI 的方法;然后,在 @SystemMessage@UserMessage 注解中分别添加系统提示与用户输入提示。

下面以 PersonAiService 接口为例,该接口定义了两个核心方法:

  • generatePersonList:请求 AI 模型生成 10 个唯一的人员数据,且数据格式需与输入对象结构一致;
  • getPersonById:从聊天记忆中读取之前生成的人员列表,返回指定 id 对应的人员数据。

5.1 AI 服务接口实现

@RegisterAiService
@ApplicationScoped
public interface PersonAiService {

    @SystemMessage("""
        你是一个擅长生成真实人员数据的助手,始终以有效的 JSON 格式返回结果。
        """)
    @UserMessage("""
        生成恰好 10 个唯一的人员数据,需满足以下要求:
        - 每个人员必须有唯一的整数 ID(如 1、2、3 等);
        - 结合不同国籍,使用真实的姓名;
        - 年龄范围在 18 至 80 岁之间;
        - 仅返回 JSON 数组,不要包含任何额外文本。
        """)
    PersonResponse generatePersonList(@MemoryId int userId);

    @SystemMessage("""
        你是一个能够从聊天记忆中调取已生成人员数据的助手。
        """)
    @UserMessage("""
        在之前为用户 {userId} 生成的人员列表中,查找并返回 ID 为 {id} 的人员数据。
        仅返回 JSON 对象,不要包含任何额外文本。
        """)
    Person getPersonById(@MemoryId int userId, int id);
}

关于上述代码,有几点补充说明:

  1. 作用域配置@RegisterAiService 注解默认创建的 Bean 为 @RequestScoped 作用域,但 Quarkus LangChain4j 文档指出,为了确保 getPersonById 方法能通过 @MemoryId 找到对应用户生成的人员列表,需将 PersonAiService 接口标记为 @ApplicationScoped
  2. 聊天记忆存储:默认已启用 InMemoryChatMemoryStore 实现,无需额外声明 Bean 即可直接使用。

5.2 数据模型定义

Quarkus LangChain4j 可自动将 LLM 返回的 JSON 响应映射为输出 POJO,但目前暂不支持直接映射到集合类型,因此需要用一个包装类包裹人员列表:

5.2.1 人员列表包装类

public class PersonResponse {
    private List<Person> persons;

    public List<Person> getPersons() {
        return persons;
    }

    public void setPersons(List<Person> persons) {
        this.persons = persons;
    }
}

5.2.2 人员实体类

public class Person {
    private Integer id;
    private String firstName;
    private String lastName;
    private int age;
    private String nationality;
    private Gender gender;

    // GETTER 和 SETTER 方法
}

5.3 REST 控制器实现

最后,通过 REST 控制器注入并使PersonAiService,与 AI 聊天模型交互。控制器暴露两个接口:

  • GET /api/{userId}/persons:根据 userId 生成人员列表;
  • GET /api/{userId}/persons/{id}:根据 userIdid 查询指定人员数据。
@Path("/api")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class PersonController {
    private static final Logger LOG = Logger.getLogger(PersonController.class);

    PersonAiService personAiService;

    public PersonController(PersonAiService personAiService) {
        this.personAiService = personAiService;
    }

    @GET
    @Path("/{userId}/persons")
    public PersonResponse generatePersons(@PathParam("userId") int userId) {
        return personAiService.generatePersonList(userId);
    }

    @GET
    @Path("/{userId}/persons/{id}")
    public Person getPersonById(@PathParam("userId") int userId, @PathParam("id") int id) {
        return personAiService.getPersonById(userId, id);
    }
}

六、Quarkus LangChain4j 多 AI 模型切换使用

6.1 配置文件设置

application.properties 文件中配置 AI 模型相关参数。使用前,需先生成 OpenAI 和 Mistral AI 的 API 令牌,并将其作为环境变量导出;此外,还可启用 AI 模型通信过程中的请求与响应日志记录,并将单次请求的默认超时时间从 10 秒延长至 20 秒(或更长)。

# 配置 AI 模型提供商,默认使用 openai
quarkus.langchain4j.chat-model.provider = ${AI_MODEL_PROVIDER:openai}
# 启用请求和响应日志
quarkus.langchain4j.log-requests = true
quarkus.langchain4j.log-responses = true

# OpenAI 配置
quarkus.langchain4j.openai.api-key = ${OPEN_AI_TOKEN}
quarkus.langchain4j.openai.timeout = 20s

# Mistral AI 配置
quarkus.langchain4j.mistralai.api-key = ${MISTRAL_AI_TOKEN}
quarkus.langchain4j.mistralai.timeout = 20s

# Ollama 配置
quarkus.langchain4j.ollama.base-url = ${OLLAMA_BASE_URL:http://localhost:11434}

6.2 集成 OpenAI 模型

运行示例应用并连接 OpenAI 时,需先设置 OPEN_AI_TOKEN 环境变量。由于 open-ai Maven Profile 默认激活,启动应用时无需额外配置其他参数:

# 导出 OpenAI API 令牌
$ export OPEN_AI_TOKEN=<your_openai_token>
# 启动 Quarkus 开发模式
$ mvn quarkus:dev

应用启动后,可调用 GET /api/{userId}/persons 接口(替换 userId 为不同值)生成人员列表,以下是示例请求与响应:

示例 1:请求用户 ID 为 1 的人员列表

$ curl http://localhost:8080/api/1/persons

响应结果:

{
    "persons": [
        {"id":1,"firstName":"Liam","lastName":"Connor","age":25,"nationality":"Irish","gender":"MALE"},
        {"id":2,"firstName":"Sofia","lastName":"Gonzalez","age":34,"nationality":"Spanish","gender":"FEMALE"},
        {"id":3,"firstName":"Akira","lastName":"Takahashi","age":29,"nationality":"Japanese","gender":"MALE"},
        {"id":4,"firstName":"Fatima","lastName":"Ali","age":42,"nationality":"Egyptian","gender":"FEMALE"},
        {"id":5,"firstName":"Lucas","lastName":"Zhang","age":38,"nationality":"Chinese","gender":"MALE"},
        {"id":6,"firstName":"Emma","lastName":"Smith","age":19,"nationality":"American","gender":"FEMALE"},
        {"id":7,"firstName":"Giovanni","lastName":"Rossi","age":57,"nationality":"Italian","gender":"MALE"},
        {"id":8,"firstName":"Maya","lastName":"Patel","age":31,"nationality":"Indian","gender":"FEMALE"},
        {"id":9,"firstName":"Hans","lastName":"Müller","age":46,"nationality":"German","gender":"MALE"},
        {"id":10,"firstName":"Chloe","lastName":"Taylor","age":72,"nationality":"British","gender":"FEMALE"}
    ]
}

示例 2:请求用户 ID 为 2 的人员列表

$ curl http://localhost:8080/api/2/persons

响应结果:

{
    "persons": [
        {"id":1,"firstName":"Liam","lastName":"Sullivan","age":28,"nationality":"Irish","gender":"MALE"},
        {"id":2,"firstName":"Sofia","lastName":"Garcia","age":34,"nationality":"Spanish","gender":"FEMALE"},
        {"id":3,"firstName":"Akira","lastName":"Takahashi","age":45,"nationality":"Japanese","gender":"MALE"},
        {"id":4,"firstName":"Maya","lastName":"Patel","age":22,"nationality":"Indian","gender":"FEMALE"},
        {"id":5,"firstName":"John","lastName":"Doe","age":51,"nationality":"American","gender":"MALE"},
        {"id":6,"firstName":"Elsa","lastName":"Johansen","age":29,"nationality":"Norwegian","gender":"FEMALE"},
        {"id":7,"firstName":"Carlos","lastName":"Silva","age":39,"nationality":"Brazilian","gender":"MALE"},
        {"id":8,"firstName":"Fatima","lastName":"Khan","age":27,"nationality":"Pakistani","gender":"FEMALE"},
        {"id":9,"firstName":"Lucas","lastName":"Schmidt","age":63,"nationality":"German","gender":"MALE"},
        {"id":10,"firstName":"Nina","lastName":"Petrova","age":78,"nationality":"Russian","gender":"FEMALE"}
    ]
}

之后,可调用 GET /api/{userId}/persons/{id} 接口,从聊天记忆中查询指定人员数据:

示例 3:查询用户 ID 为 2、人员 ID 为 4 的数据

$ curl http://localhost:8080/api/2/persons/4

响应结果:

{"id":4,"firstName":"Maya","lastName":"Patel","age":22,"nationality":"Indian","gender":"FEMALE"}

示例 4:查询用户 ID 为 1、人员 ID 为 4 的数据

$ curl http://localhost:8080/api/1/persons/4

响应结果:

{"id":4,"firstName":"Fatima","lastName":"Ali","age":42,"nationality":"Egyptian","gender":"FEMALE"}

6.3 集成 Mistral AI 模型

若需切换到 Mistral AI 模型,需执行以下步骤:

  1. AI_MODEL_PROVIDER 环境变量设置为 mistralai
  2. 导出 Mistral AI 的 API 令牌作为 MISTRAL_AI_TOKEN 环境变量;
  3. 启动应用时激活 mistral-ai Profile。
# 配置 AI 模型提供商为 Mistral AI
$ export AI_MODEL_PROVIDER=mistralai
# 导出 Mistral AI API 令牌
$ export MISTRAL_AI_TOKEN=<your_mistralai_token>
# 激活 mistral-ai Profile 并启动应用
$ mvn quarkus:dev -Pmistral-ai

应用成功启动后,日志会输出类似以下信息:

2025-06-18 16:27:12,334 INFO  [pl.pio.qua.ai.Config] (Quarkus Main Thread) Quarkus AI 示例应用启动
2025-06-18 16:27:12,334 INFO  [pl.pio.qua.ai.Config] (Quarkus Main Thread) 选定的 AI 模型提供商:MISTRALAI
2025-06-18 16:27:12,334 INFO  [pl.pio.qua.ai.Config] (Quarkus Main Thread) 应用已就绪!可用接口:
2025-06-18 16:27:12,334 INFO  [pl.pio.qua.ai.Config] (Quarkus Main Thread) GET /api/{userId}/persons - 生成 10 条人员数据
2025-06-18 16:27:12,334 INFO  [pl.pio.qua.ai.Config] (Quarkus Main Thread) GET /api/{userId}/persons/{id} - 根据 ID 查询人员数据(支持记忆)
2025-06-18 16:27:12,376 INFO  [io.quarkus] (Quarkus Main Thread) ai-showcase 1.0.0-SNAPSHOT 已启动(基于 Quarkus 3.23.3),启动耗时:2.8s
2025-06-18 16:27:12,376 INFO  [io.quarkus] (Quarkus Main Thread) 监听地址:http://localhost:8080
2025-06-18 16:27:12,377 INFO  [io.quarkus] (Quarkus Main Thread) 已激活 dev 环境,启用实时编码功能
2025-06-18 16:27:12,377 INFO  [io.quarkus] (Quarkus Main Thread) 已安装特性:[cdi, langchain4j, langchain4j-mistralai, qute, rest, ...]

此时,可重复执行 OpenAI 场景下的接口请求,获取 Mistral AI 生成的人员数据。应用日志会记录发送给 AI 模型的请求内容,示例如下:

2025-06-18 16:29:52,858 INFO  [io.qua.lan.mis.QuarkusMistralAiClient$MistralAiClientLogger] (vert.x-eventloop-thread-0) 请求:
方法:POST
URL:https://api.mistral.ai/v1/chat/completions
请求头:[...]
请求体:
"You are a helpful assistant that generates realistic person data. Always respond with valid JSON format.
Generate exactly 10 unique persons
Requirements:
- Each person must have a unique integer ID (like 1, 2, 3, etc.)
- Use realistic first and last names per each nationality
- Ages should be between 18 and 80
- Return ONLY the JSON array, no additional text

You must answer strictly in the following JSON format: {
  "persons": [
    {
      "id": (type: integer),
      "firstName": (type: string),
      "lastName": (type: string),
      "age": (type: integer),
      "nationality": (type: string),
      "gender": (type: enum, must be one of [MALE, FEMALE])
    }
  ]
}"

同时,日志也会记录 AI 模型的响应内容,示例如下:

2025-06-18 16:29:57,788 INFO  [io.qua.lan.mis.QuarkusMistralAiClient$MistralAiClientLogger] (vert.x-eventloop-thread-0) 响应:
状态码:200
响应头:[Date: Wed, 18 Jun 2025 14:29:57 GMT, Content-Type: application/json, Content-Length: 2200, ...]
响应体:
{
  "persons": [
    ...
    {"id":5,"firstName":"Hiroshi","lastName":"Tanaka","age":41,"nationality":"Japanese","gender":"MALE"},
    ...
  ]
}

6.4 集成 Ollama 模型

最后,我们来集成 Ollama 模型。默认情况下,LangChain4j Ollama 扩展使用 llama3.2 模型,若需修改模型,可在 application.properties 中配置 quarkus.langchain4j.ollama.chat-model.model-id 属性。以下以使用 llama3.3 模型为例,配置如下:

# Ollama 基础地址配置
quarkus.langchain4j.ollama.base-url = ${OLLAMA_BASE_URL:http://localhost:11434}
# 指定 Ollama 使用的模型 ID
quarkus.langchain4j.ollama.chat-model.model-id = llama3.3
# 设置 Ollama 请求超时时间
quarkus.langchain4j.ollama.timeout = 60s

6.4.1 启动 Ollama 模型

在启动应用前,需先在本地运行 llama3.3 模型(注:llama3.3 模型大小约为 42GB,若设备性能有限,可选择更小的模型):

$ ollama run llama3.3

模型启动过程可能耗时较长,成功启动后会显示类似以下信息:

pulling manifest
pulling 4824460d29f2: 100% |████████████████████████████████| 42 GB
pulling 948af2743fc7: 100% |████████████████████████████████| 1.5 KB
...
verifying sha256 digest
writing manifest
success
>>> Send a message (/? for help)

6.4.2 启动 Quarkus 应用

模型启动后,需将 AI_MODEL_PROVIDER 环境变量设置为 ollama,并激活 ollama Profile 启动应用:

# 配置 AI 模型提供商为 Ollama
$ export AI_MODEL_PROVIDER=ollama
# 激活 ollama Profile 并启动应用
$ mvn quarkus:dev -Pollama

应用成功连接 Ollama 模型后,日志会输出类似以下信息:

2025-06-18 17:07:57,963 INFO  [pl.pio.qua.ai.Config] (Quarkus Main Thread) Quarkus AI 示例应用启动
2025-06-18 17:07:57,964 INFO  [pl.pio.qua.ai.Config] (Quarkus Main Thread) 选定的 AI 模型提供商:OLLAMA
2025-06-18 17:07:57,964 INFO  [pl.pio.qua.ai.Config] (Quarkus Main Thread) 应用已就绪!可用接口:
2025-06-18 17:07:57,964 INFO  [pl.pio.qua.ai.Config] (Quarkus Main Thread) GET /api/{userId}/persons - 生成 10 条人员数据
2025-06-18 17:07:57,964 INFO  [pl.pio.qua.ai.Config] (Quarkus Main Thread) GET /api/{userId}/persons/{id} - 根据 ID 查询人员数据(支持记忆)
2025-06-18 17:07:58,088 INFO  [io.quarkus] (Quarkus Main Thread) ai-showcase 1.0.0-SNAPSHOT 已启动(基于 Quarkus 3.23.3),启动耗时:1.227s
2025-06-18 17:07:58,088 INFO  [io.quarkus] (Quarkus Main Thread) 监听地址:http://localhost:8080
2025-06-18 17:07:58,088 INFO  [io.quarkus] (Quarkus Main Thread) 已激活 dev 环境,启用实时编码功能
2025-06-18 17:07:58,088 INFO  [io.quarkus] (Quarkus Main Thread) 已安装特性:[cdi, langchain4j, langchain4j-ollama, langchain4j-ollama-dev-service, ...]

6.4.3 Ollama Dev Services 支持

Quarkus LangChain4j Ollama 扩展还提供了 Dev Services 功能——无需在本地安装 Ollama 或通过 CLI 启动模型,Quarkus 会自动以 Docker 容器的形式运行 Ollama,并在容器中启动选定的 AI 模型。此时,无需配置 quarkus.langchain4j.ollama.base-url 属性。

若需使用更小的模型(如 mistral),可先修改配置:

quarkus.langchain4j.ollama.chat-model.model-id = mistral

再按照上述方式启动应用,Quarkus 会自动拉取 Ollama 镜像并启动 mistral 模型,日志会输出类似以下镜像拉取信息:

2025-06-18 17:18:13,859 INFO  [tc.ollama/ollama:latest] (docker-java-stream-1866434735) 开始拉取镜像
2025-06-18 17:18:14,521 INFO  [tc.ollama/ollama:latest] (docker-java-stream-1866434735) 拉取镜像层:3 个待拉取,1 个已下载,0 个已解压
...
2025-06-18 17:19:45,177 INFO  [tc.ollama/ollama:latest] (docker-java-stream-1866434735) 拉取完成,共 5 个镜像层,耗时 91 秒(下载速度:26 MB/s,总大小:2 GB)
2025-06-18 17:19:48,127 INFO  [io.qua.lan.oll.dep.dev.OllamaDevServicesProcessor] (build-57) Ollama Dev Services 已启动
2025-06-18 17:21:08,044 INFO  [io.qua.lan.oll.dep.dev.DevServicesOllamaProcessor] (HttpClient-2-Worker-2) 正在下载 mistral 模型,进度:...

七、总结

不得不说,Quarkus LangChain4j 扩展的使用体验十分出色——只需几个简单的注解,就能轻松配置应用,与选定的 AI 模型实现交互。本文通过一个简洁的示例,演示了 Quarkus 与 AI 聊天模型的集成过程,同时也简要介绍了提示词设计、结构化输出、聊天记忆等核心功能。

后续,我会继续推出 Quarkus AI 系列的更多文章,深入探讨更多进阶用法,敬请期待!

赞(0) 打赏
未经允许不得转载:勤于奋 » 上手实践:Quarkus 集成 LangChain4j 与聊天模型

评论 抢沙发

觉得文章有用就打赏一下文章作者

非常感谢你的打赏,我们将继续提供更多优质内容,让我们一起创建更加美好的网络世界!

支付宝扫一扫

微信扫一扫