📖

Amazon Bedrock AgentCore Memory の Branch機能の使いどころ

に公開

AWS AI Agent ブログ祭り(Zenn: #awsaiagentblogfes, X: #AWS_AI_AGENT_ブログ祭りの第 4 日目です。

本ブログではAgentCore Memory の中でもニッチなBranch機能について、具体的なユースケースを検討し簡単なコードを書いて動作を確認してみました。

AgentCore Memory って何

AgentCore Memoryはざっくり言うとAI Agentに記憶を持たせるマネージドサービスです。Short-term MemoryとLong-term Memoryの2種類があり、それぞれ会話の生データとユーザーの好み/傾向を保存します。

詳細はこばDさんの AWS_AI_AGENT_ブログ祭り第 2 日目の記事を読んでみてください!

AgentCore Memory Branch って何

Branch機能は特定イベントから派生/分岐したイベントを作成・管理するためのものです。AI Agentとの会話の中でメイントピックから派生した別トピックの会話を別スレッドで管理した場合はこんなイメージ。

なんか便利そうなのは分かったが、正直ユースケースが分からなかったので調べたところ
公式ブログに以下のようなものが例示されていました。

メッセージ編集

ユーザーが以前のメッセージを編集する場合、ブランチによって元の会話の流れと新しい方向の両方が保持されます。ユーザーが以前の入力を変更した場合でも一貫したコンテキストの維持を可能にします。

What-ifシナリオ

メインの会話のコンテキストを保持しつつ、問題解決のために複数のパスで検討して解を探索するケース。例えば金融アドバイザーエージェントが、元の相談内容を保持したまま異なる投資戦略を探索することができる。

代替アプローチ

複雑な問題解決において、複数のエージェントが並列で異なる解法を実行するケース。各エージェントが導き出した解を別ブランチで保持してユーザーに解を選択させることができる。

Branch 機能を使うメリット

AWS Samples の Branch 機能 Tutorial 内では以下のようなメリットがあると記載されています。

コンテキストの分離

マルチエージェントシステムにおいて、各エージェントの関心ごとは異なるはずです。マルチエージェントシステムでbranchを用いることで、各エージェントにとって必要なコンテキストのみを分離して管理できるようになります。

トレーサビリティ

マルチエージェントなシステムでエージェントごとのブランチを分けることで、どのエージェントがどのように回答したか追跡が容易になります。

ユースケース実装例

旅行 AI Agent を例に簡単に実装してみます。ここではBranch機能の挙動にフォーカスするため、エラーハンドリングなし、IDのハードコードなど簡素な実装をしています。

メッセージ編集のユースケース

AI Agent との会話が長くなりすぎると、AI Agentが過去の入力内容に引っ張られて回答をすることがあります。そのような場合に特定のポイントに戻って入力を修正したくなるケースを想定しています。

1. Short-term Memoryを作成

import boto3
from datetime import datetime

cp_client = boto3.client(
    'bedrock-agentcore-control', 
    region_name='us-west-2'
)

# Short-term Memory の作成
response = cp_client.create_memory(
    name='Travel_Agent_Memory',
    description='Travel Agent Memory',
    eventExpiryDuration=30,
)

memory_id = response['memory']['id']
memory_arn = response['memory']['arn']

2. 1つめのeventを作成

create_event API でイベントを作成。ここでは特にBranchに関するパラメータは指定しない。

actorId='user1'
sessionId='session1'

dp_client = boto3.client('bedrock-agentcore', region_name='us-west-2')

event1  = dp_client.create_event(
    memoryId=memory_id,
    actorId=actorId,
    sessionId=sessionId,
    eventTimestamp=datetime.now(),
    payload=[
        {
            'conversational': {
                'content': {'text': 'キューバ行きたい'},
                'role': 'USER'
            }
        }
    ]
)

print(event1)

create_event API でBranchに関する指定をしない場合、デフォルトで作成されるmain branch にeventが追加されます。

{ ~ 省略 ~
  'event': {
      'memoryId': 'Travel_Agent_Memory-stGl25AiDB',
      'actorId': 'user1',
      'sessionId': 'session1',
      'eventId': '0000001761265000929#29128d82', #イベントごとに作成される一意な値
      'eventTimestamp': datetime.datetime(2025, 10, 24, 0, 16, 40, 929000, tzinfo=tzlocal()),
      'branch': {'name': 'main'} #デフォルトのブランチ
  }
}

3. 2つめのイベントを作成

response  = dp_client.create_event(
    memoryId=memory_id,
    actorId=actorId,
    sessionId=sessionId,
    eventTimestamp=datetime.now(),
    payload=[
        {
            'conversational': {
                'content': {'text': 'モヒート飲みたい'},
                'role': 'USER'
            }
        }
    ]
)

print(response)

さらに main branchにイベントが追加されました

{ ~省略~
 'event': {'memoryId': 'Travel_Agent_Memory-stGl25AiDB',
    'actorId': 'user1',
    'sessionId': 'session1',
    'eventId': '0000001761275218297#6533476b',
    'eventTimestamp': datetime.datetime(2025, 10, 24, 3, 6, 58, 297000, tzinfo=tzlocal()),
    'branch': {'name': 'main'}}
}

現在のmain branchの状況はこんな感じ

4. Branch の作成

create_eventにはbranch parameterがあり、どのイベントからどんな名前でbranchを作るのか指定します。ここでは1つめのイベントで"キューバに行きたい"と言っていたのですが、気が変わって"ラオスに行きたい"となった場合を想定します。ラオスの会話を続けるためのbranchを作成します。

event_id = '0000001761265000929#29128d82' # 1つめのイベントのID
response  = dp_client.create_event(
    memoryId=memory_id,
    actorId=actorId,
    sessionId=sessionId,
    eventTimestamp=datetime.now(),
    payload=[
        {
            'conversational': {
                'content': {'text': 'ラオス行きたい'},
                'role': 'USER'
            }
        }
    ],
    # branchの作成。どこのイベントからどんな名前のbranchを作るか指定する
    branch={
        'rootEventId': event_id,
        'name': 'Laos'
    }
)

新しくLaos Branchが生えて全体はこんな感じ。今後ラオスに関する会話はLaos branchに追加すれば良いわけです。

5. Branchの確認

branchにどのようなEventが保存されているかを確認するためには、list_events APIでbranch名を指定して実行します。

response = dp_client.list_events(
    memoryId=memory_id,
    sessionId=sessionId,
    actorId=actorId,
    includePayloads=True,
    filter={
        'branch': {
            'name': 'Laos',
            'includeParentBranches': True,
        }
    }
)
print(response)

ちゃんとLaos branchにイベントが追加されていることが確認できます

{ ~ 省略 ~
 'events': [{'memoryId': 'Travel_Agent_Memory-stGl25AiDB',
   'actorId': 'user1',
   'sessionId': 'session1',
   'eventId': '0000001761278018534#0ce766f7',
   'eventTimestamp': datetime.datetime(2025, 10, 24, 3, 53, 38, 534000, tzinfo=tzlocal()),
   'payload': [{'conversational': {'content': {'text': 'ラオス行きたい'},
      'role': 'USER'}}],
   'branch': {'rootEventId': '0000001761265000929#29128d82', 'name': 'Laos'}
}

マルチエージェントのユースケース

ユーザーが旅行先を入力したらホテル/飛行機予約エージェントがそれぞれ動くAgenticなワークフローを想定しています。ホテル/飛行機予約 Agentに関連する入出力を別Branchにすることでコンテキストを分離しながらEventを保存することができます。

1. ホテル/飛行機予約 Agent を作成

サクッとStrands Agentsで作成

from strands import Agent
MODEL_ID = "us.anthropic.claude-3-7-sonnet-20250219-v1:0"

HOTEL_BOOKING_PROMPT = f"""あなたはホテル予約アシスタントです。お客様がホテルを見つけたり、予約をしたり、宿泊施設や設備に関する質問にお答えするお手伝いをします。空室状況、料金、予約手続きについて明確な情報を、親切で役立つ方法で提供してください。"""

# System prompt for the flight booking specialist
FLIGHT_BOOKING_PROMPT = f"""あなたはフライト予約アシスタントです。お客様がフライトを見つけたり、予約をしたり、航空会社、路線、旅行ポリシーに関する質問にお答えするお手伝いをします。フライトの空席状況、料金、スケジュール、予約手続きについて明確な情報を、親切で役立つ方法で提供してください。"""

hotel_booking_agent = Agent(
    model=MODEL_ID,
    system_prompt=HOTEL_BOOKING_PROMPT
)

flight_booking_agent = Agent(
    model=MODEL_ID,
    system_prompt=FLIGHT_BOOKING_PROMPT
)

2. ホテル/飛行機予約 Agent のそれぞれの出力をBranchで管理

user_input = "11月2日から20日までキューバに行きたい"

response = dp_client.create_event(
    memoryId=memory_id,
    actorId=actorId,
    sessionId=sessionId,
    eventTimestamp=datetime.now(),
    payload=[
        {
            'conversational': {
                'content': {'text': user_input},
                'role': 'USER'
            }
        }
    ]
)

hotel_booking_agent_response = hotel_booking_agent(user_input)
flight_booking_agent_response = flight_booking_agent(user_input)

hotel_booking_response = hotel_booking_agent_response.message.get("content", [{}])[0].get("text","")
flight_booking_response = flight_booking_agent_response.message.get("content", [{}])[0].get("text","")

event_id = response['event']['eventId']

dp_client.create_event(
    memoryId=memory_id,
    actorId=actorId,
    sessionId=sessionId,
    eventTimestamp=datetime.now(),
    payload=[
        {
            'conversational': {
                'content': {'text': hotel_booking_response},
                'role': 'ASSISTANT'
            }
        }
    ],
    branch={
        'rootEventId': event_id,
        'name': 'hotel_booking_agent'
    }
)

dp_client.create_event(
    memoryId=memory_id,
    actorId=actorId,
    sessionId=sessionId,
    eventTimestamp=datetime.now(),
    payload=[
        {
            'conversational': {
                'content': {'text': flight_booking_response},
                'role': 'ASSISTANT'
            }
        }
    ],
    branch={
        'rootEventId': event_id,
        'name': 'flight_booking_agent'
    }
)

全体はこんな感じ

おわりに

AgentCore Memory の Branch機能のユースケースと簡素な実装例を紹介しました。この機能は特にマルチエージェントの場合に強い力を発揮しそうな予感です。AgentCore触ってみてください!

アマゾン ウェブ サービス ジャパン (有志)

Discussion