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.

Dwa główne obsługiwane scenariusze to:

  • Magazyn w pamięci: agent jest oparty na usłudze, która nie obsługuje magazynu w usłudze historii czatów (na przykład ukończenia czatu OpenAI). Domyślnie platforma Agent Framework przechowuje pełną historię czatów w pamięci w AgentSession obiekcie, ale deweloperzy mogą zapewnić niestandardową ChatHistoryProvider implementację do przechowywania historii czatów w magazynie innej firmy, jeśli jest to wymagane.
  • Magazyn w usłudze: Agent jest oparty na usłudze, która wymaga magazynu w usłudze historii czatów (na przykład agentów trwałych usługi Azure AI Foundry). Struktura agenta przechowuje identyfikator historii czatu zdalnego w AgentSession 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, program Agent Framework domyślnie przechowuje historię czatów w pamięci w AgentSession obiekcie. W takim przypadku pełna historia czatów przechowywana w obiekcie sesji oraz wszelkie nowe wiadomości zostaną udostępnione podstawowej usłudze na każdym uruchomieniu agenta. Ten projekt umożliwia naturalne środowisko konwersacyjne z agentem. Obiekt wywołujący udostępnia tylko nowy komunikat użytkownika, a agent zwraca tylko nowe odpowiedzi. Agent ma jednak dostęp do pełnej historii konwersacji i będzie go używać podczas generowania odpowiedzi.

W przypadku korzystania z uzupełniania czatu OpenAI jako podstawowej usługi dla agentów poniższy kod powoduje wyświetlenie obiektu sesji zawierającego historię czatu z uruchomienia agenta.

AIAgent agent = new OpenAIClient("<your_api_key>")
     .GetChatClient(modelName)
     .AsAIAgent(JokerInstructions, JokerName);
AgentSession session = await agent.GetNewSessionAsync();
Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", session));

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

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

Uwaga / Notatka

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

Redukcja historii czatów z magazynem w pamięci

Wbudowana InMemoryChatHistoryProvider funkcja 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 InMemoryChatHistoryProvider 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ć InMemoryChatHistoryProvider moduł reduktora, możesz udostępnić fabrykę, aby utworzyć nową InMemoryChatHistoryProvider dla każdego nowego AgentSession i przekazać go do wybranego reduktora. InMemoryChatHistoryProvider Można również przekazać opcjonalne zdarzenie wyzwalacza, które można ustawić na InMemoryChatHistoryProvider.ChatReducerTriggerEvent.AfterMessageAdded wartość lub InMemoryChatHistoryProvider.ChatReducerTriggerEvent.BeforeMessagesRetrieval.

Fabryka jest funkcją asynchroniową, która odbiera obiekt kontekstu i token anulowania.

AIAgent agent = new OpenAIClient("<your_api_key>")
    .GetChatClient(modelName)
    .AsAIAgent(new ChatClientAgentOptions
    {
        Name = JokerName,
        ChatOptions = new() { Instructions = JokerInstructions },
        ChatHistoryProviderFactory = (ctx, ct) => new ValueTask<ChatHistoryProvider>(
            new InMemoryChatHistoryProvider(
                new MessageCountingChatReducer(2),
                ctx.SerializedState,
                ctx.JsonSerializerOptions,
                InMemoryChatHistoryProvider.ChatReducerTriggerEvent.AfterMessageAdded))
    });

Uwaga / Notatka

Ta funkcja jest obsługiwana tylko w przypadku korzystania z elementu InMemoryChatHistoryProvider. 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 ChatHistoryProviderFactory dostawcę historii czató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, program Agent Framework przechowuje identyfikator historii rozmów zdalnych AgentSession 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 sesji zawierający ostatni identyfikator odpowiedzi zwrócony przez usługę.

AIAgent agent = new OpenAIClient("<your_api_key>")
     .GetOpenAIResponseClient(modelName)
     .AsAIAgent(JokerInstructions, JokerName);
AgentSession session = await agent.GetNewSessionAsync();
Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", session));

Uwaga / Notatka

Niektóre usługi, na przykład Odpowiedzi OpenAI, obsługują magazyn 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, w jaki jest używana usługa, 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 ChatHistoryProvider podstawowej.

Klasa ChatHistoryProvider definiuje interfejs do przechowywania i pobierania wiadomości czatu. Deweloperzy muszą zaimplementować InvokedAsync metody i InvokingAsync , 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 InvokingAsync podczas przetwarzania zapytania użytkownika. Jest to narzędzie implementujące ChatHistoryProvider , aby upewnić się, że rozmiar historii czatu nie przekracza okna kontekstowego bazowej usługi.

Podczas implementowania niestandardowego ChatHistoryProvider , który przechowuje historię czatów w magazynie zdalnym, historia czatów dla tej sesji powinna być przechowywana w kluczu unikatowym dla tej sesji. Implementacja ChatHistoryProvider powinna wygenerować ten klucz i zachować go w swoim stanie. ChatHistoryProvider ma metodę Serialize , którą można zastąpić, aby serializować jej stan, gdy sesja jest serializowana. Element ChatHistoryProvider powinien również dostarczyć konstruktor, który przyjmuje JsonElement jako dane wejściowe do obsługi deserializacji jego stanu.

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

Fabryka jest funkcją asynchroniową, która odbiera obiekt kontekstu i token anulowania.

AIAgent agent = new AzureOpenAIClient(
    new Uri(endpoint),
    new AzureCliCredential())
    .GetChatClient(deploymentName)
    .AsAIAgent(new ChatClientAgentOptions
    {
        Name = JokerName,
        ChatOptions = new() { Instructions = JokerInstructions },
        ChatHistoryProviderFactory = (ctx, ct) => new ValueTask<ChatHistoryProvider>(
            // Create a new chat history provider for this agent that stores the messages in a custom store.
            // Each session must get its own copy of the CustomChatHistoryProvider, since the provider
            // also contains the ID that the session is stored under.
            new CustomChatHistoryProvider(
                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 agentów

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

Nawet jeśli historia czatu jest przechowywana w magazynie zdalnym, AgentSession obiekt nadal zawiera identyfikator odwołujący się do zdalnej historii czatów. AgentSession W związku z tym utrata stanu spowoduje również utratę identyfikatora zdalnej historii czatów.

Wszystkie AgentSession dołączone do niego obiekty, a więc zapewniają metodę Serialize serializacji ich stanu. Metoda AIAgent udostępnia również metodę DeserializeSessionAsync , która ponownie tworzy sesję ze stanu serializowanego. Metoda DeserializeSessionAsync ponownie tworzy sesję z elementem ChatHistoryProvider i AIContextProvider skonfigurowanym na agencie.

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

// Re-create the session from the JsonElement.
AgentSession resumedSession = await agent.DeserializeSessionAsync(serializedSessionState);

Uwaga / Notatka

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

Ważne

Zawsze traktuj AgentSession 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 sesji z innym agentem niż ten, który został pierwotnie utworzony 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