1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Microsoft Foundry Agent Service のメモリ機能を使用して長期記憶を実装する

1
Posted at

1. Microsoft Foundry Agent Service のメモリとは

メモリ機能は、Microsoft Foundry Agent Service に組み込まれた マネージドの長期記憶機能 です。セッション・デバイス・ワークフローを跨いでエージェントの連続性を実現し、ユーザーの好みの保持、会話履歴の維持、パーソナライズされた体験の提供を可能にします。

一般にエージェントのメモリは以下の 2 カテゴリに分類されます。

カテゴリ 説明
短期メモリ (Short-term memory) 現在のセッション内の会話を追跡し、進行中のやり取りのコンテキストを維持します。エージェントオーケストレーションフレームワークがセッションコンテキストの一部として管理します。
長期メモリ (Long-term memory) セッションを跨いで凝縮された知識を保持します。モデルが過去のユーザーインタラクションを想起し、蓄積できます。抽出・統合・管理を行う永続的なシステムが必要です。

Foundry Agent Service の Memory は長期メモリとして設計されており、会話から意味ある情報を抽出し、耐久性のあるナレッジに統合し、セッション間で利用可能にします。

ちなみに以前、Azure OpenAI Developers セミナー 2025でも以下のように AI Agent における記憶の全体像について解説していますので参考まで。

image.png

2. メモリの仕組み

メモリは以下の 3 つのフェーズで動作します。

2.1. 抽出 (Extraction)

ユーザーがエージェントと対話する際、システムが会話からキー情報(ユーザーの好み、事実、関連コンテキストなど)を能動的に抽出します。例えば「ピーマンが苦手」などの好みや、最近の活動の要約が識別・保存されます。

2.2. 統合 (Consolidation)

抽出されたメモリはストアの効率性と関連性を維持するために統合されます。システムは LLM を使用して類似・重複するトピックをマージし、冗長な情報の保存を防ぎます。新しいピーマンの好みの情報など矛盾する事実は解決され、正確なメモリが維持されます。

→この「統合」操作非常に注目しています。これを考えずにメモリを実装しようとすると意図が同じなのにテキストが微妙に異なる重複データがどんどん増えていっちゃいますからね。ただし、統合の動作はメモリの種類によって異なる場合があり、プレビュー中に変更される可能性がありますので注意が必要です。

2.3. 検索 (Retrieval)

エージェントが情報を想起する必要がある場合、メモリストアから最も関連性の高いメモリを検索します。これにより、エージェントは適切なコンテキストを迅速に表面化し、自然で情報に基づいた会話を実現します

3. メモリの種類

Foundry Agent Service は 2 種類の長期メモリを抽出・保存します。

種類 説明 設定方法
ユーザープロファイルメモリ (User profile memory) ユーザーの好みや情報(希望する呼び名、食事制限、言語設定など)です。会話コンテキストに依存しない「静的」な情報として扱われ、各会話の冒頭で 1 回取得します。 Memory Store の user_profile_details で指定
チャット要約メモリ (Chat summary memory) チャットセッションでカバーされた各トピック・スレッドの凝縮された要約です。ユーザーが以前のコンテキストを繰り返すことなく会話を継続・参照できます。 Memory Store の chat_summary_enabledtrue に設定

4. メモリの利用方法

メモリを利用するには以下の 3 つの方法があります。

4.1. Memory Search Tool(エージェントツール経由)

推奨: Memory Search ツールをプロンプトエージェントにアタッチすることで、会話中にメモリストアの読み書きが可能になります。ほとんどのシナリオに最適で、メモリ管理が簡素化されます。

4.2. Memory Store APIs(API 経由)

低レベル API を使用してメモリストアと直接対話します。高度なユースケースにおいて、より多くの制御と柔軟性を提供します。

4.3. Microsoft Agent Framework の Context Provider として

新たに実装された FoundryMemoryProvider から利用できます。

5. スコープ (Scope)

