技術 約10分で読めます

エージェントメモリは記憶ではなくメモという論文を読んだ

いけさん目次

arXivに出ていた Contextual Agentic Memory is a Memo, Not True Memory を読んだ。
2026年4月30日のpreprintで、Binyan Xu、Xilin Dai、Kehuan Zhangの3名による論文だ。

主張はかなり強い。
vector store、RAG、scratchpad、長いcontext windowを「memory」と呼ぶのはカテゴリ違いで、実際にはlookup、つまり検索してメモを戻しているだけだと言う。
この言い方は煽りに見えるが、最近読んだ CTXでClaude Codeに動くメモリを足すOCR-Memory の位置づけを考えるにはちょうどいい。

CTXはgit log、コード、チャット履歴から関連断片をプロンプト前に差し込む。
OCR-Memoryは長い実行履歴を画像化し、関係する位置だけ選んで元ログを戻す。
どちらも便利だが、使い続けたモデルの重みが変わるわけではない。
この論文は、そこを「記憶」ではなく「メモ」と呼べ、とかなり正面から言ってくる。

Cを変えるかθを変えるか

論文の整理はシンプルだ。
LLMエージェントの出力を変える経路は、大きく2つに分かれる。

1つはcontextを変える経路。
プロンプト、RAG、MCPツール結果、scratchpad、skill file、memory storeから取ってきたテキストをcontext windowへ入れる。
論文ではこれをC-engineeringと呼ぶ。

もう1つはmodel weightsを変える経路。
pre-training、fine-tuning、reinforcement learning、continual learningで重みθそのものを更新する。
経験を抽象化したルールが重みに入るなら、次に似ているが同一ではない入力が来たときにも使える。

flowchart TD
    A[過去の経験] --> B[外部ストアへ保存]
    B --> C[検索してcontextへ注入]
    C --> D[同じ重みのモデルが読む]

    A --> E[経験を蒸留]
    E --> F[重みへ統合]
    F --> G[新しい入力にもルールとして効く]

今のエージェントメモリはほぼ上の経路だ。
MemGPTは情報をcontextへ出し入れする。
Generative Agentsは観測をmemory streamへ書く。
Reflexionは自己批評をepisodic bufferへ残す。
Voyagerはskillをコードとしてvector databaseへ貯める。
どれも「過去に書いたものを後で戻す」仕組みであって、モデル本体が経験で変わる仕組みではない。

ここは Claude Codeのコンテキスト劣化 で書いた話とも重なる。
1M contextがあっても、全部を同じ精度で読めるわけではない。
長い作業ログや古いツール出力を詰め込むほど、モデルは経験を身につけるのではなく、過去ログに引っ張られる。

検索は似たケースに強く、未知の組み合わせに弱い

論文の中心は、検索型メモリと重みベースの記憶では汎化のしかたが違うという点だ。
検索型は、過去に保存したケースと似ている入力に強い。
重みベースは、過去のケースから抽象化した規則を使って、まだ見ていない組み合わせへ対応できる。

たとえば社内の特殊な業務ルールがあるとする。
Aという規則、Bという規則、Cという規則を別々に見たことはある。
でも「AとCが同時に起きたときの処理」は過去ログにない。
検索型メモリは、Aの例とCの例を持ってくることはできるが、その組み合わせ規則を保証しない。

論文はこれをcompositional generalizationの問題として形式化している。
概念がk個あり、概念ペアごとに正しい出力があるとき、検索型メモリは基本的に各ペアの例を保存しておく必要がある。
必要な保存例は概念ペア数に比例して増える。
一方、重み更新で演算規則そのものを学べるなら、必要な例は仮説クラスの複雑さに依存する。

この数式の強さは、細かい定数よりも問題設定にある。
「retrieval qualityを上げれば解ける」「context windowを増やせば解ける」という逃げ道をかなり潰している。
検索で取れる例の選び方が良くなっても、保存されていない組み合わせをルールとして扱えるかは別問題だからだ。

frozen noviceという嫌な言い方

論文は、検索型メモリだけのエージェントをfrozen noviceと呼ぶ。
メモは増えるが、専門家にはならない初心者、という意味だ。

これはClaude CodeやCodexを日常的に使っていると刺さる。
プロジェクトのCLAUDE.md、過去ログ、handoff、git履歴をどれだけ丁寧に残しても、新しいセッションのモデルは同じ重みから始まる。
よく参照される情報は増えるが、モデル内部に「このプロジェクトではこの判断をする」という癖が焼き込まれるわけではない。

CTXの記事 では、Claude Codeの UserPromptSubmit hookで関連文脈を先に差し込む仕組みを見た。
あれは個人開発にはかなり実用的だ。
でも論文の分類では、CTXはC-engineeringの優秀な実装であって、θ-learningではない。

