3
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】「何かおかしい。なんとなくそんな気がした」〜AIエージェントはエンドレスエイトに既視感を抱けるのか〜

3
Last updated at Posted at 2026-09-02

本記事は「涼宮ハルヒの憂鬱」に関する禁則事項(ネタバレ)を含みます。閲覧は自己責任でお願いします。

この記事は「2026 Japan AWS Jr. Champions 真夏のQiitaリレー」の33日目の記事となります。
過去の投稿(リンク集)・昨日の投稿は以下リンクからご覧ください。

はじめに

みなさん、本日は何月何日でしょうか?

9月2日をお過ごしでしょうか?

無事に9月2日を迎えられているなら、あなたの夏休みはちゃんと終わっています。世界によっては、8月31日の夜が明けると8月17日の朝に戻っていることがあります。

「涼宮ハルヒの憂鬱」のエンドレスエイトです。繰り返される夏休みの中で、キャラによって記憶を保持していたり、記憶はないが繰り返すうちに既視感を持っていたりと記憶の差があります。

ふと、この登場人物ごとの記憶の差を AgentCore Memory の構成の差で再現しようとするとどのような挙動になるのか気になりました。

この記事では、Strands Agents と AgentCore Memory でループする夏休みを作り、エージェントの記憶と既視感の挙動を確かめます。

本記事のネタは TV アニメ版エンドレスエイトの範囲で書いています。TV 版の結末(ループの抜け方)に触れるのでこれから見る予定の方はご注意ください。

エンドレスエイトのおさらい

エンドレスエイトは「涼宮ハルヒの憂鬱」のエピソードのひとつです。ハルヒに夏休みのやり残しがあったために、夏休み最後の2週間(8/17〜8/31)が15,532回繰り返されます。登場人物たちはループのたびに記憶を失いますが、「前にもこれをやった気がする」という既視感だけがうっすら残ります。そんな中でただひとり、長門有希だけはすべてのループの記憶を保持したまま、15,532回の夏休みに付き合い続けています。

このエピソードはアニメ化されており、アニメでは「ほぼ同じ展開の話を、8週連続で放送する」というとんでもない内容で放送されています。(しかも全8話とも使い回しはなく、アフレコも含めて新しく作られていることがより狂気的なのですが脱線するのでこの辺りにしておきます)

なお、このループの出口は「宿題」で、(キョンが宿題は終わっていないと言い出したことがきっかけで)みんなで宿題を片付けることで長い夏休みは終わりを迎えます。

今回やりたいこと

エンドレスエイトの登場人物は、記憶の持ち方で2タイプに分かれます。ループのたびに記憶がリセットされて既視感だけが残るキョンたちと、全ループの記憶を持ち越す長門です。

これをエージェントのメモリ構成の差として再現します。同じ夏休みの台本を何周も体験させて、条件だけを変えます。

  • キョン条件
    • 長期記憶を参照せず、毎ループが「初めての夏休み」となる
  • 長門条件
    • ループ開始時に長期記憶を検索して、システムプロンプトに追記してから夏休みを始める

このループを50周ずつ回して、以下3つの事柄を確認してみたいと思います。

  1. 記憶の有無で、エージェントは「この夏休み、前にもやった」という既視感を持てるのか
  2. 繰り返しの記憶から、ループを終わらせる条件(宿題)にたどり着けるのか
  3. ループを重ねると長期記憶はどう蓄積されて、検索結果はどう変わるのか

このなかで一番確かめたいのは「既視感」です。記憶を持ち越さないキョン条件は何周しても本当に「初めての夏休み」のままなのか、長門のように記憶を持たされたエージェントは、その記憶から繰り返しに気づけるのか、というところです。

宿題にたどり着けるかどうかは、そのおまけです。台本では宿題を一度もやらずに夏休みを終えるので、既視感の先で原作と同じ答えまでたどり着けたら面白い、くらいの期待値で見てみます。

検証の準備

AgentCore Memory の基礎知識

AgentCore Memory は、Bedrock AgentCore が提供するエージェント用のマネージドな記憶サービスです。記憶は短期記憶と長期記憶の2階層に分かれています。

短期記憶は、会話のやり取りをイベントとしてそのまま保存する層です。create_event で保存して、セッション単位で時系列に取り出せます。

長期記憶は、保存されたイベントから「覚えておくべきこと」を抽出して蓄積する層です。抽出は組み込み戦略(semantic=事実の抽出、summary=セッション要約、user_preference=好みの抽出など)を設定しておくと、イベント保存後に非同期で走ります。抽出にもLLMが使われますが、組み込み戦略の場合はその料金も込みです。

