Partilhar via


Histórico e Memória do Chat do Agente

O histórico e a memória das conversas dos agentes são capacidades cruciais que permitem aos agentes manter o contexto nas conversas, recordar as preferências dos utilizadores e proporcionar experiências personalizadas. O Agent Framework oferece múltiplas funcionalidades para diferentes casos de uso, desde armazenamento simples de mensagens em memória até bases de dados persistentes e serviços de memória especializados.

Histórico do bate-papo

Várias opções de armazenamento do histórico de bate-papo são suportadas pelo Agent Framework. As opções disponíveis variam de acordo com o tipo de agente e o(s) serviço(s) subjacente(s) usado(s) para criar o agente.

Aqui estão os dois principais cenários suportados:

  1. Armazenamento na memória: o agente é construído em um serviço que não suporta o armazenamento em serviço do histórico de bate-papo (por exemplo, OpenAI Chat Completion). Por padrão, o Agent Framework armazenará o histórico de bate-papo completo na memória no objeto, mas os desenvolvedores podem fornecer uma implementação personalizada AgentThread para armazenar o histórico de bate-papo ChatMessageStore em um armazenamento de terceiros 3rd, se necessário.
  2. Armazenamento em serviço: o agente é criado em um serviço que requer armazenamento em serviço do histórico de bate-papo (por exemplo, Agentes Persistentes do Azure AI Foundry). O Agent Framework armazenará a id do histórico de bate-papo remoto no objeto e nenhuma outra opção de armazenamento de histórico de bate-papo AgentThread é suportada.

Armazenamento do histórico de bate-papo na memória

Ao usar um serviço que não oferece suporte ao armazenamento em serviço do histórico de bate-papo, o Agent Framework terá como padrão armazenar o histórico de bate-papo na memória no AgentThread objeto. Nesse caso, o histórico de bate-papo completo armazenado no objeto de thread, além de quaisquer novas mensagens, será fornecido ao serviço subjacente em cada execução do agente. Isso permite uma experiência de conversação natural com o agente, onde o chamador fornece apenas a nova mensagem do usuário, e o agente só retorna novas respostas, mas o agente tem acesso ao histórico completo de conversas e o usará ao gerar sua resposta.

Ao usar o OpenAI Chat Completion como o serviço subjacente para agentes, o código a seguir resultará no objeto thread que contém o histórico de bate-papo da execução do agente.

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

Onde as mensagens são armazenadas na memória, é possível recuperar a lista de mensagens do thread e manipular as mensagens diretamente, se necessário.

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

Observação

Recuperar mensagens do objeto dessa maneira só funcionará se o AgentThread armazenamento na memória estiver sendo usado.

Redução do histórico de bate-papo com armazenamento In-Memory

O built-in InMemoryChatMessageStore que é usado por padrão quando o serviço subjacente não suporta armazenamento em serviço, pode ser configurado com um redutor para gerenciar o tamanho do histórico de bate-papo. Isso é útil para evitar exceder os limites de tamanho de contexto do serviço subjacente.

O InMemoryChatMessageStore pode ter uma implementação opcional Microsoft.Extensions.AI.IChatReducer para reduzir o tamanho do histórico de bate-papo. Ele também permite que você configure o evento durante o qual o redutor é invocado, seja depois que uma mensagem é adicionada ao histórico de bate-papo ou antes que o histórico de bate-papo seja retornado para a próxima invocação.

Para configurar o InMemoryChatMessageStore com um redutor, você pode fornecer uma fábrica para construir um novo InMemoryChatMessageStore para cada novo AgentThread e passá-lo um redutor de sua escolha. O InMemoryChatMessageStore também pode ser passado um evento de gatilho opcional que pode ser definido como um InMemoryChatMessageStore.ChatReducerTriggerEvent.AfterMessageAdded ou 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)
    });

Observação

Este recurso só é suportado ao usar o InMemoryChatMessageStore. Quando um serviço tem armazenamento de histórico de bate-papo em serviço, cabe ao próprio serviço gerenciar o tamanho do histórico de bate-papo. Da mesma forma, ao usar o armazenamento de terceiros (veja abaixo), cabe à solução de armazenamento de terceiros gerenciar o tamanho do histórico de bate-papo. Se você fornecer um ChatMessageStoreFactory para um armazenamento de mensagens, mas usar um serviço com armazenamento de histórico de bate-papo integrado, a fábrica não será usada.

Armazenamento do histórico de chat do serviço de inferência

Ao usar um serviço que requer armazenamento em serviço do histórico de bate-papo, o Agent Framework armazenará a id do histórico de bate-papo remoto no AgentThread objeto.

Por exemplo, ao usar o OpenAI Responses com store=true como o serviço subjacente para agentes, o código a seguir resultará no objeto thread contendo a última id de resposta retornada pelo serviço.

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

Observação

Alguns serviços, por exemplo, o OpenAI Responses suportam o armazenamento em serviço do histórico de bate-papo (store=true) ou o fornecimento do histórico de bate-papo completo em cada invocação (store=false). Portanto, dependendo do modo em que o serviço é usado, o Agent Framework terá como padrão armazenar o histórico de bate-papo completo na memória ou armazenar uma referência de id para o histórico de bate-papo armazenado no serviço.

Armazenamento de histórico de bate-papo de 3ª parte

Ao usar um serviço que não suporta armazenamento em serviço do histórico de bate-papo, o Agent Framework permite que os desenvolvedores substituam o armazenamento padrão na memória do histórico de bate-papo pelo armazenamento de histórico de bate-papo de terceiros 3rd. O desenvolvedor é obrigado a fornecer uma subclasse da classe abstrata ChatMessageStore base.

