Condividi tramite


Cronologia e memoria di Chat agente

La cronologia e la memoria delle chat degli agenti sono funzionalità cruciali che consentono agli agenti di mantenere il contesto nelle conversazioni, ricordare le preferenze utente e offrire esperienze personalizzate. Agent Framework offre più funzionalità per soddisfare casi d'uso diversi, dalla semplice risorsa di archiviazione dei messaggi di chat in memoria ai database persistenti e ai servizi di memoria specializzati.

Cronologia chat

Varie opzioni di archiviazione della cronologia delle chat sono supportate da Agent Framework. Le opzioni disponibili variano in base al tipo di agente e ai servizi sottostanti usati per compilare l'agente.

I due scenari supportati principali sono:

  • Archiviazione in memoria: Agent è basato su un servizio che non supporta l'archiviazione in servizio della cronologia delle chat , ad esempio OpenAI Chat Completion. Per impostazione predefinita, Agent Framework archivia la cronologia delle chat completa in memoria nell'oggetto AgentSession , ma gli sviluppatori possono fornire un'implementazione personalizzata ChatHistoryProvider per archiviare la cronologia delle chat in un archivio di terze parti, se necessario.
  • Archiviazione nel servizio: Agent è basato su un servizio che richiede l'archiviazione in servizio della cronologia delle chat (ad esempio, agenti persistenti di Azure AI Foundry). Agent Framework archivia l'ID della cronologia chat remota nell'oggetto AgentSession e non sono supportate altre opzioni di archiviazione della cronologia delle chat.

Archiviazione della cronologia delle chat in memoria

Quando si usa un servizio che non supporta l'archiviazione in servizio della cronologia delle chat, Agent Framework usa per impostazione predefinita l'archiviazione della cronologia delle chat in memoria nell'oggetto AgentSession . In questo caso, la cronologia completa della chat archiviata nell'oggetto sessione, più eventuali nuovi messaggi, verrà fornita al servizio sottostante in ogni esecuzione dell'agente. Questa progettazione consente un'esperienza di conversazione naturale con l'agente. Il chiamante fornisce solo il nuovo messaggio utente e l'agente restituisce solo nuove risposte. Tuttavia, l'agente ha accesso alla cronologia completa della conversazione e lo userà durante la generazione della risposta.

Quando si usa OpenAI Chat Completion come servizio sottostante per gli agenti, il codice seguente restituisce l'oggetto sessione contenente la cronologia delle chat dall'esecuzione dell'agente.

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

Dove i messaggi vengono archiviati in memoria, è possibile recuperare l'elenco di messaggi dalla sessione e modificare direttamente i messaggi, se necessario.

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

Annotazioni

Il recupero dei messaggi dall'oggetto AgentSession in questo modo funziona solo se viene usata l'archiviazione in memoria.

Riduzione della cronologia delle chat con l'archiviazione in memoria

InMemoryChatHistoryProvider Il valore predefinito usato per impostazione predefinita quando il servizio sottostante non supporta l'archiviazione nel servizio, può essere configurato con un riduttore per gestire le dimensioni della cronologia delle chat. Ciò è utile per evitare di superare i limiti di dimensioni del contesto del servizio sottostante.

InMemoryChatHistoryProvider può accettare un'implementazione facoltativa Microsoft.Extensions.AI.IChatReducer per ridurre le dimensioni della cronologia delle chat. Consente anche di configurare l'evento durante il quale viene richiamato il reducer, dopo l'aggiunta di un messaggio alla cronologia delle chat o prima della restituzione della cronologia delle chat per la chiamata successiva.

Per configurare con un riduttoreInMemoryChatHistoryProvider, è possibile fornire una factory per costruire un nuovo per ogni nuovo InMemoryChatHistoryProviderAgentSession e passarlo un riduttore di propria scelta. InMemoryChatHistoryProvider Può anche essere passato un evento trigger facoltativo che può essere impostato su InMemoryChatHistoryProvider.ChatReducerTriggerEvent.AfterMessageAdded o InMemoryChatHistoryProvider.ChatReducerTriggerEvent.BeforeMessagesRetrieval.

La factory è una funzione asincrona che riceve un oggetto contesto e un token di annullamento.

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

