/note/tech

【無料】ドキュメントを書くな、会話を拾え

要約:

■ 1. 会話ログの価値と取りこぼし

  • AIとの会話時間の増大:
    • AIを使う人ほど、人と話すよりAIと話している時間のほうが長くなる
  • 会話に残る判断の履歴:
    • なぜその方針にしたのか、どの案を捨てたのか、何を試して駄目だったのかが会話の中に全部残っている
  • 最後に残るのは成果物だけ:
    • わたしたちが最後に残すのは、できあがったコードと短くまとめたドキュメントだけ
    • 会話そのものはセッションを閉じたら二度と開かれない
  • 取りこぼしの規模:
    • 手元で数えたところ、直近1か月の会話ログは1000本以上あった
    • ここで取りこぼしている量は想像以上である

■ 2. 手で書く前提の放棄

  • 事後のドキュメント化は続かない:
    • 大事な判断はあとでドキュメントに書き起こせばよいと毎回思っていたが、書いたことは一度もない
    • 作業が終わった直後は、いちばん書きたくないタイミングである
  • 自動収集への移行:
    • こちらが書くのをやめ、会話が終わったことを検知してセッションそのものを勝手に拾わせる
  • Hooksの役割:
    • ClaudeCodeのHooksは、特定のタイミングで外部コマンドを自動実行してくれる仕組み
    • 残る問題は、どのタイミングに仕込むかである

■ 3. 仕込み場所の4候補

  • SessionEnd:
    • セッションが終わったときに発火する
    • 会話ログ(transcript)のパスが渡ってくるので、会話全体をまるごと後処理に回せる
  • PreCompact:
    • コンテキストが要約に畳まれる直前に発火し、長い議論ほど必ず通る
    • 畳まれたあとの要約からは、捨てられた案や試して駄目だったことが消えている
    • その前に拾えるのが効く
  • SubagentStop:
    • サブエージェントが終わったところで発火する
    • 実はここがいちばん捨てている
  • Stop:
    • 応答が終わるたびに発火する
    • ターン単位なので細かいが、そのぶん取りこぼしがない
  • 粒度と発火頻度の関係:
    • 上に行くほど1回の情報量が多く、下に行くほど発火が頻繁になる
    • 全部入れる必要はなく、どれか1つを選ぶならSessionEndからでよい

■ 4. SessionEndを起点にする理由

  • 区切りとしての自然さ:
    • 1セッションが1つのまとまりなので、区切りとして自然である
  • 想定以上の発火頻度:
    • セッションの終了だけでなく、/clear でも発火する
    • 作業を切り替えるたびに /clear している人なら、1日に何度も通っている
  • 設定の簡潔さ:
    • hooksのSessionEndにtype: commandでスクリプトを登録するだけでよい
    • async: true を付けているのは、閉じる操作を待たせないためである

■ 5. 終了時の処理はキュー投入のみ

  • 終了時の知見抽出は却下:
    • 当初はSessionEndで会話ログを読み込み、その場で知見を抽出させるつもりだったがやめた
    • 素直に考えるとそうなるが、会話ログが重すぎる
  • 会話ログの実測サイズ:
    • 1セッションあたり平均で約1MB、大きいものは1本で50MBを超えていた
  • 遅い仕組みは使われない:
    • 終了時に読ませると、ClaudeCodeを閉じるたびに待たされることになる
    • 閉じるのが遅い仕組みは、確実に使わなくなる
  • キューに積むだけの設計:
    • SessionEndではsessionId、host、transcriptのパス、cwd、reason、endedAtを書き出すだけにした
    • 会話ログ本体はコピーせず、どこにあるかといつ終わったかだけ控えて即終了する
  • 重い処理の後回し:
    • 重い処理はあとからまとめて回す
    • 処理済みのセッションIDを控えておけば、同じ会話を二度読ませることもない
  • 収集と読解の分離:
    • 取りこぼさないための仕組みと、中身を読む仕組みは、分けたほうがうまくいく
  • 後処理も自動化:
    • あとから回す工程も手では回さず、自作のジョブ管理アプリに登録して毎朝の定期実行に任せている
    • 手動の工程がひとつでも残ると、そこから確実に途絶える

■ 6. SubagentStopが最大の取りこぼし

  • サブエージェントの仕組み:
    • メインの会話(親)から調査を切り出し、別の文脈で動かす仕組み
    • 親に返ってくるのは最終テキストだけ
  • 親に残らない過程:
    • 何十回もファイルを読み、grepし、当たりを外して絞り込んだ過程は、親の会話ログに一行も残らない
  • 実測した消失率:
    • サブエージェントの会話ログ30本で、全体の文章量と親に返した最終テキストの量を比べた
    • 中央値で68.9%が親に渡らずに消えていた
    • 少ないものでも29%、多いものは93%であった
  • 量的な比重:
    • 手元の会話ログ1091本のうち581本がサブエージェント側で、容量では全体の約半分を占めていた
    • ログ全体の半分が、この読み返されない側にある
  • SubagentStopの利点:
    • サブエージェント自身の会話ログのパスと、親に返した最終テキストの両方が渡ってくる
    • 返した結論とそこに至る全過程がセットで手に入るのは、ここだけである
  • 絞り込みと導入順:
    • matcherでエージェントの種類を絞り込めるので、ExploreやPlanなど調査系だけ拾うこともできる
    • 粒度が細かいぶん発火は多くなるので、SessionEndを回してから足した

■ 7. 1週間運用の結果

  • 保存された記憶は20件:
    • 最初の1週間で、記憶として保存されたのは20件であった
  • 20件の内訳:
    • やってみて分かったこと(lesson)が7件、決めたこととその理由(decision)が7件
    • プロジェクトや道具などの実体(entity)が3件、以後守るルール(rule)が2件、外部の参照先(source)が1件
  • 1セッションあたりの歩留まり:
    • 1セッションから1件残るかどうかである
  • 件数の妥当性:
    • 会話の大半はその場の作業のやり取りで、読み返す価値はない
    • 残す価値があったのがこの20件だった、それだけの話である
  • 手では書かれない20件:
    • むしろ大事なのは、その20件が手では絶対に書かれなかった20件だという点
    • 手で書く運用のままなら、良くて2、3件だったはずである
  • 継続の成果:
    • これを続けた結果、いまはかなりの数がナレッジとして蓄えられている

■ 8. 引き出す側のHooks

  • 溜めるだけでは使われない:
    • 溜めた知識は、こちらが思い出そうとしない限り使われない
    • 前に決めたと気づけるなら、そもそも記録は要らない
  • UserPromptSubmit:
    • 依頼文を送った瞬間に、関係するルールを差し込む
  • PreToolUse:
    • コマンド実行やファイル編集の直前に、その作業に関係する知見を渡す
  • 実際の発動例:
    • この記事を書いている最中にも「読者未知の内輪議論を既知前提で語らない」という過去の知見が差し込まれた
    • こちらからは何も呼び出していない
  • pushとpullの使い分け:
    • 守ってほしいルールはpush(勝手に届ける)、知識はpull(必要なときに引く)
  • 2つで1セット:
    • 溜めるHooksと引き出すHooks、この2つでやっと1セットになる

■ 9. 結論

  • 会話は、閉じる瞬間に拾う