Heim Java javaLernprogramm Zuverlässiger KI-Agent in Produktion mit Java Quarkus Langchain – Speicherteil

Zuverlässiger KI-Agent in Produktion mit Java Quarkus Langchain – Speicherteil

Nov 18, 2024 am 01:26 AM

Autoren

@herbertbeckman – LinkedIn
@rndtavares – LinkedIn

Teile des Artikels

  1. Zuverlässiger KI-Agent in der Produktion mit Java Quarkus Langchain4j – Teil 1 – KI als Service

  2. Zuverlässiger KI-Agent in Java Quarkus Langchain4j prod – Teil 2 – Speicher (dieser Artikel)

  3. Zuverlässiger KI-Agent in der Entwicklung mit Java Quarkus Langchain4j – Teil 3 – RAG (bald verfügbar)

  4. Zuverlässiger KI-Agent in der Produktion mit Java Quarkus Langchain4j – Teil 4 – Leitplanken (in Kürze verfügbar)

Einführung

Wenn wir einen Agenten erstellen, müssen wir bedenken, dass LLMs keinerlei Informationen speichern, das heißt, sie sind zustandslos. Damit unser Agent sich Informationen „merken“ kann, müssen wir eine Speicherverwaltung implementieren. Quarkus stellt uns bereits einen konfigurierten Standardspeicher zur Verfügung. Allerdings kann es Ihren Agenten buchstäblich lahmlegen, indem es den ihm zur Verfügung gestellten RAM-Speicher sprengt, wie in dieser Quarkus-Dokumentation beschrieben, wenn nicht die nötige Sorgfalt angewendet wird. Um dieses Problem nicht mehr zu haben und unseren Agenten auch in einer skalierbaren Umgebung nutzen zu können, benötigen wir einen ChatMemoryStore.

Konzepte

Wir nutzen einen Chat, um mit unserem Agenten zu interagieren, und es gibt wichtige Konzepte, die wir kennen müssen, damit unsere Interaktion mit ihm bestmöglich ablaufen kann und keine Fehler in der Produktion verursacht. Zuerst müssen wir die Arten von Nachrichten kennen, die wir verwenden, wenn wir mit ihm interagieren. Dies sind:

  • Benutzernachrichten: Die vom Endkunden gesendete Nachricht oder Anfrage. Wenn wir die Nachricht in Quarkus DevUI senden, senden wir immer eine UserMessage. Darüber hinaus wird es auch in den Ergebnissen von Tool-Aufrufen verwendet, die wir zuvor gesehen haben.

  • KI-Nachrichten (AiMessage): Die Antwortnachricht vom Modell. Immer wenn LLM unserem Agenten antwortet, erhält er eine Nachricht wie diese. Der Inhalt dieser Art von Nachricht wechselt zwischen einer Textantwort und Anforderungen zur Toolausführung.

  • SystemMessage: Diese Nachricht kann nur einmal definiert werden und befindet sich nur zur Entwicklungszeit.

Da Sie nun die drei Arten von Nachrichten kennen, die wir haben, erklären wir Ihnen anhand einiger Grafiken, wie sie sich verhalten sollten. Alle Grafiken stammen aus der Präsentation Java meets AI: Build LLM-Powered Apps with LangChain4j von Deandrea, Andrianakis, Escoffier, ich kann das Video wärmstens empfehlen.

Die erste Grafik zeigt die Verwendung der drei Nachrichtentypen. UserMessage in Blau, SystemMessage in Rot und AiMessage in Grün.

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

Diese zweite Grafik zeigt, wie „Speicher“ verwaltet werden sollte. Ein interessantes Detail ist, dass wir eine bestimmte Reihenfolge in den Nachrichten einhalten und einige Prämissen respektieren müssen.

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

  • Es darf nur 1 Nachricht vom Typ SystemMessage vorhanden sein;
  • Nach SystemMessage sollten Nachrichten immer in dieser Reihenfolge zwischen UserMessage und AiMessage wechseln. Wenn wir AiMessage nach AiMessage haben, lösen wir eine Ausnahme aus. Das Gleiche gilt für aufeinanderfolgende UserMessages.