Annotazioni

Questa funzionalità è supportata solo quando si usa .InMemoryChatHistoryProvider Quando un servizio dispone di archiviazione cronologia chat nel servizio, spetta al servizio stesso gestire le dimensioni della cronologia delle chat. Analogamente, quando si usa l'archiviazione di terze parti (vedere di seguito), è fino alla soluzione di archiviazione di terze parti per gestire le dimensioni della cronologia delle chat. Se si fornisce un ChatHistoryProviderFactory per un provider di cronologia chat ma si usa un servizio con l'archiviazione della cronologia delle chat predefinite, la factory non verrà usata.

Archiviazione della cronologia chat del servizio di inferenza

Quando si usa un servizio che richiede l'archiviazione in servizio della cronologia delle chat, Agent Framework archivia l'ID della cronologia chat remota nell'oggetto AgentSession .

Ad esempio, quando si usano le risposte OpenAI con store=true come servizio sottostante per gli agenti, il codice seguente genererà l'oggetto sessione contenente l'ultimo ID risposta restituito dal servizio.

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

Annotazioni

Alcuni servizi, ad esempio, Le risposte OpenAI supportano l'archiviazione in servizio della cronologia delle chat (store=true) o la cronologia completa delle chat in ogni chiamata (store=false). Pertanto, a seconda della modalità in cui viene usato il servizio, Agent Framework per impostazione predefinita archivia la cronologia di chat completa in memoria o archivia un riferimento ID alla cronologia delle chat archiviate del servizio.

Archiviazione della cronologia delle chat di terze parti

Quando si usa un servizio che non supporta l'archiviazione nella cronologia delle chat nel servizio, Agent Framework consente agli sviluppatori di sostituire l'archiviazione predefinita in memoria della cronologia delle chat con l'archiviazione della cronologia delle chat di terze parti. Lo sviluppatore deve fornire una sottoclasse della classe astratta ChatHistoryProvider di base.

La ChatHistoryProvider classe definisce l'interfaccia per l'archiviazione e il recupero dei messaggi di chat. Gli sviluppatori devono implementare i InvokedAsync metodi e InvokingAsync per aggiungere messaggi all'archivio remoto man mano che vengono generati e recuperare i messaggi dall'archivio remoto prima di richiamare il servizio sottostante.

L'agente userà tutti i messaggi restituiti da InvokingAsync durante l'elaborazione di una query utente. Spetta all'implementatore di ChatHistoryProvider garantire che le dimensioni della cronologia delle chat non superino la finestra di contesto del servizio sottostante.

Quando si implementa un oggetto personalizzato che archivia la cronologia ChatHistoryProvider delle chat in un archivio remoto, la cronologia delle chat per tale sessione deve essere archiviata in una chiave univoca per tale sessione. L'implementazione ChatHistoryProvider deve generare questa chiave e mantenerla nello stato. ChatHistoryProvider dispone di un Serialize metodo che può essere sottoposto a override per serializzare il relativo stato quando la sessione viene serializzata. deve ChatHistoryProvider inoltre fornire un costruttore che accetta come JsonElement input per supportare la deserializzazione dello stato.

Per fornire un oggetto personalizzato ChatHistoryProvider a un ChatClientAgentoggetto , è possibile usare l'opzione durante la ChatHistoryProviderFactory creazione dell'agente. Di seguito è riportato un esempio che illustra come passare l'implementazione personalizzata di ChatHistoryProvider a un ChatClientAgent oggetto basato sul completamento della chat OpenAI di Azure.

La factory è una funzione asincrona che riceve un oggetto contesto e un token di annullamento.

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

Suggerimento

Per un esempio dettagliato su come creare un archivio messaggi personalizzato, vedere l'esercitazione Archiviazione della cronologia chat in Archiviazione di terze parti .

Memoria a lungo termine

Agent Framework consente agli sviluppatori di fornire componenti personalizzati che possono estrarre memorie o fornire memorie a un agente.

