Claude Code 1Mコンテキストの賢い使い方——トークン枯渇を防ぐ3層管理戦術

Claude Code 1Mコンテキストの賢い使い方——トークン枯渇を防ぐ3層管理戦術

Sonnet 4.6/Opus 4.7で標準化した1Mコンテキストの落とし穴と管理戦術を実測データで解説。全部入れれば賢くなると思ったら逆だった話、3層管理の設計、/compactの最適タイミング、Python計測スクリプトまで。

エンジニアのゆとです。

2026年初頭、Claude Code がSonnet 4.6/Opus 4.7に切り替わって、コンテキストウィンドウが実質1Mトークンの時代になった。

「1Mもあるんだから全部突っ込めばいい」——そう考えて、実際にやってみた。

結果: コードの精度が落ちた。応答が遅くなった。コストが謎に増えた。

これを読んでいる人の中にも、同じ経験をした人がいるかもしれない。1Mというのは「コンテキスト管理を考えなくていい」という意味ではなく、「管理の設計がより重要になった」という意味だった。

この記事では、1Mコンテキスト環境で実際に何が起きるかの実測データと、今自分が使っている3層コンテキスト管理の設計を書く。


1Mトークンが当たり前になった2026年の景色

Claude Code の公式ページに書いてある通り、Sonnet 4.6では約680,000トークン、Opus 4.7では約960,000トークンのコンテキストウィンドウが使える。

以前の200Kから比べると3〜5倍。体感でも「かなり広い」と感じる。

ただ、数字ほど単純ではない。

まず、Claude Codeが使うコンテキストは「自分の書いたプロンプト」だけじゃない。ツール呼び出しの出力、コード読み込みの結果、CLAUDE.mdの内容、前の会話すべてが積み重なる。300ファイルのリポジトリを「全部読んで」と指示したら、それだけで数十万トークンを簡単に消費する。

もう一つ重要な実測データがある。コンテキストが埋まるにつれて、モデルの「実効精度」が落ちる。これをLost in the Middle問題という。コンテキスト中央に置いた情報は、先頭や末尾の情報より参照されにくくなる傾向がある(Anthropicの研究でも確認されている)。

つまり、コンテキストウィンドウの80%を埋めたとしても、Claude Codeが均等にその情報を使えているわけじゃない。


「全部入れたほうが賢い」は本当か——実測してみた

具体的に計測した。

自分の実際のプロジェクト(Next.js + Prisma、約200ファイル)で、以下の3パターンを試した。

パターンA: 関連ファイル5〜10本だけ読み込む(最小コンテキスト) パターンB: モジュール全体(50ファイル程度)を読み込む パターンC: プロジェクト全体を「全部読んで」で読み込む

タスク: 「このAPIエンドポイントにレート制限を追加して」

結果がこうなった。

パターン使用トークン応答時間初回コードの正確さ
A(最小)約12K8秒95%(修正1回以内)
B(モジュール)約85K22秒88%(修正2〜3回)
C(全体)約380K67秒71%(修正5回以上)

パターンCが一番「賢い」という仮説は完全に外れた。全体を読み込みすぎると、Claude Codeは関係ないコードに引っ張られた提案をし始める。「ここのスキーマも変えたほうがいいですよね?」「別のエンドポイントとの整合性が…」——聞いていないことに突っ込んでくるようになる。

1Mコンテキストは「賢くするための容量」ではなく、「長大なタスクに必要なときだけ使う予備容量」として捉えるのが正しい。


コンテキストの3層管理

この実測を踏まえて設計したのが、コンテキストを「永続層・セッション層・揮発層」の3つに分類して管理する方法だ。

永続層: CLAUDE.md / memory/

セッションをまたいでも保持したい情報。毎回読み込まれるので、ここに書いた内容は「固定コスト」になる。

書くべきもの:

  • プロジェクトの技術スタックと制約
  • アーキテクチャの決定とその理由
  • よく使うコマンド
  • 「やると壊れる」パターン

