首頁 Java java教程 使用 Java Quarkus Langchain 建立可靠的 AI 代理程式 - 記憶體部分

使用 Java Quarkus Langchain 建立可靠的 AI 代理程式 - 記憶體部分

Nov 18, 2024 am 01:26 AM

作者

@herbertbeckman - LinkedIn
@rndtavares - LinkedIn

文章部分內容

  1. 使用 Java Quarkus Langchain4j 生產的可靠 AI 代理 - 第 1 部分 - AI 即服務

  2. Java Quarkus Langchain4j 產品中的可靠 AI 代理 - 第 2 部分 - 記憶體(本文)

  3. 使用 Java Quarkus Langchain4j 生產的可靠 AI 代理 - 第 3 部分 - RAG(即將推出)

  4. 使用 Java Quarkus Langchain4j 生產的可靠 AI 代理 - 第 4 部分 - Guardrails(即將推出)

介紹

當我們建立代理時,我們必須記住LLM不儲存任何類型的信息,也就是說,它們是無狀態的。為了讓我們的智能體具有「記住」資訊的能力,我們必須實現記憶體管理。 Quarkus 已經為我們提供了配置的預設內存,但是,如果沒有採取適當的措施,它實際上可以透過炸毀可用的 RAM 內存來關閉您的代理,如 Quarkus 文件中所述。為了不再出現這個問題,並且能夠在可擴展的環境中使用我們的代理,我們需要一個 ChatMemoryStore。

概念

我們使用聊天與我們的代理進行交互,我們必須了解一些重要的概念,以便我們與他的交互能夠以最好的方式進行,並且不會導致生產中的錯誤。首先我們需要知道與他互動時使用的訊息類型,它們是:

  • 用戶訊息:最終客戶發送的訊息或請求。當我們在 Quarkus DevUI 中發送訊息時,我們總是發送 UserMessage。此外,它也用在我們之前看到的工具呼叫結果中。

  • AI訊息(AiMessage):來自模型的回應訊息。每當 LLM 回覆我們的代理商時,他都會收到這樣的訊息。此類訊息在文字回應和工具執行請求之間交替其內容。

  • SystemMessage:此訊息只能定義一次,且僅在開發時有效。

現在您已經了解了我們擁有的 3 種訊息類型,讓我們解釋一下它們在某些圖形中的表現方式。所有圖形均取自 Deandrea、Andrianakis、Escoffier 的簡報《Java meet AI: Build LLM-Powered Apps with LangChain4j》,我強烈推薦該影片。

第一張圖示範了 3 種類型的訊息的使用。 UserMessage 為藍色,SystemMessage 為紅色,AiMessage 為綠色。

Agente de IA confiável em prod com Java   Quarkus   Langchain- Parte  Memória

第二張圖示範如何管理「記憶體」。一個有趣的細節是,我們必須保持訊息中的一定順序,並且必須尊重一些前提。

Agente de IA confiável em prod com Java   Quarkus   Langchain- Parte  Memória

  • 只能有 1 個 SystemMessage 類型的訊息;
  • 在 SystemMessage 之後,訊息應始終按順序在 UserMessage 和 AiMessage 之間切換。如果 AiMessage 之後有 AiMessage,我們將拋出例外。對於連續的 UserMessage 也是如此。

您應該注意的另一個重要細節是您的 ChatMemory 的大小。互動的記憶體越大,代幣成本就越高,因為法學碩士需要處理更多文字才能提供回應。然後建立一個最適合您的用例的記憶體視窗。一個技巧是檢查客戶的平均訊息數,以了解互動的規模。我們將透過 MessageWindowChatMemory 展示實現,該類別專門在 Langchain4j 中為我們管理此操作。

現在我們已經了解了所有這些概念和前提,讓我們開始動手吧!

配置我們的 ChatMemoryStore

這裡我們將使用 MongoDB 作為 ChatMemoryStore。我們使用 MongoDB 文件並將實例上傳到 Docker。請隨意配置它。

新增與 MongoDB 的連接

讓我們先加入必要的依賴項以使用 Quarkus 連接到 MongoDB。

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-mongodb-panache</artifactId>
</dependency>
登入後複製
登入後複製

