AIエージェントのセッション間の記憶:進捗ファイル、決定ログ、git、初期化スクリプト
AIエージェントはセッションをまたいで記憶を持ちません。リポジトリのファイルに書かれていないことは、新しいセッションは知りません。AIエージェントの記憶は成果物から組み立てます。現在地を示す PROGRESS.md、理由と却下した案を示す DECISIONS.md、意図を説明する git コミット、そして開始時に読み終了時に更新するよう指示する AGENTS.md の引き継ぎプロトコルです。初期化スクリプトは環境を確認し、現在の状態を表示します。
エージェントが何も覚えていない理由
モデルは実行をまたいで状態を保持しません。エージェントが課題について「知っている」ことは、現在のセッションの文脈ウィンドウにあるものだけです。読んだファイル、コマンドの出力、あなたとの対話です。セッションが閉じられたり、文脈があふれたりすると、新しいセッションはゼロから始まり、ディスクに書かれたものだけを見ます。
分かりやすいモデルがあります。毎朝、記憶を消された状態で出社する優秀なエンジニアです。コードはすぐに把握できますが、昨日どのアーキテクチャを選んだか、なぜ他を退けたか、テストが今どんな状態かは思い出せません。記録がなければ、15〜20分を調査に費やし、あなたが意図的に退けた案を高い確率で作り直してしまいます。
つまり、AIエージェントの記憶はモデルの性質ではなく、ハーネスの一部です。自分で設計するファイルとルールの集まりです。良いニュースは、ベクトルデータベースや特別なサービスは必要ないということです。数個の Markdown ファイル、git、そして規律があれば十分です。
大きな文脈ウィンドウでは解決しない理由
もっと大きなウィンドウを持つモデルを待てばよい、と考えたくなります。しかし複雑な課題はどんなウィンドウも埋めてしまいます。エージェントはコードベースを読み、決定の履歴を蓄積し、冗長なテスト出力を受け取り、対話全体を保持します。必要なものはウィンドウの拡大とともに増えます。これは容量の問題ではなく、構造の問題です。
もう一つの効果もあります。ウィンドウの限界に近づくと、多くのモデルは終わらせることを急ぎ、検証を飛ばしたり、正しい解の代わりに簡単な解を選んだりします。Anthropic はこの観察をモデルについて述べていますが、その顕著さはモデルによって異なります。限界ぎりぎりの長いセッションは、情報を失うリスクだけでなく、作業の質を下げるリスクもあります。
圧縮:何が残り、何が失われるか
圧縮(compaction)とは、対話の前半を短い要約に置き換えて、同じセッション内に空きを作ることです。Claude Code では /compact コマンドで行えますし、上限に近づくと自動でも行われます。一方 /clear は履歴を消して会話を最初からやり直します。
圧縮の問題は、要約が通常「何を」は残しても「なぜ」を失うことです。書かれたコードはファイルに残りますが、推論、例えば「なぜ案 B を選んだか」「なぜライブラリを使わなかったか」「なぜ最適化を意図的に見送ったか」は、数語に縮められるか消えてしまいます。次のステップでエージェントは、理由が見えなくなった意図的な決定を「改善」してしまいます。
| 圧縮 | リセット(新しいセッション) | |
|---|---|---|
| 何が起きるか | 対話の前半が要約に置き換わる | セッションを閉じ、新しいセッションが状態ファイルを読む |
| 何が残るか | 圧縮時にモデルが重要と判断したもの | ファイルに書いたものだけ |
| 限界付近での焦り | 残る可能性がある | リセットされる |
| いつ適切か | タスクが現在のセッションに確実に収まる場合 | タスクの期間が長い、または不明確な場合 |
複雑な指標を使わない実践的なルールは次のとおりです。タスクがウィンドウのおよそ60%を占めたら、コードを書くのをやめて引き継ぎの準備をします。文脈が40%しか埋まっておらず、タスクの終わりが近いなら、圧縮で十分です。
エージェントの記憶を構成する四つの成果物
モデルの記憶に頼る代わりに、航海日誌を用意します。その構成は四つの部分からなり、それぞれ役割があります。
- PROGRESS.md:今どこにいるか。
- DECISIONS.md:なぜそうしたのか、別の選択肢ではなく。
- git コミット:無料の状態のチェックポイント。
- AGENTS.md の引き継ぎプロトコル:交代の際の、着任時と離任時にすべきこと。
PROGRESS.md:進捗ファイル
このファイルは一つの問いに答えます。プロジェクトは今どの状態にあるのか。エージェントはセッションの開始時に最初にこれを読み、終了前に最後に更新します。ファイルは短く、追記し続けるのではなく書き直します。履歴は git にあります。
# PROGRESS.md
## 現在の目標
商品カタログの検索(機能 search-01)
## 状態
- HEAD: 4f2a9c1
- make check: 緑、142/142 テスト
- 完了: PostgreSQL のインデックス(tsvector)、ハンドラ GET /search
- 検証済み: ユニットテスト + 3つのクエリで curl による手動確認
## ブロッカー
- カザフ語の形態素解析データがない — 判断待ち
## 次のステップ
GET /search にページングを追加。基準: 2ページ目のテスト
引き継ぎに必要な四つのフィールドに注目してください。コミットのハッシュ、テストの通過率、ブロッカー、次のステップです。新しいセッションがこのファイルだけで作業を一意に続けられないなら、テンプレートにフィールドが足りないということです。
DECISIONS.md:決定のログ
このファイルは、圧縮で失われる「なぜ」を保持します。記録の形式は、決定、理由、却下した案とその理由、制約です。
## 2026-03-14 — ユーザー設定のキャッシュに Redis を使用
- 決定: Redis、TTL 5分、書き込み時の能動的な無効化。
- 理由: すべてのリクエストで読まれ、データ量は小さい。
- 却下: PostgreSQL の materialized view — 変更が頻繁で、保守のコストに見合わない。
- 制約: 支払い設定はキャッシュしない。
ここで最も価値のある行は「却下」です。次のセッションが、既に検討して捨てた案へ決定を「最適化して戻す」ことを防ぎます。決定ログは、進捗ファイルと違って追記されるだけです。古い記録は削除せず、新しい記録によって置き換えられたとマークします。
git の履歴をチェックポイントとして使う
コミットは、作業にもともと組み込まれたバージョン付きの状態スナップショットです。エージェントは git log と git diff を読んで、何が変わったかを復元できます。履歴を記憶として機能させるには、コミットメッセージが差分の内容の言い換えではなく、意図を説明している必要があります。
# 悪い例
git commit -m "update search.py"
# 良い例
git commit -m "search: LIKE の代わりに tsvector インデックス
LIKE '%q%' はインデックスを使わず、20万件の商品で 1.8 秒かかっていた。
ページングはまだ未実装。PROGRESS.md を参照。"
テストが緑になるたびに小さく頻繁にコミットすると、ロールバックの地点にもなります。セッションが別の方向へ逸れたら、途中の修正を解きほぐすより、最後の検証済みの状態に戻るほうが簡単です。
AGENTS.md の引き継ぎプロトコル
エージェントがこれらの状態ファイルの存在を知らなければ、ファイルは役に立ちません。そのため、入口の指示ファイル(AGENTS.md または CLAUDE.md)に、着任時と離任時にすべきことを書いた短いプロトコルを置きます。
## 着任時
1. PROGRESS.md と DECISIONS.md を読む。
2. .agent/feature_list.json で有効なタスクを探す。
3. make check を実行し、リポジトリが整合していることを確認する。
4. 「次のステップ」から続ける。解決済みのことを再び開かない。
## 離任時
1. make check を実行し、緑の状態をコミットする。
2. PROGRESS.md を更新する: HEAD、テスト、ブロッカー、次のステップ。
3. 新しい決定を DECISIONS.md に追記する。
4. 作業ツリーに未完成のものを残さない。
プロトコルは短く保ってください。入口の指示ファイルの数行で済ませ、別の長い文書にはしないでください。
初期化スクリプト:セッションの開始を速くする
エージェントが全体像を手作業で集めなくて済むように、環境を確認して状態を表示する一つのスクリプトを用意します。スクリプトは冪等(何度実行しても同じ結果)にし、明確な準備完了のメッセージで終わるようにします。
#!/usr/bin/env bash
set -euo pipefail
echo "== 1. 環境 =="
command -v uv >/dev/null || { echo "FAIL: uv がインストールされていません"; exit 1; }
uv sync --frozen
echo "== 2. リポジトリ =="
if [ -n "$(git status --porcelain)" ]; then
echo "WARN: 作業ツリーが汚れています"
else
echo "OK: クリーン"
fi
echo "HEAD: $(git log -1 --oneline)"
echo "== 3. 検証 =="
make check
echo "== 4. 状態 =="
cat PROGRESS.md
echo "作業準備完了"
すると着任のプロトコルには一行で足ります。「scripts/init.sh を実行せよ」。Claude Code では、このスクリプトをセッション開始イベント(SessionStart)のフックで自動化できます。フックがなくても、ファイルの指示に従ってエージェントがスクリプトを実行します。
エージェントの記憶に入れるものと入れないもの
最大の間違いは、記憶をごみ箱にしてしまうことです。エージェントが開始時に読む一行一行が文脈の場所を占め、余計なものが重要なものを埋もれさせます。
| 入れる | 入れない |
|---|---|
| 現在の目標と次のステップ | エージェントが自分で読めるコードの言い換え |
| 理由と却下した案を含む決定 | 対話の全ログやコマンド出力 |
| コミットのハッシュとテストの状態 | 依存関係の一覧(パッケージのマニフェストにある) |
| ブロッカーと未解決の質問 | 「完璧にできた、すべて動く」だけで、検証方法の記載がないもの |
| 行き止まり:何を試して、なぜ動かなかったか | 秘密情報、トークン、パスワード |
| 受け入れ基準付きのタスク | 完了したタスクについての古いメモ |
寿命によっても分けてください。プロジェクトのルールはめったに変わらず、すべてのタスクに関わるので、入口の指示ファイルに置きます。状態はセッションごとに変わるので、進捗ファイルに置きます。両者を混ぜると、入口のファイルがすぐに古いメモで膨れ上がります。
最初のセッション:初期化を独立したフェーズにする
最初のセッションが混乱に陥ると、セッション間の記憶も役に立ちません。典型的な例は、まっさらなリポジトリに「検索を追加して」と頼み、30分後にテストの枠組みが誤って設定されていて、マイグレーションの形式も違っていたと分かるケースです。検索は動くようになりましたが、土台がゆがんでいます。
原因は、実装と初期化が異なるものを最適化していることです。実装はこのセッションで完成する機能の数を、初期化は以降のすべてのセッションの速さと信頼性を最適化します。エージェントは目に見える成果、つまりコードの方へ傾きます。だから初期化は、四つの条件からなる明確な完了基準を持つ独立したセッションに切り出すべきです。
- 起動できる:クリーンなクローンから
make setupとmake devが動く。 - 検証できる:
make testがあり、本物の通過するテストが少なくとも一つある。 - 進捗が見える:状態ファイルが存在し、読める。
- 作業を引き継げる:受け入れ基準付きのタスクのリストがある。
新しいプロジェクトを頻繁に始めるなら、リポジトリのテンプレートを用意しておくと便利です。Makefile、テストの枠組み、AGENTS.md の骨組み、.agent/ フォルダ、scripts/init.sh、リンター、CI を含めます。そうすれば、初期化はそのプロジェクト固有の部分だけで済みます。
記憶が機能しているかを確かめる
最も正直な指標は、新しいセッションの開始から最初の意味のあるコード変更までの時間です。成果物がなければ、たいてい15〜20分の調査が必要です。良い引き継ぎがあれば2〜3分です。一度、導入の前後で計ってみてください。規律が報われるかどうかが分かります。
二つ目の方法はコールドスタートのテストです。新しいセッションを開き、「続けて」と書いてから五つの質問をします。現在の目標は何か、テストはどの状態か、重要なテーマについてどんな決定がなされ、なぜか、何がブロックしているか、次のステップは何か。五つすべてにファイルを根拠にして推測なしで答えられれば、記憶は正しく機能しています。自信のない答えは、テンプレートに足りないフィールドです。
よくある間違い
- 進捗ファイルが増えるだけ。一か月後には何百行もの履歴になります。現在の状態に合わせて書き直し、履歴は git に任せてください。
- 「却下した案」の行がない。却下した案のない決定ログは、同じ議論の繰り返しを防げません。
- エージェントが感覚で状態を更新する。「すべて動く」ではなく、どう検証したかを書かせてください。コマンドと結果を書きます。
- 文脈の限界で引き継ぎをする。限界付近ではエージェントはすでに焦っています。引き継ぎは早め、およそ60%の時点で始めてください。
- 未コミットの作業ツリーで終わる。コミットされていない途中の修正は、次のセッションを混乱させます。
まとめ
AIエージェントのセッション間の記憶は、モデルではなくファイルの上に築きます。PROGRESS.md は現在の状態を、DECISIONS.md は理由と却下した案を、git コミットは意図を説明するチェックポイントを保持します。AGENTS.md の引き継ぎプロトコルは、これらを読み、更新することをエージェントに義務づけます。初期化スクリプトはセッションの開始を一つのコマンドに縮め、コールドスタートのテストは記録が十分かどうかを示します。エージェントの作業ログとブートストラップの契約は、ハーネスエンジニアリングの講座で実践的に扱っています。
よくある質問
エージェントに何かを覚えておくよう頼むだけではだめですか?
エージェントは、次のセッションが読むファイルに書いたことしか覚えません。Claude Code にはメモリファイルと CLAUDE.md がありますが、特定のタスクの状態には、リポジトリ内の明示的な進捗ファイルのほうが確実です。
PROGRESS.md と CHANGELOG は何が違いますか?
CHANGELOG はバージョンごとに利用者向けの変更を記述します。PROGRESS.md は次のエージェントセッションのための現在の作業状態を記述し、状態が変わるたびに書き直します。
一つのリポジトリで作業するプログラマー用のエージェントにベクトルデータベースは必要ですか?
通常は不要です。タスクの状態は数個の短いファイルに収まり、コードはエージェントが直接読めます。ベクトル検索が意味を持つのは、大量の外部文書から検索する必要がある場合です。
状態ファイルは git にコミットすべきですか?
はい。そうすれば状態はどのセッションからも、チームのどのメンバーからも見られ、変更の履歴も残ります。例外は個人的なメモと、秘密情報を含むものです。
エージェントが終了時に PROGRESS.md を更新し忘れたらどうしますか?
更新を離任プロトコルの完了基準の一部にして、機械的に確認してください。例えば、ファイル内の HEAD と現在のコミットを比べるフックやスクリプトを使います。