Freigeben über


Agent-Chatverlauf und Speicher

Agent-Chatverlauf und -speicher sind wichtige Funktionen, mit denen Agents kontextübergreifende Unterhaltungen verwalten, Benutzereinstellungen merken und personalisierte Erfahrungen bereitstellen können. Das Agent Framework bietet mehrere Funktionen für unterschiedliche Anwendungsfälle, von einfachem Speicher im Speicher für Chatnachrichten bis hin zu beständigen Datenbanken und speziellen Speicherdiensten.

Chatverlauf

Verschiedene Speicheroptionen für den Chatverlauf werden vom Agent Framework unterstützt. Die verfügbaren Optionen variieren je nach Agenttyp und den zugrunde liegenden Diensten, die zum Erstellen des Agents verwendet werden.

Dies sind die beiden wichtigsten Szenarien, die unterstützt werden:

  1. Speicher im Arbeitsspeicher: Der Agent basiert auf einem Dienst, der die In-Service-Speicherung des Chatverlaufs nicht unterstützt (z. B. OpenAI-Chatabschluss). Das Agent Framework speichert standardmäßig den vollständigen Chatverlauf im Speicher des AgentThread Objekts, aber Entwickler können bei Bedarf eine benutzerdefinierte ChatMessageStore Implementierung zum Speichern des Chatverlaufs in einem Drittanbieterspeicher bereitstellen.
  2. In-Service-Speicher: Der Agent basiert auf einem Dienst, der die In-Service-Speicherung des Chatverlaufs erfordert (z. B. Azure AI Foundry Persistent Agents). Das Agent Framework speichert die ID des Remotechatverlaufs im AgentThread Objekt, und es werden keine anderen Speicheroptionen für den Chatverlauf unterstützt.

Speicher des Speichers im Speicher des Speichers für den Speicher des Speichers im Speicher des Chatverlauf

Wenn Sie einen Dienst verwenden, der die In-Service-Speicherung des Chatverlaufs nicht unterstützt, speichert das Agent Framework standardmäßig den Chatverlauf im Arbeitsspeicher im AgentThread Objekt. In diesem Fall wird der vollständige Chatverlauf, der im Threadobjekt gespeichert ist, sowie alle neuen Nachrichten, dem zugrunde liegenden Dienst für jeden Agent bereitgestellt. Dies ermöglicht eine natürliche Unterhaltungserfahrung mit dem Agent, bei der der Anrufer nur die neue Benutzernachricht bereitstellt, und der Agent gibt nur neue Antworten zurück, aber der Agent hat Zugriff auf den vollständigen Unterhaltungsverlauf und verwendet ihn beim Generieren seiner Antwort.

Wenn OpenAI Chat Completion als zugrunde liegender Dienst für Agents verwendet wird, führt der folgende Code zu dem Threadobjekt, das den Chatverlauf aus dem Agent ausführt.

AIAgent agent = new OpenAIClient("<your_api_key>")
     .GetChatClient(modelName)
     .CreateAIAgent(JokerInstructions, JokerName);
AgentThread thread = agent.GetNewThread();
Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", thread));

Wenn Nachrichten im Speicher gespeichert sind, ist es möglich, die Liste der Nachrichten aus dem Thread abzurufen und die Nachrichten bei Bedarf direkt zu bearbeiten.

IList<ChatMessage>? messages = thread.GetService<IList<ChatMessage>>();

Hinweis

Das Abrufen von Nachrichten aus dem AgentThread Objekt auf diese Weise funktioniert nur, wenn Speicher im Arbeitsspeicher verwendet wird.

Reduzierung des Chatverlaufs mit In-Memory Speicher

InMemoryChatMessageStore Die integrierte Funktion, die standardmäßig verwendet wird, wenn der zugrunde liegende Dienst den In-Service-Speicher nicht unterstützt, kann mit einem Reduzierter konfiguriert werden, um die Größe des Chatverlaufs zu verwalten. Dies ist hilfreich, um zu vermeiden, dass die Grenzwerte für die Kontextgröße des zugrunde liegenden Diensts überschritten werden.

