Udostępnij przez


Historia czatu agenta i pamięć

Historia czatów agentów i pamięć to kluczowe możliwości, które umożliwiają agentom utrzymywanie kontekstu między konwersacjami, zapamiętywania preferencji użytkownika i zapewniania spersonalizowanych środowisk. Struktura agenta udostępnia wiele funkcji odpowiadających różnym przypadkom użycia, od prostego magazynu komunikatów czatu w pamięci po trwałe bazy danych i wyspecjalizowane usługi pamięci.

Historia czatów

Różne opcje przechowywania historii czatów są obsługiwane przez platformę Agent Framework. Dostępne opcje różnią się w zależności od typu agenta i bazowych usług używanych do kompilowania agenta.

Oto dwa główne scenariusze obsługiwane:

  1. Magazyn w pamięci: agent jest oparty na usłudze, która nie obsługuje magazynu w usłudze historii czatu (np. ukończenie czatu OpenAI). Struktura agentów domyślnie przechowuje pełną historię czatów w pamięci w AgentThread obiekcie, ale deweloperzy mogą zapewnić niestandardową ChatMessageStore implementację do przechowywania historii czatów w magazynie innych firm, jeśli jest to wymagane.
  2. Magazyn w usłudze: Agent jest oparty na usłudze, która wymaga magazynu w usłudze historii czatów (np. agentów trwałych usługi Azure AI Foundry). Struktura agenta będzie przechowywać identyfikator historii czatu zdalnego w AgentThread obiekcie i nie są obsługiwane żadne inne opcje przechowywania historii czatów.

Magazyn historii czatów w pamięci

W przypadku korzystania z usługi, która nie obsługuje przechowywania w usłudze historii czatów, struktura agenta będzie domyślnie przechowywać historię czatów w pamięci w AgentThread obiekcie. W takim przypadku pełna historia czatów przechowywana w obiekcie wątku oraz wszelkie nowe wiadomości zostaną udostępnione podstawowej usłudze na każdym uruchomieniu agenta. Pozwala to na naturalne środowisko konwersacyjne z agentem, gdzie obiekt wywołujący udostępnia tylko nową wiadomość użytkownika, a agent zwraca tylko nowe odpowiedzi, ale agent ma dostęp do pełnej historii konwersacji i będzie używać go podczas generowania odpowiedzi.

W przypadku korzystania z uzupełniania czatu OpenAI jako podstawowej usługi dla agentów następujący kod spowoduje, że obiekt wątku zawierający historię czatu z uruchomienia agenta.

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

W przypadku, gdy komunikaty są przechowywane w pamięci, można pobrać listę komunikatów z wątku i manipulować nimi bezpośrednio, jeśli jest to wymagane.

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

Uwaga / Notatka

Pobieranie komunikatów z AgentThread obiektu w ten sposób będzie działać tylko wtedy, gdy jest używany magazyn w pamięci.

Redukcja historii czatów przy użyciu magazynu In-Memory

Wbudowana InMemoryChatMessageStore funkcja, która jest używana domyślnie, gdy podstawowa usługa nie obsługuje magazynu w usłudze, można skonfigurować za pomocą reduktora w celu zarządzania rozmiarem historii czatów. Jest to przydatne, aby uniknąć przekroczenia limitów rozmiaru kontekstu podstawowej usługi.

Może InMemoryChatMessageStore to być implementacja opcjonalna Microsoft.Extensions.AI.IChatReducer , aby zmniejszyć rozmiar historii czatów. Umożliwia również skonfigurowanie zdarzenia, podczas którego jest wywoływany reduktor, po dodaniu komunikatu do historii czatu lub przed zwróceniem historii czatu na potrzeby następnego wywołania.

Aby skonfigurować InMemoryChatMessageStore moduł reduktora, możesz udostępnić fabrykę, aby utworzyć nową InMemoryChatMessageStore dla każdego nowego AgentThread i przekazać go do wybranego reduktora. InMemoryChatMessageStore Można również przekazać opcjonalne zdarzenie wyzwalacza, które można ustawić na InMemoryChatMessageStore.ChatReducerTriggerEvent.AfterMessageAdded wartość lub InMemoryChatMessageStore.ChatReducerTriggerEvent.BeforeMessagesRetrieval.

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

Uwaga / Notatka

