Azit Inc.

to Developer

コンテキストウィンドウの限界を解決するClaude Code Hooks活用術

    2025.12.12

    Iwase Takeshi

    はじめに

    株式会社Azitでエンジニアリングマネージャーをしている岩瀬です。
    前回の記事「需給予測モデル開発で実践するAI駆動開発手法」では、バイブコーディングからAI駆動開発への移行と、体系的な開発テンプレートについて紹介しました。今回は、その開発フローを実際に回す中で遭遇した問題と、その解決策について紹介します。
    AI駆動開発を実践していると、避けて通れない問題があります。それはコンテキストウィンドウの限界です。長時間のセッションで作業を続けていると、AIが過去の経緯を忘れ、同じミスを繰り返すようになります。
    この記事では、Claude Code Hooksを活用して作業記録を自動保存する仕組みを構築した経験を共有します。なお、この機能自体もAI駆動開発で実装しました。コーディングは全てAIが担当し、人間はアイデア出しと指示に徹しています。

    遭遇した問題:コンテキストウィンドウの限界

    LLMの本質的な制約

    LLM(大規模言語モデル)は本質的にステートレスです。つまり、前回の会話を記憶していません。会話を継続するには、毎回全ての情報をコンテキストウィンドウに含める必要があります。
    しかし、このコンテキストウィンドウには上限があります。Claude Codeの場合、200Kトークン(約15万語相当)が上限です。長時間の開発作業では、ファイルの内容、コードの変更履歴、やり取りがどんどん蓄積され、この上限に達してしまいます。
    上限に近づくと、Claude Codeは自動的にコンテキスト圧縮を実行します。過去の会話履歴を要約し、トークン数を削減するのですが、この過程で重要な情報が失われることがあります。

    世間での問題認識

    この問題は私だけが経験しているものではありません。技術ブログやSNSなど、様々な場所で開発者から報告されています。

    • 圧縮後にClaude Codeの性能が低下し、どのファイルを見ていたか忘れて再読み込みが必要になる
    • セッション中に数回の圧縮を経ると、AIが逆に生産性を下げるようになる
    • 30分前に与えた指示を完全に忘れ、設定を何度も聞いてくる
    • 複雑なタスクの途中で圧縮が発生すると、作業の継続が困難になる

    私が体験した問題

    私の場合、特に厄介だったのは先祖返りの問題です。
    コードを修正した理由をAIが忘れてしまい、以前と同じバグを再び作り込んでしまうことがありました。「なぜこの実装にしたのか」という経緯が失われると、同じ試行錯誤を繰り返すことになります。
    これはAI駆動開発において深刻な問題です。人間がプロジェクトマネジメントに徹し、AIが実装を担当する体制では、AIが過去の判断を忘れると、人間が全ての経緯を記憶・管理しなければならなくなります。それでは、AI駆動開発のメリットが大きく損なわれてしまいます。

    解決策:Claude Code Hooksによる作業記録自動作成

    Claude Code Hooksとは

    Claude Code Hooksは、特定のイベント発生時に任意のスクリプトを実行できる機能です。以下のようなイベントをトリガーにできます:

    イベント 発生タイミング
    PreCompact コンテキスト圧縮の直前
    PostCompact コンテキスト圧縮の直後
    SessionEnd セッション終了時
    PreToolUse ツール使用前
    PostToolUse ツール使用後

    この機能を活用し、コンテキスト圧縮の直前セッション終了時に作業記録を自動生成する仕組みを構築しました。

    実装アーキテクチャ

    最終的に採用したアーキテクチャは以下の通りです:

    [PreCompact/SessionEnd イベント]
        ↓
    [suggest-claude-md-hook.sh](フックスクリプト)
        ↓
    [nohup claude --dangerously-skip-permissions --print "/summarize-worklog {transcript_path}" &]
        ↓
    [バックグラウンドで作業記録生成]
        ↓
    [docs/worklog/worklog.md に自動追記]

    ポイントは以下の3点です:

    1. スラッシュコマンド方式:フック内で直接APIを叩くのではなく、新しいClaude Codeセッションを起動してスラッシュコマンドを実行
    2. バックグラウンド実行nohup ... &でバックグラウンド実行し、フック処理を即座に完了させる
    3. 権限スキップ-dangerously-skip-permissionsオプションで、バックグラウンド実行時の対話的権限確認をスキップ

    詳細な実装手順
    1. ディレクトリ構造

    .claude/
    ├── commands/
    │   └── summarize-worklog.md    # 作業記録生成スラッシュコマンド
    ├── hooks/
    │   ├── hooks.json              # フック設定
    │   ├── common.sh               # 共通ヘルパ関数
    │   └── suggest-claude-md-hook.sh  # メインフックスクリプト
    ├── logs/                       # フック実行ログ
    ├── proposals/                  # 提案ファイル格納
    └── transcripts/                # トランスクリプトバックアップ
    
    

    2. フック設定(hooks.json)

    .claude/hooks/hooks.jsonを作成します:

    {
      "PreCompact": [
        ".claude/hooks/suggest-claude-md-hook.sh"
      ],
      "SessionEnd": [
        ".claude/hooks/suggest-claude-md-hook.sh"
      ]
    }
    
    

    3. 共通ヘルパ関数(common.sh

    .claude/hooks/common.shを作成します:

    #!/bin/bash
    
    # 公式仕様準拠: Claude Code Hooks共通機能
    # 再帰発火の無限ループ防止
    if [ "${SUGGEST_CLAUDE_MD_RUNNING:-}" = "1" ]; then
        echo "[hook] already running; skip" >&2
        exit 0
    fi
    export SUGGEST_CLAUDE_MD_RUNNING=1
    
    # プロジェクトディレクトリの検出
    PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
    
    # CLAUDE.mdファイルの存在でプロジェクトルートを検証
    if [ ! -f "$PROJECT_DIR/CLAUDE.md" ]; then
        search_dir="$(pwd)"
        while [ "$search_dir" != "/" ] && [ ! -f "$search_dir/CLAUDE.md" ]; do
            search_dir="$(dirname "$search_dir")"
        done
        if [ -f "$search_dir/CLAUDE.md" ]; then
            PROJECT_DIR="$search_dir"
        fi
    fi
    
    # ディレクトリ構造の確保
    PROPOSALS_DIR="$PROJECT_DIR/.claude/proposals"
    WORKLOG_BASE_DIR="$PROJECT_DIR/docs/worklog"
    LOG_DIR="$PROJECT_DIR/.claude/logs"
    
    mkdir -p "$PROPOSALS_DIR" "$WORKLOG_BASE_DIR" "$LOG_DIR"
    
    # 作業記録ファイル
    WORKLOG_FILE="$WORKLOG_BASE_DIR/worklog.md"
    
    # ワークログファイルを初期化(存在しない場合のみ)
    if [ ! -f "$WORKLOG_FILE" ]; then
        cat > "$WORKLOG_FILE" << EOF
    # プロジェクト作業記録
    
    **作成日**: $(date +%Y-%m-%d)
    
    ## 作業記録
    
    EOF
    fi
    
    # 統一ログ機能
    HOOK_LOG="$LOG_DIR/hook_execution_$(date +%Y%m%d).log"
    
    log_info() {
        local msg="$1"
        local timestamp="$(date '+%Y-%m-%d %H:%M:%S')"
        echo "[CLAUDE-HOOK] $msg" >&2
        printf '{"timestamp":"%s","level":"INFO","message":"%s","pid":%d}\\\\n' \\\\
            "$timestamp" "$msg" "$$" >> "$HOOK_LOG"
    }
    
    log_error() {
        local msg="$1"
        local timestamp="$(date '+%Y-%m-%d %H:%M:%S')"
        echo "[CLAUDE-HOOK ERROR] $msg" >&2
        printf '{"timestamp":"%s","level":"ERROR","message":"%s","pid":%d}\\\\n' \\\\
            "$timestamp" "$msg" "$$" >> "$HOOK_LOG"
    }
    
    

    4. メインフックスクリプト(suggest-claude-md-hook.sh

    .claude/hooks/suggest-claude-md-hook.shを作成します(主要部分を抜粋):

    #!/bin/bash
    
    SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
    source "$SCRIPT_DIR/common.sh"
    
    log_info "Hook started: $(basename "$0")"
    
    # JSONペイロード処理
    payload="$(cat)"
    log_info "Hook payload received"
    
    # Python でJSON解析
    parse_official_payload() {
        local payload="$1"
        python3 -c "
    import sys, json
    payload = '''$1'''
    try:
        data = json.loads(payload)
        print('TRANSCRIPT_PATH:' + data.get('transcript_path', ''))
        print('HOOK_EVENT_NAME:' + data.get('hook_event_name', 'unknown'))
        print('SESSION_ID:' + data.get('session_id', ''))
        print('CWD:' + data.get('cwd', ''))
        print('REASON:' + data.get('reason', ''))
    except Exception as e:
        print('TRANSCRIPT_PATH:')
        print('HOOK_EVENT_NAME:unknown')
    "
    }
    
    PARSE_RESULT="$(parse_official_payload "$payload")"
    TRANSCRIPT_PATH="$(echo "$PARSE_RESULT" | grep '^TRANSCRIPT_PATH:' | cut -d: -f2-)"
    HOOK_EVENT_NAME="$(echo "$PARSE_RESULT" | grep '^HOOK_EVENT_NAME:' | cut -d: -f2-)"
    CWD="$(echo "$PARSE_RESULT" | grep '^CWD:' | cut -d: -f2-)"
    
    # CWDを使用してプロジェクトディレクトリを設定
    if [ -n "$CWD" ]; then
        export CLAUDE_PROJECT_DIR="$CWD"
        cd "$CWD" 2>/dev/null || true
    fi
    
    # トランスクリプトファイルが存在する場合、スラッシュコマンドを実行
    if [ -n "$TRANSCRIPT_PATH" ] && [ -f "$TRANSCRIPT_PATH" ]; then
        log_info "Starting Claude Code with /summarize-worklog command (background)..."
    
        SUMMARIZE_LOG="/tmp/summarize-worklog-$(date +%Y%m%d-%H%M%S)-$$.log"
    
        # バックグラウンドでClaude Codeを起動
        nohup claude --dangerously-skip-permissions --print "/summarize-worklog $TRANSCRIPT_PATH" > "$SUMMARIZE_LOG" 2>&1 &
        CLAUDE_PID=$!
    
        log_info "Claude Code started with PID: $CLAUDE_PID"
        log_info "Output log: $SUMMARIZE_LOG"
    else
        log_error "No transcript file available for summarization"
    fi
    
    echo "✅ Session recorded: worklog generation in progress"
    echo "📊 Hook Details: Event=$HOOK_EVENT_NAME"
    
    

    5. スラッシュコマンド(summarize-worklog.md

    .claude/commands/summarize-worklog.mdを作成します:

    # 作業記録要約コマンド
    
    トランスクリプトファイルを分析して作業記録を生成し、worklog.mdに追記してください。
    
    ## 引数
    - トランスクリプトファイルのパス: `$ARGUMENTS`
    
    ## 実行手順
    
    1. **トランスクリプトファイルの読み込み**
       - 指定されたJSONLファイルを読み込む
       - ファイルが存在しない場合はエラーを報告
    
    2. **会話内容の分析**
       - JSONLから会話メッセージを抽出
       - ユーザーとアシスタントのやり取りを把握
       - 実施された作業、決定事項、継続タスクを特定
    
    3. **作業記録の生成**
       以下の形式で作業記録を生成:
    
       ```markdown
       ## YYYY-MM-DD HH:MM - HH:MM - {作業タイトル}
    
       ### セッション概要
       - **作業内容**: {主要な作業の要約}
    
       ### 実施作業
       - {詳細な作業内容}
       - {決定事項と根拠}
    
       ### 重要判断・方針決定
       - {技術判断とその根拠}
    
       ### 継続タスク
       - {未完了の作業項目}
    
    
    • worklog.mdへの追記docs/worklog/worklog.md に生成した作業記録を追記
    • 追記が完了したらユーザーに報告

    開発の試行錯誤

    この機能を開発する過程で、いくつかの壁にぶつかりました。

    Phase 1: 初期実装と問題発生

    最初のアプローチは、フック内で直接claude -p(パイプモード)を使ってAPIを叩く方式でした。

    # 初期の実装(問題あり)
    echo "$PROMPT" | claude -p --output-format json
    
    

    しかし、以下の問題が発生しました:

    • jqコマンド依存:JSON解析にjqを使っていたが、環境によってはインストールされていない
    • bash互換性問題:一部の構文がmacOS標準のbashで動作しない
    • 文字エンコーディングエラー:日本語プロンプトでエラーが発生

    Phase 2: 大規模トランスクリプトでのタイムアウト

    上記の問題を解決した後、別の問題が発生しました。長時間のセッション後に生成されたトランスクリプトが909KBにも達し、APIがタイムアウトしてしまったのです。
    この時点で「今のやり方では実現できない」という実感がありました。
    解決方法を探す中で、スラッシュコマンド方式の存在を知りました。同時にAgent Skills(サブエージェント)という選択肢も検討しましたが、以下の理由からスラッシュコマンドを採用しました:

    • Agent Skills:自動トリガーされる機能で、条件に合致すると自動的にコンテキストに読み込まれる。便利だが、今回の用途では不要なタイミングでもコンテキストを消費する可能性がある
    • スラッシュコマンド:明示的に呼び出す方式で、使用しない限りコンテキストを消費しない

    作業記録生成は特定のイベント(圧縮前・セッション終了時)でのみ実行したい処理です。Agent Skillsの自動トリガーは必要なく、むしろコンテキストウィンドウの限界を解決するための機能が、逆にコンテキストを圧迫しては本末転倒です。
    この判断が、スラッシュコマンド方式への転換を後押ししました。

    Phase 3: スラッシュコマンド方式への転換

    スラッシュコマンド方式に切り替えることで、大規模トランスクリプトの問題を解決できました。
    しかし、今度は権限問題が発生しました。バックグラウンドで起動したClaude Codeがファイル読み込み権限の確認でブロックされ、処理が停止してしまいます。
    最終的に--dangerously-skip-permissionsオプションを追加することで解決しました。名前に"dangerously"とありますが、自プロジェクトの作業記録生成という限定的な用途では、リスクは低いと判断しました。

    ドキュメントにない仕様との格闘

    開発中、ドキュメントに記載されていない仕様にも苦しめられました。
    例えば、PreCompactフックのmatcher設定では、"*"(ワイルドカード)が効かず、"manual|auto"と明示的に指定する必要がありました。
    これらの問題は、AIに原因調査から修正方法まで考えさせることで解決しました。AI駆動開発の課題をAI駆動開発で解決する、という構図です。

    実際に生成された作業記録

    以下は、本機能によって実際に自動生成された作業記録の一部です。

    ## 2025-11-21 - 作業記録自動作成機能の問題分析と改善方針決定
    
    ### セッション概要
    - **作業内容**: PreCompactフックのmatcher問題修正、大規模トランスクリプト問題の分析
    
    ### 実施作業
    
    #### 1. PreCompactフックのmatcher問題修正
    - **問題**: `/compact`実行後にPreCompactフックが動作しない
    - **原因特定**: PreCompact/PostCompactイベントは特殊なmatcher仕様
      - SessionEnd等: `"*"`(ワイルドカード)で動作
      - PreCompact: `"manual"` または `"auto"` **のみ**を受け付ける
    - **修正内容**: `.claude/settings.json`のPreCompactのmatcherを`"*"` → `"manual|auto"`に変更
    - **結果**: 修正後の動作検証成功
    
    #### 2. 大規模トランスクリプト問題の分析
    - **問題発生**: 自動圧縮でフックが処理途中で停止
    - **ログ分析結果**:
      - トランスクリプトサイズ: **909,127 bytes**(通常の25-60倍)
      - Claude API呼び出し: 開始後に応答なし(タイムアウト)
    - **原因**: 大規模トランスクリプトをそのままAPIに送信し、タイムアウトが発生
    
    ### 重要判断・方針決定
    - **PreCompact/PostCompactの特殊仕様**: matcherは`"manual|auto"`が必要(`"*"`は不可)
    - **API呼び出し方式の変更**: `claude -p` → Claude Code新規起動方式へ移行決定
    
    ### 質疑応答・ナレッジ創出
    **Q**: なぜClaude APIを直接呼び出すとタイムアウトするのか?
    **A**: `claude -p`(パイプモード)は大きなプロンプトに対する制限があり、909KBのトランスクリプトはAPI側でタイムアウトに引っかかる
    
    

    このように、実施した作業、発生した問題とその原因、技術的な判断とその根拠が自動的に記録されます。

    導入効果と副次的な発見

    当初の目的:コンテキスト消失への備え

    本機能の導入効果については、まだ実感としては薄い段階です。これから実際の開発で使い込んでいく中で、効果を検証していく予定です。

    副次的な発見:ナレッジ共有への応用

    実は、この機能を開発する過程で、予想外の発見がありました。
    実際に取得できた作業記録を見たとき、「これ、ナレッジ共有に使えるじゃん」と気づいたのです。
    元々、モデル開発のナレッジをどう共有するかという課題感を持っていました。成功例や失敗例を組織的に蓄積・活用したいと考えていたのですが、具体的な方法が見えていませんでした。
    作業記録には、以下のような情報が含まれています:

    • 実施した作業の詳細
    • 技術判断とその根拠
    • 発生した問題と解決方法

    これらの情報が自動的に蓄積されるなら、複数プロジェクトの知見を横断的に検索・活用できる可能性があります。

    将来の展望:モデル開発エージェントへ

    ナレッジ共有の構想

    私たちは今後、複数のモデル開発プロジェクトが開始時期もバラバラに、並行して走る状況を想定しています。その際、AIが作業記録を横断検索して最新の情報を取得できるようになると考えています。
    具体的には、以下のアプローチを検討しています:
    1. GitHub MCP方式
    作業記録をGit管理し、GitHubにプッシュした上で、GitHub公式MCPサーバーを接続する方式です。
    この方式の最大の利点は、作業記録だけでなく、関連するソースコードや設計ドキュメントも同時に取得できることです。GitHub MCPサーバーはリポジトリ内のファイル読み取り、コード検索、コミット分析などの機能を提供しており、AIが「この作業記録に関連するコードを見せて」といった形で情報を取得できます。
    追加のインフラ構築が不要で、既存のGit/GitHubワークフローに自然に統合できる点も魅力です。
    2. 専用MCPサーバ方式
    Model Context Protocol(MCP)は、LLMが外部データソースにアクセスするための標準プロトコルです。作業記録を専用のMCPサーバとして公開することで、Claude Codeや他のMCP対応ツールから直接検索・参照できるようになります。
    GitHub方式と比べて、より柔軟な検索ロジックやフィルタリングを実装できる可能性があります。
    3. RAG方式
    Retrieval-Augmented Generation(RAG)は、外部知識ベースから関連情報を検索してLLMに提供する手法です。作業記録をベクトルデータベースに格納し、質問に関連する記録を自動で取得してコンテキストに追加します。
    セマンティック検索により、キーワードが完全一致しなくても関連する記録を取得できる点が強みです。

    どの方式を採用するかは、今後の検証で決めていく予定です。いずれの方式でも、AIが能動的にナレッジを検索・活用できるようになります。これにより:

    • トライアンドエラーの精度向上:成功例や失敗例を簡単に取得できる
    • 人に依存しない知識共有:発信の得手不得手や、共有要否の判断によるブレをなくす
    • 外注時のナレッジ回収:仮にモデル開発を外注しても、ナレッジを組織に蓄積できる

    目指す成果

    まず実現したいことは、モデル開発の効率化による提供スピードの短縮と費用の圧縮です。
    また、ジュニアエンジニアの早期戦力化も重要な目標です。過去の作業記録から成功パターンや失敗パターンをAIが自動で参照できるようになれば、経験の浅いエンジニアでも、ベテランの知見を活用しながら開発を進められます。「入社してすぐに実戦投入」というハードルを大きく下げられると考えています。
    その先に見据えているものは、モデル開発エージェントへの発展です。作業記録から学習したナレッジを活用し、より自律的にモデル開発を進められるAIエージェントを構築したいと考えています。
    実現時期としては、まだ構想段階ですが、2026年初頭には取りかかりたいと考えています。その前に、作業記録の蓄積と適正化を進める必要があります。

    おわりに

    AI駆動開発を実践する中で、コンテキストウィンドウの限界という壁にぶつかりました。しかし、その課題自体もAI駆動開発で解決できることがわかりました。
    今回紹介した作業記録自動作成機能は、まだ発展途上です。これから実際の開発で使い込みながら、改善を続けていく予定です。
    AIツールは急速に進化しています。今回紹介した方法も、将来的にはClaude Code自体の機能として取り込まれるかもしれません。しかし、現時点でできることを試行錯誤しながら形にしていくプロセス自体が、急速に進化するAIと協働する面白さだと感じています。
    AIを活用した開発に興味のある方、一緒に試行錯誤しながら新しい可能性を模索していきませんか?Azitでは、物流・SCM領域の課題解決に取り組む仲間を募集しています。
    最後まで読んでいただき、ありがとうございました。

    この記事をシェアSHARE

    この記事に関連する求人情報

    • AIデータサイエンティスト

    この記事をシェアSHARE

    MORE ARTICLES