プログラミング
SpotifyのPortalがClaude Codeのトークン使用量を90%削減
Portal by Spotify cut my Claude Code token usage by 90% (engineering.atspotify.com)
要約
Spotifyのエンジニアは、AIコーディングエージェントにおけるトークン使用量の大部分が、推論ではなくI/O処理に費やされていることに気づきました。この問題を解決するため、彼らはPortalというツールを開発し、定型的なファイル読み込みやコード生成といった単純作業を低コストなモデルに委任する「モード」を導入しました。これにより、Claude Codeのような高価なモデルは、真に高度な推論が必要なタスクにのみ使用され、トークンコストを大幅に削減することに成功しました。
全文翻訳
SpotifyのPortalがClaude Codeのトークン使用量を90%削減
AIコーディングエージェントが私にとって行うことのほとんどは、思考ではありません。それはI/Oです。
あるメソッドに関する質問に答えるために5つのファイルを読み込む。隣にある20個のテストファイルと全く同じパターンに従ってテストファイルを生成する。会議の後でドキュメントを更新する。
数千ものトークンが失われ、推論はほとんどゼロです。
シートライセンスが問題なのではなく、トークンが問題なのです。そして、あなたはこれらのすべてを、過剰に資格のあるフロンティアモデルに供給しています。
もし、退屈な作業を、同じくらいうまくこなせる安価なものにルーティングし、本当にそれが必要な問題のために高価なモデルを節約できたらどうでしょうか?
それはほとんど私だけの問題ではありません。2028年までに、AIコーディングコストは平均的な開発者の給与を上回ると予想されています。エンジニアリングリーダーの4分の1は、既に開発者あたり月額200〜500ドルのトークン費用を費やしています。一部は2,000ドルをはるかに超えています。
ツールはそれ自体で元が取れますが、それはあなたがそれを必要としない作業にフロンティアトークンを使い果たすのをやめた場合に限られます。
幸いなことに、その修正にはプラットフォームチームや新しいサブスクリプションは必要ありませんでした。たった2つのモードで十分でした。
2つのモード、コードゼロ
これはまさに、SpotifyのPortalにあるAiKAモードが構築されたユースケースです。モードとは、一時的なランタイムで実行される宣言的なエージェントです。AWS Lambdaのエージェント版のようなものです。指示を定義し、モデルを選択し、温度などのパラメータを設定し、MCPツールをアタッチします。Portalが残りを処理します。インフラの管理、APIキー、常駐サーバーは不要です。モードはPortal CLIまたはAPIから呼び出すことができます。公開(会社全体と共有)またはプライベートにすることができます。
このルーターを機能させるために、私は2つのモードを作成しました。以下の例では、どちらもGemini 2.5 Flashをワーカーモデルとして使用していますが、modelフィールドにはPortalインスタンスで設定した任意のモデルを指定できます。あなたに合ったものを選んでください。
モード1: bulk-reader
Claudeが1つの質問に答えるために複数の大きなファイルを読み込む代わりに使う場合。
name: bulk-reader
description: コード分析のためのバルクファイルリーダー - Claude CodeからのI/Oを委任します
instructions: あなたは正確なコードアナリストです。提供されたファイルを読み、質問に簡潔に答えてください。構造化された箇条書きのみを出力してください。挨拶、散文、前置きは不要です。各箇条書きの先頭には、正確な名前、タイプ、または行番号を付けてください。詳細については、ネストされた箇条書きを使用してください。呼び出し元が要求しなかったものはすべてスキップしてください。
visibility: public
model: gemini-2.5-flash
resourceLimits:
temperature: 0.2
tags:
- coding
- delegation
モード2: code-writer
テスト、設定の足場、型スタブ、または既存のパターンから出力が予測できるものに使用する場合。
name: code-writer
description: ボイラープレートコードジェネレーター - Claude Codeからの出力負荷の高い作業を委任します
instructions: 仕様と参照ファイルに基づいてコードファイルを生成します。既存のパターン、規約、命名、スタイルに正確に一致させてください。出力はコードのみとしてください。説明やマークダウンのフェンスは、要求されない限り不要です。仕様が曖昧な場合は、参照コードのパターンに一致する合理的な選択をしてください。
visibility: public
model: gemini-2.5-flash
resourceLimits:
temperature: 0.2
tags:
- coding
- delegation
「出力はコードのみ」という指示は重要です。これがなければ、モデルはすべてをマークダウンフェンスと説明的な散文で囲み、Claudeはそれを解析する必要があります。
ルーティング
この最初のバージョンは、CLAUDE.md内のルーティングルールのブロックでした。それはある程度機能しました。Claudeは指示を読み、自己ルーティングしてPortalに送られました。しかし、問題がありました。ルールは助言であり、強制ではありませんでした。Claudeはそれらを無視することができました。そして、各プロジェクトは指示の独自のコピーを必要としました。
現在のバージョンはshuntというClaude Codeプラグインです。委任はPortal CLIアクションレジストリを通じて行われるため、プラグインはAiKAプラグインが有効な任意のPortalインスタンスに対して機能します。
レイヤー1: フック
Claude Codeのフックは、各ツール呼び出しの前に発生します。Shuntは2つのPreToolUseフックを登録します。
check-file-size: 各Read呼び出しで発生します。ファイルが設定可能な行数しきい値(デフォルト: 350)を超えた場合、フックは読み取りをブロックし、Claudeに/bulk-readerスキルを使用するように指示します。ターゲットを絞った読み取りは通過します。Claudeは既に必要なセクションを知っています。
check-bash-read: 大きなファイルに対するcat、head、tail、less、moreをキャッチします。パイプされたコマンド(cat file | grep)は、それらがターゲットを絞った読み取りであるため通過します。
しきい値は、SHUNT_MIN_LINES環境変数を通じて設定可能です。シェルのプロファイルまたは.claude/settings.jsonに設定してください。
{
"env": {
"SHUNT_MIN_LINES": "500"
}
}
レイヤー2: スクリプト
Portal CLI呼び出しをラップする2つのbashスクリプトがあります。Claudeは名前付き引数でスクリプトを呼び出します。スクリプトは、リクエストの構築、アクションの呼び出し、エラーのアンラップ、標準エラーへのトークン使用量の報告など、すべてを内部で処理します。
モードは名前でアドレス指定され、Portalによって解決されます。大文字小文字を区別せず、まずあなたのモード、次にあなたのチームのモード、そして公開モードが優先されます。公開のbulk-readerをカスタマイズされたバージョンにフォークすると、設定なしであなたのバージョンが自動的に優先されます。
bulk-readは、各ファイルをXMLタグでラップして明確な境界を作り、質問とともにbulk-readerモードに送信します。
bulk-read --question "このサービスは何をしますか?" --paths src/Service.java src/Handler.java
# フォローアップ: 同じパスで再度質問する
bulk-read --question "データベースを呼び出すメソッドはどれですか?" --paths src/Service.java src/Handler.java
すべての委任はワンショットです。呼び出しは一時的であり(サーバーサイドには何も保存されません)、フォローアップでファイルを再送信することは、コーパスがワーカーモデルに送信され、Claudeのコンテキストには決して入らないため、重要な部分では無料です。
code-writeは、仕様と参照ファイルをcode-writerモードに送信し、出力からマークダウンフェンスを剥がし、直接ディスクに書き込むことができます。Claudeは生成されたコードを決して見ません。参照ファイルは必須です。パターンに一致するファイルがないと、ワーカーはプロジェクトのどのファイルにも適合しないコンテキストフリーのコードを生成します。
code-write --spec "UserServiceのテストを作成" --reference tests/OrderTest.java --target tests/UserTest.java
# 標準出力に書き出す
code-write --spec "設定スタブを生成" --reference config/existing.yaml
レイヤー3: スキル
2つのスキルファイルが、Claudeにいつどのようにスクリプトを呼び出すかを指示します。スキルは、説明と使用例を含むマークダウンファイルです。フックが読み取りをブロックすると、ブロックメッセージはClaudeに/bulk-readerスキルを指し、正確な呼び出し構文が表示されます。
このレイヤリングにより、システムは正常に劣化します。Claudeがスキル説明を読まなくても、フックは依然として高価な読み取りをブロックします。スキルはリダイレクトをよりスムーズにするだけです。
ベンチマーク
Javaモノレポを4つのシナリオでテストし、Claudeがファイルを直接読み取る場合に消費するトークンと、bulk-readerの要約を消費する場合、またはcode-writerを介してコードを書き込む場合のトークンを測定しました。平均的なbulk-readの節約量は約90%でした。
code-writeシナリオは、トークンでの測定がより困難です。shuntがない場合、Claudeは参照ファイルを読み取り、出力を高価な出力トークンとして生成します。shuntを使用すると、コードは直接ディスクに送られ、Claudeはそれを決して見ません。
うまくいかないこと
編集を委任することはできません。ワーカーモデルの要約には、信頼できる行番号が含まれていません。Claudeが分析に基づいて編集を行う必要がある場合、依然として特定のセクションを直接読み取る必要があります。フックは、この目的のために(オフセット/制限付きで)ターゲットを絞った読み取りを許可しているため、委任は理解のためのトークンを節約します。
推論を委任することはできません。ワーカーモデルは表面的なパターンを見つけましたが、私のテストでは微妙なスレッドセーフティバグを見逃しました。Claudeは、適切なコンテキストを与えられると、数秒でそれを発見しました。ルーティングは、デバッグ、アーキテクチャ上の決定、および安全クリティカルなコードを明示的に除外します。
レイテンシが積み重なります。各委任はネットワークの往復です。Claude CodeからPortalバックエンドへ、ワーカーモデルへ、そして戻ります。応答は通常10〜30秒かかり、Portalは単一の呼び出しを30秒に制限しているため、非常に大きな生成はより小さな呼び出しに分割する必要があります。これは大きな読み取りには許容できますが、小さな読み取りには逆効果です。行数しきい値が存在するのはこのためです。それ以下では、オーバーヘッドが大きすぎます。