次のプロンプトに全履歴を貼り付けることで記憶するエージェントは、メモリを持っているわけではない。膨らみ続ける請求額と、硬直したトークン上限、ターンを重ねるごとに悪化するレイテンシ曲線を持っているだけだ。本物のメモリはストレージの意思決定である。何を永続化するか、どこに置くか、推論時に関連する部分だけをどう取り出すか。それはデータベースの問題であり、プロンプトの問題として扱うことが、デモでは映えるのに2週目に破綻するエージェントを生む原因になる。

コンテキスト詰め込みの誘惑的な点は、最初はうまく機能することだ。セッション初期は履歴が短く、すべてが収まり、エージェントは記憶しているように見える。やがて会話は成長し、プロンプトもそれに伴って成長し、同時に3つのことが起こる。毎ターンすべてを再送するのでコストが上がり、モデルが毎回より多くを読むのでレイテンシが上がり、最終的にはコンテキストウィンドウに達して古い事実が前方から静かに脱落する。これはパッチを当てられるバグではない。アーキテクチャそのものの問題だ。

2種類のメモリ、2種類のストア

「メモリ」は実際には2つの異なるニーズであり、それらを混同するところから設計は間違い始める。

セッションとワーキングメモリ: キーバリューストア

直近のターン、現在のタスク状態、ユーザーの短期的な好み。これらは既知のキー、つまりセッションID、ユーザーID、スレッドIDで検索される。そのキーに対して高速な読み書きと、古くなったセッションが自動的に失効するTTLが欲しい。これはベクトル検索ではなくDynamoDBのテーブルだ。セッションかユーザーでパーティションを切り、実行中の状態を1つのアイテムとして保持し、TTLを設定し、各ターンの開始時に読み戻す。エンベディングも類似度計算も不要で、「どこまで話していたか」を高速にキー検索するだけだ。

長期的な意味記憶: ベクトルストア

エージェントがセッションをまたいで思い出すべき事実、過去の決定、学習した好み、関連する過去のやり取りは、キーで検索されるわけではない。意味で検索される。「この新しいメッセージに関連して自分が知っていることは何か」というものだ。これはエンベディングに対する意味検索であり、まさにベクトルストアの用途だ。AWSでの現実的な選択はAurora Serverless v2にpgvectorを組み合わせることだ。エンベディングはリレーショナルデータのすぐ隣に置かれ、SQLでクエリでき、そのためだけに別の専用データベースを立ち上げる必要がない。

パターン: 蓄積ではなく検索

メモリがデータベースになると、各ターンは追記をやめてクエリを始める。ループは次のようになる。

on each turn:
  1. read session state by key      (DynamoDB: where were we)
  2. embed the new user message
  3. semantic search long-term store (pgvector: what is relevant)
  4. assemble a bounded prompt:
       system + tools
       + top-k retrieved memories
       + recent turns from session state
       + new message
  5. call the model
  6. write new facts back to the stores

これでプロンプトは、ユーザーとの関係がどれだけ長く続いても上限を持つ。千ターン目の会話も十ターン目と同じサイズのプロンプトを送る。すべてを持ち運ぶのではなく、関連する一握りのメモリだけを取得するからだ。コストとレイテンシは上昇するのではなく横ばいになる。これは、RAGを機能させているのと同じ検索の規律を、ドキュメントコーパスではなくエージェント自身の履歴に適用したものだ。

プロンプトが隠してしまう、決めるべきこと

メモリをデータベースに移すことで、コンテキスト詰め込みでは無視できていた選択を強いられる。そしてその選択こそが本当のエンジニアリングだ。

  • 何を記憶する価値があるか。すべてのターンがメモリになるわけではない。永続的な事実と決定を書き込み、雑談は書き込まない。さもないとストアがノイズで埋まり、検索がそれを表面化させてしまう。
  • いつ忘れるか。セッション状態にはTTLを、長期的な事実には経年劣化または上書きのポリシーを設けて、古い好みが修正済みの好みより優先されないようにする。
  • どれだけ検索するか。top-kは調整可能なつまみだ。少なすぎるとエージェントは忘れ、多すぎると検索ステップが何の価値も加えないまま膨れ上がったプロンプトに逆戻りする。
  • 2つのストア間の一貫性。セッションメモリと意味記憶は食い違うことがある。食い違ったときにどちらを優先するか決めておく。

まとめ

コンテキスト詰め込みはメモリではなく、トークンの請求額とコンテキストの上限に代わりに決めさせるまでストレージの意思決定を先延ばしにしているだけだ。問題を分割しよう。DynamoDBによる高速なキー検索のセッション状態、Auroraのpgvectorによる意味的な長期記憶、そして両方から有界なプロンプトを組み立てるターンごとの検索ステップだ。メモリが、膨らみ続けるプロンプトではなく検索ステップを持つデータベースになった瞬間、コストとレイテンシは会話の長さに比例してスケールしなくなり、ウィンドウに何を忘れさせるかを任せるのではなく、エージェントに何を記憶させるかを自分で決められるようになる。

次に読むべき記事

AuroraとDynamoDBを大規模に運用するデータベースとインフラの側面については、クラウド分野のフィールドノートをercan.cloudで、ハブをercanermis.comで公開している。