Dies InMemoryChatMessageStore kann eine optionale Microsoft.Extensions.AI.IChatReducer Implementierung dauern, um die Größe des Chatverlaufs zu verringern. Außerdem können Sie das Ereignis konfigurieren, bei dem die Reduzierung aufgerufen wird, entweder nachdem eine Nachricht zum Chatverlauf hinzugefügt wurde oder bevor der Chatverlauf für den nächsten Aufruf zurückgegeben wird.

Um den InMemoryChatMessageStore Verkürzungsmodus zu konfigurieren, können Sie eine Factory bereitstellen, um für jedes neue eine neue InMemoryChatMessageStoreAgentThread zu erstellen und sie an einen Reduzierungsregler Ihrer Wahl zu übergeben. Dies InMemoryChatMessageStore kann auch ein optionales Auslöserereignis übergeben werden, das auf eine oder InMemoryChatMessageStore.ChatReducerTriggerEvent.AfterMessageAddedmehrere InMemoryChatMessageStore.ChatReducerTriggerEvent.BeforeMessagesRetrieval Auslöser festgelegt werden kann.

AIAgent agent = new OpenAIClient("<your_api_key>")
    .GetChatClient(modelName)
    .CreateAIAgent(new ChatClientAgentOptions
    {
        Name = JokerName,
        Instructions = JokerInstructions,
        ChatMessageStoreFactory = ctx => new InMemoryChatMessageStore(
            new MessageCountingChatReducer(2),
            ctx.SerializedState,
            ctx.JsonSerializerOptions,
            InMemoryChatMessageStore.ChatReducerTriggerEvent.AfterMessageAdded)
    });

Hinweis

Dieses Feature wird nur bei Verwendung des InMemoryChatMessageStoreFeatures unterstützt. Wenn ein Dienst über speicherinternen Chatverlauf verfügt, liegt es bei dem Dienst selbst, die Größe des Chatverlaufs zu verwalten. Ebenso gilt bei Verwendung des Drittanbieterspeichers (siehe unten) die Speicherlösung von Drittanbietern, um die Größe des Chatverlaufs zu verwalten. Wenn Sie einen ChatMessageStoreFactory Nachrichtenspeicher bereitstellen, aber einen Dienst mit integriertem Chatverlaufsspeicher verwenden, wird die Factory nicht verwendet.

Speicher für Inference-Dienstchats

Wenn Sie einen Dienst verwenden, der die In-Service-Speicherung des Chatverlaufs erfordert, speichert das Agent Framework die ID des Remotechatverlaufs im AgentThread Objekt.

Wenn Sie z. B. OpenAI-Antworten mit "store=true" als zugrunde liegender Dienst für Agents verwenden, führt der folgende Code zum Threadobjekt, das die letzte Antwort-ID enthält, die vom Dienst zurückgegeben wird.

AIAgent agent = new OpenAIClient("<your_api_key>")
     .GetOpenAIResponseClient(modelName)
     .CreateAIAgent(JokerInstructions, JokerName);
AgentThread thread = agent.GetNewThread();
Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", thread));

Hinweis

Einige Dienste, z. B. OpenAI-Antworten, unterstützen entweder die In-Service-Speicherung des Chatverlaufs (store=true) oder das Bereitstellen des vollständigen Chatverlaufs für jeden Aufruf (store=false). Je nach dem Modus, in dem der Dienst verwendet wird, speichert das Agent Framework entweder den vollständigen Chatverlauf im Speicher oder speichert einen ID-Verweis auf den gespeicherten Chatverlauf des Diensts.

Speicher des Chatverlaufs von Drittanbietern

Wenn Sie einen Dienst verwenden, der die In-Service-Speicherung des Chatverlaufs nicht unterstützt, können Entwickler den standardmäßigen Speicher im Arbeitsspeicher des Chatverlaufs durch den Speicher des Chatverlaufs von Drittanbietern ersetzen. Der Entwickler muss eine Unterklasse der abstrakten ChatMessageStore Basisklasse bereitstellen.

