跳到内容

步骤 01 - Quarkus LangChain4j 简介

要开始,请确保您使用 step-01 目录。

此步骤是工作坊的起点。它是一个简单的 Quarkus 应用程序,使用 Quarkus LangChain4j 扩展与 OpenAI 的 GPT-4o 模型进行交互。它是一个简单的聊天机器人,我们将在后续步骤中对其进行扩展。

运行应用程序

使用以下命令运行应用程序

./mvnw quarkus:dev
mvnw 权限问题

如果您遇到有关 mvnw Maven 包装器的错误,您可以通过导航到项目文件夹并执行 chmod +x mvnw 来为该文件授予执行权限。

无法展开 OPENAI_API_KEY 值

如果您遇到一个指示 java.util.NoSuchElementException: SRCFG00011: Could not expand value OPENAI_API_KEY in property quarkus.langchain4j.openai.api-key 的错误,请确保您已将环境变量 OPENAI_API_KEY 设置为您的 OpenAI API 密钥。

这将启动位于 https://:8080 的页面。打开它,然后点击右下角的红色机器人图标开始与聊天机器人聊天。

Miles of Smiles UI

与聊天机器人聊天

聊天机器人通过后端调用 GPT-4o(来自 OpenAI)。您可以进行测试并观察它具有记忆。示例

User: My name is Clement.
AI: Hi Clement, nice to meet you.
User: What is my name?
AI: Your name is Clement.

An example of discussion with the chatbot

这就是为 LLM 构建记忆的方式。 在终端中,您可以观察到后台对 OpenAI 所做的调用。注意角色 ‘user’ (UserMessage) 和 ‘assistant’ (AiMessage)。

# The request -> Sending a message to the LLM
INFO  [io.qua.lan.ope.OpenAiRestApi$OpenAiClientLogger] (vert.x-eventloop-thread-0) Request:
- method: POST
- url: https://api.openai.com/v1/chat/completions
- headers: [Accept: application/json], [Authorization: Be...ex], [Content-Type: application/json], [User-Agent: langchain4j-openai], [content-length: 378]
- body: {
  "model" : "gpt-4o",
  # The conversation so far, including the latest messages
  "messages" : [ {
    "role" : "user", # The role of the message (user or assistant)
    "content" : "My name is Clement."
  }, {
    "role" : "assistant", # Assistant means LLM
    "content" : "Hello, Clement! How can I assist you today?"
  }, {
    "role" : "user", # User means the user (you)
    "content" : "What is my name?"
  } ],
  "temperature" : 1.0,
  "top_p" : 1.0,
  "presence_penalty" : 0.0,
  "frequency_penalty" : 0.0
}

# The response from the LLM
INFO  [io.qua.lan.ope.OpenAiRestApi$OpenAiClientLogger] (vert.x-eventloop-thread-0) Response:
- status code: 200
- headers: [Content-Type: application/json], [Transfer-Encoding: chunked], [Connection: keep-alive], [access-control-expose-headers: X-Request-ID], [openai-organization: user-vyycjqq0phctctikkw1zawlm], [openai-processing-ms: 213], [openai-version: 2020-10-01], [strict-transport-security: max-age=15552000; includeSubDomains; preload], [x-ratelimit-limit-requests: 500], [x-ratelimit-limit-tokens: 30000], [x-ratelimit-remaining-requests: 499], [x-ratelimit-remaining-tokens: 29958], [x-ratelimit-reset-requests: 120ms], [x-ratelimit-reset-tokens: 84ms], [x-request-id: req_2ea6d71590bc8d857260b25d9f414c0c], [CF-Cache-Status: DYNAMIC], [Set-Cookie: __...ne], [X-Content-Type-Options: nosniff], [Set-Cookie: _c...ne], [Server: cloudflare], [CF-RAY: 8c3ed3291afc27b2-LYS], [alt-svc: h3=":443"; ma=86400]
- body: {
  "id": "chatcmpl-A7zaWTn1uMzq7Stw50Ug2Pg9TkBpV",
  "object": "chat.completion",
  "created": 1726468404,
  "model": "gpt-4o-2024-05-13",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Your name is Clement. How can I help you today?",
        "refusal": null
      },
      "logprobs": null,
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 44,
    "completion_tokens": 12,
    "total_tokens": 56,
    "completion_tokens_details": {
      "reasoning_tokens": 0
    }
  },
  "system_fingerprint": "fp_25624ae3a5"
}

