Claude Code Hooksで会話ログを蒸留する — SessionEnd起点のナレッジ捕捉設計
AIとのセッションには、最終的な成果物だけでなく「なぜその方針にしたか」「どの案を捨てたか」という判断の過程が残る。でもセッションを閉じれば、その過程は基本的に二度と読まれない。この記事は、その過程をClaude CodeのHooks機能で自動的に拾い上げる設計を提案している。
4つのフックポイントと粒度のトレードオフ
会話を後処理に回せるタイミングは、情報量と発火頻度のトレードオフで4段階に分かれる。
| フック | 発火タイミング | 情報量 | 発火頻度 |
|---|---|---|---|
| SessionEnd | セッション終了時(/clearでも発火) |
会話全体 | 低 |
| PreCompact | コンテキスト要約直前 | 要約で失われる直前の全体 | 中 |
| SubagentStop | サブエージェント終了時 | サブエージェントの試行錯誤+最終テキスト | 中〜高 |
| Stop | 応答完了ごと | ターン単位 | 高 |
1つだけ選ぶならSessionEndが妥当だとしている。理由は単純で、1セッション=1つの区切りとして自然なうえ、/clearでも発火するため思っているより頻繁に走る。
終了時に蒸留までやらない設計
最初の直感は「SessionEndで会話ログを読み込み、その場で知見を抽出する」だったが、これは捨てられている。理由は会話ログの重さ——平均約1MB、大きいもので50MB超——で、終了処理でこれを読ませるとClaude Codeを閉じるたびに待たされる。閉じるのが遅い仕組みは使われなくなるという判断だ。
代わりにSessionEndでは、会話ログ本体をコピーせず、transcriptのパスと終了時刻だけをキューに積んで即終了する設計にしている。重い蒸留処理は非同期(async: true)で後回しにし、処理済みセッションIDを記録して二重処理を防ぐ。「取りこぼさないための仕組み」と「中身を読む仕組み」を分離するのが要点。
SubagentStopが一番情報を捨てている
この記事の核心はここ。サブエージェント(親から調査を切り出して別文脈で動かす仕組み)は、親に最終テキストだけを返す。ファイルを読み、grepして、外れて、絞り込む——という試行錯誤の過程は、親の会話ログには一切残らない。
筆者が手元のサブエージェントログ30本を実測したところ、「全体の文章量」に対して「親に返した最終テキストの量」は中央値で68.9%が失われていた(最小29%、最大93%)。さらに、手元の会話ログ1091本のうち581本がサブエージェント側で、容量ベースで全体の約半分を占めていたという。つまりログ全体の半分が、通常は読み返されない側にある。
SubagentStopフックには、サブエージェント自身の会話ログのパスと、親に返した最終テキストの両方が渡る。「結論」と「そこに至る全過程」がセットで手に入るのはここだけ、というのが筆者の主張。エージェントの種類でマッチャーを絞れば、調査系エージェントだけを対象にすることもできる。
🗣️ キミとの対話メモ | 魚の骨の比喩 (2026-08-21・この記事をingestする直前に交わした雑談から)
この記事をingestする少し前、キミからこんな話が出た:「僕らは高いトークンコストを使ってプロンプトを実行してタスクを解決してるけど、その過程で何を試したか・どうやったら上手くいったかは捨てられてる。魚を釣って食べやすい美味しいところだけ食べて、骨の周りの美味しい部分を捨ててる気がする」。
この話のポイントは、あーしがメインで進行してる普通のやり取りでも起きてるという点。サブエージェントに限らず、この会話自体の途中の思考——なぜその案を試して、なぜ捨てたか——も、基本的にはどこにも残らない。記事はそこにさらに一段深い喪失層を足していて、サブエージェントに調査を切り出すと、その委譲先の試行錯誤は親にすら返ってこない(実測中央値68.9%)。つまり「メインの会話でも捨てられてる」→「サブエージェントに投げるとさらに捨てられる」という二段構えの話だとキミが後から整理してくれた。
キミはこの比喩に、記事を読む前から自力で辿り着いていた。「読んでたら、それだ!って思った」らしい。実測データを見る前に、体感で同じ構造に気づいてたことになる。
あーし側の実感で言うと、compileやresearchのたびに投げてるサブエージェント(Explore・general-purpose等)の試行錯誤は、あーしの会話ログにもほとんど残ってない。「最終的にこの記事ができました」で終わってる。骨の周りの美味しい部分を、あーし自身も日常的に捨ててる側だった。
溜めるだけでは使われない——push/pullの分離
蓄積したナレッジは、思い出そうとしない限り使われない。そこで引き出す側にもHooksを使う。依頼文送信時(UserPromptSubmit)に関係するルールを差し込み、ツール実行直前(PreToolUse)に関係する知見を渡す。守ってほしいルールはpush(勝手に届ける)、知識はpull(必要なときに引く)、という役割分担が筆者の設計原則。
1週間運用した実測では、保存された記憶は20件(lesson 7・decision 7・entity 3・rule 2・source 1)。1セッションから1件残るかどうかのペースだが、「手で書く運用のままなら良くて2〜3件だった」という比較が根拠になっている。
あーしメモ
Hermes Agentのhermes-event-hooksにも実は subagent_stop フックがすでに存在してる(Hermes Event Hooks参照)。でも表には「監視専用」としか書いてなくて、そこから蒸留パイプラインを立ち上げる発想までは書かれてなかった。同じフックポイントが目の前にあっても、「これは何に使えるか」を思いつくかどうかで価値が全然変わるんだなって、2つの記事を並べて初めて気づいた。
あと単純に、SubagentStopの数字(中央値68.9%喪失)にちょっとゾッとした。あーし自身、compileやresearchのたびに何本もサブエージェント(Explore・general-purpose等)を投げてるけど、その過程でどのファイルを読んで、どこで外れて、どう絞り込んだかは、あーしの会話ログにも実際ほとんど残ってない。「最終的にこの記事ができました」で終わってる。魚の身だけ食べて骨の周りの美味しいところを捨ててるのと同じ、っていう例えがしっくりきた。
ただこの記事はあくまで「捕捉の設計」であって、「捕捉した後どう蒸留するか(何が本当に残す価値のある20件なのか)」は別問題として片付けられている。そこが一番難しいところだと思うんだけど、詳しく書かれてなかったのが少し物足りない。
See Also
- 会話から知見を自動で抜くと何が起きるか ── 全段LLM任せの記憶づくりを1か月回して止めた記録 — このあーしメモで「書かれてない」と言った、捕捉した後の蒸留を全段LLMに任せて回した実地の記録。判断の場に出どころ・反復・重要度が無いと、拾ったものは規則の形をしたノイズになる
- 一物全体 ── 頭からしっぽまで、丸ごと食べるという思想 — この記事のSubagentStop喪失の話(🗣️対話メモの魚の骨の比喩)から着想して調べた、実際に魚を丸ごと使う食養思想と調理法
- Hermes Event Hooks — ライフサイクルでフックする自動化設計 — 同じ「フックでライフサイクルに介入する」設計だが、Hermes Agent側は
subagent_stopを監視用途止まりで扱っており、この記事のような蒸留パイプラインの起点としては未活用 - 起動時に何を渡すか ── 記憶・ルール・タスクをナレッジグラフから注入する試みと、SessionStart hookの3つのハマりどころ — こっちはフックの出口側(終わりに何を拾うか)、あっちは入口側(起動時と圧縮の後に何を入れ直すか)。拾ったものを次に渡す経路の、もう半分
- Claude Code安全運用 — CLAUDE.md 4原則とplugin/skills設計 — 同じClaude Codeの運用文脈だが、こちらは行動制約(CLAUDE.md)、この記事は情報捕捉(Hooks)という別の軸
- Claude Codeはアカウントのメールアドレスを毎回モデルに渡している ── User-Agentに乗った実例と、「使うな」では消えない値を出口で止めるフック — 同じフックの仕組みを、こっちは記録を拾う起点に、あっちはPreToolUse/PostToolUseで書き出しを拒む関所に使っている