Per implementare tale componente di memoria, lo sviluppatore deve sottoclassare la AIContextProvider classe base astratta. Questa classe ha due metodi principali, InvokingAsync e InvokedAsync. In caso di override, InvokedAsync consente agli sviluppatori di controllare tutti i messaggi forniti dagli utenti o generati dall'agente. InvokingAsync consente agli sviluppatori di inserire un contesto aggiuntivo per un'esecuzione specifica dell'agente. È possibile fornire istruzioni di sistema, messaggi aggiuntivi e funzioni aggiuntive.

Suggerimento

Per un esempio dettagliato su come creare un componente di memoria personalizzato, vedere l'esercitazione Aggiunta di memoria a un agente .

Serializzazione di AgentSession

È importante essere in grado di salvare in modo permanente un AgentSession oggetto tra le chiamate dell'agente. Ciò consente situazioni in cui un utente potrebbe porre una domanda all'agente e richiedere molto tempo per porre domande di completamento. In questo modo lo AgentSession stato può sopravvivere ai riavvii del servizio o dell'app.

Anche se la cronologia delle chat viene archiviata in un archivio remoto, l'oggetto AgentSession contiene ancora un ID che fa riferimento alla cronologia delle chat remote. La perdita dello AgentSession stato comporterà quindi anche la perdita dell'ID della cronologia delle chat remote.

AgentSession E tutti gli oggetti collegati, forniscono quindi il metodo per serializzare il Serialize relativo stato. Fornisce AIAgent inoltre un DeserializeSessionAsync metodo che ricrea una sessione dallo stato serializzato. Il DeserializeSessionAsync metodo ricrea la sessione con ChatHistoryProvider e AIContextProvider configurata nell'agente.

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

Annotazioni

AgentSession gli oggetti possono contenere più della semplice cronologia delle chat, ad esempio i provider di contesto possono anche archiviare lo stato nell'oggetto sessione. Pertanto, è importante serializzare sempre, archiviare e deserializzare l'intero AgentSession oggetto per garantire che tutto lo stato venga mantenuto.

Importante

Considera AgentSession sempre gli oggetti come oggetti opachi, a meno che non sei molto sicuro degli elementi interni. Il contenuto può variare non solo in base al tipo di agente, ma anche al tipo di servizio e alla configurazione.

Avvertimento

La deserializzazione di una sessione con un agente diverso da quello che l'ha originariamente creata o con un agente con una configurazione diversa rispetto all'agente originale, potrebbe causare errori o comportamenti imprevisti.

Tipi di memoria

Agent Framework supporta diversi tipi di memoria per supportare diversi casi d'uso, tra cui la gestione della cronologia delle chat come parte della memoria a breve termine e la fornitura di punti di estensione per l'estrazione, l'archiviazione e l'inserimento di memorie a lungo termine negli agenti.

archiviazione In-Memory (impostazione predefinita)

La forma più semplice di memoria in cui la cronologia delle conversazioni viene archiviata in memoria durante il runtime dell'applicazione. Si tratta del comportamento predefinito e non richiede alcuna configurazione aggiuntiva.

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)

Archivi messaggi permanenti

Per le applicazioni che devono rendere persistente la cronologia delle conversazioni tra le sessioni, il framework fornisce ChatMessageStore implementazioni:

ChatMessageStore predefinito

Implementazione predefinita in memoria che può essere serializzata:

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
)

Archivio messaggi Redis

Per le applicazioni di produzione che richiedono l'archiviazione permanente:

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
)

Archivio messaggi personalizzato

È possibile implementare un back-end di archiviazione personalizzato implementando :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"]

Suggerimento

Per un esempio dettagliato su come creare un archivio messaggi personalizzato, vedere l'esercitazione Archiviazione della cronologia chat in Archiviazione di terze parti .

Provider di contesto (memoria dinamica)

I provider di contesto consentono modelli di memoria sofisticati inserendo il contesto pertinente prima di ogni chiamata dell'agente:

Provider di contesto di base

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

Suggerimento

Per un esempio dettagliato su come creare un componente di memoria personalizzato, vedere l'esercitazione Aggiunta di memoria a un agente .

Servizi di memoria esterna

Il framework supporta l'integrazione con servizi di memoria specializzati come 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
)

Serializzazione e persistenza dei thread

Il framework supporta la serializzazione di interi stati del thread per la persistenza tra i riavvii dell'applicazione:

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)

Passaggi successivi