与 LLM 交互的一个非常重要的方面是它们的无状态性。要构建对话,您需要重发迄今为止交换过的所有消息。该列表包括用户消息和助手消息。这就是记忆的构建方式,以及 LLM 如何提供上下文相关的响应。我们将在后续步骤中了解如何管理这一点。

应用程序的结构

在继续之前,让我们看一下代码。

如果您打开 pom.xml 文件,您会发现该项目是一个 Quarkus 应用程序,并带有 quarkus-langchain4j-openai 扩展。

<dependency>
    <groupId>io.quarkiverse.langchain4j</groupId>
    <artifactId>quarkus-langchain4j-openai</artifactId>
</dependency>

Quarkus LangChain4j OpenAI 是一个 Quarkus 扩展,它提供了一种与语言模型 (LLM) 交互的简单方法,例如 OpenAI 的 GPT-4o。它实际上可以与任何提供 OpenAI API 的模型(如 vLLMPodman AI Lab)进行交互。Quarkus LangChain4j 抽象了调用模型的复杂性,并提供了一个简单的 API 来与之交互。

在我们的例子中,该应用程序是一个简单的聊天机器人。它使用WebSocket,这就是为什么您也可以在 pom.xml 文件中看到以下依赖项

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-websockets-next</artifactId>
</dependency>

如果您现在打开 src/main/java/dev/langchain4j/quarkus/workshop/CustomerSupportAgentWebSocket.java 文件,您可以看到 Web 套接字是如何实现的

CustomerSupportAgentWebSocket.java
package dev.langchain4j.quarkus.workshop;

import io.quarkus.websockets.next.OnOpen;
import io.quarkus.websockets.next.OnTextMessage;
import io.quarkus.websockets.next.WebSocket;

@WebSocket(path = "/customer-support-agent")
public class CustomerSupportAgentWebSocket {

    private final CustomerSupportAgent customerSupportAgent;

    public CustomerSupportAgentWebSocket(CustomerSupportAgent customerSupportAgent) {
        this.customerSupportAgent = customerSupportAgent;
    }

    @OnOpen
    public String onOpen() {
        return "Welcome to Miles of Smiles! How can I help you today?";
    }

    @OnTextMessage
    public String onTextMessage(String message) {
        return customerSupportAgent.chat(message);
    }
}

基本上,它

  1. 连接打开时欢迎用户
  2. 收到消息时调用 CustomerSupportAgent 类的 chat 方法,并将结果(通过 Web 套接字)发送回用户。

现在让我们看看应用程序的基石,CustomerSupportAgent 接口。

CustomerSupportAgent.java
package dev.langchain4j.quarkus.workshop;

import io.quarkiverse.langchain4j.RegisterAiService;
import jakarta.enterprise.context.SessionScoped;

@SessionScoped
@RegisterAiService
public interface CustomerSupportAgent {

    String chat(String userMessage);
}

此接口用 @RegisterAiService 注释,以表明它是一个 AI 服务。 AI 服务 是由 Quarkus LangChain4j 扩展管理的。它模拟了与 AI 模型的交互。如您所见,它是一个接口,而不是一个具体的类,所以您不需要实现任何东西(感谢 Quarkus!)。Quarkus LangChain4j 将在构建时为您提供实现。因此,您的应用程序只与接口中定义的方法进行交互。

此接口中有一个方法 chat,但您可以根据需要命名该方法。它将用户消息作为输入(由于它是唯一的参数,我们认为它是用户消息)并返回 AI 模型响应。 Quarkus LangChain4j 抽象了这一切。

SessionScoped?

细心的读者可能已经注意到了 @SessionScoped 注释。这是一个 CDI 注释,它将对象的作用域限定为会话。在我们的例子中,会话就是 Web 套接字。会话在用户连接到 Web 套接字时开始,在用户断开连接时结束。此注释表明 CustomerSupportAgent 对象在会话开始时创建,在会话结束时销毁。它影响我们聊天机器人的记忆,因为它会记住在此会话中到目前为止的对话。

到目前为止一切顺利!让我们继续下一步