Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
O armazenamento controla onde reside o histórico das conversas, quanto histórico é carregado e com que fiabilidade as sessões podem ser retomadas.
Modos de armazenamento incorporados
O Agent Framework suporta dois modos regulares de armazenamento:
| Modo | O que é armazenado | Uso típico |
|---|---|---|
| Estado da sessão local | Histórico completo de chat em AgentSession.state (por exemplo, via InMemoryHistoryProvider) |
Serviços que não requerem persistência de conversas do lado do servidor |
| Armazenamento gerido por serviços | Estado de conversa no serviço; AgentSession.service_session_id aponta para este estado |
Serviços com suporte nativo de conversa persistente |
Armazenamento do histórico de bate-papo na memória
Quando um fornecedor não exige 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)
Redução do tamanho do histórico em memória
Se o histórico crescer demasiado para os 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 aplica-se a fornecedores de histórico em memória. Para o histórico de gestão de serviços, o comportamento de redução é específico do fornecedor/serviço.
Armazenamento gerido por serviços
Quando o serviço gere o histórico de conversas, a sessão armazena um identificador remoto de conversa.
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 de terceiros/personalizado
Para histórico suportado por bases de dados/Redis/blobs, implemente um provedor de histórico personalizado.
Orientações principais:
- Armazenar mensagens sob uma chave com âmbito de sessão.
- Mantenha o histórico devolvido dentro dos limites do contexto do modelo.
- Persistir identificadores específicos do fornecedor no estado da sessão.
A classe base para os fornecedores de história é Microsoft.Agents.AI.ChatHistoryProvider.
Os fornecedores de histórico participam no pipeline do agente, têm a capacidade de contribuir ou sobrepor mensagens de entrada do agente e podem armazenar novas mensagens.
ChatHistoryProvider tem vários métodos virtuais que podem ser substituídos para implementar o seu próprio provedor de histórico personalizado.
Consulte as diferentes opções de implementação abaixo para mais informações sobre o que pode ser substituído.
ChatHistoryProvider Estado
Uma ChatHistoryProvider instância está associada a um agente e a mesma instância seria usada para todas as sessões.
Isto significa que o ChatHistoryProvider não deve armazenar nenhum estado específico da sessão na instância do provedor.
Pode ChatHistoryProvider ter uma referência a um cliente da base de dados num campo, mas não deve ter uma chave da base de dados para o histórico de chat num campo.
Em vez disso, ChatHistoryProvider pode armazenar quaisquer valores específicos da sessão, como chaves de base de dados, mensagens ou qualquer outra coisa relevante no próprio AgentSession. Todos os métodos virtuais em ChatHistoryProvider recebem uma referência ao atual AIAgent e AgentSession.
Para permitir o armazenamento fácil 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 ChatHistoryProvider simples normalmente sobrepõe-se a dois métodos:
- ChatHistoryProvider.ProvideChatHistoryAsync - Carregar o histórico de chat relevante e devolver as mensagens carregadas.
- ChatHistoryProvider.StoreChatHistoryAsync - Armazenar mensagens de pedido e resposta, todas elas devem ser novas.
Aqui está um exemplo de um simples ChatHistoryProvider que armazena o histórico do 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 sobrepor os seguintes métodos:
- ChatHistoryProvider.InvokingCoreAsync - Chamado antes do agente invocar o LLM e permitir a modificação da lista de mensagens de pedido.
- ChatHistoryProvider.InvokedCoreAsync - Chamado depois de o agente ter invocado o LLM e permite o acesso a todas as mensagens de pedido e resposta.
ChatHistoryProvider fornece implementações base de InvokingCoreAsync e InvokedCoreAsync.
A InvokingCoreAsync implementação base faz o seguinte:
- Chama
ProvideChatHistoryAsyncpara receber as mensagens que devem ser usadas como histórico de chat para a execução. - executa um filtro
FuncprovideOutputMessageFilteropcional nas mensagens devolvidas porProvideChatHistoryAsync. Este filtroFuncpode ser fornecido através doChatHistoryProviderconstrutor. - funde as mensagens filtradas devolvidas por
ProvideChatHistoryAsynccom as mensagens passadas ao agente pelo chamador, para produzir as mensagens de pedido do agente. O histórico do chat é anteposto às mensagens de entrada do agente. - carimba todas as mensagens filtradas devolvidas por
ProvideChatHistoryAsynccom informação de origem, indicando que as mensagens provenientes são do histórico do chat.
A InvokedCoreAsync base faz o seguinte:
- verifica se a execução falhou e, em caso afirmativo, retorna sem fazer mais nenhum processamento.
- Filtra as mensagens de pedido de agente para excluir mensagens que foram produzidas por um
ChatHistoryProvider, uma vez que queremos armazenar apenas novas mensagens e não aquelas que foram produzidas inicialmente peloChatHistoryProvider. Tenha em atenção que este filtro pode ser substituído através do parâmetrostoreInputMessageFilterno construtorChatHistoryProvider. - Passa as mensagens de pedido filtradas e todas as mensagens de resposta para
StoreChatHistoryAsyncarmazenamento.
É possível substituir estes métodos para implementar um ChatHistoryProvider, no entanto, isso requer que o implementador crie a funcionalidade fundamental por si mesmo, conforme apropriado.
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; } = [];
}
}
- Em Python, apenas um fornecedor 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)
Sessões persistentes ao longo dos reinícios
Mantenha o texto completo AgentSession, não apenas 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/fornecedor que o criou.
Sugestão
Use um fornecedor adicional de histórico de auditoria/avaliação (load_messages=False, store_context_messages=True) para captar contexto enriquecido mais entrada/saída sem afetar o carregamento do histórico primário.