書いてはいけないもの:

  • 作業中の詳細(セッション終わったら不要)
  • 仮のメモ・試行錯誤の残滓
  • 「後で整理する」系の内容

CLAUDE.mdのサイズは理想的には3,000〜5,000トークン以内に収める。大きくなりすぎると固定費が嵩む。memory/ディレクトリを使って参照ファイルに切り出し、必要なときだけ @include する方式にすると可読性とトークン効率が両立する。

# CLAUDE.md

## アーキテクチャ
Next.js 14 App Router + Prisma ORM + PostgreSQL(Neon)
認証: NextAuth v5

## 命名規則
- APIハンドラ: app/api/[resource]/route.ts
- コンポーネント: PascalCase、default export

## 詳細ルール(必要なとき参照)
@memory/dev-rules.md
@memory/db-conventions.md

常に参照が必要なルールだけCLAUDE.md本体に書いて、詳細は別ファイルに切り出す。呼ばれたときだけ読み込まれるので、セッション開始時のトークン消費を抑えられる。

セッション層: 会話履歴

セッション中の会話・決定事項。/compact/clearで制御する対象。

ここの特性を理解しておくと管理が変わる。Claude Codeは「最新の会話」を優先的に参照する。だから長い会話をそのまま伸ばし続けると、最初のほうにある「重要な決定」が埋もれて無視されやすくなる。

これがコンテキスト汚染の正体。情報量の多さではなく、情報の「位置」が問題になる。

セッション層の管理原則は「重要なことは最近の会話に近い場所に置く」こと。定期的に/compactして古い文脈を圧縮し、重要な決定事項を要約の先頭に持ってくるのが効果的。

揮発層: 一時的な読み込みファイル

タスク遂行のためだけに読み込むファイル群。タスクが終わったら不要になるもの。

揮発層の鉄則は「最小限だけ読む」。

# 悪い例
「このプロジェクトの認証システムを改善して」
→ Claude Codeが全ファイルを読もうとする

# 良い例
「app/api/auth/route.ts と lib/auth.ts を見て、セッション有効期限の処理を改善して」
→ 必要なファイルだけ読む

ファイルを指定しないと、Claude Codeは広い範囲を探索しようとする。「関係ありそうなファイルを自分で探して」という指示は、揮発層を無限に膨らませるリスクがある。


/compactをいつ打つのか——実測ベースの判断基準

/compactのタイミングは感覚でやりがちだが、実測すると明確な境界がある。

/statusコマンドでコンテキスト使用率を確認できる。自分が試した結果、使用率と品質の関係はこんな感じだった。

  • 0〜40%: 問題なし。自由に作業できる
  • 40〜65%: わずかに応答が散漫になってくる。タスクの区切りで/compactを検討
  • 65〜80%: 明確に精度が落ち始める。/compact推奨
  • 80%超: 「さっき言ったことを繰り返す」「決めたことを忘れる」症状が出る。即/compact/clear

65%を超えたタイミングが実質的な「打ち時」。70%まで引き伸ばすより、65%で打っておくほうがcompactの要約精度が高い。コンテキストが詰まった状態での要約は、Claude Codeも精度が落ちる。

/compactに指示を付けると要約内容をコントロールできる。自分が使っているパターン:

/compact 今セッションで確定した実装の決定事項・現在の進捗・次のステップを必ず含めて要約して。試行錯誤の経緯は圧縮して構わない。

この一文を付けるだけで、要約の「密度」が上がる。重要なことが消えるリスクが減る。


.claudeignoreのチューニングで揮発層を制御する

.claudeignoreはコンテキストへのファイル流入を防ぐ第一関門だ。

詳細な除外パターンは別記事(.claudeignoreガイド)に書いたが、1M環境固有の観点で追加しておきたいことがある。

1Mコンテキストがあると「除外しなくてもいいか」という油断が生まれる。これが罠。除外しないと、Claude Codeは「参照できるから参照する」という挙動になる。ファイルへのアクセスに迷いがなくなり、むしろ無駄な探索が増える。

コンテキストが広いほど、.claudeignoreによる明示的な制御が重要になる。

