← ブログに戻る

Claude Code Skills は発火しなくてもトークンを食う — 5 Skills × 7時間セッションで「使われなかった」3つが18%食べていた

claude-codeanthropicskillstokenscost-optimization

Skills はカスタムコマンドの上位互換で、しかも「使わなければタダ」だと思っていました。タダではなかったです。家賃でした。

この一文がこの記事のすべてです。残りは私が「家賃」を払っていた証拠を並べるパートになります。

ある火曜日、私は Claude Code の1セッションに Skill を5つロードして7時間動かしました。PRレビュー、TypeScript移行、DBマイグレーション検証、ログ追跡、CSV整形。このうち3つは、その日一度も発火しませんでした。発火ログを2回見直してから「本当に動いていない」と確認しました。それでもこの3つだけで、その日の総トークンの**約11%**を食べていました。発火した2つの分も合わせると、Skills 合計で 18% になります。

私はそれまで、同僚に「Skill は呼ばれたときだけトークンを食うから、入れっぱなしでも問題ない」と説明していました。これは間違っていました。しかも実測できる規模で間違っていました。

Skills が実際にロードされるタイミング

これは公式仕様で書かれていることなのに、私は流し読みしていました。

セッション開始時、Claude Code はスコープ内のすべての Skill を読みます。このとき context に入るのは SKILL.md の frontmatter にある namedescription だけです。本文はまだ入りません。本文がコンテキストに入るのは、Claude が description とプロンプトをマッチさせて発火を決めたとき、または私が明示的に /skill-name と打ったときです。本文は一度入るとセッション終了かコンパクションまで残ります。

私が見落としていたのはここから先です。description は1ターンごとに context に乗ります。セッション開始時に1回だけではありません。私が新しいメッセージを送るたび、Claude が応答を返すたびに、description はプロンプトの一部として再評価されます。1つの description が約300トークン、5つで約1,500トークンの「この Skill は何ができるか」という説明文が、毎ターン billing に乗っていたわけです。

80ターンのセッションを回せば、同じ description を160回支払うことになります。1つあたりは小さい。しかし常に乗っている。これがからくりです。

計測したセッションの構成

私は Claude Code を日常的に使っています。計測した日は、午前中に PR トリアージ、午後に長めのリファクタリング、夕方に雑なシェル探索、という普段の火曜日でした。Claude Code の1セッションを通しで開いたまま、すべての応答を --output-format json --verbose でラップして、usage フィールドをログに残しました。

~/.claude/skills/ にロードされていた5つの Skill は以下のとおりです。

Skilldescription 長用途発火した?
review-pr約310トークンPR レビュー手順はい (11回)
migrate-ts約290トークンTS 移行ヘルパーはい (2回)
migrate-db約340トークンDB マイグレ検証いいえ
trace-logs約270トークンログ追跡パターン集いいえ
clean-csv約280トークンCSV 整形レシピいいえ

description 合計は1ターンあたり約1,490トークン。これが CLAUDE.md・プロジェクトコンテキスト・会話履歴の上に毎ターン積まれていました。

セッション時間は7時間12分、84ターン、入出力合計は約210万トークン (プロンプトキャッシュはほぼ常時オン) でした。

請求書の中身

ログから、トークン消費を3つに分けました。「総消費」「Skills が一切ロードされていなかった場合の推定値」「その差分」です。実数は以下になります。

カテゴリトークン割合
会話・CLAUDE.md・コード読み込み1,720K82%
発火した Skills (review-pr + migrate-ts)147K7%
発火しなかった Skills (description のみ × 3つ)231K11%
合計2,098K100%

実際に仕事をした2つの Skill は7% を食べました。これは問題ありません。手順をタイプし直す手間を省けたぶん、たぶん同じくらいの節約をしてくれました。

問題は、一度もマッチしなかった3つです。11%。リターンはゼロです。プロンプトキャッシュが効いていると description の1ターンあたりコストはある程度吸収されますが、完全には消えません。私のプロンプトが変わるたびにキャッシュ境界の手前で description が再トークナイズされ、input_tokens に乗ります。それで11%でした。

5 Skills × 7時間タイムライン: 発火 vs ドーマント

棚卸ししてもう1日回してみた

翌朝、同じワークロード (同じ PR セット、同じ種類のプロンプト) を、前日に実際に発火した2つの Skill だけをロードして実行しました。総消費は約1,872Kトークン。前日比で約11%減です。ノイズはありますが、「ドーマントだった3つの description が払っていた家賃」の試算とほぼ一致しました。

自分の環境で同じことを確認したい場合は、claude をラップして JSON で usage を読むスクリプトを通せば一目です。

claude -p "$YOUR_PROMPT" --output-format json --verbose \
  | jq '{input: .usage.input_tokens, cached: .usage.cache_read_input_tokens, output: .usage.output_tokens}'

