Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Controles de armazenamento determinam onde o histórico de conversas reside, quanto do histórico é carregado e quão confiavelmente as sessões podem ser retomadas.
Modos de armazenamento internos
O Agent Framework dá suporte a dois modos de armazenamento regulares:
| Modo | O que é armazenado | Uso típico |
|---|---|---|
| Estado da sessão local | Histórico de chat completo em AgentSession.state (por exemplo, via InMemoryHistoryProvider) |
Serviços que não exigem persistência de conversa do lado do servidor |
| Armazenamento gerenciado pelo serviço | Estado da conversa no serviço; AgentSession.service_session_id aponta para ele |
Serviços com suporte de conversa persistente nativa |
Armazenamento de histórico de chat na memória
Quando um provedor não requer o histórico de chat do lado do servidor, o Agent Framework mantém o histórico localmente na sessão e envia mensagens relevantes em cada execução.
AIAgent agent = new OpenAIClient("<your_api_key>")
.GetChatClient(modelName)
.AsAIAgent(instructions: "You are a helpful assistant.", name: "Assistant");
AgentSession session = await agent.CreateSessionAsync();
Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", session));
// When in-memory chat history storage is used, it's possible to access the chat history
// that is stored in the session via the provider attached to the agent.
var provider = agent.GetService<InMemoryChatHistoryProvider>();
List<ChatMessage>? messages = provider?.GetMessages(session);
from agent_framework import InMemoryHistoryProvider
from agent_framework.openai import OpenAIChatClient
agent = OpenAIChatClient().as_agent(
name="StorageAgent",
instructions="You are a helpful assistant.",
context_providers=[InMemoryHistoryProvider("memory", load_messages=True)],
)
session = agent.create_session()
await agent.run("Remember that I like Italian food.", session=session)
Reduzindo o tamanho do histórico na memória
Se o histórico crescer além dos limites do modelo, aplique um redutor.
AIAgent agent = new OpenAIClient("<your_api_key>")
.GetChatClient(modelName)
.AsAIAgent(new ChatClientAgentOptions
{
Name = "Assistant",
ChatOptions = new() { Instructions = "You are a helpful assistant." },
ChatHistoryProvider = new InMemoryChatHistoryProvider(new InMemoryChatHistoryProviderOptions
{
ChatReducer = new MessageCountingChatReducer(20)
})
});
Observação
A configuração do redutor se aplica a provedores de histórico na memória. Para o histórico gerenciado pelo serviço, o comportamento de redução é específico do provedor/serviço.
Armazenamento gerenciado pelo serviço
Quando o serviço gerencia o histórico de conversas, a sessão armazena um identificador de conversa remota.
AIAgent agent = new OpenAIClient("<your_api_key>")
.GetOpenAIResponseClient(modelName)
.AsAIAgent(instructions: "You are a helpful assistant.", name: "Assistant");
AgentSession session = await agent.CreateSessionAsync();
Console.WriteLine(await agent.RunAsync("Tell me a joke about a pirate.", session));
// In this case, since we know we are working with a ChatClientAgent, we can cast
// the AgentSession to a ChatClientAgentSession to retrieve the remote conversation
// identifier.
ChatClientAgentSession typedSession = (ChatClientAgentSession)session;
Console.WriteLine(typedSession.ConversationId);
# Rehydrate when the service already has the conversation state.
session = agent.get_session(service_session_id="<service-conversation-id>")
response = await agent.run("Continue this conversation.", session=session)
Padrão de armazenamento personalizado/de terceiros
Para um histórico suportado por banco de dados/Redis/blob, implemente um provedor de histórico personalizado.
Diretrizes principais:
- Armazene mensagens em uma chave com escopo de sessão.
- Mantenha o histórico retornado dentro dos limites de contexto do modelo.
- Persista identificadores específicos do provedor no estado da sessão.
A classe base para provedores de histórico é Microsoft.Agents.AI.ChatHistoryProvider.
Os provedores de histórico participam do pipeline do agente, têm a capacidade de contribuir ou substituir mensagens de entrada do agente e podem armazenar novas mensagens.
ChatHistoryProvider tem vários métodos virtuais que podem ser substituídos para implementar seu próprio provedor de histórico personalizado.
Consulte as diferentes opções de implementação abaixo para obter mais informações sobre o que substituir.
ChatHistoryProvider Estado
Uma ChatHistoryProvider instância é anexada a um agente e a mesma instância seria usada para todas as sessões.
Isso significa que o estado específico da ChatHistoryProvider sessão não deve ser armazenado na instância do provedor.
Pode ChatHistoryProvider ter uma referência a um cliente de banco de dados em um campo, mas não deve ter no campo uma chave de banco de dados para o histórico de chat.
Em vez disso, o ChatHistoryProvider pode armazenar quaisquer valores específicos de sessão, como chaves de banco de dados, mensagens ou qualquer outra coisa que seja relevante no próprio AgentSession. Todos os métodos virtuais em ChatHistoryProvider recebem uma referência para o atual AIAgent e AgentSession.
Para facilitar o armazenamento do estado tipado no AgentSession, é fornecida uma classe utilitária.
// First define a type containing the properties to store in state
internal class MyCustomState
{
public string? DbKey { get; set; }
}
// Create the helper
var sessionStateHelper = new ProviderSessionState<MyCustomState>(
// stateInitializer is called when there is no state in the session for this ChatHistoryProvider yet
stateInitializer: currentSession => new MyCustomState() { DbKey = Guid.NewGuid().ToString() },
// The key under which to store state in the session for this provider. Make sure it does not clash with the keys of other providers.
stateKey: this.GetType().Name,
// An optional jsonSerializerOptions to control the serialization/deserialization of the custom state object
jsonSerializerOptions: myJsonSerializerOptions);
// Using the helper you can read state:
MyCustomState state = sessionStateHelper.GetOrInitializeState(session);
Console.WriteLine(state.DbKey);
// And write state:
sessionStateHelper.SaveState(session, state);
Implementação simples ChatHistoryProvider
A implementação mais simples ChatHistoryProvider normalmente substituiria dois métodos:
- ChatHistoryProvider.ProvideChatHistoryAsync – Carregar o histórico de chat relevante e retornar as mensagens carregadas.
- ChatHistoryProvider.StoreChatHistoryAsync – Armazenar mensagens de solicitação e resposta, todas as quais devem ser novas.
Aqui está um exemplo de um simples ChatHistoryProvider que armazena o histórico de chat diretamente no estado da sessão.
public sealed class SimpleInMemoryChatHistoryProvider : ChatHistoryProvider
{
private readonly ProviderSessionState<State> _sessionState;
public SimpleInMemoryChatHistoryProvider(
Func<AgentSession?, State>? stateInitializer = null,
string? stateKey = null)
{
this._sessionState = new ProviderSessionState<State>(
stateInitializer ?? (_ => new State()),
stateKey ?? this.GetType().Name);
}
public override string StateKey => this._sessionState.StateKey;
protected override ValueTask<IEnumerable<ChatMessage>> ProvideChatHistoryAsync(InvokingContext context, CancellationToken cancellationToken = default) =>
// return all messages in the session state
new(this._sessionState.GetOrInitializeState(context.Session).Messages);
protected override ValueTask StoreChatHistoryAsync(InvokedContext context, CancellationToken cancellationToken = default)
{
var state = this._sessionState.GetOrInitializeState(context.Session);
// Add both request and response messages to the session state.
var allNewMessages = context.RequestMessages.Concat(context.ResponseMessages ?? []);
state.Messages.AddRange(allNewMessages);
this._sessionState.SaveState(context.Session, state);
return default;
}
public sealed class State
{
[JsonPropertyName("messages")]
public List<ChatMessage> Messages { get; set; } = [];
}
}
Implementação avançada ChatHistoryProvider
Uma implementação mais avançada poderia optar por substituir os seguintes métodos:
- ChatHistoryProvider.InvokingCoreAsync - Chamado antes que o agente invoque o LLM, permitindo que a lista de mensagens de solicitação seja modificada.
- ChatHistoryProvider.InvokedCoreAsync - Chamado depois que o agente invocou o LLM e permite acesso a todas as mensagens de solicitação e resposta.
ChatHistoryProvider fornece implementações base de InvokingCoreAsync e InvokedCoreAsync.
A InvokingCoreAsync implementação base faz o seguinte:
- chamadas
ProvideChatHistoryAsyncpara obter as mensagens que devem ser usadas como histórico de chat para a execução - executa um filtro
FuncprovideOutputMessageFilteropcional em mensagens retornadas porProvideChatHistoryAsync. Esse filtroFuncpode ser fornecido por meio doChatHistoryProviderconstrutor. - mescla as mensagens filtradas retornadas por
ProvideChatHistoryAsynccom as mensagens passadas para o agente pelo chamador, para produzir as mensagens de solicitação do agente. O histórico de chat é anexado às mensagens de entrada do agente. - carimba todas as mensagens filtradas retornadas
ProvideChatHistoryAsynccom informações de origem, indicando que essas mensagens são provenientes do histórico de chat.
A InvokedCoreAsync base faz o seguinte:
- verifica se a execução falhou e, em caso afirmativo, retorna sem fazer nenhum processamento adicional.
- filtra as mensagens de solicitação do agente para excluir mensagens que foram produzidas por um
ChatHistoryProvider, já que queremos armazenar apenas novas mensagens e não aquelas que foram produzidas peloChatHistoryProviderem primeiro lugar. Observe que esse filtro pode ser substituído por meio dostoreInputMessageFilterparâmetro noChatHistoryProviderconstrutor. - passa as mensagens de solicitação filtradas e todas as mensagens de resposta para
StoreChatHistoryAsynco armazenamento.
É possível sobrescrever esses métodos para implementar um ChatHistoryProvider, no entanto, isso requer que o responsável desenvolva a funcionalidade básica conforme necessário.
Aqui está um exemplo de tal implementação.
public sealed class AdvancedInMemoryChatHistoryProvider : ChatHistoryProvider
{
private readonly ProviderSessionState<State> _sessionState;
public AdvancedInMemoryChatHistoryProvider(
Func<AgentSession?, State>? stateInitializer = null,
string? stateKey = null)
{
this._sessionState = new ProviderSessionState<State>(
stateInitializer ?? (_ => new State()),
stateKey ?? this.GetType().Name);
}
public override string StateKey => this._sessionState.StateKey;
protected override ValueTask<IEnumerable<ChatMessage>> InvokingCoreAsync(InvokingContext context, CancellationToken cancellationToken = default)
{
// Retrieve the chat history from the session state.
var chatHistory = this._sessionState.GetOrInitializeState(context.Session).Messages;
// Stamp the messages with this class as the source, so that they can be filtered out later if needed when storing the agent input/output.
var stampedChatHistory = chatHistory.Select(message => message.WithAgentRequestMessageSource(AgentRequestMessageSourceType.ChatHistory, this.GetType().FullName!));
// Merge the original input with the chat history to produce a combined agent input.
return new(stampedChatHistory.Concat(context.RequestMessages));
}
protected override ValueTask InvokedCoreAsync(InvokedContext context, CancellationToken cancellationToken = default)
{
if (context.InvokeException is not null)
{
return default;
}
// Since we are receiving all messages that were contributed earlier, including those from chat history, we need to filter out the messages that came from chat history
// so that we don't store message we already have in storage.
var filteredRequestMessages = context.RequestMessages.Where(m => m.GetAgentRequestMessageSourceType() != AgentRequestMessageSourceType.ChatHistory);
var state = this._sessionState.GetOrInitializeState(context.Session);
// Add both request and response messages to the state.
var allNewMessages = filteredRequestMessages.Concat(context.ResponseMessages ?? []);
state.Messages.AddRange(allNewMessages);
this._sessionState.SaveState(context.Session, state);
return default;
}
public sealed class State
{
[JsonPropertyName("messages")]
public List<ChatMessage> Messages { get; set; } = [];
}
}
- No Python, apenas um provedor de histórico deve usar
load_messages=True.
from agent_framework.openai import OpenAIChatClient
history = DatabaseHistoryProvider(db_client)
agent = OpenAIChatClient().as_agent(
name="StorageAgent",
instructions="You are a helpful assistant.",
context_providers=[history],
)
session = agent.create_session()
await agent.run("Store this conversation.", session=session)
Persistência de sessões entre reinicializações
Persista o conteúdo completo do AgentSession, não apenas o texto da mensagem.
JsonElement serialized = agent.SerializeSession(session);
// Store serialized payload in durable storage.
AgentSession resumed = await agent.DeserializeSessionAsync(serialized);
serialized = session.to_dict()
# Store serialized payload in durable storage.
resumed = AgentSession.from_dict(serialized)
Importante
Trate AgentSession como um objeto de estado opaco e restaure-o com a mesma configuração de agente/provedor que o criou.
Dica
Use um provedor de histórico de auditoria/avaliação adicional (load_messages=False, store_context_messages=True) para capturar contexto enriquecido mais entrada/saída sem afetar o carregamento do histórico primário.