エージェントチャットの履歴とメモリは、エージェントが会話全体のコンテキストを維持し、ユーザー設定を記憶し、パーソナライズされたエクスペリエンスを提供できるようにする重要な機能です。 Agent Framework には、単純なメモリ内チャット メッセージ ストレージから永続的なデータベースや特殊なメモリ サービスまで、さまざまなユース ケースに適した複数の機能が用意されています。
チャット履歴
エージェント フレームワークでは、さまざまなチャット履歴ストレージ オプションがサポートされています。 使用可能なオプションは、エージェントの種類と、エージェントのビルドに使用される基になるサービスによって異なります。
サポートされる 2 つの主なシナリオを次に示します。
-
メモリ内ストレージ: エージェントは、チャット履歴のサービス内ストレージ (OpenAI チャット完了など) をサポートしないサービス上に構築されています。 Agent Framework は既定で、
AgentThreadオブジェクトに完全なチャット履歴をメモリ内に格納しますが、開発者はカスタムのChatMessageStore実装を提供して、必要に応じてサード パーティのストアにチャット履歴を格納できます。 -
サービス内ストレージ: エージェントは、チャット履歴のサービス内ストレージ (Azure AI Foundry Persistent Agents など) を必要とするサービス上に構築されます。 エージェント フレームワークは、リモート チャット履歴の ID を
AgentThreadオブジェクトに格納します。その他のチャット履歴ストレージ オプションはサポートされていません。
メモリ内チャット履歴ストレージ
チャット履歴のサービス内ストレージをサポートしていないサービスを使用する場合、Agent Framework は既定で、 AgentThread オブジェクトにチャット履歴をメモリ内に格納します。 この場合、スレッド オブジェクトに格納されている完全なチャット履歴と新しいメッセージは、各エージェント実行で基になるサービスに提供されます。 これにより、エージェントとの自然な会話エクスペリエンスが可能になります。呼び出し元は新しいユーザー メッセージのみを提供し、エージェントは新しい回答のみを返しますが、エージェントは完全な会話履歴にアクセスでき、応答の生成時にそれを使用します。
OpenAI Chat Completion をエージェントの基になるサービスとして使用する場合、次のコードは、エージェントの実行からのチャット履歴を含むスレッド オブジェクトになります。
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 を指定しても、チャット履歴ストレージが組み込まれたサービスを使用している場合、ファクトリは使用されません。
推論サービスのチャット履歴ストレージ
チャット履歴のサービス内ストレージを必要とするサービスを使用する場合、Agent Framework はリモート チャット履歴の ID を AgentThread オブジェクトに格納します。
たとえば、エージェントの基になるサービスとして store=true の OpenAI 応答を使用する場合、次のコードは、サービスによって返された最後の応答 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 参照を格納します。
サード パーティのチャット履歴ストレージ
チャット履歴のサービス内ストレージをサポートしていないサービスを使用する場合、Agent Framework を使用すると、開発者はチャット履歴の既定のメモリ内ストレージをサード パーティのチャット履歴ストレージに置き換えることができます。 開発者は、基本抽象 ChatMessageStore クラスのサブクラスを提供する必要があります。
ChatMessageStore クラスは、チャット メッセージを格納および取得するためのインターフェイスを定義します。 開発者は、 AddMessagesAsync メソッドと GetMessagesAsync メソッドを実装して、生成されたメッセージをリモート ストアに追加し、基になるサービスを呼び出す前にリモート ストアからメッセージを取得する必要があります。
エージェントは、ユーザー クエリを処理するときに GetMessagesAsync によって返されるすべてのメッセージを使用します。 チャット履歴のサイズが基になるサービスのコンテキスト ウィンドウを超えないようにするには、 ChatMessageStore の実装者が必要です。
リモート ストアにチャット履歴を格納するカスタム ChatMessageStore を実装する場合、そのスレッドのチャット履歴は、そのスレッドに固有のキーの下に格納する必要があります。
ChatMessageStore実装では、このキーを生成し、その状態を維持する必要があります。
ChatMessageStore には、スレッドがシリアル化されるときに状態をシリアル化するためにオーバーライドできる 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);
}
});
ヒント
カスタム メッセージ ストアを作成する方法の詳細な例については、「 サード パーティのストレージにチャット履歴を保存する 」チュートリアルを参照してください。
長期記憶
Agent Framework を使用すると、開発者はメモリを抽出したり、エージェントにメモリを提供したりできるカスタム コンポーネントを提供できます。
このようなメモリ コンポーネントを実装するには、開発者は抽象基底クラス AIContextProvider サブクラス化する必要があります。 このクラスには、 InvokingAsync と InvokedAsyncの 2 つのコア メソッドがあります。
InvokedAsyncオーバーライドされると、開発者は、ユーザーによって提供またはエージェントによって生成されたすべてのメッセージを検査できます。
InvokingAsync を使用すると、開発者は特定のエージェント実行に追加のコンテキストを挿入できます。 システム命令、追加メッセージ、追加機能を提供できます。
ヒント
カスタム メモリ コンポーネントを作成する方法の詳細な例については、 エージェントへのメモリの追加 に関するチュートリアルを参照してください。
AgentThread シリアル化
エージェントの呼び出し間で AgentThread オブジェクトを保持できることが重要です。 これにより、ユーザーがエージェントの質問をしたり、フォローアップの質問に時間がかかる場合があります。 これにより、 AgentThread 状態はサービスまたはアプリの再起動後も存続できます。
チャット履歴がリモート ストアに格納されている場合でも、 AgentThread オブジェクトにはリモート チャット履歴を参照する ID が含まれます。 したがって、 AgentThread 状態を失うと、リモート チャット履歴の ID も失われます。
したがって、 AgentThread とそれにアタッチされているオブジェクトはすべて、その状態をシリアル化するための SerializeAsync メソッドを提供します。
AIAgentには、シリアル化された状態からスレッドを再作成するDeserializeThread メソッドも用意されています。
DeserializeThread メソッドは、ChatMessageStoreを使用してスレッドを再作成し、エージェントで構成AIContextProvider。
// 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 オブジェクト全体を常にシリアル化、格納、および逆シリアル化することが重要です。
Important
内部が非常に確実でない限り、 AgentThread オブジェクトは常に不透明なオブジェクトとして扱います。 内容は、エージェントの種類だけでなく、サービスの種類と構成によっても異なる場合があります。
Warnung
最初に作成したエージェントとは異なるエージェント、または元のエージェントとは異なる構成のエージェントを使用してスレッドを逆シリアル化すると、エラーや予期しない動作が発生する可能性があります。
メモリの種類
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"]
ヒント
カスタム メッセージ ストアを作成する方法の詳細な例については、「 サード パーティのストレージにチャット履歴を保存する 」チュートリアルを参照してください。
コンテキスト プロバイダー (動的メモリ)
コンテキスト プロバイダーは、各エージェント呼び出しの前に関連するコンテキストを挿入することで、高度なメモリ パターンを有効にします。
基本的なコンテキスト プロバイダー
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)