0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

/skill-doctor で未使用スキルのトークン代を実測したら4,590でした

0
Posted at

はじめに

対象読者は、Claude Code に Agent Skills を何本も追加していて、それがコンテキストをどれだけ食っているか把握できていない開発者です。

スキルは1本ずつ増やしていくものなので、増やしている最中は重さを感じません。ところが増えたスキルの description は、使っても使わなくても毎ターンのシステムプロンプトに載り続けます。「たぶん重いんだろうな」とは思っていても、では実際に何トークンなのかと聞かれると答えられない。この宙ぶらりんな状態を解消するコマンドが Claude Code 2.1.261 で入りました。

TL;DR

  • Claude Code 2.1.261 で /skill-doctor が追加され、ロード済みスキルの一覧・常駐コンテキスト量・直近7日の使用回数を一度に確認できるようになりました
  • 筆者の環境(プロジェクト31本 + ユーザー設定1本 + claude.ai 同期6本=計38本)で実測したところ、毎ターンの常駐コストは概算合計で約4,590トークン でした
  • 常駐量を決めているのは SKILL.md 本体ではなく frontmatter の description の長さ です。実測では 654 文字の description が約230トークン、57 文字が約20トークンでした
  • description を1行(30〜35文字)に書き直した5本で再測定すると、840トークンが合計100トークン未満まで落ちました
  • ただし /skill-doctor の使用回数はマシンローカルの履歴に依存し、claude.ai 同期スキルは削除しても次回同期で戻ります

/skill-doctor が測っているもの

まず誤解しやすい点を先に整理します。/skill-doctorcontext 列が示すのは、そのスキルがシステムプロンプトに載せている1行リスティングのコスト です。SKILL.md の本文全体ではありません。出力の脚注にもこう書かれています。

context = this skill's one-line listing in the system prompt, included every turn
(dash = not in the current listing, costs nothing; full SKILL.md loads only when it runs)

つまりスキル1本あたりのコストは2種類に分かれています。

本文をどれだけ厚くしても、起動しない限り毎ターンのコストにはなりません。逆に description を長く書くと、そのスキルを一度も使わない日でも全ターンで支払い続けることになります。/skill-doctor はこの後者だけを取り出して見せてくれます。

実測1: 38本で毎ターン約4,590トークン

検証は Claude Code 2.1.261(claude --version で確認)で行いました。以下の出力形式(~ 表記や脚注の文言)はこのバージョンでの実測であり、将来のバージョンで変わる可能性があります。プロジェクトの .claude/skills を一時ディレクトリへコピーし、そこを作業ディレクトリにして headless で実行しています。

claude --version
# 2.1.261 (Claude Code)

mkdir -p /tmp/skill-doctor-demo/.claude
cp -r ~/your-project/.claude/skills /tmp/skill-doctor-demo/.claude/skills
cd /tmp/skill-doctor-demo
claude -p "/skill-doctor" --permission-prompts none

--permission-prompts none は 2.1.259 で追加された、無人実行で承認プロンプトを出さないためのオプションです。スケジュール実行やCIから叩く場合はこれを付けないと承認待ちで固まります。

出力は次の形式で返ってきました(一部抜粋)。

Skills loaded this session

  skill                    source           context  7d tokens   uses  last used
  session-start-hook       userSettings         ~90          -     0×  never
  analyze-trends           projectSettings      ~30          -     0×  never
  hourly-dispatch          projectSettings     ~190          -     0×  never
  interactive-guide        projectSettings     ~230          -     0×  never
  research-runner          projectSettings     ~160          -     0×  never
  verify-draft             projectSettings      ~20          -     0×  never
  docx                     claude.ai sync      ~340          -     0×  never
  pptx                     claude.ai sync      ~320          -     0×  never
  xlsx                     claude.ai sync      ~320          -     0×  never