Die ChatMessageStore Klasse definiert die Schnittstelle zum Speichern und Abrufen von Chatnachrichten. Entwickler müssen die AddMessagesAsync Und GetMessagesAsync Methoden zum Hinzufügen von Nachrichten zum Remotespeicher implementieren, während sie generiert werden, und Nachrichten aus dem Remotespeicher abrufen, bevor Sie den zugrunde liegenden Dienst aufrufen.

Der Agent verwendet alle Nachrichten, die beim GetMessagesAsync Verarbeiten einer Benutzerabfrage zurückgegeben werden. Es liegt an der Implementierung ChatMessageStore , sicherzustellen, dass die Größe des Chatverlaufs das Kontextfenster des zugrunde liegenden Diensts nicht überschreitet.

Bei der Implementierung eines benutzerdefinierten Typs ChatMessageStore , der den Chatverlauf in einem Remotespeicher speichert, sollte der Chatverlauf für diesen Thread unter einem für diesen Thread eindeutigen Schlüssel gespeichert werden. Die ChatMessageStore Implementierung sollte diesen Schlüssel generieren und in seinem Zustand beibehalten. ChatMessageStore verfügt über eine Serialize Methode, die überschrieben werden kann, um den Zustand zu serialisieren, wenn der Thread serialisiert wird. Außerdem ChatMessageStore sollte ein Konstruktor bereitgestellt werden, der als JsonElement Eingabe zur Unterstützung der Deserialisierung des Zustands verwendet wird.

Zum Bereitstellen einer benutzerdefinierten ChatMessageStore Option für ein ChatClientAgent, können Sie beim Erstellen des Agents die ChatMessageStoreFactory Option verwenden. Im Folgenden sehen Sie ein Beispiel, das zeigt, wie Sie die benutzerdefinierte Implementierung ChatMessageStore eines ChatClientAgent Elements übergeben, das auf azure OpenAI-Chatabschluss basiert.

AIAgent agent = new AzureOpenAIClient(
    new Uri(endpoint),
    new AzureCliCredential())
     .GetChatClient(deploymentName)
     .CreateAIAgent(new ChatClientAgentOptions
     {
         Name = JokerName,
         Instructions = JokerInstructions,
         ChatMessageStoreFactory = ctx =>
         {
             // Create a new chat message store for this agent that stores the messages in a custom store.
             // Each thread must get its own copy of the CustomMessageStore, since the store
             // also contains the id that the thread is stored under.
             return new CustomMessageStore(vectorStore, ctx.SerializedState, ctx.JsonSerializerOptions);
         }
     });

Tipp

Ein ausführliches Beispiel zum Erstellen eines benutzerdefinierten Nachrichtenspeichers finden Sie im Lernprogramm zum Speichern des Chatverlaufs in Drittanbieterspeichern .

Langzeitspeicher

Das Agent Framework ermöglicht Es Entwicklern, benutzerdefinierte Komponenten bereitzustellen, die Erinnerungen extrahieren oder Erinnerungen an einen Agent bereitstellen können.

Um eine solche Speicherkomponente zu implementieren, muss der Entwickler die AIContextProvider abstrakte Basisklasse unterklassen. Diese Klasse verfügt über zwei Kernmethoden InvokingAsync und InvokedAsync. Wenn sie außer Kraft gesetzt werden, InvokedAsync können Entwickler alle von Benutzern bereitgestellten oder vom Agent generierten Nachrichten überprüfen. InvokingAsync ermöglicht Entwicklern, zusätzlichen Kontext für eine bestimmte Agentausführung einzugeben. Systemanweisungen, zusätzliche Meldungen und zusätzliche Funktionen können bereitgestellt werden.

Tipp

Ein ausführliches Beispiel zum Erstellen einer benutzerdefinierten Speicherkomponente finden Sie im Lernprogramm zum Hinzufügen von Arbeitsspeicher zu einem Agent .

AgentThread Serialisierung

Es ist wichtig, ein AgentThread Objekt zwischen Agentaufrufen beizubehalten. Dies ermöglicht Situationen, in denen ein Benutzer eine Frage des Agenten stellen kann, und eine lange Zeit in Anspruch nehmen kann, um Nachverfolgungsfragen zu stellen. Dadurch kann der AgentThread Status dienst- oder App-Neustarts überleben.

