8月28日、Jordy Zomerが「I accidentally turned LLM memory into program analysis :: pwning.systems」と題した記事を公開した。LLMエージェントに「維持される知識状態」を持たせるためにDatalogエンジンを実装したところ、脆弱性調査のメモリ問題が静的解析エンジンと同じ構造の問題であると気づくに至った経緯を詳しく紹介している。
結論から言えば、このアプローチはベンチマーク上でフルコンテキストの約38分の1のトークン数を用いながら、GPT-4.1にすべての会話を渡す手法の2倍以上のF1スコアを達成した。LLMの「メモリ」をベクトルDBで解決しようとしてきた従来のアプローチとは、問題の捉え方から根本的に異なる。
LLMのメモリ問題は「データベース問題」だった
LLMを使った脆弱性調査では、セッションが数時間に及ぶと、モデルが以前に否定した仮説を再度提案したり、すでに無効になった観察結果から推論を続けたりする問題が生じる。
この問題に対する既存のアプローチは、過去の会話や観察結果をベクトルDBに格納し、必要なときに関連する断片を取り出して渡すというものだ。しかしZomerはこのアプローチに根本的な違和感を覚えた。
「私が欲しかったのは、モデルが何を言ったかを記憶することではなく、現時点で何が真であるかを維持することだった。」
例えば調査中に以下の事実を確立したとする。
attacker controls object_a
object_a points to object_b
object_b is a kernel object
2時間後にLLDBで object_a が実際には object_b を指していないことが判明した場合、ベクトルDBには矛盾した情報が混在することになる。LLMが「どの結論が今も有効か」を毎回再構築しなければならず、ここでハルシネーションが起きやすい。
Zomerはこの問題の構造を眺めているうち、プログラム解析(静的解析)とまったく同じ問題であることに気づいた。静的解析でも「あるコード上の事実が変わったとき、依存する結論だけを再計算する」という課題は長年研究されてきた領域であり、Datalogはその解法として確立されている。
Datalogエンジン「Lemmalog」の誕生
プログラム解析では、事実(facts)とルール(rules)から新しい事実を導出し、入力事実が変われば影響を受ける結論だけを更新する(増分評価)。これはまさにZomerがLLMに求めていた挙動だ。
この着想から生まれたのが**Lemmalog**である。Datalog(関係データベースのクエリ言語を起源とする宣言型論理プログラミング言語で、PrologのサブセットとしてDB研究から発展した)をベースにしたメモリエンジンで、役割を以下のように分割する。
- LLMが担う部分:ソースコード、デバッガ出力、自然言語メモを解釈し、構造化された事実に変換する
- Lemmalogが担う部分:事実の管理、ルール適用による派生事実の計算、無効化の伝播
source code,
debugger output,
natural language notes
|
v(LLMが変換)
structured facts
|
v(Lemmalogが処理)
deductive rules
|
v
maintained state
「パーサが確率的で、それ以降は決定論的」という構造を、Zomerはコンパイラのフロントエンドとバックエンドに喩えている。LLMは「自然言語を構造化する」部分だけを担い、論理的な一貫性の維持はDatalogエンジンが行う。
「なぜ真か」を追跡するProvenance
Lemmalogの特に面白い機能がProvenance(来歴追跡)だ。
数時間のエージェント実行の末に candidate_3_is_exploitable という結論が得られたとき、「なぜそう判断したのか」をLemmalogに問い合わせると、以下のような依存ツリーが返る。
candidate_3_is_exploitable
|
+-- attacker_controls_pointer
| |
| +-- observation_41
|
+-- pointer_reaches_target
|
+-- observation_57
+-- rule_12
observation_41 が誤りだと判明した場合、その結論は自動的に無効化される。また、LLMが「このポインタはattacker-controlledだと既に確認した」と自信を持って発言した際に、Lemmalogにその来歴がなければ、それは維持された状態に含まれていないと判断できる。ハルシネーションを完全には防げないが、根拠のない結論が調査に紛れ込むことを大幅に難しくする。
さらに、事実には有効期間(validity interval)を付与できる。
viable(primitive_a) [10:14, 12:37)
not_viable(primitive_a) [12:37, ...)
これにより「今は使えるか」と「なぜ以前はそう考えたのか」の両方に答えられる。矛盾する事実をLLMに渡して判断させる必要がなくなる。
ベンチマーク結果:コンテキスト38分の1でフルコンテキストの2倍超の精度
性能検証には**MemEval**(LongMemEvalおよびLoCoMoの2ベンチマークを含む、長期会話メモリの評価スイート)を使用している。事実の抽出にはClaude Sonnet 4.6を、回答と評価にはベンチマーク標準のモデルを用いた。
LongMemEval(102問)の結果は以下の通りだ。
| System | F1 | 備考 |
|---|---|---|
| PropMem | 0.550 | 命題ベースのメモリ管理手法 |
| SimpleMem | 0.480 | 要約ベースのアプローチ |
| Lemmalog | 0.463 ± 0.010 | 本記事の手法 |
| OpenClaw | 0.244 | グラフベースのメモリ手法 |
| Full Context (GPT-4.1) | 0.197〜0.222 | 全会話をコンテキストに渡す手法 |
PropMemやSimpleMemには及ばないものの、GPT-4.1にフル会話を渡す手法の2倍以上のF1を達成している。より注目すべきは文脈量だ。
- フルコンテキスト:約104,000トークン/問
- Lemmalog:約2,700トークン/問
約38倍のコンテキスト削減で、フルコンテキスト手法を大きく上回る精度を出している。コストと速度の面でも実用的な差になる。なお、LoCoMoベンチマークの結果については元記事に詳細が記載されている。
カテゴリ別では「Knowledge Update(知識の更新)」でLemmalogが0.579とトップスコアを記録した(PropMem: 0.528、Full Context: 0.202)。Lemmalogが持つRetraction(事実の撤回)機構が直接効いている領域で、「以前はAだったが、今はBだ」という状態変化の追跡がとりわけ得意だとわかる。
ベクトルDBとの使い分け
Zomerはベクトルデータベースを否定しているわけではない。「過去に見つけたこのアロケーションパスについて何があったか」という問いにはセマンティック検索が有効だ。しかしコサイン類似度は真偽を判定しない。
「メモリという言葉の下には、実は2つの異なる問題が潜んでいる。1つは『過去のどの情報がこの質問に関連するか』、もう1つは『これまでに学んだことを踏まえて、今何が真であるか』だ。」
現在はLemmalogと検索ベースのアプローチを組み合わせて使っているという。実際の脆弱性調査では、Lemmalogが「現在の知識状態」を管理し、過去の観察ログへのセマンティック検索と併用することで、両者の欠点を補い合う構成になっている。
「LLMが何を言ったか」ではなく「今何が真か」を管理する——この問いの立て方の転換が、LLMエージェントのメモリ設計に新たな視点をもたらしている。
詳細はI accidentally turned LLM memory into program analysis :: pwning.systemsを参照していただきたい。




