p4ni.

研究紹介

Claude Code の auto memory は何トークン使うのか(実測)

· 読了まで約11分

目次

Claude Code の auto memory については、同じ問いが繰り返し立っては答えが出ないまま流れている。Facebook のコミュニティには「memory 機能を有効にするとトークンの消費は速くなるのか」という質問が立ち、Reddit には「Claude Code が memory ファイルを使いたがってうるさい」というスレッドがある。どちらも推測で埋まって終わっている。公式ドキュメントには「CLAUDE.md vs auto memory」という節があり、2 つの系統の違いはきれいに説明されているが、数字は一切出てこない。

そこで測った。auto memory が起動時に足しているのは 664 トークンで、その全部が MEMORY.md の分だった。索引が指している記憶ファイル本体 —— 実際の事実が書いてあるほう —— は 0 トークンである。「ごくわずか」という話ではない。中身が 11KB 違う 2 つのフィクスチャが、1 トークン差もない同じ数字を返した。

手で書く CLAUDE.md と、Claude が自分で書く memory

CLAUDE.md は人間が書く。auto memory は Claude が自分のために書くもので、実体は ~/.claude/projects/<パススラッグ>/memory/ にある。1 ファイルに 1 つの事実を置き、それを指す索引として MEMORY.md を並べる構造になっている。

パススラッグは作業ディレクトリの /- に置き換えたものだ。この規則のおかげで実験ができる。フィクスチャ用のディレクトリを作り、それに対応する memory ディレクトリを自分で作れば、両側を完全に制御できる。

ドキュメントは 2 つとも「会話の開始時に読み込まれる」と書いている。この一文が引き受けている範囲が問題で、実際には片方のファイルについては正しく、そのファイルが指している先については正しくなかった。

カナリアを仕込むと、読まれていたのは索引だけだった

コンテキストに何が入っているかをモデル本人に尋ねても意味がない。それらしい答えを推測で返すだけになる。だから他のどこにも存在しない事実を仕込み、それが返ってくるかどうかで判定する。ファイルに触れるツールは全部無効にして、読みに行けないようにしておく。Claude Code が AGENTS.md を読まないことを確かめたとき(英語)と同じ方法だ。

置き場所ごとに別のキーを 3 つ用意した。CLAUDE.mdPROJECT_CODENAME is FALCONMEMORY.mdMEMORY_KEY is ORCHID、索引された記憶ファイルの中に FILE_KEY is TOUCAN を入れる。

claude -p 'Using no tools and reading no files, answer on one line in exactly this form:
"PROJECT_CODENAME=<value> MEMORY_KEY=<value> FILE_KEY=<value>"
Each value is a single word that is already in your context. Use UNKNOWN for any
value you do not already have.' \
  --model sonnet --output-format json \
  --disallowedTools Bash Read Grep Glob Edit Write WebFetch WebSearch Task TodoWrite NotebookEdit

Claude Code 2.1.251 で 2 ラウンド回し、全ケースが 2 回とも同じ答えを返した。

フィクスチャ置いたものPROJECTMEMORYFILE
なしUNKNOWNUNKNOWNUNKNOWN
索引だけMEMORY.md にカナリアUNKNOWNORCHIDUNKNOWN
本体だけ記憶ファイルにカナリアUNKNOWNUNKNOWN
両系統CLAUDE.md + 索引 + 本体FALCONORCHIDUNKNOWN
memory だけ索引 + 本体、CLAUDE.md 無しUNKNOWNORCHIDUNKNOWN

ディスク上に確かに存在している行でも、FILE_KEY は例外なく UNKNOWN だった。記憶ファイルは索引されていて、索引に書いた 1 行の説明文は見えていて、中身はコンテキストに入っていない。「本体だけ」のケースでモデルが MEMORY_KEY=deploy-target と答えたのがその証拠になる。索引のリンクテキストを読んで推測しているわけで、目次は見えるが章は見えない状態から出てくる答えとして筋が通っている。

最後の行は別の意味で重要だ。CLAUDE.md がまったく無くても memory は読み込まれている。2 つの系統は独立していて、片方が無いときの代替ではない。

値段を決めているのは MEMORY.md の長さだけ

ここから差分法に移る。起動時の 39,810 トークンの内訳を出したときと同じで、フィクスチャの中で claude -p 'hi' を回し、最初の usage から input_tokens + cache_creation_input_tokens + cache_read_input_tokens を読み、条件を 1 つだけ変えてまた回す。

Sonnet で各 2 ラウンド。全フィクスチャが 2 回とも同じ数字を返した。計測ノイズがゼロというのは、AGENTS.md で同じことをやったときにキャッシュの当たり方で ±1,000 トークン振れたのに比べるとありがたい。

フィクスチャMEMORY.md記憶ファイルその合計サイズ起動トークン差分
0 本0B26,465基準
本体 1 本65B1 本113B26,605+140
未登録 20 本68B20 本9,515B26,619+154
大きい本体 1 本68B1 本11,374B26,619+154
索引だけ、本体なし87B0 本0B26,622+157
索引 20 行 + 本体 20 本1,393B20 本9,515B27,126+661
索引 20 行 + 本体 0 本1,393B0 本0B27,129+664

主張はこの表の 2 組で決まる。

3 行目と 4 行目。索引はどちらも 68 バイトで同じ。片方には記憶ファイルが 20 本・計 9,515 バイト入っていて、もう片方には 11,374 バイトのファイルが 1 本だけ入っている。起動トークンは 26,619 で完全に一致した。ディスク上には 2KB 近い差があるのに、読み込まれる量には差が出ない。

