トークンは足りないのに、枠は余っている
普段の開発はほとんど Claude Code のサブスクリプションで行っている。いまの Claude Fable 5.1 と Opus 5.5 は非常によく働いてくれて、SOTA と呼んで差し支えないと思う。唯一の問題は、トークンがすぐに足りなくなることだ。
その一方で、手元には他のモデルの枠が余っている。以前 Xiaomi の MiMo の無料枠をもらったし、他の中国のモデルもときどき格安で使える機会がある。友人が使わなくなった GLM のサブスクリプションもある。そこで、単純で、やることがほぼ決まっている作業はこれらのモデルに任せて、Claude には本当に Claude が必要な部分だけをやってもらえないかと考え始めた。
最初の試み、TUI 経由で別の harness を操作する
最初に試したのは herdr だ。Claude Code から herdr の API を呼び出し、別のペインで opencode などの harness を起動して指示を送り、画面を読み取って結果や進捗を確認できる。
動くには動いたが、どこか不自然だった。さらに悪いことに、Claude Code が他の harness の権限確認を承認できてしまうこと、そして二つの harness が互いに承認し合って権限チェックそのものを回避できてしまうことに気づいた。そもそも herdr 自体が、Claude Code の権限の仕組みや Codex のサンドボックスの完全に外側にある。これは危ないと感じた。
二度目の試み、skill の裏に置いた harness
以前、paimon という自作の harness も書いていた。これに組み込みの skill を追加して、Claude Code や Codex から直接呼び出せるようにした。使い方は claude -p に近いが、いくつか機能を足してある。呼び出し側は paimon のセッション履歴を範囲指定で問い合わせられる。また、paimon がタスクを終えてプロセスが終了したあとでも、起動し直して同じセッションの続きから作業させられる。
理屈の上では claude -p より優れているはずだが、実際の使い心地はやはり良くなかった。Claude がいつも paimon に仕事を任せてくれるわけではない。最終結果だけでなくもっと文脈を知りたいとき、paimon のセッション履歴をたどるには何ステップも必要で、そのステップ自体がトークンを消費する。単純なタスクでは、節約できた分よりも多くのトークンを使っていたのではないかと思う。
直感的には、本当の問題はもっと深いところにあった。skill にヒントを書いておいても、Claude Code は paimon を自分のサブエージェントの一つとしては扱ってくれなかったのだ。
着想
そこで思い出したのが、こうした安いモデルの多くは Anthropic の API と互換性があるということだ。以前にも、環境変数をいくつか設定するだけで Claude Code から使ったことがあった。
それならローカルにプロキシを立てればいいのではないか。プロキシを起動し、Claude Code をそこに向けて起動し、独自のモデル名を持つカスタムサブエージェントをいくつか登録する。LLM へのリクエストはすべてプロキシを通る。Fable や Opus 宛てのリクエストはそのまま Anthropic に転送する。カスタムサブエージェント、たとえば安価な explore エージェントからのリクエストは、MiMo や Kimi、GLM の Anthropic 互換エンドポイントに送る。Claude Code から見れば、安いモデルが組み込みのサブエージェントと同格の存在になる。
少し調べてみると、状況は思っていたよりも良かった。Claude Code はベース URL の環境変数に加えて、コマンドライン引数でサブエージェントを定義でき、それぞれに用途の説明、権限、使えるツールを指定できる。つまり Claude Code の設定には一切手を入れなくていい。ラッパーがプロキシを起動し、環境変数と追加の引数を付けて Claude Code を起動するだけだ。素の claude を実行すれば、これまでとまったく同じように動く。
既存の解決策もある。たとえば Claude Code Router だ。ただ、私が必要とする以上に多機能で、かなり複雑でもある。私の場合はもっと単純で、これらのモデルはもともと Anthropic 形式のプロトコルに対応しているので、プロトコル変換がいらない。だから自分で作ることにした。
cameo
期待したとおり簡単だった。4、5 ツイート分のプロンプト一つで、Claude Code と Opus 5.5 が一発で仕上げてくれた。
出来上がったのが cameo で、400 行ほどの小さな Go プログラムだ。名前は映画用語の cameo 出演、つまり短い特別出演から取っている。これらの他社製モデルがここで果たす役割はまさにそれだ。Claude Code の中にサブエージェントとして一、二場面だけ登場し、主役はあくまで Claude が務める。
cameo は TOML ファイルを読み込み、localhost で HTTP プロキシを起動し、設定されたサブエージェントをコマンドライン引数に変換して Claude Code を起動する。プロキシが見るのは各リクエストの model フィールドだけだ。そのモデルが cameo のサブエージェントのものであれば、プロバイダー側のモデル名と API キーに差し替えてそちらへ転送する。それ以外はすべて手を加えずに Anthropic へ送る。メッセージの他の部分を理解する必要がないので、コードはこれだけ小さく済んでいる。
以下はプロジェクトに付属している設定例だ。
[agents.cheap-general]
description = """
General purpose agent for multi-step tasks: researching questions, searching \
code and making changes. Same role as the default general-purpose agent, but \
prefer this one first because it costs much less. If the result is not good \
enough, fall back to general-purpose."""
prompt = """
You are a general purpose software engineering agent. Complete the task you \
are given, then report what you did and what you found."""
url = "https://api.deepseek.com/anthropic"
key = "$DEEPSEEK_API_KEY"
model = "deepseek-flash"
[agents.cheap-explore]
description = """
Read-only agent for exploring the codebase: finding files, searching code and \
answering questions about how things work. Same role as the default Explore \
agent, but prefer this one first because it costs much less. If the result is \
not good enough, fall back to Explore."""
prompt = """
You are a read-only codebase exploration agent. Find what you are asked for \
and report it with file paths and line numbers. Never modify anything."""
tools = ["Read", "Grep", "Glob"]
url = "https://open.bigmodel.cn/api/anthropic"
key = "$GLM_API_KEY"
model = "glm-5.3"
各 [agents.<name>] セクションが Claude Code のサブエージェントになる。description には、そのエージェントをいつ使うべきか、他のエージェントと比べてどの程度優先すべきかを書く。上の二つは組み込みの general-purpose と Explore に一対一で対応しているが、より安価な選択肢であることを明記してある。両方を残したくなければ、エージェントに Explore や general-purpose という名前を付ければ、組み込みのものを置き換えられる。
あとは claude を実行していた場面で cameo を実行するだけだ。引数はすべてそのまま claude に渡される。
実際に使ってみると、Claude Code は paimon を呼んでいた頃よりもはるかに頻繁に安い方のエージェントを選んでくれる。明示的に指定したときは特にそうだ。
Codex はどうするか
次は Codex に対応したいと考えていた。これらの安いモデルは OpenAI 互換のエンドポイントも提供しているからだ。しかし少し調べたところ、Codex はすでに Responses API へ全面的に移行しており、これらのモデルの大半は古い Chat Completions API にしか対応していないことがわかった。Codex に対応するにはプロトコル変換を自分で書くことになり、それでは Claude Code Router を再実装するのと変わらない。だからやらないことにした。少なくとも、これらのモデルが Responses API に対応するまでは。