11 現場の知見 — 使ってみないとわからないこと
実際にClaude Codeを使い込んで得た実践知。公式ドキュメントには書いていない「暗黙知」をまとめる。
📑 セクション一覧(目次)
- A. コンテキスト経済学 — トークン消費の実態・雪だるま式コスト
- B. 落とし穴(ハマりポイント) — 2ファイル問題・MCP復活・フックブロック等
- C. 効率テクニック — compact・BGエージェント・model切替
- D. よくある誤解 — コスト/メモリ/clear/compact等の誤解
- E. Cron管理の注意点 — 7日失効・膨張バグ・スクリプト化
- F. APIスクリプト共通基盤 — api_base.py・プロバイダ追加手順
- G. コード行数の正確な計測 — 自動生成除外・3層証明
- H. 並行セッションの競合回避 — active-sessionsボード運用
A. コンテキスト経済学
トークンがどう消費されているかを理解することが、効率的な使い方の第一歩。
入力:出力 = 13:1 は正常
ChatGPT等のチャットAIでは「短い質問 → 長い回答」なので出力の方が多くなりがち。しかしClaude Codeでは逆転する。
理由: 毎ターンで「システムプロンプト + MCP定義 + メモリ + 過去の会話すべて + ツール結果」を丸ごと再送しているため。
ターン1: [システム + MCP + メモリ] = ~40K入力 → 回答 = ~1K出力
ターン5: [システム + MCP + メモリ + 過去4回] = ~60K入力 → 回答 = ~1K出力
ターン10: [システム + MCP + メモリ + 過去9回] = ~100K入力 → 回答 = ~1K出力
→ セッションが長くなるほど、1ターンあたりのコストが雪だるま式に増加。
200Kの実質使える量は〜160K
コンテキストウィンドウは最大200Kトークンだが、ユーザーが自由に使えるのは約160K。
| 固定消費(毎ターン) | 推定サイズ |
|---|---|
| MCPツール定義(6サーバー83ツール) | ~21K |
| システムプロンプト(CLAUDE.md等) | ~15K |
| Auto Memory(MEMORY.md + 各ファイル) | ~5〜10K |
| その他システム指示 | ~5K |
| 合計 | ~40K |
→ 会話を始める前に既に20%消費済み。長いセッションではすぐに上限に近づく。
MCP定義だけで毎ターン消費
MCPツールは呼び出した時だけコストが発生するわけではない。ツールの定義(名前・説明・パラメータ)が毎ターンの入力に含まれる。
- 83ツールの定義 ≒ ~21Kトークン/ターン
- 10ターンのセッション → MCP定義だけで 21K × 10 = 210Kトークン(入力側)
- 使わないサーバーは削除するのが最も効果的な節約策
/context 1回で~4Kトークン消費
コンテキスト使用量を確認する /context コマンド自身もトークンを消費する。1回約4K。多用は避け、気になった時だけ使う。
32MBバイト上限(画像が唯一の犯人)
トークンとは別に**リクエストペイロードのバイトサイズ上限(32MB)**がある。Request too large (max 32MB) が出たらこれ。
2つの上限を混同しないこと(これが核心):
| 上限 | 単位 | MCP削減の効果 |
|---|---|---|
| 1M窓 | トークン | ✅ 効く(MCP固定費32kが長時間・複数セッションで効く) |
| 32MB | バイト | ❌ 無意味(テキスト定義全部で0.3MB・物理的に届かない) |
- 犯人は画像。テキストだけで32MBには絶対届かない(1M窓を使い切っても3-4MB)。32MB到達の唯一の経路は画像のbase64蓄積。画像1枚は数百KB〜1MBだが、履歴に残って毎ターン再送され雪だるま式に積もる。画像1枚追加が「最後の1滴」で弾かれる。
- テキスト定義(MCP/スキル等)は無罪。全部で0.3MB。MCP削減は1M窓の節約には効くが32MB問題には無意味。
/context実測で全体110kトークン=0.5MB。 - curl(proxy/status)では測れない。
last_request_mbは proxy→GLM 送信後のCDN化サイズ(常に0.4MB前後)。32MBチェックは Claude Code 側が proxy 送信前に弾くので proxy に届かず反映されない。正しく測るなら JSONLファイルサイズ(画像base64含む実会話サイズ)。 - 対策: 画像セッションは短く(=/clear惜しまない)・
/compactで画像を履歴から消す・同じ画像を何度もReadしない。画像分析はsubagentに委任(テキスト結果だけメインに戻せばメイン履歴に画像が残らない)。
⚠️ 訂正・補足(2026-06-30): 上記「テキストだけで32MBには絶対届かない・唯一の経路は画像」は auto-compact 有効前提の記述だった。実際は環境・条件により経路が変わる:
- auto-compact 無効(
DISABLE_AUTO_COMPACT設定環境=本環境)の場合: 履歴が圧縮されず、画像を使わなくてもツール結果(ファイルRead・コマンド出力等の巨大テキスト)の蓄積で20-30往復で32MB到達し得る。純会話テキスト(1M tok ≈ 3-4MB)では届かないが、ツール結果込みなら別。 - auto-compact 有効の場合: 1M窓到達で圧縮されるため、純テキストでは32MB届かず画像が主因(従来記述の通り)。
- MCP/スキル定義(0.3MB)は無罪なのは変わらない。テキスト削減(柱2-A等)は1M窓節約には効くが、32MB対策としては auto-compact 無効下の「ツール結果蓄積」に対してのみ寄与し得る。
- 詳細・継続課題は [[2026-06-30_32MB問題の基礎知見と対策現況]](01_DECISIONS/claude-code/)を参照。
🎯 追訂正(2026-07-01)・真因確定: 上記(画像が犯人・auto-compact無効下のツール結果蓄積・proxy送信前弾き)は全て主因誤認だった。真因は glm-rate-proxy の
aiohttp.web.Applicationがclient_max_size未設定(デフォルト1MB) で、画像添付リクエストが1MB超で 413弾き されていた。proxyログに413記録=proxy到達済み(「送信前弾き」は誤り)。CCは「max 32MB」と固定嘘表示(実効1MB)。「画像2枚1.3MBが通り3枚目で32MBエラー」の25倍跳びがこの乖離の証拠。対策はproxy.pyのcreate_app()にclient_max_size=32*1024*1024追加の1行(commit 9ac5f24fd)で32MB問題は解消。デスクトップ版で出ないのはproxy非経由のため。詳細は [[2026-07-01_32MBエラー真因解明-proxy-client_max_size修正]]。 🔬 検証(2026-07-02・18枚画像積載): 18枚(base64想定41MB ≫ 32MB)でもproxy通過・413ゼロ。JSONL実測で画像はURL参照(maas-log-prodの短いURL・1行約30KB)で履歴保存されbase64では蓄積しないことが判明。→ 6/19「画像履歴累積で雪だるま式32MB到達」は過大評価。画像では32MB到達は構造的に不可能。残る到達経路は「巨大テキスト(ツール結果の蓄積)」のみ。
- statusLineで常時監視:
llm-status.shがJSONL実サイズをReq X.XMB/32で表示(27MB黄・30MB赤)。色が変わったら画像整理の合図。
B. 落とし穴(ハマりポイント)
設定ファイルが2つ存在する(2ファイル問題)
~/.claude.json ← CLI自動生成(隠れファイル)
~/.claude/settings.json ← ユーザー編集用
Claude Codeは両方をマージして読み込む。片方だけ編集してもう片方に古い設定が残っていると、変更が反映されない。
対策: MCPサーバーの追加・削除は手動編集ではなく claude mcp add/remove コマンドを使う。このコマンドは両ファイルを正しく更新する。
MCP削除が「復活」する
上記2ファイル問題が原因で、settings.json だけ編集してMCPサーバーを削除しても、~/.claude.json に定義が残っていれば次回起動時に復元される。
対策: 削除後は両ファイルを確認:
claude mcp list
cat ~/.claude.json | jq '.mcpServers | keys'
cat ~/.claude/settings.json | jq '.mcpServers | keys'
フックがツールを意図せずブロック
PreToolUseフックのスクリプトがエラー(exit code ≠ 0)を返すと、そのツールの実行がサイレントにブロックされる。エラーメッセージは表示されるが、「フックがブロックした」とは明示されないことがある。
対策: 予期せぬツールブロックが起きたら、まずPreToolUseフックのスクリプトを確認。
/clear しても MCP・メモリは残る
/clear がリセットするのは会話履歴のみ。以下は消えない:
- システムプロンプト(CLAUDE.md)
- MCPツール定義
- Auto Memory
- 設定(permissions, hooks)
完全リセットしたい場合は /clear → セッション再起動。
Read ツールで巨大ファイルを読むとコンテキスト爆発
1つのファイルでも数千〜数万行なら数万トークン消費。特にログファイル・JSON・生成コードに注意。
対策: offset と limit パラメータで必要な範囲だけ読む。
セッション開始時にMCPが全ツール一括ロード
MCPツールは「必要になったら読み込む」のではなく、セッション開始時に全ツール定義を一括でロードする。これがコンテキストの固定消費の主因。
C. 効率テクニック
作業が長くなったら /compact か新セッション
セッションが長くなると過去の会話が蓄積し、1ターンあたりの入力トークンが増加し続ける。
/compact: 古い会話を要約して圧縮。会話の文脈は維持- 新セッション:
/clearまたは再起動。完全にリセット - 自動コンパクション: ~167K到達時に自動発動(33Kバッファ)
手動 /compact を適度に使うのが、最もバランスが良い。
バックグラウンドエージェントで並列作業
Agent ツールを run_in_background: true で使うと、メインコンテキストを消費せずに独立タスクを実行できる。
メイン: ガイドを書く
├── BG Agent: リポジトリAを調査
└── BG Agent: リポジトリBを調査
→ メインのコンテキストを節約しつつ、複数タスクを同時進行可能。
/model haiku で軽作業は高速化・低コスト化
単純な質問・フォーマット変換・短い修正等は、安価なモデルで十分。
/model haiku # Haiku に切替
# ... 軽作業 ...
/model sonnet # Sonnet に戻す
CLAUDE.md は短く保つ
CLAUDE.mdの内容は毎ターン読み込まれる。長すぎると毎回無駄にトークンを消費。
- グローバルCLAUDE.md: 共通ルールのみ(現状〜5Kトークン)
- プロジェクトCLAUDE.md: プロジェクト固有のみ
- 長いドキュメントは別ファイルにして、必要時だけ参照
D. よくある誤解
「会話履歴がコンテキスト圧迫の主因」
半分正解・半分不正解。
- MCP定義(~21K)は固定で毎ターン消費 — これがベースライン
- 会話履歴は変動するが、単発のやりとりでは小さい
- 長いセッションになると会話蓄積が主因に変わる
→ 短いセッションではMCP固定コストが目立つ。長いセッションでは会話蓄積が目立つ。
「MCPツールは呼んだ時だけコスト発生」
違う。ツール定義だけで毎ターン消費。呼び出しは追加コスト。
→ 使っていないサーバーを残しておくと、1ターンごとに無駄なトークンを消費し続ける。
「メモリは無料」
違う。MEMORY.md + 各メモリファイルが毎ターン読み込まれ、トークンを消費。
- メモリファイルが増えすぎると、それだけで数千トークン/ターンの固定消費になる
- 古い・不要なメモリは定期的に整理する
「/clear = すべてリセット」
違う。/clearがリセットするのは会話履歴のみ。
| リセットされる | 残る |
|---|---|
| 会話履歴 | システムプロンプト(CLAUDE.md) |
| MCPツール定義 | |
| Auto Memory | |
| 設定(permissions, hooks) |
「コンパクション(compact)= 要約品質が高い」
注意が必要。自動コンパクションは古い会話を要約して捨てるが、詳細が失われる。重要な文脈は compact 前にメモリに保存するか、自分で要約を控えておく。
E. Cron管理の注意点
Cron は7日で自動失効する
Claude Code の CronCreate で登録したCronジョブは、durable=false の場合は7日後に自動削除される。durable=true も実質7日が上限とされている(2026年6月時点)。
→ 放置するとCronが静かに消える。
自己更新プロンプトは膨張バグを引き起こす
「このプロンプト全文をそのままコピーして再登録せよ」というプロンプトをCronに埋め込むと、再登録のたびにプロンプトが重複コピーされ続け、最終的に8倍以上の長さになる。
初回: "このプロンプト全文を..."(100文字)
1回目実行後: "このプロンプト全文を...このプロンプト全文を..."(200文字)
N回目実行後: N倍の長さに膨張
推奨: スクリプト方式で永続化する
Cron定義を外部スクリプトに書き出し、更新専用Cronがそれを読んで再登録する方式が安全。
構成例:
# ~/bin/renew-crons.sh
# このファイルは実行ではなく、Claudeがcatして読むための定義ファイル
#
# 1. Knowledge Lint
# schedule: "3 3 * * 0,2,4"
# prompt: "bash ~/.claude/scripts/knowledge-lint/run-lint.sh && ..."
#
# 2. Cron更新タスク(このスクリプト自身)
# schedule: "0 3 */6 * *"
# prompt: "bash ~/bin/renew-crons.sh を cat して全Cronを再登録せよ"
更新Cronのプロンプト(短く固定):
bash ~/bin/renew-crons.sh を cat して、記載された全Cronを
CronCreate(durable=true, recurring=true) で再登録せよ。
既存の同名Cronは削除してから登録すること。
ポイント:
- プロンプト本文にCron定義を書かない(スクリプトに委譲する)
- スクリプト内容を変更してもプロンプトは変わらないため膨張しない
- 更新スケジュールは
0 3 */6 * *(6日ごと)で7日失効の前に再登録
F. APIスクリプト共通基盤(api_base.py)
外部API(Gemini・Last.fm等)をCLIから叩くとき、共通基盤 lib/api_base.py を経由するとプロバイダを増やすたびに同じパターンが使い回せる。2プロバイダで同一雛形が再利用可能なことを実証済み。
設計思想:CCはAPIを直接叩かない
SKILL.md → scripts/api/<provider>.py → lib/api_base.py → 統一JSON
スクリプトは統一JSON(status/summary/full_data/error)を返し、CCはsummaryのみ受け取る。巨大なfull_data(生レスポンス)はキャッシュファイルに退避するのでトークン膨張を防げる。
api_base.py の中身(4関数のみ)
| 関数 | 役割 |
|---|---|
run_api(call_fn, summary_fn, cache_key) |
API呼び出しをラップし全例外を統一JSONへ捕捉 |
make_error_result(error) |
例外をエラーJSONに正規化 |
make_success_result(summary, full_data, cache_key) |
成功JSON生成 + full_dataを~/tmp/api_cache/に退避 |
_cache_dir() |
キャッシュディレクトリ生成 |
核心は run_api の抽象:プロバイダ固有ロジックは call_fn(API呼び出し)と summary_fn(結果→成功JSON)の2関数に閉じ込め、基盤側は汎用的。
新しいAPIを追加する手順(5ステップ)
- キー取得 — ユーザーが
~/.secrets.envに<PROVIDER>_API_KEY追加(値は会話・ファイルに書かない) - TDD実装 —
scripts/api/<provider>.pyを新設。下記雛形の_call/_summarizeだけ書き換える。tests/test_<provider>.pyでparse系純粋関数を先にテスト - スキル統合 — 呼び出し元SKILL.mdにPhase追加(🔷ユーザー確認ポイント付き)
- 実URL検証 — 本物の入力でE2Eを通す(モックじゃなく実API)
- 完了記録 — SSOT
01_DECISIONS/claude-code/に完了ファイル。prev:で前フェーズにリンク
雛形(コピペして _call/_summarize だけ書き換え)
import json
from lib.api_base import run_api, make_success_result
def _call():
# プロバイダ固有のAPI呼び出し(SDK / urllib)
...
def _summarize(result, cache_key):
# result → summary文字列 + full_dataをmake_success_resultへ
return make_success_result(summary=..., full_data=result, cache_key=cache_key)
if __name__ == "__main__":
args = parse_args()
cache_key = f"{provider}_{args.xxx}"
print(json.dumps(run_api(_call, _summarize, cache_key), ensure_ascii=False))
現場で踏んだ罠
- Gemini モデル廃止:
gemini-2.0-flashは404。最新GAモデルを都度確認(現gemini-3.5-flash) - SDK引数の勘:
Part.from_uriはfile_uri=(url=ではない) - 自動特定の誤認: Last.fm
track.searchはYouTube動画タイトルのノイズで誤認しやすい → 🔷ユーザー確認 + 手動指定フォールバック必須 - 片方指定バグ:
--track/--artistの片方だけ指定すると無視される → XORバリデーション - timeout は今は死にパラメータ: docstringに「将来signalで適用」と明記(SDK側がタイムアウト保持)
詳細(アーキテクチャ全体・フェーズ別成果): SSOT 01_DECISIONS/claude-code/2026-06-18_サードパーティAPI統合アーキテクチャ.md
G. コード行数の正確な計測
ポートフォリオや経歴書に「コード行数」を記載する際、自動生成ファイルを除外しないと数字が大きく狂う。
必ず除外すべきディレクトリ
| ディレクトリ | 正体 | 実例 |
|---|---|---|
mutants/ |
ミューテーションテストの自動生成コピー | NexusCoreで5.5万行が混入 |
evaluation/ |
外部評価フレームワーク(evalplus等) | 実コードとは別物 |
archive/ |
廃止・旧版コード | 現行プロダクトではない |
.venv/ venv/ |
Python仮想環境 | サードパーティライブラリ |
node_modules/ dist/ |
JS依存・ビルド成果物 | 自分が書いたコードではない |
__pycache__/ .git/ 等 |
システム生成物 | 全て除外 |
異常値の見つけ方
計測後に以下を確認する:
- ファイルが10,000行超 → 生成物の可能性が高い。冒頭20行を確認する
- ディレクトリ平均が300行超 → そのディレクトリの中身をlsして確認
- バイト数÷行数が10未満または500超 → バイナリ混入 or minified JS
「本当に読んで計測した」3層証明
数字の信頼性を担保するために:
- ランダムサンプル — 計測ファイルから3件抽出して冒頭5行を表示
- バイト整合性 — 最大ファイルの「バイト数÷行数」が10〜500の範囲か確認
- git log — 各リポジトリの最終コミット日時・総コミット数を表示
→ /code-metrics スキルがこれらを自動実行する。
H. 並行セッションの競合回避(active-sessionsボード)
問題:複数セッションが「共通ファイル」を逆向きに触る
通常4セッション程度を並行運用するが、トピックを分けていても境界が曖昧な「共通ファイル」を複数セッションが同時に逆向きに触り、矛盾・無効化が起きる。
共通ファイル(9種) — 複数セッションの境界をまたぐ設定・運用ファイル:
~/.claude/settings.json~/.claude/CLAUDE.mdSKILL.md群(skills/*/SKILL.md)- hook群(
scripts/hooks/*・scripts/obsidian/*) 00★SYSTEM/自動化.md00★SYSTEM/全体マップ_MOC.md00★SYSTEM/repo-index.yaml/リポジトリ索引.md00★SYSTEM/MCPツール使い分けガイド.md00★SYSTEM/リンク運用方針.md
実例(同一日内で発生): check-link-policy.sh が3セッションで「修正 / 削除 / 活用」と分裂。どれか1人が作業しても別の1人が無効化する状態になった。
根本原因は 各セッションが他の並行セッションの「進行中方針」を知らないこと。git log には未commitの意図は載らない。
解決:作業宣言の共有ボード
00★SYSTEM/active-sessions.md に「いま誰がどの共通ファイルを・どうするか」を宣言する。
| タイミング | 担当スキル | ボードへの書き込み |
|---|---|---|
| 開始時 | resume-session | 自分の行を追加(触る予定ファイル・方針を宣言) |
| 記録時 | ssot-record | 共通ファイルを触ったら自分の行を更新 |
| 終了時 | new-session | 自分の行に ✅終了(or削除) |
競合発見時のフロー(共通ファイルを触る前):
- 同方向(両方「修正」等)→ 協調。片方に任せるか連携
- 逆方向(修正 vs 削除 等)→ 止まる。必ずユーザー判断。勝手に進めない
運用のポイント
- 即push徹底: ボード変更時は5分auto-syncを待たず手動commit+push(ラグで他セッションが見逃すのを防ぐ)
- stale掃除: 24h超・状態不明のエントリは次セッション開始時に確認・掃除可
- 完全防止ではない: Phase1は手動運用。宣言忘れはgit logで事後検出(Phase2でPreToolUse hookによる自動警告を予定)
根本原則: 逆方向の変更は勝手にやらない。必ずユーザー判断。
💡 やさしい補足(初心者向け)
この章に書いてある「使ってみないと分からない実話」を、日常の言葉で言い直します。
- 「長く使うほど高くつく」: 1回の質問にかかる料金が、会話が長引くほどじわじわ上がります(毎回それまでの全履歴を読み直すから)。長くなったら
/compact(要約して短くする)か、新しい会話に切り替えるのが節約のコツです。 - 「追加機能は減らす方が安い」: 「MCP」という追加機能(Web検索・GitHub操作など)は、使っていなくても毎回料金がかかります(説明書を毎回読み込むから)。使わないものは外すのが一番の節約です。
- 「設定ファイルが2つあってややこしい」: Claude Codeの設定は2つのファイルに分かれていて、両方を合わせて読まれます。片方だけ直してもう片方が古いままだと「直したはずなのに効かない」になります。MCPの追加・削除は手作業ではなく専用コマンド(
claude mcp ...)を使うと、両方正しく直ります。 - 「危ない命令は自動で止まる」: この環境では、ファイルを消す命令やシステムを壊す命令を実行前に自動でブロックする仕組み(フック)が入っています。万が一Claudeが危なそうなことを言っても、勝手には実行されません。
- 「複数のClaudeを同時に動かすとぶつかる」: 複数のセッションが同じファイルを逆向きにいじると台無しになります。「active-sessionsボード」という共有メモでぶつからないようにする運用をしています(上のH節)。