Ta funkcja jest obsługiwana tylko w przypadku korzystania z elementu InMemoryChatMessageStore. Gdy usługa ma magazyn historii czatów w usłudze, do samej usługi należy zarządzać rozmiarem historii czatów. Podobnie w przypadku korzystania z magazynu innej firmy (patrz poniżej) jest to rozwiązanie magazynu innej firmy w celu zarządzania rozmiarem historii czatów. Jeśli udostępnisz ChatMessageStoreFactory magazyn komunikatów, ale używasz usługi z wbudowanym magazynem historii czatów, fabryka nie będzie używana.

Magazyn historii czatów usługi wnioskowania

W przypadku korzystania z usługi, która wymaga przechowywania w usłudze historii czatów, struktura agenta będzie przechowywać identyfikator historii rozmów zdalnych AgentThread w obiekcie.

Na przykład w przypadku używania odpowiedzi OpenAI z wartością store=true jako podstawowej usługi dla agentów następujący kod spowoduje, że obiekt wątku zawierający ostatni identyfikator odpowiedzi zwrócony przez usługę.

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

Uwaga / Notatka

Niektóre usługi, np. Odpowiedzi openAI obsługują przechowywanie w usłudze historii czatów (store=true) lub zapewnienie pełnej historii czatów na każdym wywołaniu (store=false). W związku z tym, w zależności od trybu używanego przez usługę, struktura agenta będzie domyślnie przechowywać pełną historię czatów w pamięci lub przechowywać odwołanie identyfikatora do przechowywanej historii czatów usługi.

Magazyn historii czatów innych firm

W przypadku korzystania z usługi, która nie obsługuje przechowywania w usłudze historii czatów, platforma Agent Framework umożliwia deweloperom zastąpienie domyślnego magazynu historii czatów w pamięci magazynem historii czatów innej firmy. Deweloper musi podać podklasę klasy abstrakcyjnej ChatMessageStore podstawowej.

Klasa ChatMessageStore definiuje interfejs do przechowywania i pobierania wiadomości czatu. Deweloperzy muszą zaimplementować AddMessagesAsync metody i GetMessagesAsync , aby dodać komunikaty do magazynu zdalnego podczas ich generowania i pobierać komunikaty z magazynu zdalnego przed wywołaniem usługi bazowej.

Agent będzie używać wszystkich komunikatów zwracanych GetMessagesAsync podczas przetwarzania zapytania użytkownika. Jest to narzędzie implementujące ChatMessageStore , aby upewnić się, że rozmiar historii czatu nie przekracza okna kontekstowego bazowej usługi.

Podczas implementowania niestandardowego ChatMessageStore , który przechowuje historię czatów w magazynie zdalnym, historia czatów dla tego wątku powinna być przechowywana w kluczu unikatowym dla tego wątku. Implementacja ChatMessageStore powinna wygenerować ten klucz i zachować go w swoim stanie. ChatMessageStore ma metodę Serialize , która może zostać zastąpiona w celu serializacji stanu, gdy wątek jest serializowany. Element ChatMessageStore powinien również dostarczyć konstruktor, który przyjmuje JsonElement jako dane wejściowe do obsługi deserializacji jego stanu.

Aby podać niestandardowy ChatMessageStore element do ChatClientAgentelementu , możesz użyć ChatMessageStoreFactory opcji podczas tworzenia agenta. Oto przykład pokazujący sposób przekazywania niestandardowej implementacji do elementu ChatMessageStore opartego na uzupełnianiu czatu ChatClientAgent usługi Azure OpenAI.

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

Wskazówka

Szczegółowy przykład tworzenia niestandardowego magazynu wiadomości można znaleźć w samouczku Przechowywanie historii czatów w usłudze 3rd Party Storage .

Pamięć długoterminowa

Struktura agentów umożliwia deweloperom udostępnianie niestandardowych składników, które mogą wyodrębniać wspomnienia lub udostępniać wspomnienia agentowi.

Aby zaimplementować taki składnik pamięci, deweloper musi podklasować abstrakcyjną klasę AIContextProvider bazową. Ta klasa ma dwie podstawowe metody i InvokingAsyncInvokedAsync. Po zastąpieniu InvokedAsync umożliwia deweloperom sprawdzanie wszystkich komunikatów udostępnianych przez użytkowników lub generowanych przez agenta. InvokingAsync umożliwia deweloperom wprowadzanie dodatkowego kontekstu dla określonego uruchomienia agenta. Można podać instrukcje systemowe, dodatkowe komunikaty i dodatkowe funkcje.

