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