依賴關係之後,我們需要在 src/main/resources/application.properties 中新增連線設定。

quarkus.mongodb.connection-string=mongodb://${MONGODB_USER}:${MONGODB_PASSWORD}@localhost:27017
quarkus.mongodb.database=chat_memory
登入後複製
登入後複製

我們仍然無法測試與基礎的連接,因為我們需要先建立實體和儲存庫。

建立我們的實體和儲存庫

現在讓我們實現我們的互動實體。該實體將製作我們的訊息清單。每當有新客戶連線時,就會產生新的互動。如果我們需要重複使用此交互,我們只需輸入相同的交互標識符即可。

package <seupacote>;

import dev.langchain4j.data.message.ChatMessage;
import io.quarkus.mongodb.panache.common.MongoEntity;
import org.bson.codecs.pojo.annotations.BsonId;

import java.util.List;
import java.util.Objects;

@MongoEntity(collection = "interactions")
public class InteractionEntity {

    @BsonId
    private String interactionId;
    private List<ChatMessage> messages;

    public InteractionEntity() {
    }

    public InteractionEntity(String interactionId, List<ChatMessage> messages) {
        this.interactionId = interactionId;
        this.messages = messages;
    }

    public String getInteractionId() {
        return interactionId;
    }

    public void setInteractionId(String interactionId) {
        this.interactionId = interactionId;
    }

    public List<ChatMessage> getMessages() {
        return messages;
    }

    public void setMessages(List<ChatMessage> messages) {
        this.messages = messages;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        InteractionEntity that = (InteractionEntity) o;
        return Objects.equals(interactionId, that.interactionId);
    }

    @Override
    public int hashCode() {
        return Objects.hash(interactionId, messages);
    }
}
登入後複製
登入後複製

我們現在可以建立我們的儲存庫。

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-mongodb-panache</artifactId>
</dependency>
登入後複製
登入後複製

現在我們將實作一些langchain4j元件,ChatMemoryStore和ChatMemoryProvider。 ChatMemoryProvider 是我們將在代理程式中使用的類別。在其中,我們將新增一個 ChatMemoryStore,它將使用我們的儲存庫將訊息儲存在 MongoDB 中。關注 ChatMemoryStore:

quarkus.mongodb.connection-string=mongodb://${MONGODB_USER}:${MONGODB_PASSWORD}@localhost:27017
quarkus.mongodb.database=chat_memory
登入後複製
登入後複製

ChatMemoryProvider 將如下圖所示:

package <seupacote>;

import dev.langchain4j.data.message.ChatMessage;
import io.quarkus.mongodb.panache.common.MongoEntity;
import org.bson.codecs.pojo.annotations.BsonId;

import java.util.List;
import java.util.Objects;

@MongoEntity(collection = "interactions")
public class InteractionEntity {

    @BsonId
    private String interactionId;
    private List<ChatMessage> messages;

    public InteractionEntity() {
    }

    public InteractionEntity(String interactionId, List<ChatMessage> messages) {
        this.interactionId = interactionId;
        this.messages = messages;
    }

    public String getInteractionId() {
        return interactionId;
    }

    public void setInteractionId(String interactionId) {
        this.interactionId = interactionId;
    }

    public List<ChatMessage> getMessages() {
        return messages;
    }

    public void setMessages(List<ChatMessage> messages) {
        this.messages = messages;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        InteractionEntity that = (InteractionEntity) o;
        return Objects.equals(interactionId, that.interactionId);
    }

    @Override
    public int hashCode() {
        return Objects.hash(interactionId, messages);
    }
}
登入後複製
登入後複製

注意 MessageWindowChatMemory。這是我們實作文章開頭提到的訊息視窗的地方。在 maxMessages() 方法中,您必須將其變更為您認為最適合您的場景的數字。我建議使用場景中曾經存在的最大數量的消息,或者使用平均值。這裡我們定義任意數100。

現在讓我們更改代理程式以使用新的 ChatMemoryProvider 並新增 MemoryId。它應該看起來像這樣:

package <seupacote>;

import dev.langchain4j.data.message.ChatMessage;
import io.quarkus.mongodb.panache.PanacheMongoRepositoryBase;

