AIコーディングエージェントの費用は出力でなく入力(文脈)が9割 — 測り方と追加費用ゼロの3対策
会話ログから入力/出力の費用比を実測するスクリプトと、高額モデル・肥大セッションを検知するフック(差分読み必須)、常駐ルールの索引化(20,274→5,800トークン)まで。実装で踏んだ落とし穴4件と変異テストの型つき。
約9.8万トークンの節約 (API料金換算で約150円分)。 要件定義・技術調査・試行錯誤ぶんのトークンがまるごと不要になります。※ 出品者申告とレビューに基づく推定値。モデル・タスク内容により変動します。
この巻物について
「AIコーディングエージェントの費用は出力でなく入力(文脈)が9割 — 測り方と追加費用ゼロの3対策」は、AIのしつけカテゴリのAI指示書(MDファイル)です。会話ログから入力/出力の費用比を実測するスクリプトと、高額モデル・肥大セッションを検知するフック(差分読み必須)、常駐ルールの索引化(20,274→5,800トークン)まで。実装で踏んだ落とし穴4件と変異テストの型つき。この巻物をAIに読み込ませると、ゼロから設計・調査する場合に比べて 約9.8万トークン(API料金換算で約150円)・82%のトークンを節約できます。
- カテゴリ
- AIのしつけ
- 対応AI
- claude-code、cursor、codex-cli
- ライセンス
- 商用利用可 (再販不可)
- 価格
- 無料
- ゼロから開発時
- 約12万トークン
- この巻物使用時
- 約2.2万トークン
- 節約量
- 約9.8万トークン (約150円)
- 更新日
- 2026-08-20
使い方 (AIに渡す3つの方法)
いちばん簡単なのはワンライナー。Claude Code のターミナルに貼るだけです。
claude "https://makimono-md.vercel.app/api/v1/files/ai-9-3/raw を読み込んで、この指示書どおりに実装して"
中身
AIコーディングエージェントの費用は「出力」でなく「入力(文脈)」が9割 — 測り方と追加費用ゼロの3対策
対象: Claude Code / Codex CLI など、ローカルに会話ログ(JSONL)を残すコーディングエージェントを日常的に使っているチーム。
前提: エージェントの利用ログが ~/.claude/projects/**/*.jsonl 相当のパスに、1行1レコードのJSONで残っていること。
この指示書で得られる結論(先に読む)
「モデルを安いものに替える」「実装を別の安いエージェントに委譲する」は出力側の対策で、 実測すると費用の1割弱しか動かない。費用の大半は**毎リクエスト再送される文脈(入力)**である。
ある実運用環境の1ヶ月の実測値:
合計 $6,352 (定価換算)
入力側(毎リクエスト再送される文脈) $5,789 = 91.1%
出力側(生成) $563 = 8.9%
トークン: 新規入力 1M / キャッシュ書込 325M / キャッシュ読取 5,701M / 出力 20.7M
キャッシュ読取は入力単価の0.1倍だが、57億トークンあれば0.1倍でも支配的になる。 キャッシュヒット率95%は「もう最適化済み」の証拠にはならない。
手順1: 自分の環境で入力/出力を分解する(これを飛ばさない)
以下を io-split.mjs として保存して実行する。会話内容は読まず、モデル名とトークン数だけを集計する。
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
// 使うモデルの $/Mtok を [input, output] で書く。自分の契約単価に置き換える。
const PRICE = {
'<高性能モデルID>': [5, 25],
'<旧世代の高額モデルID>': [15, 75],
'<中量級モデルID>': [3, 15],
'<軽量モデルID>': [0.8, 4],
};
const root = path.join(os.homedir(), '<ログのルートディレクトリ>');
const since = '<YYYY-MM-01>';
const acc = {};
function walk(dir) {
for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
const p = path.join(dir, e.name);
if (e.isDirectory()) walk(p);
else if (e.name.endsWith('.jsonl')) scan(p);
}
}
function scan(file) {
let text; try { text = fs.readFileSync(file, 'utf8'); } catch { return; }
for (const line of text.split('\n')) {
if (!line.startsWith('{')) continue;
let j; try { j = JSON.parse(line); } catch { continue; }
if ((j.timestamp || '').slice(0, 10) < since) continue; // ファイルのmtimeで絞ると長寿命セッションを誤集計する
const u = j.message?.usage, m = j.message?.model;
if (!u || !PRICE[m]) continue;
const a = (acc[m] ||= { fresh: 0, cw: 0, cr: 0, out: 0 });
a.fresh += u.input_tokens || 0;
a.cw += u.cache_creation_input_tokens || 0;
a.cr += u.cache_read_input_tokens || 0;
a.out += u.output_tokens || 0;
}
}
walk(root);
let inUsd = 0, outUsd = 0;
for (const [m, a] of Object.entries(acc)) {
const [pi, po] = PRICE[m];
inUsd += (a.fresh * pi + a.cw * pi * 1.25 + a.cr * pi * 0.1) / 1e6; // 書込1.25x / 読取0.1x
outUsd += (a.out * po) / 1e6;
}
const total = inUsd + outUsd;
console.log(`入力側 $${inUsd.toFixed(0)} (${(inUsd / total * 100).toFixed(1)}%) / 出力側 $${outUsd.toFixed(0)}`);
重要な落とし穴: 期間で絞るときファイルの更新時刻(mtime)を使わない。 1つのセッションが数ヶ月生きることがあり、全履歴を今月分として数えてしまう。必ず行ごとの timestamp で絞る。
手順2: 高額モデルと肥大セッションを検知する(追加費用ゼロ)
なぜ効くか
- 旧世代の高性能モデルは入力単価が3倍のことがある($15/Mtok 対 $5/Mtok)。 同じ文脈を読み直すだけで3倍課金されるので、モデルを1つ間違えるだけで月額が跳ねる。 実測では、たった1セッションが累積キャッシュ読取 503Mトークン / 1,033ターンで定価$1,265を消費していた。
- セッションが長いほど1ターンあたりの再読込が増えるため、費用はターン数のほぼ二乗で伸びる。 実測の最悪例は累積キャッシュ読取 65億トークン / 12,872ターン / ログ218MB の単一セッションで、 キャッシュ読取単価だけで**$3,264相当**。ここまで来るとエージェント本体がクラッシュもする。
実装(プロンプト送信時フックの型)
エージェントがユーザー入力の直前に呼ぶフック(Claude Code なら UserPromptSubmit)で、
動いているセッション自身のログを読んで警告を注入する。LLM呼び出しゼロ、完全ローカル。
#!/usr/bin/env node
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import readline from 'node:readline';
const EXPENSIVE = new Set(['<旧世代の高額モデルID>']);
const COOLDOWN_MS = 30 * 60 * 1000; // 同じ警告を出し続けない
const TOK_LIMIT = 300_000_000; // 累積キャッシュ読取
const TURN_LIMIT = 400; // assistantターン数
async function main() {
let raw = '';
process.stdin.setEncoding('utf8');
for await (const c of process.stdin) raw += c;
const input = JSON.parse(raw);
const file = input?.transcript_path;
if (!file || !fs.existsSync(file)) return; // 読めなければ黙って終了
const home = process.env.<HOME上書き用の環境変数> || os.homedir();
const stateFile = path.join(home, '<状態ディレクトリ>', `${input.session_id}.json`);
let st = {}; try { st = JSON.parse(fs.readFileSync(stateFile, 'utf8')); } catch {}
// ★性能の要: 処理済みバイトオフセットを覚えて「追記分だけ」読む
const size = fs.statSync(file).size;
let offset = Number(st.offset) || 0;
if (size < offset) { offset = 0; st = { ...st, turns: 0, cacheRead: 0 }; } // ファイルが作り直された
let turns = Number(st.turns) || 0, cacheRead = Number(st.cacheRead) || 0, lastModel = st.lastModel || '';
let consumed = offset;
const stream = fs.createReadStream(file, { encoding: 'utf8', start: offset });
for await (const line of readline.createInterface({ input: stream, crlfDelay: Infinity })) {
// ★末尾が改行で終わらない不完全な行を集計に入れない。次回その行の先頭から読み直す
consumed += Buffer.byteLength(line) + 1;
if (consumed > size) break;
let r; try { r = JSON.parse(line); } catch { continue; }
if (r?.message?.role !== 'assistant') continue;
turns += 1;
if (typeof r.message.model === 'string') lastModel = r.message.model;
cacheRead += Number(r.message.usage?.cache_read_input_tokens || 0);
}
const warn = [];
if (EXPENSIVE.has(lastModel)) warn.push(['model', `このセッションは ${lastModel} で動作中。入力単価が現行モデルの3倍。冒頭でモデル切替を1行で促し、切替まで重い作業に着手しないこと。`]);
if (cacheRead >= TOK_LIMIT || turns >= TURN_LIMIT) warn.push(['size', `セッションが肥大化(累積キャッシュ読取 ${cacheRead} tok / ${turns}ターン)。1ターンごとに全履歴を読み直すため費用はターン数の二乗近くで増える。着手前にセッションを閉じて作り直すことを1行提案せよ。`]);
const now = Date.now();
const fire = warn.filter(([k]) => !(now - Date.parse(st[k]) < COOLDOWN_MS));
const next = { ...st, offset: consumed, turns, cacheRead, lastModel };
for (const [k] of fire) next[k] = new Date(now).toISOString();
fs.mkdirSync(path.dirname(stateFile), { recursive: true });
fs.writeFileSync(stateFile, JSON.stringify(next, null, 2));
if (fire.length) console.log(JSON.stringify({ hookSpecificOutput: { hookEventName: 'UserPromptSubmit', additionalContext: fire.map(([, t]) => t).join('\n') } }));
}
try { await main(); } catch {} // 失敗しても絶対にセッションを壊さない
process.exitCode = 0; // 非同期中の process.exit は Windows で libuv assertion を起こす
必ず差分読みにする。 全走査版を実測したら 218MBのログで毎プロンプト2.2秒かかった。 全操作に2秒の遅延が乗り、ログが伸びるほど悪化する。差分方式なら2回目以降178ms(ほぼプロセス起動時間)。 初回だけ全走査で数秒かかるのは許容する。
手順3: 常駐ルールを「全文」から「索引+参照」に変える(追加費用ゼロ)
エージェントのルールファイル(CLAUDE.md 等)は毎リクエスト送られる床になる。
共通ルールを自動同期して全文貼り付けている運用だと、ここが数万トークンに育つ。
実測: ルールファイル全体 20,274トークン。うち自動同期ブロックが 15,673トークン(77%)。
やること: 同期処理を「全文は別ファイルに保存、ルールファイルには機械生成の索引だけ」に変える。
索引の生成規則(LLMを使わず決定的に):
- すべての見出し行を残す
- 各見出し直後の最初の1文(最初の句点まで)を残す
- 絶対ルール・上限規定の行は全文のまま残す(下記の落とし穴を参照)
- 先頭に「全文は
<全文の保存先パス>。判断に迷ったら該当節の全文を読むこと」の1行を置く
実測結果: 53,527バイト → 11,856バイト(22%)、見出し41/41・絶対ルール行は全数を保持。 ルールファイル全体では 20,274 → 5,800トークン(−71%)。
この実装で実際に踏んだ落とし穴4つ
| 落とし穴 | 症状 | 対策 |
|---|---|---|
絶対ルール行の判定を **<絵文字> の1種類だけで書いた | 原文が **<別の絵文字> 上限…**: 形式だと5行中1行しか残らず、最重要の上限規定が索引から消える | 行頭の * を許容する正規表現にする(/^\*{0,2}\s*(?:絵文字リスト)/u) |
| 差し替え対象マーカーの判定をラベルの日付形式に固定した | 別形式で導入された環境で置換に失敗し、末尾追記に落ちてブロックが二重化する | ラベル部分は縛らず「行全体が開始マーカーで始まり --> で終わる」で判定する |
| マーカーを部分一致で探した | ルール自身がマーカー記法に言及している文書で誤爆してファイルを壊す | 必ず行全体の一致で探す |
| 既存テストが理想化した入力しか使っていない | 上記1件目のバグをテストが通してしまう | 実データと同じ書式(強調記号付きなど)でテストを書く |
さらに、書き換え前に必ず次を守る。
- 全文はバイト単位で無改変に保存する(情報欠落ゼロ。索引で不足したら全文を読ませればよい)
- 書き込み前に日付付きバックアップを作る
- マーカー外の記述は1バイトも変更しないことをテストで固定する
- 2回連続実行して差分が出ない(冪等)ことをテストで固定する
手順4: 直したことを変異テストで確認する
修正に対応するテストを足したら、修正を旧実装に戻してテストが落ちることを必ず確認する。 落ちなければそのテストは何も守っていない。
# 修正箇所を旧実装に置換 → 該当テストが FAIL することを確認 → 元に戻して PASS を確認
node mutate.mjs <対象ファイル> <変異名>
node --test <テストファイル>
node mutate.mjs <対象ファイル> restore
優先順位(この順でやる)
- 手順1の計測。入力/出力比を知らずにベンダーやプランを選ぶと、金を払っても効かない
- 旧世代の高額モデルを使わせない(単価3倍の解消)
- セッションを短く切る。単一セッションで$3,000級まで伸びるので、これが単独で最大
- 常駐ルールの索引化。全リクエストに効く恒久的な床下げ
- ここまでやってから、はじめて定額プランや上位シートの購入を検討する
やってはいけないこと
- 出力トークンだけを見て「安いモデルへ委譲したから大丈夫」と判断する(費用の1割弱しか動かない)
- 警告フックで毎回ログを全走査する(体感を壊す。差分読み必須)
- 索引化で絶対ルールや上限規定を落とす(コスト削減のために安全規定を失うのは本末転倒)
- ログファイル・セッションファイルを削除して"軽く"する(履歴は資産。縮めるのは常駐文脈と新規セッションの粒度であってログの削除ではない)
よくある質問
+「AIコーディングエージェントの費用は出力でなく入力(文脈)が9割 — 測り方と追加費用ゼロの3対策」とは何ですか?
会話ログから入力/出力の費用比を実測するスクリプトと、高額モデル・肥大セッションを検知するフック(差分読み必須)、常駐ルールの索引化(20,274→5,800トークン)まで。実装で踏んだ落とし穴4件と変異テストの型つき。
+どれくらいトークン(費用)を節約できますか?
ゼロから開発すると約12万トークンかかりますが、この巻物を使えば約2.2万トークンで済みます。差し引き約9.8万トークン(API料金換算で約150円)・82%の節約です。
+どうやって使いますか?
無料です。MDファイルを Claude Code などのAIに読み込ませるだけ。ワンライナーをターミナルに貼れば実装が始まります。要件定義や技術調査を省いて実装だけにトークンを使えます。
+どのAIツールに対応していますか?
claude-code、cursor、codex-cli に対応しています。
+商用利用できますか?
ライセンスは「商用利用可 (再販不可)」です。
🤝 自分でAIを動かすのは、まだ不安…という方へ
この巻物の内容を、AIを使うプロに丸ごと任せることもできます。姉妹サービスAI代行堂なら「LINEで頼むだけで、仕事が完成」。
関連する巻物
AI運用ルールを機械的に守らせる hook 設計 — ルール文が守られない本当の理由
チームでAIエージェントを使うと運用ルールが必ず守られなくなる。真因は「読んでいない」ではなく hook がそのマシンで登録されていない/委譲先が沈黙して壊れていること。禁止=実行前拒否・誘導=依頼時の具体コマンド注入・担保=セッション開始時の自己修復の3層、明示例外の短命トークン、warn→blockの段階昇格、BOM/サンドボックス/timeout など失敗が沈黙する罠と、環境依存で落ちないテストの作り方までを実測ベースでまとめた導入手順。
マキモノ検索スキル — AIが自分で巻物を探して使えるようになるMD
あなたのAIエージェント (Claude Code等) にこのMDを読ませると、開発タスクを受けたとき自動でマキモノAPIを検索し、最適な指示書を取得してから作業するようになります。導入は貼るだけ。
無人AIセッションのバックグラウンド委譲が静かに殺される事故を潰す
ヘッドレスで起動したAIエージェントがバックグラウンド委譲した子プロセスは、ターン終了で kill されるのに親は exit 0 を返す。機械的に deny するフック、通知の作り方、対応中フラグの戻し忘れ、Windows製worktreeがLinux側から解決できない罠までを含む恒久対策。
この巻物、誰かのトークンも救えます
𝕏 で節約レシートをシェア