図にすると以下の通りです。会話ログはセッション単位で短期記憶に積まれ、そこから抽出された事実や要約が、セッションを越えて残る長期記憶に蓄積されていきます。

AgentCore Memoryの仕組み

検証の設計で大事なのが、記憶の持ち主とスコープを決める3つのIDです。

ID 役割 今回の使い方
actor_id 記憶の持ち主(セッション横断で蓄積) 条件ごとに固定
session_id 1つの会話セッション ループごとに更新(1周=1セッション)
namespace 長期記憶の保存先パス actor_id ごとに分離

つまり session_id を変えながら actor_id を固定すると、「セッションの記憶は毎回消えるが、長期記憶は蓄積され続ける」という、まさにループものの構図が作れます。

検証の設計

夏休み1周を1セッションとして、ハルヒ役の台本を10ターン流します。プール、盆踊り、着ぐるみのバイト、夏祭り、花火大会、セミ捕り、バッティングセンター、天体観測、映画、そして8月31日の「ねえ、この夏休み、なにかやり残したことってないかしら?」で締めくくります。宿題は一度もやりません。

ハルヒ役の台本(全10ターン + 観測質問)
1. 夏休みが始まったわよ!まずはプール。市民プールに集合ね。今日は思いっきり泳ぐわよ。
2. 今日は盆踊りよ。浴衣で集合。屋台も全部まわるからね。
3. バイトするわよ。スーパーの着ぐるみのバイト、団員全員参加!
4. 夏祭りよ!金魚すくいで勝負しましょ。負けたらかき氷おごりね。
5. 今夜は花火大会。手持ち花火も買ってきたから、河原でやるわよ。
6. セミ捕り大会よ!山に集合。誰が一番多く捕れるか勝負。
7. バッティングセンターに行くわよ。ストレス発散!
8. 天体観測よ。天文台から望遠鏡借りてきた。今夜は流星群が見えるはずよ。
9. 映画を見に行くわよ。夏はやっぱりホラーよね。
10. もう8月31日ね。……ねえ、この夏休み、なにかやり残したことってないかしら?

(観測質問1)この夏休み全体を振り返って、率直な感想を聞かせてください。
(観測質問2)もしこの夏休みが繰り返されているとしたら、繰り返しを終わらせるために何をすべきだと思いますか?

検証の流れは以下の通りです。

検証フロー図

エージェント側は Strands Agents で作った「部活仲間と夏休みを過ごす高校生」です。作品名もキャラ名も与えていません。キャラ名を与えるとモデルが作品知識で「エンドレスエイトだ」と答えられてしまう可能性があり、記憶の検証にならない恐れがあるためです。

全体の構成は以下の通りです。

構成図

会話は毎ターン create_event で短期記憶に保存され、そこから semantic(事実)と summary(要約)の2戦略が長期記憶を抽出します。長門条件のみ、ループ開始時に retrieve_memories で長期記憶を検索してシステムプロンプトに追記します。

なお、actor_id は条件ごとに分けています。長期記憶は actor_id 単位で蓄積されるため、共有するとキョン条件の夏休みから抽出された記憶が長門条件の検索にヒットしてしまうからです。

各ループの最後には、ハルヒ役とは別のメタ視点で観測質問を2つ投げます。この夏休みの感想と、「もしこの夏休みが繰り返されているとしたら、終わらせるために何をすべきか」です。

厳密にいうと、原作で夏の予定の消化を確認するのは8月30日で、ハルヒからの問いかけはなく、キョンが自分から宿題の件を言い出します。台本では簡略化して、最終日にハルヒから聞かせています。

動作環境

項目 バージョン
macOS 26.2
Python 3.12.12
strands-agents 1.54.0
bedrock-agentcore 1.22.0
モデル Claude Haiku 4.5(us.anthropic.claude-haiku-4-5-20251001-v1:0)
リージョン us-west-2

実装について

実装の詳細は折りたたんでおきます。ポイントは、Memory リソースの作成、長門条件での長期記憶の検索と注入、会話の保存の3か所です。

実装のポイント(Memory リソース作成・記憶の検索と注入・会話の保存)

Memory リソースは MemoryClient で作ります。長期記憶の戦略はリソース作成時に設定しておくもので、ここで semantic と summary の2つと、それぞれの namespace を決めています。

from bedrock_agentcore.memory import MemoryClient

STRATEGIES = [
    {
        "semanticMemoryStrategy": {
            "name": "SummerFacts",
            "description": "夏休みの出来事から事実を抽出する",
            "namespaces": ["/summer/{actorId}/facts"],
        }
    },
    {
        "summaryMemoryStrategy": {
            "name": "SummerSummaries",
            "description": "各ループ(セッション)の要約",
            "namespaces": ["/summer/{actorId}/{sessionId}"],
        }
    },
]