Selbst wenn der Chatverlauf in einem Remotespeicher gespeichert ist, enthält das AgentThread Objekt weiterhin eine ID, die auf den Remotechatverlauf verweist. Der Verlust des AgentThread Zustands führt daher dazu, dass auch die ID des Remotechatverlaufs verloren geht.

Die AgentThread und alle objekte, die an sie angefügt sind, stellen daher die SerializeAsync Methode zum Serialisieren ihres Zustands bereit. Außerdem AIAgent wird eine DeserializeThread Methode bereitgestellt, mit der ein Thread aus dem serialisierten Zustand neu erstellt wird. Die DeserializeThread Methode erstellt den Thread erneut mit dem ChatMessageStore agent konfigurierten und AIContextProvider konfigurierten Thread.

// Serialize the thread state to a JsonElement, so it can be stored for later use.
JsonElement serializedThreadState = thread.Serialize();

// Re-create the thread from the JsonElement.
AgentThread resumedThread = AIAgent.DeserializeThread(serializedThreadState);

Hinweis

AgentThread Objekte können mehr als nur Chatverlauf enthalten, z. B. Kontextanbieter können den Zustand auch im Threadobjekt speichern. Daher ist es wichtig, das gesamte Objekt immer zu serialisieren, zu speichern und deserialisieren, um sicherzustellen, dass der gesamte AgentThread Zustand beibehalten wird.

Von Bedeutung

Behandeln Sie AgentThread Objekte immer als undurchsichtige Objekte, es sei denn, Sie sind sehr sicher von den Internen. Der Inhalt kann nicht nur nach Agenttyp, sondern auch nach Diensttyp und Konfiguration variieren.

Warnung

Das Deserialisieren eines Threads mit einem anderen Agent als dem, der ihn ursprünglich erstellt hat, oder mit einem Agent, der eine andere Konfiguration als der ursprüngliche Agent aufweist, kann zu Fehlern oder unerwartetem Verhalten führen.

Speichertypen

Das Agent Framework unterstützt mehrere Speichertypen, um unterschiedliche Anwendungsfälle zu berücksichtigen, einschließlich der Verwaltung des Chatverlaufs als Teil des kurzfristigen Speichers und das Bereitstellen von Erweiterungspunkten zum Extrahieren, Speichern und Einfügen von langfristigen Erinnerungen in Agents.

In-Memory Speicher (Standard)

Die einfachste Form des Speichers, in dem der Unterhaltungsverlauf während der Anwendungslaufzeit im Arbeitsspeicher gespeichert wird. Dies ist das Standardverhalten und erfordert keine zusätzliche Konfiguration.

from agent_framework import ChatAgent
from agent_framework.openai import OpenAIChatClient

# Default behavior - uses in-memory storage
agent = ChatAgent(
    chat_client=OpenAIChatClient(),
    instructions="You are a helpful assistant."
)

# Conversation history is maintained in memory for this thread
thread = agent.get_new_thread()

response = await agent.run("Hello, my name is Alice", thread=thread)

Beständige Nachrichtenspeicher

Für Anwendungen, die aufgezeichnete Unterhaltungen über Sitzungen hinweg beibehalten müssen, stellt das Framework Implementierungen bereit ChatMessageStore :

Integrierter ChatMessageStore

Die standardmäßige In-Memory-Implementierung, die serialisiert werden kann:

from agent_framework import ChatMessageStore

# Create a custom message store
def create_message_store():
    return ChatMessageStore()

agent = ChatAgent(
    chat_client=OpenAIChatClient(),
    instructions="You are a helpful assistant.",
    chat_message_store_factory=create_message_store
)

Redis-Nachrichtenspeicher

Für Produktionsanwendungen, die beständigen Speicher erfordern:

from agent_framework.redis import RedisChatMessageStore

def create_redis_store():
    return RedisChatMessageStore(
        redis_url="redis://localhost:6379",
        thread_id="user_session_123",
        max_messages=100  # Keep last 100 messages
    )

agent = ChatAgent(
    chat_client=OpenAIChatClient(),
    instructions="You are a helpful assistant.",
    chat_message_store_factory=create_redis_store
)

