創刊準備号 · 2026年秋English · RSS
AgentKoji

エージェントのメモリ設計

Claude Code メモリの索引 MEMORY.md と、毎回読ませる引き継ぎ文書が247KBまで膨らんだ。索引の書き方と、腐るメモの見分け方をまとめる。

エージェントにメモを残させると、最初は賢くなったように見えます。数か月たつと、メモは膨らみ、古くなり、ときには嘘をつきます。この記事では、編集部が Claude Code のメモリと引き継ぎ文書で踏んだ失敗と、いまの設計をまとめます。

試した環境は、編集部の検証環境(Linux、Claude Code 2.1.301、Python 3.13.5、2026-10-16)です。

Claude Code メモリと MEMORY.md の上限

Claude Code の auto memory の索引 MEMORY.md は、セッションの開始時に先頭200行か25KBまでしか読まれません(出典:https://code.claude.com/docs/en/memory 、2026-10-16確認)。手元で日本語の索引の行を200行並べると 13,199 bytes だったので、行数で管理すれば足ります。CLAUDE.md 側の行数の考え方は「CLAUDE.mdは削るほど効く」に書きました。

索引の各行に「いつ開くか」を書く

メモは、存在するだけでは効きません。読まれて初めて効きます。「着手前に読む」と書いたメモがあったのに、読まれないまま同じ穴に落ちたことがあります。

そこで、索引の1行ごとに、開くきっかけを書くことにしました。「commit が拒否された時」「本番の再開を議論する時は必読」のような書き方です。同じ場面で開くメモは1本にまとめ、まとめ元を merged_from に残します。

メモを書くときは、Why:(なぜ)と How to apply:(どう使うか)の2項目を必須にしています。書き込みそのものは hook で確認(ask)にして、人が通すかどうかを決めます。

メモには方針だけを書き、状態は書かない

メモリに置くのは、長く変わらない方針だけです。「いま何をしているか」という状態は、引き継ぎ文書と git の側を正とします。

状態を書いたメモは腐ります。引き継ぎ文書にあった環境変数の値を信じてデバッグしたら、その値は3日前に変わっていました。判断の直前に、実際の値を取り直します。

前のセッションの診断も、コードとデータで確かめ直します。「commit したのに行が無い」という診断は誤りでした。タイムゾーンのせいで、処理が3時間前に止まったように見えていただけでした。

結論が変わったメモには版数を付ける

訂正したメモには版数を付け、「第3版を先に読め」と書きます。欠陥が見つかった分析スクリプトは archive に移したうえで、次の3点をそろえます。

古い数字が引用されるのを、機械的に防ぐためです。

毎回読む文書は膨らむ

セッションの開始時に読ませる文書は、膨らむと最大のコンテキストの負債になります。編集部では、タスク表が90〜120KB、引き継ぎ文書が247KBになりました。タスク表の「最終更新」の行に、40セッション分のログがつながって65KBになったこともあります。

いまは、終わったタスクを1行に縮め、作業のログは引き継ぎ文書とコミットの本文に置いています。

メモが判断を縛る

エージェントが書いたメモは、次のエージェントの枠になります。 「新しい方針を探して」と頼んでも、メモに残っていた過去の成果の数字が基準点になり、既存の方針の改良に戻っていきました。新しい案を探させるときは、そのメモを読ませない構成も考えます。

注入された文脈を疑う

頼んでいない別の LLM のサービスを、エージェントが使おうとしたことがあります。API の設定は正常でした。原因は、外部のツールが CLAUDE.md に書き込んでいた過去の活動のログでした。

そのツールを外したあとも、ほかの AI ツールの設定ディレクトリや、ホーム直下の ~/CLAUDE.md に、計40件の書き込みが残っていました。Claude Code は、作業ディレクトリから上の階層の CLAUDE.md を、すべて起動時に読み込みます(出典:https://code.claude.com/docs/en/memory 、2026-10-16確認)。ホーム直下のファイルは、その下のどのプロジェクトでも読まれます。

上の階層に何があるかは、次のように確かめられます。

#!/usr/bin/env bash
# find-claude-md.sh:cwd から / まで遡って CLAUDE.md を列挙する
d=$(cd "${1:-.}" && pwd)
while :; do
  for f in "$d/CLAUDE.md" "$d/.claude/CLAUDE.md" "$d/CLAUDE.local.md"; do
    [ -f "$f" ] && echo "$f ($(wc -c <"$f") bytes)"
  done
  [ "$d" = / ] && break; d=$(dirname "$d")
done

試しにホーム直下へ CLAUDE.md を置くと、プロジェクトの外にある1本も一覧に出てきました。設定を疑う前に、注入されている文脈を疑います。実際に読み込まれたファイルは、セッションの中の /context でも確認できます(同出典)。

エージェントの報告を実数で確かめる仕組みは「AIの「完了しました」を信じない仕組み」に書きました。

知識ベースは3層に分け、期限を持たせる

メモが増えたら、知識ベースとして整理します。編集部は「LLM Wiki」と呼ばれる形を使っています。元の資料(Raw)、LLM がまとめたページ(Wiki)、書き方の決まり(Schema)の3層です。知識ベースが放置される一番の理由は手入れの手間なので、その手入れを LLM に任せます。

各ページには status、confidence、review_due を持たせ、期限切れを lint で拾います。手元で動かした lint です。

import sys, re, pathlib, datetime
today = datetime.date(2026, 10, 16)
for p in sorted(pathlib.Path(sys.argv[1]).glob("*.md")):
    fm = dict(re.findall(r'^(\w+):\s*(.+)$', p.read_text().split('---')[1], re.M))
    miss = [k for k in ("status", "confidence", "review_due") if k not in fm]
    if miss: print(f"{p.name}: 欠落 {miss}"); continue
    if datetime.date.fromisoformat(fm["review_due"]) < today:
        print(f"{p.name}: 期限切れ {fm['review_due']}")
a.md: 期限切れ 2026-09-30
c.md: 欠落 ['confidence', 'review_due']

AI が作ったものと、人が書いたノートは、置き場所を分けます。編集部のノート置き場は、9割以上が機械の生成物になっていました。「探しにくい」の本当の原因は、フォルダの構成ではなく、生成物が混ざっていたことでした。解決したのは、整理ではなく隔離です。

公式の資料が無い分野の知識には、主張ごとに「公式」「コミュニティ」「異論あり」「反証済み」の等級を付けて、実測で育てます。

まとめ