32 skills loaded but never invoked. Each one adds to the system prompt every turn.
6 skills synced from claude.ai loaded but never invoked.

source 列でどこから来たスキルかが分かるのが助かります。内訳と合計は次のとおりでした。

source 本数 context 合計(概算)
projectSettings(リポジトリの .claude/skills 31 約3,200
userSettings(~/.claude 1 約90
claude.ai sync(claude.ai から同期) 6 約1,300
合計 38 約4,590

context 列は ~ 付きの概算値なので、合計も概算の積み上げです。それでも桁は動きません。一度も使っていない38本のために、毎ターン4,000トークン超を払っている という事実が数字で出てきます。

意外だったのは claude.ai 同期スキルの重さでした。6本しかないのに1,300トークン、全体の28%を占めています。docx が340、pptxxlsx が各320で、これらは description に対象ファイル形式やトリガー条件を長文で書き込んでいるためです。ドキュメント生成をしないプロジェクトでも、同期している限り毎ターン払い続けます。

常駐量を決めているのは description の長さ

context の値が何に比例しているのかを確かめるため、各 SKILL.md の frontmatter から description を抜き出して文字数を数えました。

import re, glob, os

for p in sorted(glob.glob('.claude/skills/*/SKILL.md')):
    with open(p, encoding='utf-8') as f:
        t = f.read()
    name = os.path.basename(os.path.dirname(p))
    m = re.match(r'^---\n(.*?)\n---\n', t, re.S)
    if not m:
        print(name, 'NO_FRONTMATTER')
        continue
    d = re.search(r'^description:\s*(.*?)(?=\n[a-z-]+:|\Z)', m.group(1), re.S | re.M)
    print(name, len(d.group(1).strip()) if d else 0)

YAML の折り畳み記法(複数行 description の継続行インデント)は厳密に展開していないので、文字数は近似値です。傾向を見るには十分ですが、正確に測るなら yaml.safe_load で読んでください。

結果を /skill-doctor の実測値と並べると、対応関係がはっきりします。

スキル description の文字数 context(実測)
interactive-guide 654 約230
self-improvement-loop 547 約190
hourly-dispatch 538 約190
research-runner 472 約160
code-review 420 約70
write-article 270 約100
quality-review 121 約50
analyze-trends 77 約30
verify-draft 57 約20

日本語混じりでおおむね3文字前後が1トークンという換算で、description の長さがそのまま常駐コストになっています。code-review だけ文字数の割に軽い(420文字で約70)のは、description に英数字とマークダウン記法が多く含まれるためと思われますが、この点は /skill-doctor の出力からは切り分けられません。

プロジェクト31本の description は合計9,135文字ありました。書いているときは「発火精度を上げるためにトリガー例を足しておこう」という判断の積み重ねですが、その総和が毎ターンの固定費になっていたわけです。

実測2: description を1行にすると840が100未満になる

相関が見えたところで、削れば本当に減るのかを対照実験で確かめました。先ほどのコピーをもう1つ作り、context の重い5本(interactive-guide / self-improvement-loop / hourly-dispatch / research-runner / code-review)の description だけを30〜35文字の1行に書き換えます。本文(手順部分)は一切触っていません。

# before(interactive-guide・654文字の一部)
description: Claude が代行できないユーザー操作(外部サービスの管理画面設定・アカウント作成・
  ローカル PC の GUI 操作・2FA/CAPTCHA 等)を、全体像サマリー提示 → 1 工程ずつ案内 →
  完了確認、のループでユーザーと交互に進めるスキル。各工程の手順・URL は提示直前に……

# after(33文字)
description: Claude が代行できない外部サービスの画面操作を1工程ずつ案内する。

同じコマンドで再測定した結果がこちらです。

  code-review              projectSettings     < 20          -     0×  never
  hourly-dispatch          projectSettings     < 20          -     0×  never
  interactive-guide        projectSettings     < 20          -     0×  never
  research-runner          projectSettings     < 20          -     0×  never
  self-improvement-loop    projectSettings     < 20          -     0×  never

5本の合計は 840トークン(230+190+190+160+70)から、100トークン未満 まで落ちました。差分は740トークン以上です。~ 表示だった値が < 20 に変わっている点にも注目してください。20トークンを下回ると /skill-doctor は概算値ではなく閾値表示に切り替わります。

削減量の意味を数える際は注意が必要です。システムプロンプトはプロンプトキャッシュに載るため、740トークン×ターン数がそのまま請求額の差になるわけではありません。それでもキャッシュ書き込みは発生しますし、コンテキストウィンドウの占有は確実に減ります。「毎ターン確実に載る固定費が740トークン軽くなった」という読み方が正確です。

著者視点の発見ポイント

実際に回してみて、想定と違った点が3つありました。

1つ目は、削るべき対象が本文ではなく frontmatter だったことです。 スキルを軽くしようとすると、つい SKILL.md の手順を削りたくなります。しかし本文は起動時にしかロードされないので、常駐コストの観点では削っても1トークンも減りません。逆に「description に判断基準を詳しく書いて誤発火を防ぐ」という設計は、そのまま毎ターンの固定費になります。詳細な判断基準は本文か参照ファイルに置き、description はトリガーの見分けに必要な最小限にする、という切り分けが有効です。

2つ目は、7d tokens 列が空だったことです。 出力の脚注には「直近7日間のこのマシン上のセッションでスキルに帰属したトークン数」とあります。筆者はコンテナが毎回破棄されるクラウド実行環境で試したため、履歴が存在せず全行がダッシュになりました。「使われていないスキル」を使用実績から判定したい場合、この列が埋まるのは同じマシンで継続的に使っているローカル環境だけです。クラウドやCIで測る場合は、context 列(常駐コスト)だけが有効な指標になります。

3つ目は、claude.ai 同期スキルの扱いです。 出力にはこう書かれていました。

Disable in /skills, or turn them off on claude.ai
— a deleted synced copy is re-downloaded on the next sync.

ローカルのファイルを消しても次の同期で戻ってきます。公式ドキュメントによると同期が走るのは CLAUDE_CODE_SYNC_SKILLS=1 を付けた非対話実行時と、claude.ai 連携セッションのときで、常時バックグラウンドで巡回しているわけではありません。とはいえ恒久的に外すなら claude.ai 側の設定か /skills での無効化が必要です。ファイル削除で対処しようとして、しばらくしてから「また増えている」と気づく流れは避けられます。

運用に落とすなら

一度測って終わりにすると、description は再び伸びていきます。筆者のリポジトリではルールファイルの常駐バイト数に予算を設けて、超過をスクリプトで機械的に弾く仕組みを持っていますが、スキルの description は同じ管理下に置けていませんでした。/skill-doctor はその穴を埋める公式の計測手段になります。

運用としては次の3段階が現実的です。

  1. スキルを追加した直後に /skill-doctor を回し、追加分の context を確認する
  2. 100トークンを超えたら description を書き直す(判断基準は本文へ移す)
  3. 使わなくなったスキルは /skills で無効化する(ファイル削除は同期スキルには効かない)

計測できるようになった以上、「たぶん重い」で放置する理由はなくなりました。

まとめ

/skill-doctor は Claude Code 2.1.261 で追加された、ロード済みスキルの常駐コンテキストコストを可視化するコマンドです。筆者の38本構成では毎ターン約4,590トークンが常駐しており、その量は SKILL.md 本体ではなく frontmatter の description の長さで決まっていました。重い5本の description を1行に書き直す対照実験では、840トークンが100トークン未満まで落ちています。

スキルは足すのが簡単で、減らす動機が生まれにくい資産です。数字が出るようになったことで、棚卸しの判断を勘ではなく実測でできるようになりました。

関連記事

参考リンク

0
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?