0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AgentCore Memory の CreateEvent が JSON ペイロードに対応、会話じゃないデータもそのまま長期記憶にできるように

0
Posted at

はじめに

Amazon Bedrock AgentCore Memoryは、エージェントとユーザーの会話から長期記憶を自動抽出してくれる機能ですが、これまでCreateEvent(短期記憶にイベントを1件書き込むAPI)に渡せるのは実質「会話文(role + text)」か「抽出対象外のバイナリ(blob)」の2択でした。

行動ログやシステムイベントのような構造化データを覚えさせたい場合、無理やり会話文っぽい形式に変換する必要があったんですね。

2026年8月20日、CreateEventJSON形式のペイロードにそのまま対応しました。

JSONペイロード対応

AgentCore Memoryは「短期記憶(イベントの生ログ)」と「長期記憶(そこから抽出された事実や好み)」の2層構造になっています。CreateEventは前者にイベントを1件書き込むAPIで、その内容が非同期で後者に抽出される仕組みです。

これまでのpayloadは次の2種類でした。

  • conversational: role(USER/ASSISTANT)とtextを持つ会話メッセージ。長期記憶抽出の対象
  • blob: 画像などのバイナリデータ。短期記憶にのみ残り、長期記憶には抽出されない

今回追加されたjsonは、行動イベントやアクティビティログのような非会話データをそのまま渡せる型で、conversationalと同様に長期記憶抽出の対象になります。1つのCreateEvent呼び出しで複数のpayload項目を混在できるので、会話文とJSONを同じイベントにまとめて送ることも可能です。

何がうれしいのか

ECサイトの閲覧履歴、IoTデバイスのセンサーイベント、アプリのテレメトリのように、もともと構造化データとして存在するものを覚えさせたい場面は多いはずです。これまでは{"eventType": "product_viewed", "productId": "cam-9921"}のようなデータを「カメラcam-9921を見ました」という文章に変換してから渡す必要がありましたが、今後はそのままjsonペイロードとして送るだけで済みます。変換ロジックが要らなくなる分、エージェント側の実装がシンプルになります。

やってみた

手順1: メモリ専用プロジェクトを作る

今回はJSONペイロードの挙動だけを確認したいので、エージェント本体(ランタイム)は作らず、AgentCore CLIでメモリリソースだけを持つプロジェクトを作成します。

agentcore create --project-name memjson --no-agent
cd memjson

手順2: 4つの抽出戦略を追加する

JSONペイロードがどの抽出戦略でも扱われることを見るため、4種類の戦略(semantic/summarization/user_preference/episodic)をすべて有効にしたメモリを追加します。

4種類の戦略については過去に以下の記事で詳しく解説していますので、そちらを参照してください。

agentcore add memory \
  --name memjsonDemo \
  --strategies SEMANTIC,SUMMARIZATION,USER_PREFERENCE,EPISODIC \
  --expiry 7

手順3: デプロイして実リソースを作る

agentcore deploy -y

手順4: JSONペイロードでイベントを作る

CreateEvent自体はエージェントの実行中コードから呼ばれるAPIで、AgentCore CLIにはサブコマンドがありません。ここはエージェントコードと同じ経路として、boto3のbedrock-agentcoreクライアントから呼び出します。会話文とJSONの行動ログを1つのイベントにまとめて送ってみます。

import boto3, datetime

REGION = "ap-northeast-1"
control = boto3.client("bedrock-agentcore-control", region_name=REGION)
client = boto3.client("bedrock-agentcore", region_name=REGION)

# メモリIDは "<プロジェクト名>_<メモリ名>-<ランダムサフィックス>" になるので、
# agentcore add memory で付けた名前から動的に探す
MEMORY_ID = next(
    m["id"] for m in control.list_memories()["memories"] if "memjsonDemo" in m["id"]
)

resp = client.create_event(
    memoryId=MEMORY_ID,
    actorId="demo-user-001",
    sessionId="session-memjson-001",
    eventTimestamp=datetime.datetime.now(datetime.timezone.utc),
    payload=[
        {
            "conversational": {
                "content": {"text": "来週の出張用に、静かで電源席があるカフェを探してほしい"},
                "role": "USER",
            }
        },
        {
            "json": {
                "content": {
                    "eventType": "preference_set",
                    "attributes": {"noiseLevel": "quiet", "requiresPowerOutlet": True, "priceRange": "moderate"},
                }
            }
        },
    ],
)
print("eventId:", resp["event"]["eventId"])

手順5: 長期記憶への抽出を確認する

抽出は非同期処理なので、少し待ってからretrieve_memory_recordsで4つの戦略すべての名前空間を確認します。

import time

time.sleep(60)

namespaces = {
    "facts (semantic)": "/users/demo-user-001/facts",
    "preferences (user preference)": "/users/demo-user-001/preferences",
    "summaries (summarization)": "/summaries/demo-user-001/session-memjson-001",
    "episodes (episodic)": "/episodes/demo-user-001/session-memjson-001",
}
for label, namespace in namespaces.items():
    records = client.retrieve_memory_records(
        memoryId=MEMORY_ID,
        namespace=namespace,
        searchCriteria={"searchQuery": "カフェ 電源 静か", "topK": 3},
    )
    print(f"=== {label} ===")
    for r in records["memoryRecordSummaries"]:
        print(" -", r["content"]["text"])

実際に実行すると、4つの戦略すべてでJSONペイロードの内容が反映された記憶が確認できました(長さの都合上、内容は要約しています)。

=== facts (semantic) ===
 - ユーザーはカフェに電源席があることを必要とする。
 - ユーザーは価格帯が手頃(moderate)のカフェを好む。
=== preferences (user preference) ===
 - {"context":"JSONペイロードでもnoiseLevel: quiet、requiresPowerOutlet: trueが記録されている", "preference":"静かで電源席があるカフェを好む", ...}
=== summaries (summarization) ===
 - <topic name="カフェ検索リクエスト"> 静か・電源席必須・価格帯moderateという条件でのカフェ検索リクエスト </topic>
=== episodes (episodic) ===
 - {"situation":"...noiseLevel: quiet、priceRange: moderate、requiresPowerOutlet: trueが記録されている", "intent":"出張中に利用できるカフェを提案してもらうこと", ...}

preferencescontextepisodessituationフィールドには「JSONペイロードでもnoiseLevel: quietが記録されている」のように、JSONペイロードの内容を参照したことが抽出結果の説明文に明示されており、4つの戦略すべてで会話文とJSONの両方が使われたことが確認できます。

ここで注意したいのは、送った{"noiseLevel": "quiet", ...}という構造がそのまま長期記憶に複製されるわけではない、という点です。短期記憶(list_events)にはJSONペイロードがそのまま残りますが、長期記憶の各レコードは戦略の種類によらず常にLLMが生成したテキストであり、元のペイロード構造を保持しているわけではありません。

検証後はagentcore remove memory --name memjsonDemo -yのあとagentcore deploy -yを実行して、メモリリソースごと削除してくださいね。

参考リンク

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?