Ein weiteres wichtiges Detail, auf das Sie achten sollten, ist die Größe Ihres ChatMemory. Je größer der Speicher Ihrer Interaktion, desto höher sind die Token-Kosten, da der LLM mehr Text verarbeiten muss, um eine Antwort bereitzustellen. Richten Sie dann ein Speicherfenster ein, das am besten zu Ihrem Anwendungsfall passt. Ein Tipp ist, die durchschnittliche Anzahl der Nachrichten Ihrer Kunden zu überprüfen, um eine Vorstellung vom Umfang der Interaktion zu bekommen. Wir zeigen die Implementierung durch MessageWindowChatMemory, die Klasse, die darauf spezialisiert ist, dies für uns in Langchain4j zu verwalten.

Jetzt, da wir all diese Konzepte und Prämissen kennen, machen wir uns die Hände schmutzig!

Konfigurieren unseres ChatMemoryStore

Hier verwenden wir MongoDB als ChatMemoryStore. Wir verwenden das MongoDB-Dokument und laden eine Instanz auf Docker hoch. Konfigurieren Sie es gerne nach Ihren Wünschen.

Hinzufügen unserer Verbindung zu MongoDB

Beginnen wir mit dem Hinzufügen der notwendigen Abhängigkeit, um mithilfe von Quarkus eine Verbindung zu MongoDB herzustellen.

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-mongodb-panache</artifactId>
</dependency>
Nach dem Login kopieren
Nach dem Login kopieren

Nach den Abhängigkeiten müssen wir die Verbindungseinstellungen in unserem src/main/resources/application.properties hinzufügen.

quarkus.mongodb.connection-string=mongodb://${MONGODB_USER}:${MONGODB_PASSWORD}@localhost:27017
quarkus.mongodb.database=chat_memory
Nach dem Login kopieren
Nach dem Login kopieren

Wir können unsere Verbindung zur Basis immer noch nicht testen, da wir zuerst unsere Entitäten und Repositorys erstellen müssen.

Erstellen unserer Entität und unseres Repositorys

Jetzt implementieren wir unsere Interaktionsentität. Diese Entität wird unsere Liste der Nachrichten erstellen lassen. Immer wenn ein neuer Kunde eine Verbindung herstellt, wird eine neue Interaktion generiert. Wenn wir diese Interaktion wiederverwenden müssen, geben wir einfach denselben Interaktionsbezeichner ein.

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);
    }
}
Nach dem Login kopieren
Nach dem Login kopieren

Wir können jetzt unser Repository erstellen.

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-mongodb-panache</artifactId>
</dependency>
Nach dem Login kopieren
Nach dem Login kopieren

Jetzt werden wir einige langchain4j-Komponenten implementieren, den ChatMemoryStore und den ChatMemoryProvider. ChatMemoryProvider ist die Klasse, die wir in unserem Agenten verwenden werden. Darin werden wir einen ChatMemoryStore hinzufügen, der unser Repository zum Speichern von Nachrichten in unserer MongoDB verwendet. Folgen Sie ChatMemoryStore:

quarkus.mongodb.connection-string=mongodb://${MONGODB_USER}:${MONGODB_PASSWORD}@localhost:27017
quarkus.mongodb.database=chat_memory
Nach dem Login kopieren
Nach dem Login kopieren

Der ChatMemoryProvider sieht folgendermaßen aus:

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);
    }
}
Nach dem Login kopieren
Nach dem Login kopieren

Beachten Sie den MessageWindowChatMemory. Hier implementieren wir das Nachrichtenfenster, das wir am Anfang des Artikels erwähnt haben. In der Methode maxMessages() müssen Sie sie auf die Zahl ändern, die Ihrer Meinung nach für Ihr Szenario am besten geeignet ist. Ich empfehle, die größte Anzahl von Nachrichten zu verwenden, die jemals in Ihrem Szenario vorhanden waren, oder den Durchschnitt zu verwenden. Hier definieren wir die beliebige Zahl 100.