A ChatMessageStore classe define a interface para armazenar e recuperar mensagens de chat. Os desenvolvedores devem implementar os AddMessagesAsync métodos e GetMessagesAsync para adicionar mensagens ao armazenamento remoto à medida que são geradas e recuperar mensagens do repositório remoto antes de invocar o serviço subjacente.

O agente usará todas as mensagens retornadas ao GetMessagesAsync processar uma consulta de usuário. Cabe ao implementador garantir que o tamanho do histórico de ChatMessageStore bate-papo não exceda a janela de contexto do serviço subjacente.

Ao implementar um personalizado ChatMessageStore que armazena o histórico de bate-papo em um repositório remoto, o histórico de bate-papo desse thread deve ser armazenado sob uma chave exclusiva para esse thread. A ChatMessageStore implementação deve gerar essa chave e mantê-la em seu estado. ChatMessageStore tem um Serialize método que pode ser substituído para serializar seu estado quando o thread é serializado. O ChatMessageStore também deve fornecer um construtor que usa um JsonElement como entrada para dar suporte à desserialização de seu estado.

Para fornecer um personalizado ChatMessageStore para um ChatClientAgent, você pode usar a ChatMessageStoreFactory opção ao criar o agente. Aqui está um exemplo mostrando como passar a implementação personalizada de para um ChatMessageStore que é baseado na conclusão do ChatClientAgent Chat do 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);
         }
     });

Sugestão

Para obter um exemplo detalhado sobre como criar um armazenamento de mensagens personalizado, consulte o tutorial Armazenando histórico de bate-papo no armazenamento de terceiros 3rd .

Memória de longo prazo

O Agent Framework permite que os desenvolvedores forneçam componentes personalizados que podem extrair memórias ou fornecer memórias a um agente.

Para implementar esse componente de memória, o desenvolvedor precisa subclassificar a AIContextProvider classe base abstrata. Esta classe tem dois métodos principais, InvokingAsync e InvokedAsync. Quando substituído, InvokedAsync permite que os desenvolvedores inspecionem todas as mensagens fornecidas pelos usuários ou geradas pelo agente. InvokingAsync permite que os desenvolvedores injetem contexto adicional para uma execução específica do agente. Instruções do sistema, mensagens adicionais e funções adicionais podem ser fornecidas.

Sugestão

Para obter um exemplo detalhado sobre como criar um componente de memória personalizado, consulte o tutorial Adicionando memória a um agente .

Serialização do AgentThread

É importante ser capaz de persistir um AgentThread objeto entre as invocações do agente. Isso permite situações em que um usuário pode fazer uma pergunta ao agente e levar muito tempo para fazer perguntas de acompanhamento. Isso permite que o AgentThread estado sobreviva às reinicializações de serviço ou aplicativo.

Mesmo que o histórico de bate-papo seja armazenado em um armazenamento remoto, o AgentThread objeto ainda contém uma id que faz referência ao histórico de bate-papo remoto. Perder o AgentThread estado, portanto, resultará em também perder o id do histórico de bate-papo remoto.

O AgentThread bem como quaisquer objetos anexados a ele, todos, portanto, fornecem o SerializeAsync método para serializar seu estado. O AIAgent também fornece um DeserializeThread método que recria um thread a partir do estado serializado. O DeserializeThread método recria o thread com o ChatMessageStore e AIContextProvider configurado no agente.

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

Observação

AgentThread Os objetos podem conter mais do que apenas o histórico de bate-papo, por exemplo, os provedores de contexto também podem armazenar o estado no objeto de thread. Portanto, é importante sempre serializar, armazenar e desserializar todo AgentThread o objeto para garantir que todo o estado seja preservado.

Importante

Trate AgentThread sempre os objetos como objetos opacos, a menos que tenha muita certeza dos elementos internos. O conteúdo pode variar não apenas por tipo de agente, mas também por tipo de serviço e configuração.

Advertência

Desserializar um thread com um agente diferente daquele que o criou originalmente, ou com um agente que tem uma configuração diferente do agente original, pode resultar em erros ou comportamento inesperado.

Tipos de memória

O Agent Framework suporta vários tipos de memória para acomodar diferentes casos de uso, incluindo o gerenciamento do histórico de bate-papo como parte da memória de curto prazo e o fornecimento de pontos de extensão para extrair, armazenar e injetar memórias de longo prazo nos agentes.

In-Memory Storage (padrão)

A forma mais simples de memória em que o histórico de conversas é armazenado na memória durante o tempo de execução do aplicativo. Este é o comportamento padrão e não requer nenhuma configuração adicional.

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)

Armazenamentos de mensagens persistentes

Para aplicativos que precisam persistir o histórico de conversas entre sessões, a estrutura fornece ChatMessageStore implementações:

ChatMessageStore incorporado

A implementação na memória padrão que pode ser serializada:

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
)

Armazenamento de Mensagens Redis

Para aplicativos de produção que exigem armazenamento persistente:

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
)

Armazenamento de mensagens personalizadas

Você pode implementar seu próprio back-end de armazenamento implementando o 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"]

Sugestão

Para obter um exemplo detalhado sobre como criar um armazenamento de mensagens personalizado, consulte o tutorial Armazenando histórico de bate-papo no armazenamento de terceiros 3rd .

Provedores de contexto (memória dinâmica)

Os provedores de contexto permitem padrões de memória sofisticados injetando contexto relevante antes de cada chamada de agente:

Provedor de contexto básico

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

Sugestão

Para obter um exemplo detalhado sobre como criar um componente de memória personalizado, consulte o tutorial Adicionando memória a um agente .

Serviços de memória externa

A estrutura suporta a integração com serviços de memória especializados como 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
)

Serialização e persistência de threads

A estrutura suporta a serialização de estados de thread inteiros para persistência em reinicializações de aplicativos:

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)

Próximos passos