에이전트 채팅 기록 및 메모리는 에이전트가 대화 간에 컨텍스트를 유지하고, 사용자 기본 설정을 기억하며, 개인 설정된 환경을 제공할 수 있는 중요한 기능입니다. 에이전트 프레임워크는 간단한 메모리 내 채팅 메시지 스토리지에서 영구 데이터베이스 및 특수 메모리 서비스에 이르기까지 다양한 사용 사례에 적합한 여러 기능을 제공합니다.
채팅 기록
다양한 채팅 기록 스토리지 옵션은 에이전트 프레임워크에서 지원됩니다. 사용 가능한 옵션은 에이전트 유형 및 에이전트를 빌드하는 데 사용되는 기본 서비스에 따라 다릅니다.
지원되는 두 가지 주요 시나리오는 다음과 같습니다.
-
메모리 내 스토리지: 에이전트는 채팅 기록의 서비스 내 스토리지(예: OpenAI 채팅 완료)를 지원하지 않는 서비스를 기반으로 합니다. 기본적으로 Agent Framework는 전체 채팅 기록을 개체에 메모리
AgentSession에 저장하지만, 개발자는 필요한 경우 타사 저장소에 채팅 기록을 저장하는 사용자 지정ChatHistoryProvider구현을 제공할 수 있습니다. -
서비스 내 스토리지: 에이전트는 채팅 기록(예: Azure AI Foundry 영구 에이전트)의 서비스 내 스토리지가 필요한 서비스를 기반으로 합니다. 에이전트 프레임워크는 개체에 원격 채팅 기록의 ID를
AgentSession저장하며 다른 채팅 기록 스토리지 옵션은 지원되지 않습니다.
메모리 내 채팅 기록 스토리지
채팅 기록의 서비스 내 스토리지를 지원하지 않는 서비스를 사용하는 경우 Agent Framework는 기본적으로 개체에 채팅 기록을 메모리 AgentSession 에 저장합니다. 이 경우 세션 개체에 저장된 전체 채팅 기록과 새 메시지는 각 에이전트 실행의 기본 서비스에 제공됩니다. 이 디자인을 통해 에이전트와의 자연스러운 대화 환경을 구현할 수 있습니다. 호출자는 새 사용자 메시지만 제공하고 에이전트는 새 답변만 반환합니다. 그러나 에이전트는 전체 대화 기록에 액세스할 수 있으며 응답을 생성할 때 사용합니다.
OpenAI 채팅 완료를 에이전트의 기본 서비스로 사용하는 경우 다음 코드는 에이전트 실행의 채팅 기록을 포함하는 세션 개체를 생성합니다.
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 하려면 팩터리를 제공하여 새 리듀서마다 InMemoryChatHistoryProvider 새로 AgentSession 생성하고 선택한 리듀서에 전달할 수 있습니다.
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. 서비스에 서비스 내 채팅 기록 스토리지가 있는 경우 채팅 기록의 크기를 관리하는 것은 서비스 자체에 달려 있습니다. 마찬가지로 타사 스토리지를 사용하는 경우(아래 참조) 채팅 기록 크기를 관리하는 것은 타사 스토리지 솔루션에 달려 있습니다. 채팅 기록 공급자를 ChatHistoryProviderFactory 제공하지만 기본 제공 채팅 기록 스토리지가 있는 서비스를 사용하는 경우 팩터리는 사용되지 않습니다.
유추 서비스 채팅 기록 스토리지
채팅 기록의 서비스 내 스토리지가 필요한 서비스를 사용하는 경우 Agent Framework는 개체에 원격 채팅 기록의 ID를 AgentSession 저장합니다.
예를 들어 store=true와 함께 OpenAI Response를 에이전트의 기본 서비스로 사용하는 경우 다음 코드는 서비스에서 반환된 마지막 응답 ID를 포함하는 세션 개체를 생성합니다.
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 응답과 같은 일부 서비스는 채팅 기록의 서비스 내 스토리지(store=true)를 지원하거나 각 호출에 대한 전체 채팅 기록(store=false)을 제공합니다. 따라서 서비스가 사용되는 모드에 따라 Agent Framework는 기본적으로 전체 채팅 기록을 메모리에 저장하거나 서비스 저장된 채팅 기록에 대한 ID 참조를 저장합니다.
타사 채팅 기록 스토리지
채팅 기록의 서비스 내 스토리지를 지원하지 않는 서비스를 사용하는 경우 에이전트 프레임워크를 사용하면 개발자가 채팅 기록의 기본 메모리 내 스토리지를 타사 채팅 기록 스토리지로 바꿀 수 있습니다. 개발자는 기본 추상 ChatHistoryProvider 클래스의 하위 클래스를 제공해야 합니다.
클래스는 ChatHistoryProvider 채팅 메시지를 저장하고 검색하기 위한 인터페이스를 정의합니다. 개발자는 생성될 때 원격 저장소에 메시지를 추가하고 기본 서비스를 호출하기 전에 원격 저장소에서 메시지를 검색하는 메서드 및 InvokedAsync 메서드를 구현 InvokingAsync 해야 합니다.
에이전트는 사용자 쿼리를 처리할 때 반환되는 InvokingAsync 모든 메시지를 사용합니다. 채팅 기록의 ChatHistoryProvider 크기가 기본 서비스의 컨텍스트 창을 초과하지 않도록 하는 것은 구현자에게 달려 있습니다.
원격 저장소에 채팅 기록을 저장하는 사용자 지정 ChatHistoryProvider 을 구현할 때 해당 세션의 채팅 기록은 해당 세션에 고유한 키 아래에 저장되어야 합니다. 구현은 ChatHistoryProvider 이 키를 생성하고 해당 상태로 유지해야 합니다.
ChatHistoryProvider 에는 Serialize 세션이 serialize될 때 해당 상태를 직렬화하도록 재정의할 수 있는 메서드가 있습니다.
ChatHistoryProvider 또한 해당 상태의 역직렬화를 지원하기 위해 입력으로 사용하는 JsonElement 생성자를 제공해야 합니다.
사용자 지정 ChatHistoryProvider 을 제공하려면 에이전트를 ChatClientAgent만들 때 이 ChatHistoryProviderFactory 옵션을 사용할 수 있습니다.
다음은 Azure OpenAI 채팅 완료를 기반으로 하는 사용자 지정 구현 ChatHistoryProvider 을 ChatClientAgent 전달하는 방법을 보여 주는 예제입니다.
팩터리는 컨텍스트 개체 및 취소 토큰을 수신하는 비동기 함수입니다.
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))
});
팁 (조언)
사용자 지정 메시지 저장소를 만드는 방법에 대한 자세한 예제는 타사 스토리지의 채팅 기록 저장 자습서를 참조하세요.
장기 메모리
에이전트 프레임워크를 사용하면 개발자가 추억을 추출하거나 에이전트에 추억을 제공할 수 있는 사용자 지정 구성 요소를 제공할 수 있습니다.
이러한 메모리 구성 요소를 구현하려면 개발자가 추상 기본 클래스를 AIContextProvider 서브클래스해야 합니다. 이 클래스에는 두 가지 핵심 메서드와 InvokingAsyncInvokedAsync. 재정의 InvokedAsync 되는 경우 개발자는 사용자가 제공하거나 에이전트에서 생성한 모든 메시지를 검사할 수 있습니다.
InvokingAsync 를 사용하면 개발자가 특정 에이전트 실행에 대한 추가 컨텍스트를 삽입할 수 있습니다. 시스템 지침, 추가 메시지 및 추가 함수를 제공할 수 있습니다.
팁 (조언)
사용자 지정 메모리 구성 요소를 만드는 방법에 대한 자세한 예제는 에이전트에 메모리 추가 자습서를 참조하세요.
AgentSession Serialization
에이전트 호출 간에 개체를 AgentSession 유지할 수 있어야 합니다. 이렇게 하면 사용자가 에이전트에 대한 질문을 하고 후속 질문을 하는 데 시간이 오래 걸릴 수 있습니다. 이렇게 하면 AgentSession 상태가 서비스 또는 앱 다시 시작에서 살아남을 수 있습니다.
채팅 기록이 원격 저장소 AgentSession 에 저장되어 있더라도 개체에는 원격 채팅 기록을 참조하는 ID가 계속 포함됩니다.
AgentSession 따라서 상태를 잃으면 원격 채팅 기록의 ID도 손실됩니다.
AgentSession 연결된 개체뿐만 아니라 해당 상태를 직렬화하는 메서드를 모두 제공합니다Serialize.
AIAgent 또한 serialize된 상태에서 세션을 다시 만드는 메서드도 제공합니다DeserializeSessionAsync. 메서드는 DeserializeSessionAsync 에이전트에 구성된 세션을 ChatHistoryProviderAIContextProvider 다시 만듭니다.
// 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 합니다. 콘텐츠는 에이전트 유형뿐만 아니라 서비스 유형 및 구성에 따라 달라질 수 있습니다.
경고
원래 만든 에이전트와 다른 에이전트로 세션을 역직렬화하거나 원래 에이전트와 다른 구성을 가진 에이전트를 사용하여 세션을 역직렬화하면 오류 또는 예기치 않은 동작이 발생할 수 있습니다.
메모리 형식
에이전트 프레임워크는 단기 메모리의 일부로 채팅 기록을 관리하고 에이전트에 장기 기억을 추출, 저장 및 주입하기 위한 확장 지점을 제공하는 등 다양한 사용 사례를 수용하기 위해 여러 가지 유형의 메모리를 지원합니다.
In-Memory Storage(기본값)
애플리케이션 런타임 동안 대화 기록이 메모리에 저장되는 가장 간단한 형태의 메모리입니다. 이는 기본 동작이며 추가 구성이 필요하지 않습니다.
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
serialize할 수 있는 기본 메모리 내 구현:
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"]
팁 (조언)
사용자 지정 메시지 저장소를 만드는 방법에 대한 자세한 예제는 타사 스토리지의 채팅 기록 저장 자습서를 참조하세요.
컨텍스트 공급자(동적 메모리)
컨텍스트 공급자는 각 에이전트 호출 전에 관련 컨텍스트를 삽입하여 정교한 메모리 패턴을 사용하도록 설정합니다.
기본 컨텍스트 공급자
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
)
스레드 Serialization 및 지속성
프레임워크는 애플리케이션을 다시 시작하는 동안 지속성을 위해 전체 스레드 상태를 직렬화할 수 있습니다.
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)