Lassen Sie uns nun unseren Agenten ändern, um unseren neuen ChatMemoryProvider zu verwenden und MemoryId hinzuzufügen. Es sollte so aussehen:

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);
    }

}
Nach dem Login kopieren

Dies sollte unseren AgentWSEndpoint beschädigen. Ändern wir es so, dass es die Interaktionskennung erhält und wir sie als unsere MemoryId verwenden können:

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());
    }
}

Nach dem Login kopieren

Wir können unseren Agenten jetzt erneut testen. Dazu stellen wir einfach eine Verbindung zum Websocket her, indem wir jederzeit eine UUID übergeben. Sie können hier eine neue UUID generieren oder den Befehl uuidgen unter Linux verwenden.

Wenn wir den Test durchführen, erhalten Sie keine Antwort vom Agenten. Dies liegt daran, dass der Agent Probleme beim Schreiben unserer Nachrichten an MongoDB hat und Ihnen dies durch eine Ausnahme anzeigt. Damit wir prüfen können, ob diese Ausnahme auftritt, müssen wir unserer Datei src/main/resources/application.properties eine neue Eigenschaft hinzufügen. Dabei handelt es sich um die Protokollebene, die wir in Quarkus sehen möchten. Fügen Sie dann die folgende Zeile hinzu:

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();
    }
}
Nach dem Login kopieren

Testen Sie jetzt den Agenten. Die Ausnahme sollte folgende sein:

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);
}
Nach dem Login kopieren

Diese Ausnahme tritt auf, weil MongoDB die ChatMessage-Schnittstelle von Langchain4j nicht verarbeiten kann. Daher müssen wir einen Codec implementieren, um dies zu ermöglichen. Quarkus selbst bietet uns bereits einen Codec an, aber wir müssen klarstellen, dass wir ihn nutzen wollen. Anschließend erstellen wir die Klassen ChatMessageCodec und ChatMessageCodecProvider wie folgt:

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
        );
    }

}
Nach dem Login kopieren
quarkus.log.level=DEBUG
Nach dem Login kopieren

Fertig! Jetzt können wir die Nachrichten in unserer MongoDB testen und verifizieren. Bei der Abfrage können wir die drei Nachrichtentypen im Nachrichtenarray des Dokuments überprüfen.

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

Damit endet der zweite Teil unserer Serie. Wir hoffen, es hat euch gefallen und wir sehen uns in Teil 3.

Das obige ist der detaillierte Inhalt vonZuverlässiger KI-Agent in Produktion mit Java Quarkus Langchain – Speicherteil. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

Video Face Swap

Video Face Swap

Tauschen Sie Gesichter in jedem Video mühelos mit unserem völlig kostenlosen KI-Gesichtstausch-Tool aus!

Heißer Artikel

<🎜>: Bubble Gum Simulator Infinity - So erhalten und verwenden Sie Royal Keys
4 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
Nordhold: Fusionssystem, erklärt
4 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
Mandragora: Flüstern des Hexenbaum
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌

Heiße Werkzeuge

Notepad++7.3.1

Notepad++7.3.1

Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version

SublimeText3 chinesische Version

Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1

Senden Sie Studio 13.0.1

Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6

Dreamweaver CS6

Visuelle Webentwicklungstools

SublimeText3 Mac-Version

SublimeText3 Mac-Version

Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Heiße Themen

Java-Tutorial
1672
14
PHP-Tutorial
1277
29
C#-Tutorial
1256
24
Verursacht die Sicherheitssoftware des Unternehmens, die die Anwendung nicht ausführt? Wie kann man es beheben und es lösen? Verursacht die Sicherheitssoftware des Unternehmens, die die Anwendung nicht ausführt? Wie kann man es beheben und es lösen? Apr 19, 2025 pm 04:51 PM