ターンごとの input_tokens がベースラインで上方ドリフトしているなら、それは description の家賃を払っているサインです。

なぜこれが意外だったか

私は Skill を「プログラミング言語の import 文と同じで、呼ばれない限りコストは0」と勘違いしていました。import がタダなのはコンパイラが未参照のものを捨てられるからです。Claude Code はそれができません。description こそが「いつこの Skill を呼ぶか」を判定する材料なので、description は毎ターンプロンプトに乗っている必要があるからです。遅延ロードしたら、そもそも呼び出すかどうかの判断ができなくなります。

これは設計上のトレードオフで、しかも正しいトレードオフです。ただし、その帰結として「インストールされているだけで使われていない Skill」の限界コストは0ではありません。ターンごとの小さな税金です。長いセッションでは積み上がります。

ここで Hooks と混同しないでください。Hooks は Claude Code がイベントに応じて意図的に発火させる仕組みです (pre-tool / post-tool / session-end 等)。Hooks の定義は description としてシステムプロンプトには入らず、settings.json から harness が呼ぶだけです。使われていない Hook のコストは本当にゼロです。使われていない Skill のコストは description × 全ターン分です。 ここは別物として認識しておきたいところです。

MCP server とも違います。MCP server はセッション開始時にツールリスト全体をシステムプロンプトに乗せます (1サーバーで27,000トークン規模という別の計測例もあります) が、こちらはサーバー単位の固定費です。Skill のほうが1つあたりは小さいですが、数が増えがちで、毎ターン × Skill 数 で乗算的に効いてきます。

Skill 棚卸し5ステップ

私は今、これを月1回のルーティンにしています。10分で終わります。

  1. スコープ内のすべての Skill をリストアップ: ls ~/.claude/skills/ と、プロジェクトレベルの .claude/skills/ と、Plugin 由来の Skill。全部紙に書き出します。
  2. 各 Skill が最後に発火した日時を出す: セッションを --output-format json でロギングしているなら、tool-use エントリから Skill 名を grep するだけ。していないなら記憶頼りになりますが、記憶はだいたい不正確です。
  3. 過去30日で1回も発火していないものを「候補」にマーク: ここではまだ削除しません。フラグだけ立てます。
  4. 候補を1週間だけ “倉庫” に移動: 私は mv ~/.claude/skills/<name>/ ~/.claude/skills-attic/ のように物理的に動かしています。1週間使って、不便を感じなければ、それは家賃でした。
  5. 同じワークロードで input_tokens のベースラインを再計測: 候補を抜いた状態で同じ種類の作業をして、input_tokens が明確に下がるなら、節約が見えた状態です。

罠を1つ挙げるなら、「30日発火していない=即削除」にはしないことです。四半期に1回しか使わないけれど、その1回で価値を出してくれる Skill があったりします。「倉庫に移動」が安全な中間状態です。

ちなみに Zenn の Claude Code Hooks 7パターン記事 (2026-05-29) と混ざりやすいので念のため明記しておくと、この記事は Hooks の話ではなく Skills の話です。Hooks は「意図的にイベントで発火させる装置」、Skills は「条件付きで発火するが、ロードされているだけで description が課金される装置」。機構が違います。

私の手元で何を変えたか

3つの Skill を倉庫に移しました。1つは来月、DB マイグレーションの予定があるので戻ってきます。残り2つはたぶんこのまま消えます。発火していた2つはそのまま。

この記事を書いている今のセッションも Skill 2つ運用です。ターンごとの input_tokens のログがきれいに横這いになりました (以前はじわじわ右肩上がりでした)。11% という数字は、口頭で言うと地味に聞こえます。Claude Code Max の月額換算なら ¥30,000 × 18% ≒ ¥5,400/月、未発火3つ分だけでも ¥3,300/月。Sonnet API の従量プランなら使い方次第ですが、いずれにせよ実弾です。「テキストファイル3つをコンテキストに置いておくため」に毎月支払っている、と言うと我ながら情けない金額の出方をしています。

たくさん Skill を入れたい気持ちは正しいです。便利だからです。ただ、その便利さには「ターンごとの税金」がついていて、その税金は見に行かないと見えない、ということだけ知っておいてください。

モニターの上に貼っておきたい一文はこうです。ロード済み ≠ アクティブ ≠ 呼ばれたとき分だけ請求、ではない。

claude -pusage.input_tokens を眺めてみてください。あの数字は最初からこの話を教えてくれていたのに、私はずっと見ていませんでした。


Hooks / MCP / Sub-agents / Plugins とあわせて「どの拡張機構がどこにコストを乗せているか」を1冊にまとめたのが Claude Code Mastery です。今回の「description が毎ターン billing に乗る」というカラクリの根っこにある context window 経済学は Context Engineering のほうにまとめてあります。