Benutzerdefinierter Nachrichtenspeicher

Sie können Ihr eigenes Speicher-Back-End implementieren, indem Sie folgendes ChatMessageStoreProtocolimplementieren:

from agent_framework import ChatMessage, ChatMessageStoreProtocol
from typing import Any
from collections.abc import Sequence

class DatabaseMessageStore(ChatMessageStoreProtocol):
    def __init__(self, connection_string: str):
        self.connection_string = connection_string
        self._messages: list[ChatMessage] = []

    async def add_messages(self, messages: Sequence[ChatMessage]) -> None:
        """Add messages to database."""
        # Implement database insertion logic
        self._messages.extend(messages)

    async def list_messages(self) -> list[ChatMessage]:
        """Retrieve messages from database."""
        # Implement database query logic
        return self._messages

    async def serialize(self, **kwargs: Any) -> Any:
        """Serialize store state for persistence."""
        return {"connection_string": self.connection_string}

    async def update_from_state(self, serialized_store_state: Any, **kwargs: Any) -> None:
        """Update store from serialized state."""
        if serialized_store_state:
            self.connection_string = serialized_store_state["connection_string"]

Tipp

Ein ausführliches Beispiel zum Erstellen eines benutzerdefinierten Nachrichtenspeichers finden Sie im Lernprogramm zum Speichern des Chatverlaufs in Drittanbieterspeichern .

Kontextanbieter (dynamischer Speicher)

Kontextanbieter ermöglichen komplexe Speichermuster, indem sie relevanten Kontext vor jedem Agentaufruf einfügen:

Einfacher Kontextanbieter

from agent_framework import ContextProvider, Context, ChatMessage
from collections.abc import MutableSequence
from typing import Any

class UserPreferencesMemory(ContextProvider):
    def __init__(self):
        self.preferences = {}

    async def invoking(self, messages: ChatMessage | MutableSequence[ChatMessage], **kwargs: Any) -> Context:
        """Provide user preferences before each invocation."""
        if self.preferences:
            preferences_text = ", ".join([f"{k}: {v}" for k, v in self.preferences.items()])
            instructions = f"User preferences: {preferences_text}"
            return Context(instructions=instructions)
        return Context()

    async def invoked(
        self,
        request_messages: ChatMessage | Sequence[ChatMessage],
        response_messages: ChatMessage | Sequence[ChatMessage] | None = None,
        invoke_exception: Exception | None = None,
        **kwargs: Any,
    ) -> None:
        """Extract and store user preferences from the conversation."""
        # Implement preference extraction logic
        pass

Tipp

Ein ausführliches Beispiel zum Erstellen einer benutzerdefinierten Speicherkomponente finden Sie im Lernprogramm zum Hinzufügen von Arbeitsspeicher zu einem Agent .

Externe Speicherdienste

Das Framework unterstützt die Integration mit spezialisierten Speicherdiensten wie Mem0:

from agent_framework.mem0 import Mem0Provider

# Using Mem0 for advanced memory capabilities
memory_provider = Mem0Provider(
    api_key="your-mem0-api-key",
    user_id="user_123",
    application_id="my_app"
)

agent = ChatAgent(
    chat_client=OpenAIChatClient(),
    instructions="You are a helpful assistant with memory.",
    context_providers=memory_provider
)

Thread serialisierung und Persistenz

Das Framework unterstützt die Serialisierung vollständiger Threadzustände zur Persistenz über Anwendungsneustarts hinweg:

import json

# Create agent and thread
agent = ChatAgent(chat_client=OpenAIChatClient())
thread = agent.get_new_thread()

# Have conversation
await agent.run("Hello, my name is Alice", thread=thread)

# Serialize thread state
serialized_thread = await thread.serialize()
# Save to file/database
with open("thread_state.json", "w") as f:
    json.dump(serialized_thread, f)

# Later, restore the thread
with open("thread_state.json", "r") as f:
    thread_data = json.load(f)

restored_thread = await agent.deserialize_thread(thread_data)
# Continue conversation with full context
await agent.run("What's my name?", thread=restored_thread)

Nächste Schritte