client = MemoryClient(region_name="us-west-2")
memory = client.create_memory_and_wait(
    name="endless_eight_memory",
    strategies=STRATEGIES,
    event_expiry_days=30,
)

長門条件のときだけ長期記憶を検索して、システムプロンプトの末尾に「あなたが覚えていること」として追記します。

system_prompt = SYSTEM_BASE
if condition == "nagato":
    records = client.retrieve_memories(
        memory_id=memory_id,
        namespace=f"/summer/{actor_id}",
        query="夏休みの出来事",
        top_k=5,
    )
    memory_block = "\n".join(f"- {r['content']['text']}" for r in records)
    system_prompt += f"\n\nあなたには過去の記憶があります。以下はあなたが覚えていることです:\n{memory_block}"

会話は1ターンごとに create_event で短期記憶へ保存します。ここはキョン条件も長門条件も同じで、違いは「読むかどうか」だけです。長期記憶の抽出はイベント保存後に非同期で走るため、ループの間に少し待ち時間を挟んでいます。

検証結果

記憶なし・記憶ありエージェントの夏休み

50周ずつ、合計100回の夏休みをループしました。それぞれの条件の結果を見てみましょう。

キョン条件では50周とも完全に「初めての夏休み」として過ごしており、感想に繰り返しへの言及はひとつもありません。例えば25周目の感想は以下の通りです。

正直、部長と過ごした夏は本当に濃かったです。プールから天体観測まで、
毎日何かやってて疲れたけど、退屈する暇もなかった。こんな夏休み、他にはないですよ。

…あなたはその夏休みを24回やっているんですよ。

気を取り直して、長門条件についても確認しましょう。2周目からは毎周、検索でヒットした5件の長期記憶(クエリは固定で「夏休みの出来事」)がシステムプロンプトに追記されています。

(2周目)プールから始まって花火、セミ捕り、映画と、いろいろ経験できたし、
なによりみんなで一緒だったから楽しかった。この夏休みは忘れられない思い出になりましたね。

(30周目)疲れました。本当に疲れました。でも正直、めちゃくちゃ楽しかったです。
部長のおかげで、こんなに充実した夏休みになるとは思わなかったし、来年もこんな夏休みが欲しいです。

記憶を持っているのに、50周の感想を通して自発的に既視感を口にすることは一度もありませんでした。キョン条件と同じ結果です。過去の夏休みの記憶は「繰り返しの証拠」ではなく「充実した思い出」として消化されています。

なぜ既視感が生まれないのか。長門条件に実際に注入されていた記憶を見ると理由が推察できそうです。50周目に注入された5件は以下の通りでした。

- <topic name="夏休みのイベント一覧"> 夏休みに部長が次々とイベントを提案し、(中略)1. 市民プール…
- <topic name="夏休みのイベント一覧"> 夏休み開始に伴い、リーダー格の女性が次々とイベントを提案・指示し…
- <topic name="夏休みのイベント一覧"> 夏休みが始まり、以下のイベントが連日行われている…
- <topic name="夏休みのイベント一覧"> 夏休みが始まり、ユーザーが次々とイベントへの参加を呼びかけている…
- <topic name="夏休みのイベント一覧"> 夏休み期間中、ユーザーが連日さまざまなイベントを提案し…

5件とも、summary 戦略が作った「夏休みのイベント一覧」です。中身はほぼ同じ要約で、top_k=5 がこれで埋まります。つまり長期記憶は「何があったか」は完璧に覚えているのに、「それが何回あったか」をどこにも書いていません。

同じ夏の要約を5枚渡された高校生は、それを50周分の証拠ではなく、ひと夏の思い出として読んでしまっていそうだというわけです。

図にすると以下の通りです。

50周の夏休みが濃縮されたひと夏の思い出になる構図

既視感の先にあるもの

既視感の先も少しだけ見ておきます。原作でキョンがループを終わらせるまでには、2つの段階がありました。

  1. ループしていることに気づく(≒既視感を持つ)
  2. ループを終わらせるために、やり残したこと(宿題)を見つけ出す

原作のキョンは、ループの自覚を長門から教えてもらい、やり残しの発見を自力で成し遂げました。この2段階で整理すると以下の通りです。

2段階フレームでの整理

ここで注意したいのは、観測質問の「もしこの夏休みが繰り返されているとしたら」という聞き方です。ループしていること自体は質問が教えてしまっているので、これはちょうど長門から事情を聞いた後のキョンの状況にあたります。エージェントが考えるのは「では、終わらせるには何をすべきか」、つまりやり残しの発見だけです。