ここを混ぜると期待値を間違える。
検索型メモリは「昨日の判断を探す」「前に触ったファイルを思い出す」「ユーザーの好みを戻す」には効く。
ただし、それを積み上げても、モデルが未知の組み合わせへ強くなるとは限らない。

メモリ汚染は一回の注入で終わらない

セキュリティ面の指摘もかなり嫌なところを突いている。
外部ストアに書き込むメモリは、prompt injectionを一過性の問題から永続的な問題へ変える。

Webページ、Issue、メール、PDFの中に悪意ある指示がある。
エージェントがそれを一度読み、要約や「学び」としてmemory storeへ保存する。
次のセッションで関連文脈としてそれが注入される。
その時点では元のWebページをもう読んでいなくても、汚染されたメモだけが残る。

flowchart TD
    A[外部データに悪意ある指示] --> B[エージェントが読む]
    B --> C[要約や反省として保存]
    C --> D[次回セッションで検索ヒット]
    D --> E[contextへ再注入]
    E --> F[一時的な攻撃が永続化]

これは GitHubとOpenAIのプロンプトインジェクション対策 で見たinstruction hierarchyの問題より厄介だ。
その場のtool messageを低信頼として扱うだけでは足りない。
低信頼の外部データが、後日「過去の有用な記憶」として戻ってくるからだ。

記憶ストアを持つなら、保存時点の信頼境界、出典、書き込んだ理由、削除手段が必要になる。
単にvector databaseへ入れてsimilarity searchするだけだと、攻撃者は「将来のセッションへ残る命令」を置きに来る。

重みへ入れれば全部解決ではない

論文は重みベースの記憶をかなり強く推しているが、そこにもすぐ使える答えがあるわけではない。
プロダクションのエージェントで、ユーザーごと・プロジェクトごとに安全に継続fine-tuningするのは重い。
誤った経験を重みに焼き込むと、外部ストアより消しにくい。
権限、監査、ロールバック、忘却、データ分離の問題も出る。

だから現実的には、検索型メモリを捨てる話にはならない。
論文自身も、fast episodic lookupとslow weight consolidationの共存を提案している。
直近のログ、tool output、ユーザー設定、根拠付きの判断は外部ストアでよい。
繰り返し成功した手順や、複数ケースから抽出できるルールだけを、別のconsolidation pipelineで重みやadapterへ畳み込む。

今の個人開発環境でそこまでやるなら、まずは外部メモリの名前を正しく呼ぶほうが先だ。
CLAUDE.md、CTX、YourMemory、OCR-Memory、WUPHFは、どれも役に立つ。
ただしそれらは「賢くなったモデル」ではなく、「探しやすくなった作業記録」だと見たほうが事故が少ない。

ベンチマークも recall だけでは足りない

この論文が言うなら、メモリベンチマークの測り方も変わる。
過去に言った好みを思い出せるか、以前の会話から正しいfactを拾えるか、needleを見つけられるか。
それらは必要だが、検索型メモリが得意な方向へ寄っている。

エージェントが本当に経験から伸びているかを見るなら、同じ作業ログを貯めたあとで、未経験の組み合わせタスクが良くなるかを見る必要がある。
たとえば、A社固有のAPI作法とB社固有の監査ルールを別々に学んだエージェントが、A社APIをB社監査ルール下で扱う新タスクへ対応できるか。
過去ログに同じ組み合わせがないなら、検索だけではかなり苦しい。

OCR-Memory のような研究は、長い履歴から必要箇所を見つける性能を上げている。
それは重要だ。
ただし、履歴から「どこを見るか」と、経験から「どう判断するか」は別の能力になる。

定理の中身

上で「数式の強さ」と書いたが定理そのものを出していなかったので載せておく。
前提と記号から順に見ていく。

概念が kk 個あるとする。
たとえばコードベースの規約が10種類あれば k=10k = 10 だ。
概念の集合を F={f1,,fk}F = \{f_1, \ldots, f_k\}、2つの概念を組み合わせたときの正解出力を返す操作を :F×FY\oplus: F \times F \to Y と書く。
「規約Aと規約Cが同時に効くとき、正しいコードはこう書く」という対応表のようなものだ。

概念ペアの全数は N=(k2)N = \binom{k}{2}(k個から2つ選ぶ組み合わせ数)。
k=10k = 10 なら45ペア、k=100k = 100 なら4950ペア。
データセット DD に含まれる既知の例が nn ペア分あり、残りは未見になる。

CGC(Compositional Generalization Capacity)は「まだ見ていない概念ペアに正しく答えられる確率」で、こう定義される。

CGC(M,D)=Pr(fi,fj)[M(fi,fj)=(fi,fj)]CGC(M, D) = \Pr_{(f_i, f_j)} \bigl[M(f_i, f_j) = \oplus(f_i, f_j)\bigr]

ペアを一様ランダムに選んだとき、モデル MM の出力が正解 (fi,fj)\oplus(f_i, f_j) と一致する確率だ。
CGCが高ければDに含まれない組み合わせにもちゃんと対応できている。
低ければ、保存済みのペアしか当てられていない。

