はじめに
appium-cli というツールを作成している最中に、「GitHub Copilot CLI が内部でどれだけ token を消費しているか?」を確認したくなりました。今回は、OpenTelemetry ログを使って正確に計測する方法 についてメモを残しておくことにします。
この記事では、「Yahoo のエンタメニュースを 3 本取得する」 という具体的なタスクを実際に実行し、取得した OTel ログから GitHub Copilot CLI が内部で消費する token 数を確認する。
まとめ
-
OPILOT_OTEL_FILE_EXPORTER_PATHを設定するだけで OTel ログが取れる -
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=trueで「どの token がどの処理に使われたか」をログで追える - 集計対象は
chatspan -
input_tokensは cache 込みの総量
STEP 1:計測環境を整える(環境変数 2 つ)
GitHub Copilot CLI v1.0.43 以上で使えます。
# ① token 数だけ知りたいとき(最小構成)
COPILOT_OTEL_FILE_EXPORTER_PATH=/tmp/copilot-otel.jsonl \
copilot -p "タスク" --allow-all -s
# ② 「何の token か」まで把握したいとき(プロンプト・応答も記録)
COPILOT_OTEL_FILE_EXPORTER_PATH=/tmp/copilot-otel.jsonl \
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true \
copilot -p "タスク" --allow-all -s
| 環境変数 | 効果 |
|---|---|
| COPILOT_OTEL_FILE_EXPORTER_PATH | 設定するだけで OTel が有効になり JSON Lines 形式でログが出力される |
| OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT | プロンプト・応答・ツール引数の本文もキャプチャする(デフォルト false) |
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true は注意
コード・ファイル内容・ユーザープロンプト・ツール引数がすべてログに記録されます。信頼できるローカル環境だけで使用する
STEP 2:OTel ログの構造を把握する
出力された JSONL には複数種類のレコードが混在しています。1 回の Copilot 実行では、ざっくりこういう構造になります。
各レコードの役割と、token 計測での扱いを整理します。
| レコード | 役割 | token 計測での扱い |
|---|---|---|
chat <model> |
LLM API を 1 回呼び出した単位 = ターンごとの token が入っている | ✅ 集計する |
invoke_agent |
セッション全体の root span。全ターン合計を保持 | 照合のみ |
execute_tool |
ツール実行ログ | 参照のみ |
gen_ai.client.token.usage |
セッション合計の Histogram メトリクス | 照合のみ |
STEP 3:token フィールドの意味
chat span には以下の token フィールドがあります。実際のログはこのような形です。
{
"gen_ai.usage.input_tokens": 20913,
"gen_ai.usage.output_tokens": 429,
"gen_ai.usage.cache_creation.input_tokens": 8220,
"gen_ai.usage.cache_read.input_tokens": 12683
}
最重要ポイント:input_tokens は cache 込みの総量です。cache_creation と cache_read は内訳です。
| フィールド | 意味 | 集計ルール |
|---|---|---|
| gen_ai.usage.input_tokens | そのターンで送った総入力 token | ✅ これを合計する |
| gen_ai.usage.output_tokens | そのターンで返ってきた出力 token | ✅ これを合計する |
| gen_ai.usage.cache_creation.input_tokens | cache への新規書き込み分(input_tokens の内訳) |
内訳として参照 |
| gen_ai.usage.cache_read.input_tokens | cache ヒット分(input_tokens の内訳) |
内訳として参照 |
| github.copilot.current_tokens | 送信前コンテキストの推定サイズ | 実使用量としては使わない |
github.copilot.current_tokens は CompactionProcessor によるコンテキスト管理上の推定値のようです。OTel の gen_ai.usage.input_tokens と一致しないケースがあるようなので。正確な API 使用量は gen_ai.usage.input_tokens を参照するのが良いでしょう。
STEP 4:集計スクリプトで数値化する
chat span だけを抽出して集計する Python スクリプト
# token_summary.py
import json
import sys
path = sys.argv[1] if len(sys.argv) > 1 else "/tmp/copilot-otel.jsonl"
spans = []
with open(path) as f:
for line in f:
obj = json.loads(line)
if obj.get("type") == "span" and obj.get("name", "").startswith("chat "):
a = obj.get("attributes", {})
spans.append({
"turn": a.get("github.copilot.turn_id"),
"input": a.get("gen_ai.usage.input_tokens", 0),
"output": a.get("gen_ai.usage.output_tokens", 0),
"cache_create": a.get("gen_ai.usage.cache_creation.input_tokens", 0),
"cache_read": a.get("gen_ai.usage.cache_read.input_tokens", 0),
})
print(f"turns: {len(spans)}")
print(f"total input: {sum(s['input'] for s in spans):,}")
print(f"total output: {sum(s['output'] for s in spans):,}")
print()
print(f"{'turn':<6} {'input':>10} {'output':>8} {'cache_create':>14} {'cache_read':>12}")
print("-" * 56)
for s in spans:
print(
f"{str(s['turn']):<6} {s['input']:>10,} {s['output']:>8,}"
f" {s['cache_create']:>14,} {s['cache_read']:>12,}"
)
python3 token_summary.py /tmp/copilot-otel.jsonl
これで「何 turn あったか」「合計 input / output はいくつか」「cache がどれくらい効いていたか」まで一気に確認します
STEP 5:実際に計測してみる
ここからは実測です。「Yahoo のエンタメニュースを 3 本取得してまとめる」というタスクを、bash 経由の playwright-cli コマンドのみを使うよう Copilot に指示して実行しました。
実行コマンド
COPILOT_OTEL_FILE_EXPORTER_PATH=docs/evidence/yahoo-playwright-cli-otel-20260508-195200.jsonl \
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true \
copilot -p "yahoo.co.jp で最新のエンタメニュースを3本取得して、ざっくり内容をまとめて教えてください。bash で playwright-cli コマンドだけを使うこと。playwright-browser_* ツールは使わないこと。snapshot は --raw snapshot --filename を使って docs/evidence/ 配下に保存し、必要な情報だけを読むこと。" \
--allow-all --model claude-haiku-4.5 -s
計測結果サマリー
| 指標 | 値 |
|---|---|
| モデル | claude-haiku-4.5 |
chat span 数(=ターン数) |
22 |
| 合計 input tokens | 639,715 |
| 合計 output tokens | 5,498 |
invoke_agent input / output |
639,715 / 5,498 |
invoke_agent の値が chat 合計と一致しています。「全ターンの合算値を持つ root span」なので、chat 合計と同じ値になることが確認できます。