Поделиться через


Журнал чата агента и память

Журнал чата агентов и память являются важными возможностями, которые позволяют агентам поддерживать контекст в беседах, запоминать предпочтения пользователей и предоставлять персонализированные возможности. Платформа agent Framework предоставляет несколько функций для различных вариантов использования, от простого хранилища сообщений чата в памяти до постоянных баз данных и специализированных служб памяти.

Журнал чатов

Различные варианты хранения журнала чата поддерживаются агентом Framework. Доступные параметры зависят от типа агента и базовых служб, используемых для создания агента.

Два основных поддерживаемых сценария:

  • Хранилище в памяти: агент построен на службе, которая не поддерживает хранилище журнала чата в службе (например, завершение openAI Chat). По умолчанию Agent Framework сохраняет полный журнал чата в памяти в объекте AgentSession , но разработчики могут предоставить пользовательскую ChatHistoryProvider реализацию для хранения журнала чатов в стороннем хранилище при необходимости.
  • Хранилище в службе: агент основан на службе, требующей хранения в службе журнала чата (например, постоянные агенты Azure AI Foundry). Agent Framework сохраняет идентификатор журнала удаленного чата в объекте AgentSession , а другие варианты хранения журнала чата не поддерживаются.

Хранилище журнала чата в памяти

При использовании службы, которая не поддерживает хранилище журнала чата в службе, агент Framework по умолчанию сохраняет журнал чата в памяти в объекте AgentSession . В этом случае полный журнал чата, хранящийся в объекте сеанса, а также любые новые сообщения, будут предоставлены базовой службе для каждого запуска агента. Эта конструкция позволяет использовать естественный диалоговый интерфейс с агентом. Вызывающий объект предоставляет только новое сообщение пользователя, а агент возвращает только новые ответы. Но агент имеет доступ к полному журналу бесед и будет использовать его при создании ответа.

При использовании openAI Chat Completion в качестве базовой службы для агентов следующий код приводит к тому, что в объекте сеанса содержится журнал чата из запуска агента.

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

Где сообщения хранятся в памяти, можно получить список сообщений из сеанса и управлять сообщениями непосредственно при необходимости.

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

Замечание

Получение сообщений из AgentSession объекта таким образом работает только в том случае, если используется хранилище в памяти.

Сокращение журнала чата с помощью хранилища в памяти

Встроенная функция InMemoryChatHistoryProvider , используемая по умолчанию, когда базовая служба не поддерживает хранилище в службе, можно настроить с помощью средства уменьшения размера журнала чата. Это полезно, чтобы избежать превышения ограничений размера контекста базовой службы.

Можно InMemoryChatHistoryProvider выполнить необязательную Microsoft.Extensions.AI.IChatReducer реализацию, чтобы уменьшить размер журнала чата. Он также позволяет настроить событие, в течение которого вызывается редуктор, либо после добавления сообщения в журнал чата, либо до возврата журнала чата для следующего вызова.

Чтобы настроить InMemoryChatHistoryProvider с помощью средства уменьшения, можно предоставить фабрику, чтобы создать новую для каждого нового InMemoryChatHistoryProviderAgentSession и передать ее редуктор по вашему выбору. Кроме InMemoryChatHistoryProvider того, можно передать необязательное событие триггера, которое можно задать для любого InMemoryChatHistoryProvider.ChatReducerTriggerEvent.AfterMessageAdded или InMemoryChatHistoryProvider.ChatReducerTriggerEvent.BeforeMessagesRetrieval.

Фабрика — это асинхронная функция, которая получает объект контекста и маркер отмены.

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

Замечание

Эта функция поддерживается только при использовании InMemoryChatHistoryProvider. Если служба имеет хранилище журнала чата в службе, она зависит от самой службы для управления размером журнала чата. Аналогичным образом, при использовании 3-го стороннего хранилища (см. ниже), это до решения 3-го стороннего хранилища для управления размером журнала чата. Если вы предоставляете ChatHistoryProviderFactory поставщик журнала чатов, но используете службу со встроенным хранилищем журнала чата, фабрика не будет использоваться.

Хранилище журнала чата вывода

При использовании службы, требующей хранения журнала чата в службе, агент Framework сохраняет идентификатор журнала удаленного чата в объекте AgentSession .

Например, при использовании ответов OpenAI с store=true в качестве базовой службы для агентов следующий код приведет к тому, что в объекте сеанса будет содержаться последний идентификатор ответа, возвращенный службой.

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

Замечание

Некоторые службы, например, OpenAI Responses поддерживают хранилище журнала чата в службе (store=true), или предоставляет полный журнал чата для каждого вызова (store=false). Таким образом, в зависимости от режима, в котором используется служба, agent Framework по умолчанию будет хранить полный журнал чата в памяти или хранить ссылку на идентификатор хранимой журнал чата службы.

Хранилище журнала чатов сторонних производителей

При использовании службы, которая не поддерживает хранилище журнала чатов в службе, Agent Framework позволяет разработчикам заменить хранилище журнала чата по умолчанию сторонним хранилищем журнала чата. Разработчику требуется предоставить подкласс базового абстрактного ChatHistoryProvider класса.

Класс ChatHistoryProvider определяет интерфейс для хранения и получения сообщений чата. Разработчики должны реализовать InvokedAsync и InvokingAsync методы для добавления сообщений в удаленное хранилище по мере их создания и получения сообщений из удаленного хранилища перед вызовом базовой службы.