次に、凍結モデル(重みが変わらないモデル)がcontext内に KK 件のfew-shot例を見て規則を推論する場合を考える。
この精度には上限 αˉ<1\bar{\alpha} < 1 がある。

この上限は仮定ではなく、情報理論のFanoの不等式から導出できる。
Fanoの不等式が言っていることは単純で、「観測データから候補を区別する情報量が足りなければ正解率は1に届かない」。
正しい規則の候補(仮説クラス HH)が大きく logH>KlogY\log|H| > K \log|Y| を満たすとき、KK 件の例だけでは候補を1つに絞れない。
出力の選択肢が Y|Y| 通りで KK 件見ても、得られる情報は高々 KlogYK \log|Y| ビット。
候補を区別するのに logH\log|H| ビット必要なら、足りない分だけ誤りが残る。
context windowを埋め尽くしてfew-shotを増やしても、候補空間が大きければ完全精度にはたどり着けない。

Theorem 1は、目標精度 1δ1 - \deltaδ\delta が小さいほど高精度を要求)に必要なサンプル数が、検索型と重み型で構造的に分離することを示す。
δ<1αˉ\delta < 1 - \bar{\alpha} という条件つきで、これはin-contextの精度天井を超えた精度を狙う場面だ。

検索型は Ω(k2)\Omega(k^2) の例がいる。

nR1δαˉ1αˉ(k2)n_R \geq \frac{1 - \delta - \bar{\alpha}}{1 - \bar{\alpha}} \binom{k}{2}

検索型は「過去に見た似たペアを持ってきて参考にする」仕組みなので、未見ペアへの精度はin-contextの天井 αˉ\bar{\alpha} に引っ張られる。
天井を超える精度を保証するには、結局すべてのペアの大部分を保存しておくしかない。
(k2)\binom{k}{2} はだいたい k2/2k^2/2 なので、概念の数の二乗に比例して必要な保存例が増える。

重み型は仮説クラスのVC次元 dd に依存する分で済む。

nP=O(d+log(1/δ)δ)n_P = O\left(\frac{d + \log(1/\delta)}{\delta}\right)

VC次元は「この規則体系がどれくらい複雑か」を測る学習理論の指標で、概念の個数 kk とは別に決まる。
「2つの概念のうち優先度が高い方を採用する」のような構造のある体系なら d=O(k)d = O(k) 程度、もっと単純なら d=O(1)d = O(1) もありえる。
重み更新はルール体系の複雑さだけに依存するので、概念が増えてもサンプル数がペア数ほどは膨らまない。

両者のギャップは

nRnP=Ω(k2d)\frac{n_R}{n_P} = \Omega\left(\frac{k^2}{d}\right)

構造のある操作で d=O(k)d = O(k) なら Ω(k)\Omega(k) 倍、d=O(1)d = O(1) なら Ω(k2)\Omega(k^2) 倍まで開く。
概念の種類が増えるほど、検索型は保存すべき例がペア数の二乗で増えるが、重み更新はVC次元の分だけで済む。
retrieval精度を上げてもcontext windowを広げても、この二乗ギャップは縮まらない。

セキュリティ面にも式がある。
1回のインタラクションでの注入成功確率 p0p_0、累積インタラクション回数 N(t)N(t) として

P(compromised by t)=1(1p0)N(t)P(\text{compromised by } t) = 1 - (1 - p_0)^{N(t)}

各インタラクションが独立に確率 p0p_0 で汚染されるとき、N(t)N(t) 回すべてを無事に乗り切る確率が (1p0)N(t)(1 - p_0)^{N(t)} で、その補事象が「少なくとも1回やられる確率」になる。
N(t)N(t) \to \infty で1に収束する。
メモリ汚染のセクションで書いた「一過性が永続になる」話の定量版で、論文はこれをevil-squaredと呼ぶ。

定理に数字を入れてみる。

nRnP=Ω(k2/d)\frac{n_R}{n_P} = \Omega(k^2/d)d=O(k)d = O(k) を代入するとギャップは Ω(k)\Omega(k) になる。
k=10k = 10 で約10倍、k=50k = 50 で約50倍、k=100k = 100 で約100倍。

コードベースの規約、API仕様、監査ルールの種類が100を超えるプロジェクトはざらにある。
概念が1つ増えるたびに保存すべき組み合わせ例が約 kk 件増えるので、運用が長くなるほど差が開く。

セキュリティ側も計算する。
1回あたりの注入成功率 p0=0.01p_0 = 0.01(1%)、1日10回のインタラクションを30日続けて N(t)=300N(t) = 300 とすると

P(compromised by 30d)=10.993000.951P(\text{compromised by 30d}) = 1 - 0.99^{300} \approx 0.951

1%でも300回で95%汚染される。

参考