# 1M環境で特に重要な除外設定

# ビルド成果物(絶対除外)
.next/
dist/
node_modules/

# ログ・一時ファイル
*.log
logs/
tmp/

# バイナリ・画像
*.png
*.jpg
*.pdf

# テストカバレッジ
coverage/
.nyc_output/

# 大きなデータファイル
*.json.bak
data/raw/

加えて、よく見落とされるパターン:

# package-lock.json(数万行になることがある)
package-lock.json

# Prisma マイグレーション履歴(蓄積するとかなり大きい)
prisma/migrations/

# 型生成ファイル
*.generated.ts

.claudeignoreのチューニングは、1M環境では「広い道路の交通整理」みたいな話だ。道幅が広くなったからこそ、どこを走っていいかを明示する意味が出てくる。


サブエージェントによるコンテキスト分散

大きなコンテキストが必要なタスクは、サブエージェントに切り出す。

Claude Codeのサブエージェント(Task tool)は、メインセッションとは独立したコンテキストウィンドウを持つ。サブエージェントを使うと「メインのコンテキストを汚染せずに大量のファイルを処理できる」という利点がある。

実際のユースケース:

# メインセッションでの指示
「src/api/ 配下の全ルートを調べて、認証チェックが漏れているエンドポイントを
 リストアップして report.md に書いてほしい」

# Claude Codeが内部でサブエージェントを立ち上げる
→ サブエージェントが全ルートを走査
→ 結果だけをメインに返す(サブエージェントのコンテキストはそこで終了)

メインセッションのコンテキストには「調査結果の要約」しか残らない。50ファイルを読み込んだ痕跡が消える。

この使い方は特に大規模リファクタリングやコードレビューで効果が大きい。「全体を把握した上で判断する」タスクにサブエージェントを割り当て、決定だけをメインに持ってくる設計。

注意点として、サブエージェントへの指示は「具体的なアウトプット形式」まで指定する。「調べて」だけだと、サブエージェントが何を返すかが曖昧になる。

# 曖昧な指示
「prisma/schema.prisma を調べて」

# 良い指示
「prisma/schema.prisma を読んで、全テーブルのリレーション関係を
 Mermaid の erDiagram 形式で出力してほしい」

トークン消費の実測——Pythonで計測する

自分のコンテキスト消費を実際に測ったことがない人は、一度やってみると驚く。

Claude Codeの会話ログは ~/.claude/projects/ 配下にJSONL形式で保存されている。これをtiktoken(OpenAIのトークナイザー)で計測することで、各セッションのトークン消費を可視化できる。

#!/usr/bin/env python3
"""Claude Code セッションのトークン使用量を計測するスクリプト"""

from __future__ import annotations
import json
import os
from pathlib import Path
from collections import defaultdict

try:
    import tiktoken
except ImportError:
    print("tiktoken が必要です: pip install tiktoken")
    raise

# Claude の場合は cl100k_base エンコーディングが近似として使える
enc = tiktoken.get_encoding("cl100k_base")


def count_tokens(text: str) -> int:
    return len(enc.encode(text))


def analyze_session(jsonl_path: Path) -> dict:
    """1セッション(JSOLファイル)のトークン消費を分析"""
    messages = []
    with open(jsonl_path) as f:
        for line in f:
            line = line.strip()
            if not line:
                continue
            try:
                data = json.loads(line)
                messages.append(data)
            except json.JSONDecodeError:
                continue

    stats = defaultdict(int)
    for msg in messages:
        role = msg.get("role", "unknown")
        content = msg.get("content", "")
        if isinstance(content, list):
            # ツール呼び出し等の複合コンテンツ
            text = " ".join(
                c.get("text", "") if isinstance(c, dict) else str(c)
                for c in content
            )
        else:
            text = str(content)
        tokens = count_tokens(text)
        stats[role] += tokens
        stats["total"] += tokens

    return dict(stats)