Fehlerbehebung und Lösungen für die Sicherheitssoftware des Unternehmens, die dazu führt, dass einige Anwendungen nicht ordnungsgemäß funktionieren. Viele Unternehmen werden Sicherheitssoftware bereitstellen, um die interne Netzwerksicherheit zu gewährleisten. ...

Wie konvertiere ich Namen in Zahlen, um die Sortierung zu implementieren und die Konsistenz in Gruppen aufrechtzuerhalten? Wie konvertiere ich Namen in Zahlen, um die Sortierung zu implementieren und die Konsistenz in Gruppen aufrechtzuerhalten? Apr 19, 2025 pm 11:30 PM

Lösungen zum Umwandeln von Namen in Zahlen zur Implementierung der Sortierung in vielen Anwendungsszenarien müssen Benutzer möglicherweise in Gruppen sortieren, insbesondere in einem ...

Wie vereinfachte ich Probleme mit der Feldzuordnung im Systemdocking mithilfe des Mapstruct? Wie vereinfachte ich Probleme mit der Feldzuordnung im Systemdocking mithilfe des Mapstruct? Apr 19, 2025 pm 06:21 PM

Die Verarbeitung von Feldzuordnungen im Systemdocken stößt häufig auf ein schwieriges Problem bei der Durchführung von Systemdocken: So kartieren Sie die Schnittstellenfelder des Systems und ...

Wie identifiziert Intellij IDEA die Portnummer eines Spring -Boot -Projekts, ohne ein Protokoll auszugeben? Wie identifiziert Intellij IDEA die Portnummer eines Spring -Boot -Projekts, ohne ein Protokoll auszugeben? Apr 19, 2025 pm 11:45 PM

Beginnen Sie den Frühling mit der Intellijideaultimate -Version ...

Wie kann ich elegante Entitätsklassenvariablennamen erhalten, um Datenbankabfragebedingungen zu erstellen? Wie kann ich elegante Entitätsklassenvariablennamen erhalten, um Datenbankabfragebedingungen zu erstellen? Apr 19, 2025 pm 11:42 PM

Bei Verwendung von MyBatis-Plus oder anderen ORM-Frameworks für Datenbankvorgänge müssen häufig Abfragebedingungen basierend auf dem Attributnamen der Entitätsklasse erstellt werden. Wenn Sie jedes Mal manuell ...

Wie kann ich Java -Objekte sicher in Arrays umwandeln? Wie kann ich Java -Objekte sicher in Arrays umwandeln? Apr 19, 2025 pm 11:33 PM

Konvertierung von Java-Objekten und -Arrays: Eingehende Diskussion der Risiken und korrekten Methoden zur Konvertierung des Guss-Typs Viele Java-Anfänger werden auf die Umwandlung eines Objekts in ein Array stoßen ...

E-Commerce-Plattform SKU und SPU-Datenbankdesign: Wie berücksichtigen Sie sowohl benutzerdefinierte Attribute als auch Attributloses Produkte? E-Commerce-Plattform SKU und SPU-Datenbankdesign: Wie berücksichtigen Sie sowohl benutzerdefinierte Attribute als auch Attributloses Produkte? Apr 19, 2025 pm 11:27 PM

Detaillierte Erläuterung des Designs von SKU- und SPU-Tabellen auf E-Commerce-Plattformen In diesem Artikel werden die Datenbankdesignprobleme von SKU und SPU in E-Commerce-Plattformen erörtert, insbesondere wie man mit benutzerdefinierten Verkäufen umgeht ...

Wie verwendet ich die Redis -Cache -Lösung, um die Anforderungen der Produktranking -Liste effizient zu erkennen? Wie verwendet ich die Redis -Cache -Lösung, um die Anforderungen der Produktranking -Liste effizient zu erkennen? Apr 19, 2025 pm 11:36 PM

Wie erkennt die Redis -Caching -Lösung die Anforderungen der Produktranking -Liste? Während des Entwicklungsprozesses müssen wir uns häufig mit den Anforderungen der Ranglisten befassen, z. B. das Anzeigen eines ...

See all articles