scope パラメータはメモリの分割方法を制御します。メモリストア内の各スコープは 独立したメモリアイテムのコレクション を保持します。

  • 静的スコープ: UUID やシステム内の安定した識別子を渡す
  • 自動ユーザースコープ: {{$userId}} を指定すると、リクエスト認証ヘッダーからテナント ID (TID) とオブジェクト ID (OID) を自動抽出

例えばカスタマーサポートエージェントでメモリ機能を使う場合、各ユーザーごとに独自のメモリを持たせるのがいいですね。 {{$userId}} で Microsoft Entra ID との連携ができるのは強い!メモリに関する機構の実装およびセキュリティ・ガバナンスを考えると Foundry のフルマネージドメモリ機能に責任を委任するのは良い考えかもしれません。

6. 事前準備

  • Azure サブスクリプション
  • Microsoft Foundry プロジェクト(承認・権限を構成済み)
  • チャットモデルのデプロイ(例: gpt-5.2
  • Embeddings モデルのデプロイ(例: text-embedding-3-small
  • Python SDK: pip install "azure-ai-projects>=2.0.0b4"

7. Memory Store の作成

エージェントごとに専用の Memory Store を作成し、メモリアクセスと最適化の明確な境界を確立します。作成時には、メモリコンテンツを処理するチャットモデル(メモリの抽出・統合に使用)と埋め込みモデル(メモリの検索時にベクトル類似度を計算)のデプロイメントを指定します。

AIProjectClient は Microsoft Foundry プロジェクトへの接続クライアントであり、DefaultAzureCredential によるキーレス認証が推奨されます。環境変数 FOUNDRY_PROJECT_ENDPOINT にはプロジェクトエンドポイント(https://{ai-services-account}.services.ai.azure.com/api/projects/{project-name})を設定します。

import os
from azure.ai.projects import AIProjectClient
from azure.ai.projects.models import MemoryStoreDefaultDefinition, MemoryStoreDefaultOptions
from azure.identity import DefaultAzureCredential

project_client = AIProjectClient(
    endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
    credential=DefaultAzureCredential(),
)

memory_store_name = "my_memory_store"

# メモリストアオプションを指定
options = MemoryStoreDefaultOptions(
    chat_summary_enabled=True,
    user_profile_enabled=True,
    user_profile_details="Cafe エージェントのための食品嗜好の情報を抽出してください。
    年齢、財務情報、正確な位置情報、認証情報など、無関係または機密性の高いデータは避けてください"
)

# メモリストアを作成
definition = MemoryStoreDefaultDefinition(
    chat_model="gpt-5.2",
    embedding_model="text-embedding-3-small",
    options=options
)

memory_store = project_client.beta.memory_stores.create(
    name=memory_store_name,
    definition=definition,
    description="Cafe Agent のための Memory store",
)
print(f"Created memory store: {memory_store.name}")

7.1. Memory Search Tool をエージェントにアタッチ

Memory Store を作成した後、Memory Search ツールをプロンプトエージェントにアタッチします。このツールにより、エージェントは会話中にメモリストアの読み取りと書き込みの両方が可能になります。scopeupdate_delay を構成して、メモリの分割方法と更新タイミングを制御します。

from azure.ai.projects.models import MemorySearchPreviewTool, PromptAgentDefinition

scope = "user_123"  # または "{{$userId}}" で認証トークンから自動取得

openai_client = project_client.get_openai_client()

# Memory Search ツールを作成
tool = MemorySearchPreviewTool(
    memory_store_name=memory_store_name,
    scope=scope,
    update_delay=1,  # 非アクティブ後 1 秒でメモリ更新(本番では 300 推奨)
)

# Memory Search ツール付きプロンプトエージェントを作成
agent = project_client.agents.create_version(
    agent_name="MyAgent",
    definition=PromptAgentDefinition(
        model="gpt-5.2",
        instructions="あなたはCafeエージェントです。ユーザーの好みを理解し、関連する記憶を検索して、パーソナライズされたコーヒーの提案を提供します。",
        tools=[tool],
    )
)
print(f"Agent created (id: {agent.id}, name: {agent.name})")

update_delay で会話の非アクティブ期間(秒)経過後にメモリ更新をトリガーします。エージェントレスポンスのたびに内部的に update_memories が呼び出されますが、実際の長期メモリへの書き込みは update_delay で設定された非アクティブ期間後にデバウンスされます。

7.2. Responses API から使う

会話の作成とエージェントレスポンスのリクエストを行います。各会話の開始時にユーザープロファイルメモリが自動注入され、エージェントは即座に永続的なコンテキストを保持できます。チャット要約メモリは各ターンで最新メッセージに基づいて検索され、レスポンスに反映されます。

import time

# 会話を作成
conversation = openai_client.conversations.create()

# ユーザーメッセージを送信
response = openai_client.responses.create(
    input="私は Hanabucks の Cafelatte Tall が好みです",
    conversation=conversation.id,
    extra_body={"agent_reference": {"name": agent.name, "type": "agent_reference"}},
)
print(f"Response: {response.output_text}")

# メモリが保存されるまで待機
print("Waiting for memories to be stored...")
time.sleep(65)

# 新しい会話でメモリが利用されることを確認
new_conversation = openai_client.conversations.create()
new_response = openai_client.responses.create(
    input="私のいつものコーヒーを注文してください",
    conversation=new_conversation.id,
    extra_body={"agent_reference": {"name": agent.name, "type": "agent_reference"}},
)
print(f"Response with memory: {new_response.output_text}")

セッション 1

Created conversation (id: conv_9284b62debda733800yXxEdkaxipdSlsbo6eX4rzlTPfRcuKda)
Response output: CafelatteのTallサイズがお好みなんですね!Hanabucksのカフェラテはエスプレッソとスチームミルクのバランスが良く、まろやかで飲みやすいのが特徴です。
もし新しい味を試してみたい場合は、同じくミルク多めでエスプレッソのコクが味わえる「バニララテ」や、「キャラメルマキアート」もおすすめです。甘さが加わって少し違った楽しみ方ができますよ。
また、ミルクを豆乳やアーモンドミルクに変えてみると、風味が変わってヘルシー感もアップします。ご興味あれば教えてくださいね!

セッション 2

Created new conversation (id: conv_3ac49857e9417b5c00xRq4kM7YSZosZZWQgIqTeUiSh29mLHKJ)Response output: かしこまりました。いつものHanabucksのカフェラテ、トールサイズでご注文を承ります。ご準備いたしますので、少々お待ちください。

いいですね、チャットセッションを跨いで私の好みが引き継がれました。

7.3. Microsoft Agent Framework から使う

Microsoft Agent Framework に FoundryMemoryProvider が実装されましたのでこれを使用して Microsoft Foundry に接続し、Agent に Context Provider としてアタッチします。

from agent_framework.azure import AzureOpenAIResponsesClient, FoundryMemoryProvider

# FoundryMemoryProvider を作成
      memory_provider = FoundryMemoryProvider(
          project_client=project_client,
          memory_store_name=memory_store_name,
          scope="user_123",   # ユーザーIDでメモリをスコープ分離
          update_delay=0,     # デモ用: 即座にメモリ更新 (本番では300秒推奨)
      )

      # メモリ付きエージェントを作成
      async with Agent(
          name="FoundryMemoryAgent",
          client=client,
          instructions="""あなたは旅行に詳しい親切なアシスタントです。
              過去の会話からのメモリが自動的に提供されます。
              ユーザーの名前が分かる場合は必ず名前で呼びかけてください。
              ユーザーの好みに基づいてパーソナライズされた提案を行ってください。""",
          context_providers=[
              memory_provider,
              InMemoryHistoryProvider(load_messages=False), # 履歴ロード無効(メモリのみをテスト)
          ],
          default_options={"store": False},  # サービス側にも会話履歴を保存しない
      ) as agent:

8. Memory Store APIs によるメモリ操作

8.1. メモリの追加 (Update Memories)

会話コンテンツをメモリストアに提供して記憶を追加します。システムがメモリ抽出・統合を含む前処理・後処理を行います。

scope = "user_123"

user_message = {
    "role": "user",
    "content": "私はめちゃくちゃ甘いキャラメルマキアートが好きで、通常は朝に飲む",
    "type": "message"
}

update_poller = project_client.beta.memory_stores.begin_update_memories(
    name=memory_store_name,
    scope=scope,
    items=[user_message],
    update_delay=0,  # 即座にメモリ更新をトリガー
)

update_result = update_poller.result()
print(f"Updated with {len(update_result.memory_operations)} memory operations")
for operation in update_result.memory_operations:
    print(f"  - {operation.kind}: {operation.memory_item.content}")

8.2. メモリの検索 (Search Memories)

from azure.ai.projects.models import MemorySearchOptions

query_message = {
    "role": "user",
    "content": "私の好きなものは?",
    "type": "message"
}

search_response = project_client.beta.memory_stores.search_memories(
    name=memory_store_name,
    scope=scope,
    items=[query_message],
    options=MemorySearchOptions(max_memories=5)
)
print(f"Found {len(search_response.memories)} memories")
for memory in search_response.memories:
    print(f"  - {memory.memory_item.content}")

Found 1 memories - Memory ID: 363731f934c344d194198f039b9e8cbd, Content: User prefers Hanabucks Cafelatte in Tall size.

現状の実装なのですが、どんなに「日本語で保存して」と user_profile_details で指示しても英語で保存されてしまいますね。これだと日本語の表現に関するコンテキストが保存時に失われる可能性があります。また、メモリ検索のオプションが max_memories しかないので、キーワード検索のような検索結果 0 件にはなりません。チャット要約メモリについては Threshold を実装して関係ないメモリの検索+注入を防ぎたいですよね。あと日付ソートも欲しry... 今後の Update に期待しましょう。

8.3. 静的メモリとコンテキストメモリの取得

Memory Store APIs で search_memories を呼び出す際、メモリの取得パターンは静的メモリコンテキストメモリの 2 種類に分かれます。これは「セクション 3. メモリの種類」で説明したユーザープロファイルメモリとチャット要約メモリの取得方法の違いに対応しています。

  • 静的メモリ (Static memories): ユーザープロファイルメモリに相当します。この実装方式だとユーザーの好み・設定などの情報は、メッセージの意味的類似度ではうまく検索できないことが多いです(例: 「コーヒーを注文して」というメッセージから「キャラメルマキアート好き」を意味的に引き当てるのは困難)。そのため、各会話の冒頭で 1 回取得してプロンプトに注入することが推奨されています。
  • コンテキストメモリ (Contextual memories): 最新のメッセージに意味的に関連するユーザープロファイルメモリおよびチャット要約メモリの両方を返します。会話のターンごとに呼び出し、エージェントのレスポンス生成に必要なコンテキストを動的に取得します。

2 つの取得パターンは、同じ search_memories API のパラメータの有無で使い分けます。

取得パターン 対象メモリ API の呼び出し方
静的メモリ ユーザープロファイルメモリ search_memoriesscope のみ指定(itemsprevious_search_id を渡さない)
コンテキストメモリ ユーザープロファイル + チャット要約メモリ search_memoriesitems(最新メッセージ)を指定して呼び出し

9. メモリの削除

スコープ単位の削除

特定ユーザーのデータ削除要求に対応できます。

project_client.beta.memory_stores.delete_scope(
    name=memory_store_name,
    scope="user_123"
)

メモリストア全体の削除

全スコープのすべてのメモリを含むメモリストアを削除します。

delete_response = project_client.beta.memory_stores.delete(memory_store_name)

今後の展望

以前 AutoGen で実験的に実装されていた Task-Centric Memory も実装してもいいかもしれないですね。

観点 Foundry Memory AutoGen Task-Centric Memory
記憶の中心 ユーザー、会話継続 タスク、知見、解法
主な保存内容 user_profilechat_summary task + insight
主目的 パーソナライズ、継続性 タスク学習、再利用、教示反映
学習ループ 会話から抽出・統合 助言、デモ、失敗学習

参考

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?