import java.util.List;

public class InteractionRepository implements PanacheMongoRepositoryBase<InteractionEntity, String> {

    public InteractionEntity findByInteractionId(String interactionId) {
        return findById(interactionId);
    }

    public void updateMessages(String interactionId, List<ChatMessage> messages) {
        persistOrUpdate(new InteractionEntity(interactionId, messages));
    }

    public void deleteMessages(String interactionId) {
        deleteById(interactionId);
    }

}
登入後複製

這應該會破壞我們的 AgentWSEndpoint。讓我們對其進行更改,以便它接收交互標識符,並且可以將其用作 MemoryId:

package <seupacote>;

import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.store.memory.chat.ChatMemoryStore;

import java.util.List;
import java.util.Objects;

public class MongoDBChatMemoryStore implements ChatMemoryStore {

    private InteractionRepository interactionRepository = new InteractionRepository();

    @Override
    public List<ChatMessage> getMessages(Object memoryId) {
        var interactionEntity = interactionRepository.findByInteractionId(memoryId.toString());
        return Objects.isNull(interactionEntity) ? List.of() : interactionEntity.getMessages();
    }

    @Override
    public void updateMessages(Object memoryId, List<ChatMessage> messages) {
        interactionRepository.updateMessages(memoryId.toString(), messages);
    }

    @Override
    public void deleteMessages(Object memoryId) {
        interactionRepository.deleteMessages(memoryId.toString());
    }
}

登入後複製

我們現在可以再次測試我們的代理。為此,我們只需在需要時傳遞 UUID 即可連接到 Websocket。您可以在這裡產生新的 UUID,或在 Linux 中使用 uuidgen 指令。

當我們進行測試時,您將不會收到代理的任何回應。發生這種情況是因為代理在將訊息寫入 MongoDB 時遇到問題,它會透過異常向您顯示這一點。為了檢查是否發生了此異常,我們必須在 src/main/resources/application.properties 中新增一個屬性,這是我們希望在 Quarkus 中看到的日誌等級。然後,在其中加入以下行:

package <seupacote>;

import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;

import java.util.function.Supplier;

public class MongoDBChatMemoryProvider implements Supplier<ChatMemoryProvider> {

    private MongoDBChatMemoryStore mongoDBChatMemoryStore = new MongoDBChatMemoryStore();

    @Override
    public ChatMemoryProvider get() {
        return memoryId -> MessageWindowChatMemory.builder()
                .maxMessages(100)
                .id(memoryId)
                .chatMemoryStore(mongoDBChatMemoryStore)
                .build();
    }
}
登入後複製

現在測試代理程式。例外應該是這樣的:

package <seupacote>;

import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;
import io.quarkiverse.langchain4j.RegisterAiService;
import io.quarkiverse.langchain4j.ToolBox;
import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
@RegisterAiService(
        chatMemoryProviderSupplier = MongoDBChatMemoryProvider.class
)
public interface Agent {

    @ToolBox(AgentTools.class)
    @SystemMessage("""
            Você é um agente especializado em futebol brasileiro, seu nome é FutAgentBR
            Você sabe responder sobre os principais títulos dos principais times brasileiros e da seleção brasileira
            Sua resposta precisa ser educada, você pode deve responder em Português brasileiro e de forma relevante a pergunta feita

            Quando você não souber a resposta, responda que você não sabe responder nesse momento mas saberá em futuras versões.
            """)
    String chat(@MemoryId String interactionId, @UserMessage String message);
}
登入後複製

出現這個異常是因為MongoDB無法處理Langchain4j的ChatMessage接口,所以我們必須實作一個編解碼器來實現這一點。 Quarkus 本身已經為我們提供了一個編解碼器,但我們需要明確地說明我們想要使用它。然後,我們將建立 ChatMessageCodec 和 ChatMessageCodecProvider 類,如下所示:

package <seupacote>;

import io.quarkus.websockets.next.OnTextMessage;
import io.quarkus.websockets.next.WebSocket;
import io.quarkus.websockets.next.WebSocketConnection;
import jakarta.inject.Inject;