Wskazówka

Szczegółowy przykład tworzenia niestandardowego składnika pamięci można znaleźć w samouczku Dodawanie pamięci do agenta .

Serializacja elementu AgentThread

Ważne jest, aby móc utrwalać AgentThread obiekt między wywołaniami agenta. Pozwala to na sytuacje, w których użytkownik może zadać pytanie agentowi i zająć dużo czasu na zadawanie pytań. AgentThread Dzięki temu stan może przetrwać ponowne uruchomienie usługi lub aplikacji.

Nawet jeśli historia czatu jest przechowywana w magazynie zdalnym, AgentThread obiekt nadal zawiera identyfikator odwołujący się do zdalnej historii czatów. AgentThread Utrata stanu spowoduje zatem utratę identyfikatora historii czatu zdalnego.

Wszystkie AgentThread dołączone do niego obiekty, a więc zapewniają metodę SerializeAsync serializacji ich stanu. Element AIAgent udostępnia również metodę DeserializeThread , która ponownie tworzy wątek ze stanu serializowanego. Metoda DeserializeThread ponownie tworzy wątek z elementem ChatMessageStore i AIContextProvider skonfigurowanym na agencie.

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

Uwaga / Notatka

AgentThread obiekty mogą zawierać więcej niż tylko historię czatu, np. dostawcy kontekstu mogą również przechowywać stan w obiekcie wątku. Dlatego ważne jest, aby zawsze serializować, przechowywać i deserializować cały AgentThread obiekt, aby upewnić się, że cały stan jest zachowany.

Ważne

Zawsze traktuj AgentThread obiekty jako nieprzezroczyste obiekty, chyba że masz pewność co do wewnętrznych. Zawartość może się różnić nie tylko według typu agenta, ale także według typu usługi i konfiguracji.

Ostrzeżenie

Deserializowanie wątku z innym agentem niż ten, który pierwotnie go utworzył lub agent, który ma inną konfigurację niż oryginalny agent, może spowodować błędy lub nieoczekiwane zachowanie.

Typy pamięci

Struktura agentów obsługuje kilka typów pamięci, aby pomieścić różne przypadki użycia, w tym zarządzanie historią czatów w ramach krótkoterminowej pamięci i udostępnianie punktów rozszerzenia do wyodrębniania, przechowywania i wstrzykiwania długoterminowych pamięci do agentów.

In-Memory Storage (ustawienie domyślne)

Najprostsza forma pamięci, w której historia konwersacji jest przechowywana w pamięci podczas wykonywania aplikacji. Jest to zachowanie domyślne i nie wymaga dodatkowej konfiguracji.

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)

Trwałe magazyny komunikatów

W przypadku aplikacji, które muszą utrwalać historię konwersacji między sesjami, platforma udostępnia ChatMessageStore implementacje:

Wbudowany magazyn ChatMessageStore

Domyślna implementacja w pamięci, którą można serializować:

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
)

Magazyn komunikatów Redis

W przypadku aplikacji produkcyjnych wymagających magazynu trwałego:

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
)

Niestandardowy magazyn komunikatów

Możesz zaimplementować własne zaplecze magazynu, implementując metodę ChatMessageStoreProtocol:

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"]

Wskazówka

Szczegółowy przykład tworzenia niestandardowego magazynu wiadomości można znaleźć w samouczku Przechowywanie historii czatów w usłudze 3rd Party Storage .

Dostawcy kontekstu (pamięć dynamiczna)

Dostawcy kontekstu umożliwiają zaawansowane wzorce pamięci przez wstrzyknięcie odpowiedniego kontekstu przed wywołaniem każdego agenta:

Podstawowy dostawca kontekstu

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

Wskazówka

Szczegółowy przykład tworzenia niestandardowego składnika pamięci można znaleźć w samouczku Dodawanie pamięci do agenta .

Usługi pamięci zewnętrznej

Platforma obsługuje integrację z wyspecjalizowanymi usługami pamięci, takimi jak 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
)

Serializacja wątków i trwałość

Platforma obsługuje serializowanie całych stanów wątków w celu trwałości w przypadku ponownego uruchamiania aplikacji:

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)

Dalsze kroki