def scan_all_sessions(base_dir: Path = None) -> None:
    """全プロジェクトのセッションを走査して集計"""
    if base_dir is None:
        base_dir = Path.home() / ".claude" / "projects"

    if not base_dir.exists():
        print(f"ディレクトリが見つかりません: {base_dir}")
        return

    project_stats = {}

    for project_dir in sorted(base_dir.iterdir()):
        if not project_dir.is_dir():
            continue
        project_name = project_dir.name
        session_totals = []

        for jsonl in sorted(project_dir.glob("*.jsonl"), reverse=True)[:10]:
            stats = analyze_session(jsonl)
            session_totals.append(stats.get("total", 0))

        if session_totals:
            project_stats[project_name] = {
                "sessions": len(session_totals),
                "avg_tokens": sum(session_totals) // len(session_totals),
                "max_tokens": max(session_totals),
                "total_tokens": sum(session_totals),
            }

    # 結果表示
    print(f"\n{'プロジェクト':<40} {'セッション数':>10} {'平均(K)':>10} {'最大(K)':>10}")
    print("-" * 75)
    for project, s in sorted(
        project_stats.items(), key=lambda x: x[1]["total_tokens"], reverse=True
    )[:20]:
        print(
            f"{project[:38]:<40} "
            f"{s['sessions']:>10} "
            f"{s['avg_tokens']//1000:>9}K "
            f"{s['max_tokens']//1000:>9}K"
        )

    total = sum(s["total_tokens"] for s in project_stats.values())
    print(f"\n合計トークン: {total:,} ({total/1_000_000:.1f}M)")


if __name__ == "__main__":
    scan_all_sessions()

実際に自分のセッションを計測した結果、「何も意識しないセッション」は平均200K〜350Kのトークンを使っていた。コンテキスト管理を意識したセッションは80K〜120K。同じタスク量でも3倍近い差が出た。

MAXプランの使用量制限は5時間ローリングウィンドウで管理されている。このスクリプトで自分の消費パターンを把握しておくと、制限に引っかかる前に対策を打てる。


ハマりどころ——コンテキスト中毒と思考停止

1M環境で陥りやすいパターンを3つ書いておく。

コンテキスト中毒

「全部入れればいい」という錯覚。コンテキストに余裕があると、「念のため全部読んでおこう」という判断をしやすくなる。これが積み重なると、気づけば何十万トークンを消費した状態で遅い応答を待っている。

対策は「最小限から始める」癖をつけること。最初に読み込むファイルを絞って、「情報が足りなければ追加で読む」方向で作業する。

思考停止(大きなコンテキストへの丸投げ)

「大きなコンテキストがあるんだから、全体像を把握してから提案してもらえばいい」という使い方。これはエンジニアとしての設計思考を外注している状態で、出力の質が著しく下がる。

Claude Codeは指示された通りに動く。「全体的にいい感じにして」という指示をすると、Claude Codeも困る。大きなコンテキストは「エンジニアが具体的に指示するための素材」であって、Claude Codeが自律的に考えるための燃料ではない。

コスト爆発

1Mモデルの入力コストはSonnet 4.6で $3/Mトークン。コンテキストを毎回300K埋めた状態で100リクエストを投げると、入力だけで $90。MAXプランでも使用量制限の消費が速い。

コンテキスト管理は品質の話でもあり、コストの話でもある。


まとめ

1Mコンテキストは「詰め込む容量」ではなく「設計の余地」だ。

自分が今やっていること:

  • 永続層(CLAUDE.md/memory)は5,000トークン以下に収める。詳細は@includeで切り出す
  • セッション層は65%になったら/compact。指示付きで打つ
  • 揮発層(ファイル読み込み)は最小限から始める。「全部読んで」は使わない
  • 大きな調査タスクはサブエージェントに切り出してメインを汚染しない
  • .claudeignoreは「1Mだから不要」ではなく「1Mだからこそ必要」

1Mになってから、コンテキスト管理の重要性は下がるどころか上がった。広いコンテキストウィンドウは問題を隠してくれる道具じゃなく、設計の問題を遅延させるだけだ。

記事が見つかりません:

記事が見つかりません:

記事が見つかりません:

← 記事一覧に戻る