Java Quarkus Langchain を使用した本番環境の信頼性の高い AI エージェント - メモリ部分
著者
@herbertbeckman - LinkedIn
@rndtavares - LinkedIn
記事の一部
Java Quarkus Langchain4j を使用した本番環境の信頼できる AI エージェント - パート 1 - サービスとしての AI
Java Quarkus Langchain4j 製品の信頼できる AI エージェント - パート 2 - メモリ (この記事)
Java Quarkus Langchain4j を使用した本番環境の信頼できる AI エージェント - パート 3 - RAG (近日公開予定)
Java Quarkus Langchain4j を使用した本番環境の信頼できる AI エージェント - パート 4 - ガードレール (近日公開予定)
導入
エージェントを作成するときは、LLM はいかなる種類の情報も保存しない、つまりステートレスであることに留意する必要があります。エージェントが情報を「記憶」できるようにするには、メモリ管理を実装する必要があります。 Quarkus は設定済みのデフォルト メモリをすでに提供していますが、十分な注意を払わないと、この Quarkus ドキュメントで説明されているように、利用可能な RAM メモリを爆破してエージェントを文字通りダウンさせる可能性があります。この問題を解決し、スケーラブルな環境でエージェントを使用できるようにするには、ChatMemoryStore が必要です。
コンセプト
私たちはエージェントと対話するためにチャットを使用します。エージェントとの対話が可能な限り最良の方法で行われ、本番環境でバグが発生しないようにするために、知っておく必要がある重要な概念があります。まず、彼とやり取りするときに使用するメッセージの種類を知る必要があります。
ユーザーメッセージ: エンドカスタマーによって送信されたメッセージまたはリクエスト。 Quarkus DevUI でメッセージを送信するときは、常に UserMessage を送信します。さらに、以前見たツール呼び出しの結果にも使用されています。
AI メッセージ (AiMessage): モデルからの応答メッセージ。 LLM がエージェントに応答すると、次のようなメッセージが届きます。このタイプのメッセージは、テキスト応答とツール実行リクエストの間で内容を交互に返します。
SystemMessage: このメッセージは開発時にのみ 1 回だけ定義できます。
3 種類のメッセージがわかったので、いくつかのグラフィックを使用してメッセージがどのように動作するかを説明しましょう。すべてのグラフィックは、Deandrea、Andrianakis、Escoffier によるプレゼンテーション「Java meets AI: Build LLM-Powered Apps with LangChain4j」から取得したものです。このビデオを強くお勧めします。
最初のグラフは、3 種類のメッセージの使用を示しています。 UserMessage は青、SystemMessage は赤、AiMessage は緑です。
この 2 番目のグラフは、「メモリ」をどのように管理すべきかを示しています。興味深い詳細は、メッセージ内で特定の順序を維持する必要があり、いくつかの前提条件を尊重する必要があるということです。
- SystemMessage タイプのメッセージは 1 つだけである必要があります;
- SystemMessage の後、メッセージは常に UserMessage と AiMessage の間でこの順序で切り替わる必要があります。 AiMessage の後に AiMessage がある場合、例外がスローされます。連続する UserMessage についても同様です。
注意すべきもう 1 つの重要な詳細は、ChatMemory のサイズです。インタラクションのメモリが大きければ大きいほど、LLM が応答を提供するためにより多くのテキストを処理する必要があるため、トークンのコストが高くなります。次に、ユースケースに最適なメモリウィンドウを確立します。ヒントの 1 つは、顧客からのメッセージの平均数を確認して、インタラクションの規模を把握することです。 Langchain4j でこれを管理することに特化したクラスである MessageWindowChatMemory を介して実装を示します。
これらの概念と前提をすべて理解したので、実際に手を動かしてみましょう!
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
最初にエンティティとリポジトリを作成する必要があるため、ベースへの接続をテストすることはできません。
エンティティとリポジトリの作成
次に、Interaction エンティティを実装しましょう。このエンティティには、メッセージのリストが作成されます。新しい顧客が接続するたびに、新しいインタラクションが生成されます。このインタラクションを再利用する必要がある場合は、同じインタラクション識別子を入力するだけです。
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 は、エージェントで使用するクラスです。その中に、リポジトリを使用してメッセージを MongoDB に保存する ChatMemoryStore を追加します。 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 内のメッセージをテストして検証できるようになりました。クエリを実行すると、ドキュメントのメッセージ配列内の 3 種類のメッセージを確認できます。
これでシリーズの第 2 部は終了です。楽しんでいただければ幸いです。パート 3 でお会いしましょう。
以上がJava Quarkus Langchain を使用した本番環境の信頼性の高い AI エージェント - メモリ部分の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック











一部のアプリケーションが適切に機能しないようにする会社のセキュリティソフトウェアのトラブルシューティングとソリューション。多くの企業は、内部ネットワークセキュリティを確保するためにセキュリティソフトウェアを展開します。 ...

多くのアプリケーションシナリオでソートを実装するために名前を数値に変換するソリューションでは、ユーザーはグループ、特に1つでソートする必要がある場合があります...

システムドッキングでのフィールドマッピング処理は、システムドッキングを実行する際に難しい問題に遭遇することがよくあります。システムのインターフェイスフィールドを効果的にマッピングする方法A ...

intellijideaultimatiateバージョンを使用してスプリングを開始します...

データベース操作にMyBatis-Plusまたはその他のORMフレームワークを使用する場合、エンティティクラスの属性名に基づいてクエリ条件を構築する必要があることがよくあります。あなたが毎回手動で...

Javaオブジェクトと配列の変換:リスクの詳細な議論と鋳造タイプ変換の正しい方法多くのJava初心者は、オブジェクトのアレイへの変換に遭遇します...

eコマースプラットフォーム上のSKUおよびSPUテーブルの設計の詳細な説明この記事では、eコマースプラットフォームでのSKUとSPUのデータベース設計の問題、特にユーザー定義の販売を扱う方法について説明します。

Redisキャッシュソリューションは、製品ランキングリストの要件をどのように実現しますか?開発プロセス中に、多くの場合、ランキングの要件に対処する必要があります。
