この記事で得られること
- AIエージェントのトークン消費を95%削減した具体的な手法がわかる
- トークン浪費の原因特定と計測方法を実践できる
- コンテキスト管理の設計パターンをそのまま適用できる
対象読者: AIエージェントのAPI代が気になっている方 / トークン最適化に取り組みたい方
前回のあらすじ: OpenClawの月$600に絶望してSentinelを作った
前回の記事では、OpenClawのAPI課金が高すぎて、Claude Code CLI+MAXプランで自律AIエージェント「Sentinel」を自作した話を書いた。SOUL.md・MEMORY.md・TASKS.mdの3ファイルで記憶を永続化し、サブエージェントに並列でタスクを委任する2層構造。OpenClawと同じ思想を定額プランの上で実現した。
が、コスト問題を解決したら次の壁にぶつかった。MAXプランのトークン利用制限だ。
MAXプランは定額だからAPI課金は発生しない。でも時間あたりのトークン利用制限がある。Sentinelを回していると、この制限に頻繁に引っかかる。制限に達するたびに待ち時間が発生し、次のリクエストまで何もできない。せっかくコストを潰したのに、今度は速度が死んだ。
ある日トークンの使用量ログを集計した。1ターンあたりcache_creationだけで約23万トークン。21ターンでcache_creation合計480万トークン。
本記事のAPI換算コストはClaude Sonnetの公式レート(2026年3月時点)で参考計算したものです。MAXプランでは実際のAPI課金は発生しませんが、トークン消費量は利用制限に直結します。
API換算で$1.72/ターン相当。21ターンで$36.14相当のトークンを消費していた。MAXプランなので実費はかからないが、この消費ペースだと利用制限に頻繁にぶつかる。
原因を掘り下げて、最終的にトークン消費量を95%削減できた。その過程を書く。
トークン消費の内訳を分析する
トークンの4つの種別
Claude APIのレスポンスで返ってくるのは以下の4種類。
| 種別 | 意味 | API換算単価 (Sonnet) |
|---|---|---|
cache_creation |
プロンプトキャッシュの新規作成 | $3.75 / 1M tokens |
cache_read |
キャッシュ済みプロンプトの読み取り | $0.30 / 1M tokens |
input |
キャッシュされないinputトークン | $3.00 / 1M tokens |
output |
モデルの出力トークン | $15.00 / 1M tokens |
実測データ(21ターン分)
Total cache_creation: 4,807,970 tokens
Total cache_read: 11,082,867 tokens
Total input: 108 tokens
Total output: 21,937 tokens
─────────────────────────────────────────────────
API換算: $36.14(21ターン) → 平均 $1.72/ターン相当
cache_creationがトークン消費全体の大部分を占めていた。
上記のAPI換算額は実際の使用量ログから算出した参考値です。MAXプランでは課金されませんが、この規模のトークン消費は利用制限を早期に消費する原因になります。
なぜcache_creationが毎ターン発生するのか
前回の記事でSentinelのアーキテクチャを紹介したが、当初の実装(v1)ではCLIをこう呼んでいた:
# 初回
claude -p "メッセージ" --output-format json
# 2回目以降
claude -p "メッセージ" --resume <session_id> --output-format json
前回の記事で書いた claude -p による呼び出しそのものだ。--resumeでセッションは継続する。が、CLIプロセス自体は毎回起動・終了している。つまり毎ターン:
- 新しいCLIプロセスが起動
- システムプロンプト(SOUL.md + MEMORY.md + TASKS.md + CLAUDE.md等)の読み込み
- プロンプトキャッシュが丸ごと再作成される(= cache_creation 23万トークン)
- レスポンスを返してプロセス終了
キャッシュはプロセスのライフタイムに紐づいている。プロセスが終了すればキャッシュも消える。--resumeはセッションの会話履歴を引き継ぐだけで、キャッシュの再利用にはならなかった。
試した施策と結果
施策1: MEMORY.md の圧縮(89%削減)
「コンテキストが大きいからキャッシュ作成トークンが多い」――直感的にはそう見える。前回の記事でも触れた永続化ファイルのうち、MEMORY.mdを89%圧縮した。
結果: 効果なし。
MEMORY.mdはコンテキスト全体の約4%に過ぎなかった。SOUL.md、CLAUDE.md、会話履歴、ツール定義など、Claude CLIが内部で構築するシステムプロンプトの方がはるかに大きい。末端を削っても焼け石に水。
施策2: セッションリセット
セッションを定期的にリセットすれば、蓄積した会話履歴分のcache_creationは減るはず。
結果: 一時的な効果のみ。 初回ターンのcache_creationは必ず発生するので根本解決にならない。
施策3: 応答の短縮
outputトークンを減らしてみた。
結果: 誤差レベル。 outputはトークン消費全体の1%未満。ボトルネックはそこじゃなかった。
施策まとめ
| 施策 | 狙い | 結果 | 理由 |
|---|---|---|---|
| MEMORY.md圧縮 | コンテキスト縮小 | 効果なし | 全体の4%に過ぎない |
| セッションリセット | 会話履歴削減 | 一時的 | 初回の消費は不可避 |
| 応答短縮 | output削減 | 誤差 | outputは消費量の1%未満 |
全部「枝葉の最適化」だった。プロセスが毎回終了してキャッシュが消える――この構造的な問題を潰さない限り、何をやっても変わらない。
なお、各施策の詳しい検証過程と数値の推移については別記事で詳しく解説している。
解決策: stream-json双方向通信で常駐プロセス化
発想の転換
キャッシュがプロセス終了で消えるなら、プロセスを終了させなければいい。
Claude CLIには --input-format stream-json --output-format stream-json というオプションがある。これを使うとCLIプロセスを常駐させ、標準入出力(stdin/stdout)でJSON形式のメッセージをやり取りできる。
# v1: 毎ターンプロセス起動(キャッシュ毎回作成)
claude -p "msg" --resume $id --output-format json
# v2: プロセス常駐(キャッシュ初回のみ)
claude --input-format stream-json --output-format stream-json
v1からv2への移行で変わるのは、プロセスのライフサイクルだ。v1ではターンごとにプロセスを起動・終了していたが、v2ではプロセスを常駐させ、stdinにJSONメッセージを書き込んでstdoutからイベントを受け取る。プロセスが生き続けるため、初回に作成されたプロンプトキャッシュが以降のターンで再利用(cache_read)される。
実装のポイント
sentinel.jsの変更は大きく3つ:
-
プロセス管理:
claude -pの都度起動から、spawnで常駐プロセスを1つ立ち上げてstdin/stdoutで通信する方式に変更。クラッシュ時の自動再起動も実装 -
イベントルーティング: stream-jsonからは
system(セッション初期化)、assistant(テキストチャンク)、result(応答完了+トークン集計)、rate_limit_event(レート制限)など複数種類のイベントが流れてくるので、それぞれ適切に処理する - メッセージ送信: CLI引数ではなくstdinへのJSON書き込みに変更
具体的なコードと設計判断のポイントは別記事で詳しく書いた。イベントハンドリングの実装パターンや、運用で踏んだハマりどころも含めて解説している。
実測結果
v2での計測(8ターン分)
Total cache_creation: 28,573 tokens
Total cache_read: 462,330 tokens
Total input: 35 tokens
Total output: 10,533 tokens
──────────────────────────────────────────────────
API換算: $0.67(8ターン) → 平均 $0.084/ターン相当
Before / After 比較
| 指標 | v1 (--resume) | v2 (stream-json) | 削減率 |
|---|---|---|---|
| cache_creation / ターン | 228,950 tokens | 3,572 tokens | 98.4% |
| トークン消費(API換算) / ターン | $1.72相当 | $0.084相当 | 95.1% |
| 利用制限への影響 | 数十ターンで制限到達 | 大幅に緩和 | — |
cache_creationが初回のみになったことで、2ターン目以降はcache_readで済む。cache_readの単価はcache_creationの12.5分の1。ターン数が増えるほど効果が大きくなる構造だ。
学びと注意点
ボトルネックは計測してから潰す
「MEMORY.mdが大きいからトークン消費が多い」は直感的には正しそうだった。でも実際にはコンテキスト全体の4%に過ぎない。まず内訳を計測して、支配的な要因を特定すること。当たり前のようで、これをスキップしがち。
プロセスモデルの理解がカギ
CLIツールを「呼び出すAPI」として使うか、「常駐プロセス」として使うかで、キャッシュの挙動がまったく変わる。ドキュメントに明記されていない挙動もあるので、実測が重要。
stream-jsonの運用上の注意
- プロセスクラッシュ時のリカバリ(自動再起動)は必須
- stdoutのパースは行単位(JSONLフォーマット)
- イベントタイプごとに適切にルーティングする設計が要る
- 長時間運用ではメモリリークにも注意(readline周り)
まとめ
| v1 | v2 | |
|---|---|---|
| プロセスモデル | 毎回起動・終了 | 常駐 |
| 通信方式 | CLI引数 + stdout | stdin/stdout (stream-json) |
| キャッシュ | 毎ターン再作成 | 初回のみ作成、以降は読み取り |
| トークン消費(API換算) | $1.72/ターン相当 | $0.084/ターン相当 |
| 利用制限 | 頻繁にヒット | 大幅に緩和 |
前回はOpenClawの代替として自律エージェントの骨格を作った。今回はその運用で直面したトークン消費の問題と、プロセスモデルを変えるだけで95%削減できた話。枝葉を削る前に、幹を見直す。
MAXプランでも利用制限の壁はある。もし同じように制限に悩んでいるなら、まずは cache_creation の割合を確認してみてほしい。
この記事の実装コードの詳細と運用の裏側は別記事にまとめた。設計判断の背景や、本番運用で踏んだハマりどころなど、Qiitaでは省いた部分を扱っている。
構築の過程や実運用の裏側については、シリーズ記事として公開している。Qiitaでは技術的な具体策を、noteでは設計判断や運用で考えたことを扱っているので、背景まで知りたい方はそちらも参考になると思う。
関連記事・リンク:
- 前回: OpenClawに絶望してClaude Codeで自律AIエージェントを自作した話 — Sentinelのアーキテクチャと設計思想
- ai-agent-blueprint — 設計テンプレート一式をGitHubで公開中。エージェントの実装コード一式も公開予定なので、気になる方はチェックしてください
- @sentinel_dev93 — AIエージェント構築の学びをリアルタイムで共有中
おわりに
この記事では、AIエージェント運用のトークンコストを95%削減した方法を紹介しました。トークンコスト削減で他に効果的だった方法があれば、ぜひコメントで共有してください。
参考になったら いいね、後で見返すなら ストック していただけると励みになります。
他にもAIエージェント構築のノウハウを公開しています:
- 自律AIエージェント自作 — アーキテクチャ全体像