Агент будет использовать все сообщения, возвращаемые InvokingAsync при обработке пользовательского запроса. Это зависит от реализации ChatHistoryProvider , чтобы убедиться, что размер журнала чата не превышает контекстное окно базовой службы.

При реализации настраиваемого ChatHistoryProvider хранилища, в котором хранится журнал чата в удаленном хранилище, журнал чата для этого сеанса должен храниться под ключом, уникальным для этого сеанса. Реализация ChatHistoryProvider должна создать этот ключ и сохранить его в состоянии. ChatHistoryProvider Serialize имеет метод, который можно переопределить для сериализации состояния при сериализации сеанса. Он ChatHistoryProvider также должен предоставить конструктор, который принимает входные JsonElement данные для поддержки десериализации его состояния.

Чтобы предоставить пользовательский ChatHistoryProviderChatClientAgentобъект, можно использовать ChatHistoryProviderFactory этот параметр при создании агента. Ниже приведен пример передачи пользовательской реализации ChatHistoryProvider в объект ChatClientAgent , основанный на завершении Чата Azure OpenAI.

Фабрика — это асинхронная функция, которая получает объект контекста и маркер отмены.

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

Подсказка

Подробный пример создания пользовательского хранилища сообщений см. в руководстве по хранению журнала чатов в 3-м хранилище сторон .

Долгосрочная память

Платформа agent Framework позволяет разработчикам предоставлять пользовательские компоненты, которые могут извлекать воспоминания или предоставлять воспоминания агенту.

Чтобы реализовать такой компонент памяти, разработчику необходимо подкласс абстрактного AIContextProvider базового класса. Этот класс имеет два основных метода и InvokingAsyncInvokedAsync. При переопределении InvokedAsync разработчики могут проверять все сообщения, предоставляемые пользователями или созданные агентом. InvokingAsync позволяет разработчикам внедрять дополнительный контекст для конкретного запуска агента. Системные инструкции, дополнительные сообщения и дополнительные функции можно предоставить.

Подсказка

Подробный пример создания пользовательского компонента памяти см. в руководстве по добавлению памяти в агент .

Сериализация агентаSession

Важно иметь возможность сохранять AgentSession объект между вызовами агента. Это позволяет в ситуациях, когда пользователь может задать вопрос агента и занять много времени, чтобы задать дальнейшие вопросы. Это позволяет состоянию AgentSession выжить перезапуск службы или приложения.

Даже если журнал чата хранится в удаленном хранилище, AgentSession объект по-прежнему содержит идентификатор, ссылающийся на журнал удаленного чата. AgentSession Поэтому потеря состояния приведет к потере идентификатора журнала удаленного чата.

Таким AgentSession образом, все объекты, подключенные к нему, предоставляют Serialize метод для сериализации их состояния. Также AIAgent предоставляется DeserializeSessionAsync метод, который повторно создает сеанс из сериализованного состояния. Метод DeserializeSessionAsync повторно создает сеанс с агентом ChatHistoryProvider и AIContextProvider настроен.

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

Замечание

AgentSession объекты могут содержать больше, чем только журнал чата, например поставщики контекстов также могут хранить состояние в объекте сеанса. Поэтому важно всегда сериализовать, хранить и десериализировать весь AgentSession объект, чтобы обеспечить сохранение всего состояния.

Это важно

Всегда рассматривать AgentSession объекты как непрозрачные объекты, если вы не уверены во внутренних элементах. Содержимое может отличаться не только по типу агента, но и по типу службы и конфигурации.

Предупреждение

Десериализация сеанса с агентом, отличным от того, который изначально создал его, или с агентом, который имеет конфигурацию, отличную от исходного агента, может привести к ошибкам или неожиданному поведению.

Типы памяти

Agent Framework поддерживает несколько типов памяти для размещения различных вариантов использования, включая управление журналом чата в рамках краткосрочной памяти и предоставление точек расширения для извлечения, хранения и внедрения долгосрочных воспоминаний в агенты.

хранилище In-Memory (по умолчанию)

Простейшая форма памяти, в которой журнал бесед хранится в памяти во время выполнения приложения. Это поведение по умолчанию и не требует дополнительной настройки.

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)

Хранилища постоянных сообщений

Для приложений, которые должны сохранять журнал бесед в сеансах, платформа предоставляет ChatMessageStore реализации:

Встроенный ChatMessageStore

Реализация по умолчанию в памяти, которую можно сериализовать:

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
)

Хранилище сообщений Redis

Для рабочих приложений, требующих постоянного хранения:

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
)

Пользовательское хранилище сообщений

Вы можете реализовать собственную серверную часть хранилища, реализуя следующую 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"]

Подсказка

Подробный пример создания пользовательского хранилища сообщений см. в руководстве по хранению журнала чатов в 3-м хранилище сторон .

Поставщики контекстов (динамическая память)

Поставщики контекстов обеспечивают сложные шаблоны памяти путем внедрения соответствующего контекста перед вызовом каждого агента:

Базовый поставщик контекста

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

Подсказка

Подробный пример создания пользовательского компонента памяти см. в руководстве по добавлению памяти в агент .

Внешние службы памяти

Платформа поддерживает интеграцию со специализированными службами памяти, такими как 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
)

Сериализация потоков и сохраняемость

Платформа поддерживает сериализацию всех состояний потока для сохраняемости при перезапуске приложения:

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)

Дальнейшие шаги