ジェネラティブエージェンツの遠藤です。
Amazon Bedrock AgentCoreは、まさに「これ欲しかったやつ!!」の塊で、テンションが爆上がりしています・・・!
そんな勢いで始めた『一通りさわり倒してみる』シリーズ、今回はAgentCore Memory編をお届けします。
前回はAgentCoreがいかに熱いかの感想と実際にAgentCore Runtimeを触ってみたまとめになっているので、ぜひそちらもご覧下さい。
Memoryに関する第一印象としては「よくぞこの仕組みをマネージドにしてくれた!」という感じですね。
エージェントとのやり取りを短期記憶としてAWSに渡しておくと、それを利用して非同期ジョブで自動的に長期記憶化して保存してくれるのは面白い方向性なんじゃないかと思います。
ただ、現時点だと以下の点がまだ見えないので周辺環境も含めてしばらく実験が必要だなーと感じています。
- 長期記憶のストラテジーがどの程度有効に動くのかが未知数
- トークン効率化などはそんなに考慮されてなさそうに見える
- LangGraphのCheckpointerやMemory Store周りとのつなぎ込みがどうなるか
とはいえ、データストアの選定から非同期ジョブの設計、インフラ構築、そしてその後の運用管理まで自前でやろうとするととても大変です。
それが一式AWSのマネージドサービスとしてまとまってるのはとても熱いですね・・・!
現時点ではLangGraph周りとのインテグレーション例は公開されていないため、この記事ではAWSのインフラの機能としてMemoryを触るとどんな感じかという話をしていきます。
AgentCore Memory
とてもざっくりまとめると、Memoryの要点は以下の3点だと触っていて感じています。
- Memoryに渡すべきインプットは短期記憶のみで良い
- 長期記憶は短期記憶から非同期で自動生成される
- 記憶はユーザーとセッションごとに適切に区切って管理できる
長期記憶は指定したストラテジー(最初からある3種類かカスタムのもの)に従って短期記憶から非同期に生成されます。
仕組みとしてはかなり洗練されていると思うので、あとはこれが実際のエージェント開発でどの程度うまく機能するかが今後の注目ポイントですね。
そこは今後いろいろなベンチマークが出たり実践したりで見えてくるポイントだと思うのでワクワクしますね。
細かいところが気になる方は是非公式のドキュメントもご覧ください。
メモリの作成
ここからは実際にMemoryを触っていこうと思います。
はじめに「短期記憶と長期記憶をセットでしまっておく箱」を作る必要があります。
この箱は以下のコードで作成することができ、作成後にIDやARNが返ってきます。
実際の記憶の操作はこの時返ってきたIDを利用して行うことになります。
# agent_core_memory_init.py from bedrock_agentcore.memory import MemoryClient client = MemoryClient(region_name="us-west-2") memory = client.create_memory( name="WeatherForecasterAgentMemory", description="Memory for weather forecaster", ) print(f"Memory ID: {memory.get('id')}") print(f"Memory: {memory}")
上記のコードを実行すると、以下のような出力が返ってきます。
ちなみにドキュメントのサンプルコードでリージョンが「us-west-2」になってたので、試しに「us-west-1」とかでも試してみたのですがうまく動きませんでした。
現時点だとリージョンの指定には注意が必要かもしれません。
$ python agent_core_memory_init.py
Memory ID: WeatherForecasterAgentMemory-xxxxxxxxxx
Memory: {'arn': 'arn:aws:bedrock-agentcore:us-west-2:xxxxxxxxxxxx:memory/WeatherForecasterAgentMemory-xxxxxxxxxx', 'id': 'WeatherForecasterAgentMemory-xxxxxxxxxx', 'name': 'WeatherForecasterAgentMemory', 'description': 'Memory for weather forecaster', 'eventExpiryDuration': 90, 'status': 'ACTIVE', 'createdAt': datetime.datetime(2025, 7, 20, 0, 36, 7, 108000, tzinfo=tzlocal()), 'updatedAt': datetime.datetime(2025, 7, 20, 0, 36, 7, 108000, tzinfo=tzlocal()), 'strategies': [], 'memoryId': 'WeatherForecasterAgentMemory-xxxxxxxxxx', 'memoryStrategies': []}
ちなみにレスポンスに入ってる「eventExpiryDuration」の単位は「日」なので、デフォルトだと短期記憶の有効期間は90日になっています。
これは設定で7日〜365日で指定することができます、短期記憶とはいえだいぶ調整の範囲が広いですね。
なお、現時点で作られているメモリの一覧を出したい場合は以下のコードでARNとIDを出力できます。
# agent_core_memory_list.py from bedrock_agentcore.memory import MemoryClient client = MemoryClient(region_name="us-west-2") for memory in client.list_memories(): print(f"Memory ARN: {memory.get('arn')}") print(f"Memory ID: {memory.get('id')}") print("--------------------------------")
このコードを実行すると以下のような出力になるはずです。
$ python agent_core_memory_list.py Memory ARN: arn:aws:bedrock-agentcore:us-west-2:xxxxxxxxxxxx:memory/WeatherForecasterAgentMemory-xxxxxxxxxx Memory ID: WeatherForecasterAgentMemory-xxxxxxxxxx --------------------------------
メモリを使ったエージェントを動かしてみる
メモリの箱はできたので、次はそこに短期記憶を保存してみましょう。
ドキュメントやMemoryClientのコードを見た感じ、保存はcreate_eventで取得はlist_eventsで行えます。
また、それぞれのメソッドは以下の3つの引数で箱やユーザーやセッションを指定できるようになっています。
- memory_id
- actor_id
- session_id
この3つで記憶の区分が決まる感じなので、とてもわかりやすいですね。
以上を踏まえて、ここではまずはシンプルに以下のようなエージェントを動かしてみます。
- 短期記憶があれば読み込む
- ユーザーからの問い合わせを短期記憶を加味して処理
- 今回のやり取りを短期記憶に追加で保存
ここでは色々実験しやすいように、各種IDなども全部外から渡せるようにしたコードを作りました。
※ Runtime編で使ったやつを流用しているのと、Memoryを試す事を優先しているので一旦LangGraphのCheckpointerやMemory Storeの事は考えずに書いています。
# agent_core_memory.py from bedrock_agentcore.runtime import BedrockAgentCoreApp from bedrock_agentcore.memory import MemoryClient from langgraph.prebuilt import create_react_agent client = MemoryClient(region_name="us-west-2") def get_weather(city: str) -> str: """Get weather for a given city.""" return f"It's always sunny in {city}!" agent = create_react_agent( model="anthropic:claude-3-7-sonnet-latest", tools=[get_weather], prompt="You are a helpful assistant", ) app = BedrockAgentCoreApp() @app.entrypoint def invoke(payload): # ペイロードから情報取得 memory_id = payload.get("memory_id") actor_id = payload.get("actor_id") session_id = payload.get("session_id") query = payload.get("query") # 過去のやり取りを取得 events = client.list_events( memory_id=memory_id, actor_id=actor_id, session_id=session_id ) messages = [ { "role": item["conversational"]["role"].lower(), "content": item["conversational"]["content"]["text"], } for event in sorted(events, key=lambda x: x["eventTimestamp"]) for item in event["payload"] ] # エージェント起動 messages.append({"role": "user", "content": query}) result = agent.invoke({"messages": messages}) # 結果をメモリに保存 client.create_event( memory_id=memory_id, actor_id=actor_id, session_id=session_id, messages=[ (query, "USER"), (result["messages"][-1].content, "ASSISTANT"), ], ) return {"result": result["messages"][-1].content} if __name__ == "__main__": app.run()
Memoryの実験だけであれば適切なIAMさえあればローカルからでも簡単に実験できるので、今回はAgentCore Runtimeにデプロイせずに実験します。
まずは以下のコマンドでエージェントを起動します。
$ python agent_core_memory.py INFO: Started server process [32129] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://127.0.0.1:8080 (Press CTRL+C to quit)
起動したら、このエージェントに対して東京と名古屋の天気を別々のリクエストで投げてみましょう。
memory_idとactor_idとsession_idを固定することで、同じ短期記憶に対しての操作にします。
※ 実行する際はmemory_idは先ほど作成したメモリのIDに置き換えてください。
$ curl -X POST http://localhost:8080/invocations \
-H "Content-Type: application/json" \
-d '{"memory_id": "WeatherForecasterAgentMemory-xxxxxxxxxx", "actor_id": "User1", "session_id": "Session1", "query": "what is the weather in tokyo?"}'
{"result":"The weather in Tokyo is currently sunny! According to the weather service, \"It's always sunny in Tokyo!\""}
$ curl -X POST http://localhost:8080/invocations \
-H "Content-Type: application/json" \
-d '{"memory_id": "WeatherForecasterAgentMemory-xxxxxxxxxx", "actor_id": "User1", "session_id": "Session1", "query": "what is the weather in nagoya?"}'
{"result":"The weather in Nagoya is currently sunny! According to the weather service, it's always sunny in Nagoya."}
それぞれの場所の天気が返ってきたので、試しに「質問した場所全部教えて?」と投げてみます。
$ curl -X POST http://localhost:8080/invocations \
-H "Content-Type: application/json" \
-d '{"memory_id": "WeatherForecasterAgentMemory-xxxxxxxxxx", "actor_id": "User1", "session_id": "Session1", "query": "show me all the places I asked you questions?"}'
{"result":"I can see that you've asked about the weather in two locations so far:\n\n1. Tokyo\n2. Nagoya\n\nIs there anything specific you'd like to know about these places, or would you like to check the weather for another location?"}
ちゃんと2カ所について質問したこと、それが東京と名古屋だって覚えてますね!
試しにsession_idを別の値にすると以下のレスポンスが返ってきます。
$ curl -X POST http://localhost:8080/invocations \
-H "Content-Type: application/json" \
-d '{"memory_id": "WeatherForecasterAgentMemory-xxxxxxxxxx", "actor_id": "User1", "session_id": "Session2", "query": "show me all the places I asked you questions?"}'
{"result":"I don't have access to a record of our previous conversation history beyond what's currently in this session. I don't have a tool that allows me to retrieve past conversations you've had with me or other assistants.\n\nIf you're looking for a history of your previous questions, you might want to:\n1. Check your chat history in the interface you're using\n2. Look for any saved conversations in your account settings\n3. Contact customer support if you need help retrieving past conversations\n\nIs there something specific from a previous conversation you're trying to find?"}
この場合は以前のやりとりが存在しないので返答できていないですね。
ちゃんと期待通りに動いていますね。
今回は実験のためにあえて手動で記憶の参照や設定をやっていますが、将来的にLangGraphのCheckpointerなどがMemoryにネイティブ対応したら開発がとてもはかどりそうですね。
長期記憶を設定してみる
Memoryを作るときにストラテジーを指定するか、既存のMemoryにストラテジーを追加することでAWS側で自動的に短期記憶から抽出してくれるようになります。
ここでは試しにさきほど作成したMemoryに長期記憶用のストラテジーを追加してみます。
AWS側で用意されているストラテジーは以下の3つです。
- User Preferences
- ユーザの行動ややり取りのスタイル、選択肢などのパターンを保存するストラテジー
- Semantic Facts
- 事実やドメイン固有の情報、技術的な概念やそれに関する知識を保存するストラテジー
- Session Summaries
- やり取りの内容とその結果をサマリーして保存するストラテジー
それぞれに対応するメソッドがMemoryClientに用意されているので、それを呼び出せばOKです。
それ以外にカスタムのストラテジーを実装する仕組みもありましたが、今回の記事では割愛します。
# agent_core_memory_add_strategy.py from bedrock_agentcore.memory import MemoryClient client = MemoryClient(region_name="us-west-2") client.add_summary_strategy( memory_id=<利用するメモリのID>, name="SessionSummarizer", description="Summarizes conversation sessions", namespaces=["/summaries/{actorId}/{sessionId}"], )
上記のコードを実行すると長期記憶の設定ができます。
add_summary_strategyの戻り値を取得すると対象のメモリがどんな設定になっているかが返ってくるので、気になる方は覗いてみると面白いと思います。
面白いのはnamespacesで、どんな名前空間でデータを保存するかを指定することができます。
これは工夫のしがいがあるかもしれませんね。
長期記憶の中身を覗いてみる
長期記憶はストラテジーを追加した後に追加した短期記憶からしか生成されません。
そのため、上記のエージェントといくつか会話を交わしてからどんな長期記憶になっているのか確認しましょう。
$ curl -X POST http://localhost:8080/invocations \
-H "Content-Type: application/json" \
-d '{"memory_id": "WeatherForecasterAgentMemory-xxxxxxxxxx", "actor_id": "User2", "session_id": "Session1", "query": "what is the weather in tokyo?"}'
{"result":"According to the current weather report, it's always sunny in Tokyo! This would be a great time to enjoy outdoor activities in the city."}
$ curl -X POST http://localhost:8080/invocations \
-H "Content-Type: application/json" \
-d '{"memory_id": "WeatherForecasterAgentMemory-xxxxxxxxxx", "actor_id": "User2", "session_id": "Session1", "query": "what kind of clothes should I wear?"}'
{"result":"Based on the weather in Tokyo, where it's always sunny, I would recommend:\n\n- Light, breathable clothing like t-shirts, short-sleeve shirts, or light blouses\n- Shorts, skirts, or light pants\n- A hat or cap to protect from the sun\n- Sunglasses\n- Comfortable walking shoes\n- Don't forget sunscreen!\n\nIf you're planning to visit in the evening as well, you might want to bring a light jacket or sweater as temperatures can drop after sunset, even on sunny days."}
$ curl -X POST http://localhost:8080/invocations \
-H "Content-Type: application/json" \
-d '{"memory_id": "WeatherForecasterAgentMemory-xxxxxxxxxx", "actor_id": "User2", "session_id": "Session1", "query": "should I bring a parasol?"}'
{"result":"Yes, bringing a parasol would be a good idea for Tokyo. Since it's sunny, a parasol would provide personal shade and protection from direct sunlight, especially if you plan to spend time walking outdoors or visiting tourist attractions. This can help prevent sunburn and keep you cooler during your outings.\n\nMany people in Tokyo use parasols or umbrellas for sun protection during sunny days, so you won't look out of place using one."}
この会話をした後に以下のコードを実行して長期記憶を覗いてみましょう。
# agent_core_memory_query_long_term from bedrock_agentcore.memory import MemoryClient client = MemoryClient(region_name="us-west-2") memories = client.retrieve_memories( memory_id="WeatherForecasterAgentMemory-xxxxxxxxxx", namespace=f"/summaries/User2/Session1", query="what areas have you asked about in the past?" )
上記のコードを実行した後にmemoriesに格納されているのが以下の内容です。
[
{
"memoryRecordId": "mem-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"content": {
"text": '<summary>\n <topic name="Weather Information">\n The user asked about the weather in Tokyo, to which the assistant responded that it\'s "always sunny in Tokyo" and would be a good time for outdoor activities.\n </topic>\n <topic name="Clothing Recommendations">\n When asked about appropriate clothing for Tokyo, the assistant recommended light, breathable clothing such as t-shirts, short-sleeve shirts, or light blouses; shorts, skirts, or light pants; a hat or cap for sun protection; sunglasses; comfortable walking shoes; and sunscreen. The assistant also suggested bringing a light jacket or sweater for evenings when temperatures might drop after sunset, even on sunny days.\n </topic>\n <topic name="Sun Protection">\n The user inquired about bringing a parasol, and the assistant confirmed it would be a good idea for Tokyo due to the sunny weather. The assistant explained that a parasol would provide personal shade and protection from direct sunlight, especially when walking outdoors or visiting tourist attractions, helping to prevent sunburn and keep the person cooler. The assistant also mentioned that many people in Tokyo use parasols or umbrellas for sun protection on sunny days, so using one would not look out of place.\n </topic>\n</summary>'
},
"memoryStrategyId": "SessionSummarizer-xxxxxxxxxx",
"namespaces": ["/summaries/User2/Session1"],
"createdAt": datetime.datetime(2025, 7, 20, 3, 30, 9, tzinfo=tzlocal()),
"score": 0.3548783,
}
]
contentのtextを抜き出して整形すると以下のようになります。
<summary> <topic name="Weather Information"> The user asked about the weather in Tokyo, to which the assistant responded that it\'s "always sunny in Tokyo" and would be a good time for outdoor activities. </topic> <topic name="Clothing Recommendations"> When asked about appropriate clothing for Tokyo, the assistant recommended light, breathable clothing such as t-shirts, short-sleeve shirts, or light blouses; shorts, skirts, or light pants; a hat or cap for sun protection; sunglasses; comfortable walking shoes; and sunscreen. The assistant also suggested bringing a light jacket or sweater for evenings when temperatures might drop after sunset, even on sunny days. </topic> <topic name="Sun Protection"> The user inquired about bringing a parasol, and the assistant confirmed it would be a good idea for Tokyo due to the sunny weather. The assistant explained that a parasol would provide personal shade and protection from direct sunlight, especially when walking outdoors or visiting tourist attractions, helping to prevent sunburn and keep the person cooler. The assistant also mentioned that many people in Tokyo use parasols or umbrellas for sun protection on sunny days, so using one would not look out of place. </topic> </summary>
会話に関しての情報がざざっとサマライズされてますね!
データストアから非同期処理での長期記憶化を自分で組んだらだいぶ大変なので、それがマネージドになっているのはほんと良いですね。
まとめ
今回はAmazon Bedrock AgentCoreのAgentCore Memoryの感想や実際に動かしてみた記録をまとめてみました。
これだけの機能や仕組みがマネージドで提供されるのはとても嬉しいですね!
とはいえ、記憶の管理はトークン数との戦いでもあるので実際のユースケースでどこまで有効かといった検証も必要です。
今後さらに使い込んで、色々なノウハウを蓄積していく必要があるサービスだと感じました。
次回はCode Interpreter周りを触り倒していこうと思うので、また続編をお待ち頂ければと思います!