import java.util.Objects;
import java.util.UUID;

@WebSocket(path = "/ws/{interactionId}")
public class AgentWSEndpoint {

    private final Agent agent;

    private final WebSocketConnection connection;

    @Inject
    AgentWSEndpoint(Agent agent, WebSocketConnection connection) {
        this.agent = agent;
        this.connection = connection;
    }

    @OnTextMessage
    String reply(String message) {
        var interactionId = connection.pathParam("interactionId");
        return agent.chat(
                Objects.isNull(interactionId) || interactionId.isBlank()
                        ? UUID.randomUUID().toString()
                        : interactionId,
                message
        );
    }

}
登入後複製
quarkus.log.level=DEBUG
登入後複製

準備好了!現在我們可以測試並驗證 MongoDB 中的消息。查詢時,我們可以在文件的messages數組中查看這3種訊息。

Agente de IA confiável em prod com Java   Quarkus   Langchain- Parte  Memória

我們系列的第二部分到此結束。我們希望您喜歡它並在第 3 部分中見到您。

以上是使用 Java Quarkus Langchain 建立可靠的 AI 代理程式 - 記憶體部分的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

<🎜>:泡泡膠模擬器無窮大 - 如何獲取和使用皇家鑰匙
4 週前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系統,解釋
4 週前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆樹的耳語 - 如何解鎖抓鉤
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1672
14
CakePHP 教程
1428
52
Laravel 教程
1333
25
PHP教程
1277
29
C# 教程
1257
24
公司安全軟件導致應用無法運行?如何排查和解決? 公司安全軟件導致應用無法運行?如何排查和解決? Apr 19, 2025 pm 04:51 PM

公司安全軟件導致部分應用無法正常運行的排查與解決方法許多公司為了保障內部網絡安全,會部署安全軟件。 ...

如何將姓名轉換為數字以實現排序並保持群組中的一致性? 如何將姓名轉換為數字以實現排序並保持群組中的一致性? Apr 19, 2025 pm 11:30 PM

將姓名轉換為數字以實現排序的解決方案在許多應用場景中,用戶可能需要在群組中進行排序,尤其是在一個用...

如何使用MapStruct簡化系統對接中的字段映射問題? 如何使用MapStruct簡化系統對接中的字段映射問題? Apr 19, 2025 pm 06:21 PM

系統對接中的字段映射處理在進行系統對接時,常常會遇到一個棘手的問題:如何將A系統的接口字段有效地映�...

IntelliJ IDEA是如何在不輸出日誌的情況下識別Spring Boot項目的端口號的? IntelliJ IDEA是如何在不輸出日誌的情況下識別Spring Boot項目的端口號的? Apr 19, 2025 pm 11:45 PM

在使用IntelliJIDEAUltimate版本啟動Spring...

如何優雅地獲取實體類變量名構建數據庫查詢條件? 如何優雅地獲取實體類變量名構建數據庫查詢條件? Apr 19, 2025 pm 11:42 PM

在使用MyBatis-Plus或其他ORM框架進行數據庫操作時,經常需要根據實體類的屬性名構造查詢條件。如果每次都手動...

Java對像如何安全地轉換為數組? Java對像如何安全地轉換為數組? Apr 19, 2025 pm 11:33 PM

Java對象與數組的轉換:深入探討強制類型轉換的風險與正確方法很多Java初學者會遇到將一個對象轉換成數組的�...

電商平台SKU和SPU數據庫設計:如何兼顧用戶自定義屬性和無屬性商品? 電商平台SKU和SPU數據庫設計:如何兼顧用戶自定義屬性和無屬性商品? Apr 19, 2025 pm 11:27 PM

電商平台SKU和SPU表設計詳解本文將探討電商平台中SKU和SPU的數據庫設計問題,特別是如何處理用戶自定義銷售屬...

如何利用Redis緩存方案高效實現產品排行榜列表的需求? 如何利用Redis緩存方案高效實現產品排行榜列表的需求? Apr 19, 2025 pm 11:36 PM

Redis緩存方案如何實現產品排行榜列表的需求?在開發過程中,我們常常需要處理排行榜的需求,例如展示一個�...

See all articles