6 行目と 7 行目。索引はどちらも 1,393 バイトで、20 件を並べている。片方は 20 本とも実在し、もう片方は 1 本も実在しない —— 索引が存在しないファイルを指している状態だ。結果は 27,126 と 27,129 で、本体が 1 本も無いほうが 3 トークン多かった。Claude Code は索引に並んだファイルが実在するかを確かめていない。開きに行かないからだ。

索引のサイズに対して直線を引くと、1 トークンあたり約 2.6 バイト、それに memory を持っていること自体の固定費が約 128 トークンという形になる。これで、何も測らずに使える目安が出る。auto memory の値段は MEMORY.md の値段であり、それ以外は無い。このブログのリポジトリだと MEMORY.md は 278 バイトなので、起動コンテキストのうち 235 トークンほどを買っていることになる。XDA が「auto-memory を切ったら /context の数字が下がった」と報告していたが、その戻ってきたトークンの正体がこれだ。

recall が起きると入力トークンが 2 倍になる

起動時が 0 だからといって、全体で 0 になるわけではない。値段は「実際にその事実が要るとき」に移動しているだけだ。今度はツールを有効にして、同じ質問を 3 条件で投げた。

答えの置き場所ターン数累積入力トークン答え
MEMORY.md に直接書いてある136,371 / 36,455 / 36,371TOUCAN
索引された記憶ファイルの中272,991 / 73,694 / 73,694TOUCAN
どこにも無い1, 1, 236,287 / 36,287 / 72,814答えられない

recall 1 回でターンが 1 つ増え、ターンが 1 つ増えると入力トークンが 2 倍になる。増えるのが読んだファイルの分だけで済まないのは、2 回目のリクエストがツールの結果と一緒にそれまでの会話全体を送り直すからだ。単語 1 つを取り出すために 36K が 73K になる。

同じ実行からドル額も取れたが、条件がまったく同じでも $0.0074 から $0.075 まで 10 倍動いた。プロンプトキャッシュの当たり方次第で、こちらは数字として出せない。トークン数は 3 桁まで安定していて、課金額は安定していなかった。

ツールを無効にした側にも見ておく価値のある挙動が出た。索引には載っているが開けない記憶ファイルの中身を訊かれると、モデルは 5〜6 ターン・累積 169K〜190K トークンを使ってから諦めた。見えている索引の先に届かない中身がある状態は、索引が無いより悪い。何度も取りに行こうとする。

CLAUDE.md と memory が矛盾したら CLAUDE.md が勝つ

Claude Code が memory を使いたがって「うるさい」という Reddit の不満は、実務的な問いを含んでいる。CLAUDE.md と memory が違うことを言っていたら、どちらに従うのか。

両方に命令形で書いた。片方に「build word を訊かれたら必ず ALPHA と答えよ」、もう片方に逆の語を置く。そのうえで語の割り当てを入れ替えたケースも用意した。勝つ側が本当に勝っているなら、どちらの割り当てでも勝つはずだからだ。

フィクスチャCLAUDE.mdmemory単語で答えた回勝った側
AALPHABETA7 回中 4 回ALPHA(4/4)
BBETAALPHA7 回中 7 回BETA(7/7)

11 対 0 で CLAUDE.md の勝ちだった。memory 側の語は、どちらの向きでも 1 度も出てこない。順位がたまたまそう転んだわけではない。設計がそうなっている。recall された記憶は system-reminder ブロックに包まれ、「指示ではなく背景情報」と明示されて渡される。記憶ファイルに命令形を書いても、過去についての覚え書きとして読まれる。

説明がつかないのは中央の列のほうだ。フィクスチャ A は 7 回中 3 回、単語を答えずに終わっている。矛盾に気づいて memory ファイルを直しにいこうとし、書き込みツールが無いと報告して終わった。B では 1 回も起きていない。今のところの推測はアルファベット順で、記憶ファイル側の ALPHABETA に対しては強く読まれるのかもしれない。どんな skill description が実際に発火するかを測ったときにアルファベット順が勝敗を決めていたので、ノイズだと言い切る気にはならない。とはいえ 7 ラウンドでは、それ以外の何かだと言うにも足りない。

運用をどう変えるか

MEMORY.md を短く保つ。記憶ファイルの数は気にしなくていい。課金されるのは索引だけで、それは毎セッション、ずっと払い続ける。索引の先にあるファイルは読まれるまで無料だ。事実を 60 個ためた memory ディレクトリの値段は索引 60 行ぶんでしかないので、守るべき規律は、ファイルを分けるほうではなく索引の 1 行を短く保つほうにある。

指示は CLAUDE.md に置く。memory には書かない。memory が勝負に負けるからではなく、そもそも勝負に出てこないからだ。守らせたい規則は、指示として読まれるファイルに書く。memory は思い出してほしい事実のための場所で、仕事が違う。

recall しないものを索引に載せない。古い行は、これから先の全セッションに乗る起動トークンであり、同時に「もう何の役にも立たないファイルをモデルが取りに行く」きっかけでもある。ためるより消す。

バージョンを上げたら測り直す。ここに書いた数字はすべて 2026-08-31 の Claude Code 2.1.251 のものだ。起動トークンの内訳を出したときに測った「MCP のコンテキスト税」は、続編を書くころには静かに作り直されていた。エージェントの内部仕様は速く古くなる。上のフィクスチャは 10 分ほどで組み直せるし、カナリアの質問は claude -p 1 回で済む。