はじめに:コスト上限ガードが一度も動いていなかった
当社(合同会社ジョインクラス、社員は僕1人)は、launchd の定時ジョブから claude -p を無人で叩いて、部門ごとの状況整理・記事ネタ補充・リード収集などを回しています。
月の上限は $200、$160 で Slack に警告を出す仕組みも作っていました。安心していたのですが、2026年9月25日に中身を確かめたら このガードは一度も作動しない状態 でした。
原因は単純で、コスト記録の関数が「呼び出し側が渡した概算値」を足すだけの作りだったからです。朝ダイジェストのスクリプトは毎回固定で 0.02 を渡していました。架空の数字で上限判定をしていたわけです。
この記事では、まず 実測できるようにした方法 を書き、そのあと 実測を見ながら削った5箇所 を、実際に動いているコードと一緒に紹介します。
前提:概算をやめて total_cost_usd を記録する
claude --output-format json で実行すると、応答の JSON に total_cost_usd と usage が入っています。これをそのまま積めば実測になります。
当社の実装は2ファイルです。まずラッパーです。
#!/bin/bash
# claude を無人実行するときの共通ラッパー。実コストを記録する。
# 使い方:
# source .company/scripts/lib/claude-run.sh
# RESULT=$(claude_run dept-dev --effort low -p "…")
#
# 標準出力には応答テキストだけを出すので、従来の `claude -p` と差し替えられる。
# 注意: --bare は認証を読まないため "Not logged in" で失敗する。使わない。
_CLAUDE_RUN_LIB="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
claude_run() {
local label="$1"; shift
claude --output-format json \
--fallback-model claude-sonnet-5 \
--max-budget-usd 3 \
"$@" 2>/dev/null \
| python3 "$_CLAUDE_RUN_LIB/record-cost.py" "$label"
}
ポイントは 標準出力に応答テキストだけを流す ことです。既存の RESULT=$(claude -p "...") を RESULT=$(claude_run ラベル -p "...") に置き換えるだけで済むので、十数本あるスクリプトを一気に移行できました。
次に、JSON を受け取って記録する側です(要点だけ抜粋)。
def main():
label = sys.argv[1]
raw = sys.stdin.read()
try:
d = json.loads(raw)
except json.JSONDecodeError:
sys.stdout.write(raw) # JSONでなければ素通しする
return 0
sys.stdout.write(d.get("result") or "")
path = pathlib.Path(os.environ.get("COST_FILE") or DEFAULT_COST_FILE)
month = datetime.date.today().strftime("%Y-%m")
try:
t = json.loads(path.read_text(encoding="utf-8"))
except Exception:
t = {}
if t.get("month") != month: # 月が替わったら前月分を退避して開き直す
if t.get("month"):
path.with_name(f"cost-tracker-{t['month']}.json").write_text(
json.dumps(t, ensure_ascii=False, indent=1), encoding="utf-8")
t = {"month": month}
cost = float(d.get("total_cost_usd") or 0)
usage = d.get("usage") or {}
before = float(t.get("total_usd") or 0)
t["total_usd"] = round(before + cost, 6)
jobs = t.setdefault("jobs", {})
j = jobs.setdefault(label, {"calls": 0, "usd": 0.0, "errors": 0, "cache_creation_tokens": 0})
j["calls"] += 1
j["usd"] = round(j["usd"] + cost, 6)
j["cache_creation_tokens"] = j.get("cache_creation_tokens", 0) + int(
usage.get("cache_creation_input_tokens") or 0)
if d.get("is_error"):
j["errors"] = j.get("errors", 0) + 1
path.write_text(json.dumps(t, ensure_ascii=False, indent=1), encoding="utf-8")
# 閾値は跨いだ瞬間だけ通知する(毎回鳴らすと無視されるようになる)
now = t["total_usd"]
if before < STOP_USD <= now:
notify_slack(f":rotating_light: 月間AI支出が ${now:.2f} に到達")
elif before < WARN_USD <= now:
notify_slack(f":warning: 月間AI支出 ${now:.2f} / ${STOP_USD:.0f}")
ジョブ単位で cache_creation_tokens まで残しているのが効きました。実際に貯まったデータはこんな形です(2026年11月10日時点の一部)。
{
"month": "2026-11",
"total_usd": 30.3496,
"jobs": {
"dept-dev": { "calls": 10, "usd": 3.505745, "cache_creation_tokens": 362369 },
"sme-concerns": { "calls": 10, "usd": 6.30988, "cache_creation_tokens": 620803 },
"replenish": { "calls": 25, "usd": 12.151766, "cache_creation_tokens": 1041967 }
}
}
落とし穴
-
2>/dev/nullを外してデバッグする: 認証切れなどは stderr に出ます。移行直後は外して1回手で叩いてください。 -
--bareは使わない: 起動を軽くしたくて試しましたが、認証情報を読まずNot logged inで失敗しました。 - JSON でない出力は素通しする: パースに失敗しても応答を捨てないようにしておかないと、後続の処理が空文字で動いてしまいます。
計測してわかったこと:1回あたりの固定費が大きい
計測を始めてまず驚いたのは、「2+2は?」と聞くだけで1回 $0.27〜0.47 かかっていた ことです。
内訳を見ると、キャッシュ作成が約41,000トークン。そのうち自社のファイル(CLAUDE.md・エージェント定義・スキル)は約5,800トークンしかありません。残りは Claude Code 側の固定オーバーヘッドです。
つまり「CLAUDE.md を短くすればいい」という話の効果は小さくて、効くのは呼び出しの回数と、1回の中で何をさせるか でした。以下の5箇所はこの前提で削っています。
削った5箇所
1. 定型ジョブは --effort low にする
部門ごとの状況整理のような定型の仕事に深い推論はいりません。
source "$SCRIPT_DIR/lib/claude-run.sh"
RESULT=$(claude_run dept-dev --effort low -p "
...(STATE.md を読んで差分を要約する指示)
")
当社では --effort low を付けていないジョブが1本だけ残っていて、比べると差が出ています。
| ジョブ | effort | 1回あたり |
|---|---|---|
| dept-dev | low | 約 $0.35 |
| dept-sales | low | 約 $0.32 |
| sme-concerns | 指定なし | 約 $0.63 |
プロンプトの中身が違うので純粋な比較ではありません。ただ、キャッシュ作成トークンも約1.7倍あり、ツール呼び出しが増えて往復が多くなっている形跡があります。検証方法は簡単で、同じジョブを --effort low あり・なしで数回ずつ回し、jobs.<ラベル>.usd / calls を比べてください。
2. --allowedTools で工程ごとに道具を絞る
記事ネタ補充(replenish)は一番呼び出しが多いジョブです。調査工程と執筆工程で、渡すツールを分けています。
# 調査工程:読むだけ
claude_run replenish --effort low --allowedTools "Read,WebSearch,WebFetch" -p "..."
# 執筆工程:ネット検索は不要
claude_run replenish --effort low --allowedTools "Write,Read,Edit" -p "..."
執筆中に「念のため検索」が始まると、検索結果がそのままコンテキストに乗ってトークンが膨らみます。道具を渡さなければ寄り道できません。セキュリティ面でも、無人実行で余計な権限を持たせないのは良いことです。
3. --max-budget-usd 3 で1回の暴走を止める
ラッパーに入れている --max-budget-usd 3 は、1回の実行で使える上限です。無人ジョブで怖いのは平均ではなく、ループにはまった1回 です。月の上限ガードだけだと、気づいたときには1日分が溶けています。
値は普段の1回あたり($0.3〜0.6)の5倍以上にしてあります。きつくしすぎると、正常な処理が途中で打ち切られて記事が半分だけ書かれる、といった別の事故が起きるからです。
4. 実行前に月の上限をチェックする
記録だけでは止まりません。各スクリプトの先頭で、実測値を見て止めます。
check_cost_limit() {
local tracker="$COMPANY_DIR/scripts/cost-tracker.json"
[ -f "$tracker" ] || return 0
local total=$(jq '.total_usd' "$tracker")
if (( $(echo "$total >= 200" | bc -l) )); then
log_error "月間コスト上限到達: \$${total}。実行を中止。"
return 1
fi
}
# 各ジョブの冒頭
check_cost_limit || exit 0
関数自体は前からありました。変わったのは 読んでいる数字が本物になったこと だけです。最初に書いた「ガードが動いていなかった」問題は、ここで初めて解消しています。
5. 呼び出し回数そのものを減らす
固定費が1回 約41,000トークンなら、10回を1回にまとめるのが一番効きます。当社では次の順で見直しました。
-
jobsをusdの大きい順に並べる - 上位のジョブについて「毎回呼ぶ必要があるか」「前回から入力が変わっていないのに呼んでいないか」を見る
- 変わっていなければ
claudeを呼ばずに終わる
上位を出すコマンドはこれだけです。
jq -r '.jobs | to_entries | sort_by(-.value.usd)[]
| "\(.value.usd | tostring | .[0:6])\t\(.value.calls)回\t\(.key)"' \
.company/scripts/cost-tracker.json
当社の11月は replenish(25回・約$12)が全体の4割を占めていました。次に手を入れるのはここです。
結果
月ごとの実測は次のとおりです。
| 月 | 実測合計 | 備考 |
|---|---|---|
| 2026-09 | $26.41 | 9/25 から計測開始(約6日分) |
| 2026-10 | $128.96 | 1か月まるごと計測 |
| 2026-11 | $30.35 | 10日時点 |
正直に書くと、9月は計測期間が短く、「削る前」のきれいな比較はできていません。それでも10月の $128.96 に対して、11月は10日時点で $30.35(このペースなら月 $90 前後)です。そして何より、$160 警告・$200 停止のガードが本物の数字で動くようになった ことが一番の成果でした。
まとめ:今日やること
-
claude -pを--output-format jsonに変えて、total_cost_usdをジョブ別に記録する - 1週間貯めてから、
usdの大きいジョブを見る - 定型ジョブは
--effort low、工程ごとに--allowedToolsを絞る -
--max-budget-usdで1回分、check_cost_limitで月分の上限をかける - 最後に、呼び出し回数そのものを減らす
順番が大事です。計測より先に CLAUDE.md を削っても、当社の場合は全体の1割強にしか効きませんでした。
経営の目線で言うと、AIのコストで怖いのは高いことではなく、わからないこと です。数字が見えれば判断できます。僕はこの作業で「コストは10分の1になるが、売上が10倍になるわけではない」ことも改めて実感しました。浮いたお金と時間は、売上に直接つながる仕事に回しています。
参考書籍
CLAUDE.md・設定・コスト管理の設計を体系的にまとめた本を書いています。
- 『CLAUDE.md設計パターン』
- launchd / cron での無人実行まわりは『Claude Code 全自動化バイブル』