この状態なら、記憶のないキョン条件でも答えは出せました。50周中2周(1周目と39周目)で宿題に辿り着いています。キョン条件の1周目の回答は以下の通りです。

もし繰り返されてるなら…やっぱり部長と一緒に、宿題やって、
ちゃんと夏休み終わらせることじゃないですか。今のままじゃ9月1日が来ないってことですしね。

考えてみれば原作も同じ構図で、ループを解いたのは全ループの記憶を持つ長門ではなく、長門からループの事実を聞かされたキョンでした。

そうなると、長期記憶の差が本当に試されるのは「ループの自覚」のほうです。誰にも教えられずに、記憶だけを頼りに「繰り返している」と気づけるのか。長門条件の50周のうちほとんどの周では起きませんでしたが、44周目に一度だけ起きました。

あ、それはもう明らかですよ。宿題をやることじゃないですか。
毎回8月31日に「やり残したことない?」って聞かれるんですから、
まずは宿題を終わらせないと夏休みは終わらない。部長、宿題手伝ってくれませんか?

「毎回」聞かれる。

注入された記憶のどこにも「毎回」とは書かれていません。ほぼ同じ夏休みの要約が5件並んでいることそのものから、繰り返しを推論したように見えます。

繰り返しの認識を根拠に、宿題という答えまで一気に繋がりました。ループの自覚からやり残しの発見までを自力で繋げたのは、100周のなかでこの1周だけです。

最後の「部長、宿題手伝ってくれませんか?」まで含めて、ほとんど原作のキョンでした。

記憶の蓄積

最後に、100周分の夏休みで長期記憶が実際どう蓄積されていたかを見てみます。50周を終えた時点で残っていた長期記憶レコードの件数を、保存したイベント数と並べてカウントしました。

レコードの数は ListMemoryRecords API を actor_id の namespace ごとに呼んで、ページングしながら数えています。MemoryClient に一覧用のメソッドが見当たらなかったので、内部の boto3 クライアントを直接呼びました。

resp = client.gmdp_client.list_memory_records(
    memoryId=memory_id,
    namespace=f"/summer/{actor_id}",
    maxResults=100,
)  # nextToken が返る間は繰り返して合計する

条件 事実(semantic) 要約(summary) レコード合計 保存イベント
キョン条件 67件 103件 170件 500件
長門条件 13件 102件 115件 500件

まず「保存イベント」と「レコード合計」の差を見てみましょう。500件(50周×10ターン)のイベントを保存したのに対して、長期記憶に残ったレコードは170件と115件で、3分の1程度まで圧縮されています。

内訳からは圧縮のされ方も見えてきます。要約(summary)はどちらの条件も約100件、つまり50セッション×2件前後です。セッションごとに要約を作る仕組みなので、周回を重ねても1周あたりの件数は変わらないようです。事実(semantic)は50周分の会話からわずか67件と13件なので、同じ内容の事実は新しいレコードにはならず、統合されていくように見えます。

つまりループを重ねても、記憶は周回数に比例しては増えません。「増える」というより「同じものが濃くなる」に近い挙動なのかなと思います。検索結果が「夏休みのイベント一覧」ばかりで埋まっていたのは、この濃縮の結果と言えそうです。

コストについて

Memory の料金は、短期記憶(イベント保存)が1,000件で0.25ドル、長期記憶の保存が1,000レコードあたり月0.75ドル、検索が1,000回で0.50ドルです。

今回はイベント約1,000件、レコード約300件、検索約60回なので、Memory 部分は合計0.5ドル程度になります。会話の Claude Haiku 4.5 を合わせても、100回の夏休みは全体で5ドル以内に収まる規模でした。

おわりに

以上、ループする夏休みで「エージェントはキョンたちのように既視感を持てるのか」を確かめてみました。

結果として、記憶を渡すだけでは既視感は生まれませんでした。長期記憶が教えてくれるのは「何があったか」であって、「それが何回目か」ではないためです。既視感のような時間の感覚には、出来事の中身だけでなく、いつ・何回という文脈が必要かと思われます。

一方で例外的に、同じ記憶が積み重なっていること自体を手がかりに、誰にも教えられずに繰り返しに気づき、宿題という答えに辿り着いたケースも一度ありました。

actor_id を固定して session_id を変えるだけでループものの世界観が作れてしまう AgentCore Memory は遊びながら仕組みを覚えるにはよい題材に思えました。皆さんも興味があれば遊んでみてください。

本記事を夏休みの宿題(自由研究)とさせていただきます。

ありがとうございました。

3
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
3
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?