0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Claude Codeの推論を国内に置き換える — ai& InferenceのAnthropic互換APIで、AIエージェント開発はどこまで成立するか

0
Posted at

01-eyecatch.png

結論から(忙しい人向け)

観点 結果
Claude Code直結(Anthropic互換) ✅ 動く。env 7行だけ。LiteLLMもプロキシも不要
ツール連鎖(実タスク完走) GLM-5.2: 2/2 ✅ / Kimi-K2.7-code: 2/2 ✅(本家Opus 4.8も2/2)
prompt caching 自動でやってくれた。cache_control 不要。 実測79〜97%ヒット、キャッシュ入力は通常の1/4単価
1タスクあたり円コスト(対 本家Opus 4.8) Kimi-K2.7-code: 1/5〜1/6 / GLM-5.2: 1/2〜1/3.4
速度 Kimi はタスクBで本家より2.1倍速い。タスクAは1.3倍遅い。GLMは2.6倍遅い
/cost の信頼性 入力トークンを0で返すので実額の約1/5に過少表示される
現実的な使いどころ 雑務枠・サブエージェント枠だけ逃がす / コードを国外に出せない案件

いちばん驚いたのは、2モデルとも2タスクとも、人間の介入ゼロで完走したことです。「非Claudeモデルはツール連鎖の途中で壊れる」という予想は外れました。壊れたのは計測のほうでした。

この記事でやること

ai& Inferenceは「OpenAIとAnthropicのSDKに互換」を謳う国内推論プラットフォームです。

この記事では、Anthropic互換APIのクライアントとして一番要求が厳しいもの——つまり Claude Code ——をai&に直結します。チャット1往復なら互換レイヤーの粗は見えませんが、Claude Codeは1つの指示の裏でツール呼び出し(ファイル読み書き・Bash実行)を何十回も連鎖させ、ストリーミングでしか喋らず、システムプロンプトにcache_controlを付け、anthropic-betaヘッダーを送りつけてきます。互換を名乗るAPIにとって、これ以上意地悪なクライアントはありませんね。

手順

繋ぐ手順そのものは、実はai&の公式ドキュメントにClaude Code連携ページがあります。だからこの記事の価値は「繋ぎ方」ではありません。繋いだあとに何が静かに壊れるかです。

いちばん大きいのはこれでした。

Claude Codeの /cost は、ai&に対しては嘘をつく。 入力トークンを常に0と記録するので、表示される金額は実額を大幅に下回る。

なぜそうなるかを含めて、以下を実測で埋めています。

  1. Anthropic互換エンドポイントの素の挙動(認証・tool_use・betaヘッダー・prompt caching)
  2. 実際のコーディングタスク2本(リファクタ1本・バグ修正1本)を、本家Claude / GLM-5.2 / Kimi-K2.7-code で回し切る
  3. どこまで動いて、どこで壊れるかを、レイテンシ・成功可否・円コストの実測で並べる

今回は、「本家Claude Codeの代わりになるか」ではなく「どの作業なら任せられるか」まで突き詰めて、使い倒すのが目的です。

先に前提と免責

Anthropicが公式にサポートするClaude Codeの接続先は、本家APIのほかは「Claudeモデルの別ホスティング」(Amazon Bedrock / Google Vertex AI / Microsoft Foundry)と「Messages API互換ゲートウェイ」の2系統です。ai&のような互換APIで非Claudeモデルを動かす構成は公式サポート外で、tool callingやthinking、prompt cachingなどの動作保証はありません。この記事はその「保証がない」の中身を実測で埋める試みであって、本番運用の推奨ではありません。

また。ai&の売りは「ひとつのAPI、国内完結」「低コスト、低レイテンシー」です。コーディングエージェントは呼び出し回数が多く、リクエスト内容がコードそのものなので、データを国外に出せない案件・単価が厳しい案件でこの構成が成立するなら実務的な意味があります。このあたりが大事なのではないでしょうか。

本記事の円建ての数字は、**私のorgの課金通貨をJPYにできたので、**円で出ています。ai&の課金通貨はorg作成時にUSD/JPYのどちらかで固定される設定で、公式のモデルカタログはUSD表記です。自分のorgがどちらかは GET /v1/modelscurrency フィールドで確認してください。

いまの日本円、弱い……っ!!

Step 0:Claude Codeを繋ぐ前に、素のMessages APIで互換を確かめる

いきなりClaude Codeを繋ぐと、壊れたときに「Claude Codeの設定が悪い」のか「エンドポイントの互換が不完全」なのか切り分けられなくなります。まず素のMessages API形式で、tool定義を1つ含むリクエストを投げます。

ベースURLは https://api.aiand.com です。ここは間違えやすくて、先行記事にある /anthropic/v1/messages404です。Anthropic互換もOpenAI互換も同じホストの /v1 配下に同居しています。

curl -sS "https://api.aiand.com/v1/messages" \
  -H "content-type: application/json" \
  -H "Authorization: Bearer $AIAND_API_KEY" \
  -H "anthropic-version: 2023-06-01" \
  -d '{
    "model": "zai-org/glm-5.2",
    "max_tokens": 512,
    "tools": [{
      "name": "get_file",
      "description": "Read a file from the repo",
      "input_schema": {
        "type": "object",
        "properties": { "path": { "type": "string" } },
        "required": ["path"]
      }
    }],
    "messages": [{ "role": "user", "content": "README.md を読んで" }]
  }' | jq .

結果(bench/probe_step0.ps1 で4パターン投げたもの)。

プローブ 結果
Authorization: Bearer ✅ 200 / stop_reason: tool_use
x-api-key ✅ 200(両方通る
anthropic-version ヘッダーなし ✅ 200(必須ではない)
anthropic-beta: prompt-caching-2024-07-31,... ✅ 200(弾かれない

4つとも通りました。返ってきた tool_use ブロックはこの形です。

{
  "type": "tool_use",
  "id": "chatcmpl-tool-bf52fd0850de7256",
  "name": "get_file",
  "input": { "path": "README.md" }
}

idchatcmpl-tool-... になっているのが面白いところで、これはai&が内部でOpenAI形式に変換して推論し、Anthropic形式に戻していることの痕跡です(公式も「compatibility adapter」と明言しています)。形式としては正しいのでClaude Codeは問題なく食べます。

ここで2つ、ドキュメントに反する / 載っていない事実が出ました。

公式のMessages APIページは「認証は他のエンドポイントと同じ Authorization: Bearer」と書いていますが、実際は x-api-key も通ります。移行ページがAnthropic SDKの api_key= を例示している(=x-api-key が飛ぶ)ので、こちらが正です。つまりClaude Codeの ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKENどちらでも動く。ただし公式手順に合わせて AUTH_TOKEN 側を使うのが無難だとおもいます。

また、anthropic-beta ヘッダーが素通りします。これは「対応している」のではなく「知らないヘッダーを無視している」だけで、公式も「対応する概念のないパラメータは黙って無視する」と明記しています。だから CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1 は不要です。 互換ゲートウェイでこれを立てるのは定番の作法ですが、ai&に関しては立てなくても落ちません。

Step 1:Claude Codeの接続先を差し替える

公式のClaude Code連携ページどおりに書くとこうです。~/.claude/settings.json(または案件ディレクトリの .claude/settings.local.json)の env ブロックに置きます。

{
  "env": {
    "ANTHROPIC_BASE_URL": "https://api.aiand.com",
    "ANTHROPIC_AUTH_TOKEN": "sk-...",
    "ANTHROPIC_API_KEY": "",
    "ANTHROPIC_DEFAULT_OPUS_MODEL": "moonshotai/kimi-k2.7-code",
    "ANTHROPIC_DEFAULT_SONNET_MODEL": "moonshotai/kimi-k2.7-code",
    "ANTHROPIC_DEFAULT_HAIKU_MODEL": "deepseek-ai/deepseek-v4-flash",
    "CLAUDE_CODE_SUBAGENT_MODEL": "moonshotai/kimi-k2.7-code"
  }
}

(メインを moonshotai/kimi-k2.7-code にしているのは、後述のベンチで一番成績が良かったからです)

解説していきます。

ANTHROPIC_API_KEY を空文字で明示する。 ANTHROPIC_API_KEYx-api-keyANTHROPIC_AUTH_TOKENAuthorization: Bearer として送られます。ai&は両方受けるので「どちらでも動く」のですが、両方設定すると優先順位で混乱します。公式が空文字を明示しているのはそのためです。

モデルは4スロットある。 Claude Codeは内部でOpus/Sonnet/Haiku相当の3枠に加えて、サブエージェント枠を使い分けます。ANTHROPIC_DEFAULT_HAIKU_MODEL は会話タイトル生成などの雑務、CLAUDE_CODE_SUBAGENT_MODEL はTaskツールで起動する子エージェントが使います。ここを未設定にすると雑務やサブエージェントまで大きいモデルに流れて円が漏れます。ai&のCatalogで一番安い軽量モデル(deepseek-ai/deepseek-v4-flash、入力¥25/出力¥40)を指しておきます。

モデルIDは自分のorgで確認する。 公式のClaude Code連携ページは moonshotai/kimi-k2.6 を例示していますが、私のorgのカタログにこのモデルはありませんでした。カタログはorgごとに違います。GET /v1/models が唯一の真実です。

(Invoke-RestMethod https://api.aiand.com/v1/models `
  -Headers @{ Authorization = "Bearer $env:AIAND_API_KEY" }).data |
  Select-Object id, currency, input_per_1m, output_per_1m

私のorg(JPY建て)ではこう返りました。単価は100万トークンあたりの円です。

モデルID 入力 出力 キャッシュ入力 コンテキスト
qwen/qwen3.6-27b 無料 無料 262K
deepseek-ai/deepseek-v4-flash ¥25 ¥40 ¥10 1M
openai/gpt-oss-120b ¥25 ¥95 ¥10 131K
google/gemma-4-31b-it ¥30 ¥80 ¥8 262K
moonshotai/kimi-k2.7-code ¥125 ¥560 ¥30 262K
deepseek-ai/deepseek-v4-pro ¥160 ¥400 ¥30 1M
zai-org/glm-5.2 ¥160 ¥650 ¥40 1M

cached_input_per_1m という列があります。これは公式のカタログにもPricingページにも載っていないフィールドです。 自分でAPIを叩いてると、時々こういうのを見つけられて、わくわくしますね。

本家と切り替えて使うので、settings.jsonに固定せずaliasで持つ手もあります。

# ~/.bashrc
alias claude-aiand='ANTHROPIC_BASE_URL=https://api.aiand.com ANTHROPIC_AUTH_TOKEN=$AIAND_API_KEY ANTHROPIC_DEFAULT_SONNET_MODEL=zai-org/glm-5.2 ANTHROPIC_DEFAULT_HAIKU_MODEL=deepseek-ai/deepseek-v4-flash claude'
alias claude-official='env -u ANTHROPIC_BASE_URL -u ANTHROPIC_AUTH_TOKEN -u ANTHROPIC_DEFAULT_SONNET_MODEL -u ANTHROPIC_DEFAULT_HAIKU_MODEL claude'

claude-official側で env -u を使って明示的に変数を剥がしているのがミソで、これをやらないとexportの残留で「本家に戻したつもりがai&に投げていた」が起きます。

Step 2:本当に切り替わったかを確認する — ここが最大の罠

公式ドキュメントの末尾に、小さくこう書いてあります。

以前にAnthropicアカウントでClaude Codeにサインインしていた場合、claude 内で一度 /logout してください。キャッシュされたOAuth認証情報が環境変数や settings.json より優先され、黙ってai&ではなくAnthropicに接続し続けます。

エラーは出ません。課金も本家に走ります。ベンチマークを組んでいる人間にとってこれは致命的で、「GLM-5.2の結果」として集めた表が、実は全部本家Claudeの結果だった、という事故が起き……まし……た!!
(だいぶ書き直しました)

対策として、まず /status で適用中のベースURLとモデルを目視します。

/status

そのうえで、ヘッドレス実行では機械的に検証します。 claude -p --output-format stream-json の最終行(type: "result")には modelUsage というオブジェクトが入っていて、そのキーが実際に課金されたモデル名です。

"modelUsage": {
  "deepseek-ai/deepseek-v4-flash": { "inputTokens": 0, "outputTokens": 13, ... },
  "zai-org/glm-5.2":               { "inputTokens": 0, "outputTokens": 2058, ... }
}

ここに claude-* が現れたら、その計測結果は捨てる。本記事のベンチスクリプトはこれを実装していて、食い違ったら verified を強制的に false にします。ゲートを機械化しないとエージェントのベンチマークは成立しません。

おや、上のJSONをよく見てください。inputTokens0 です。これは書き間違いではありません。

/cost は嘘をつく:ストリーミングで入力トークンが消える

ai&のAnthropic互換 /v1/messages は、ストリーミング時に入力トークン数を返しません。

非ストリーミングなら正しく返ります。

// POST /v1/messages(stream なし)
"usage": { "input_tokens": 158, "cache_read_input_tokens": 0, "output_tokens": 28 }

ところが "stream": true を付けると、こうなります。

event: message_start
data: {"type":"message_start","message":{..."usage":{"input_tokens":0,"output_tokens":0}}}

event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"max_tokens"},"usage":{"output_tokens":16}}

event: message_stop

message_startinput_tokens0message_delta には output_tokens しかなく、cache_read_input_tokens はどこにも現れません。同じプロンプトをOpenAI互換の /v1/chat/completions にストリーミングで投げると、最終チャンクに "usage":{"prompt_tokens":1668,"completion_tokens":16} が入り、さらに event: metrics というトレーラーで実課金額まで返ってきます。

data: {"choices":[],"usage":{"prompt_tokens":1668,"total_tokens":1684,"completion_tokens":16}}
data: [DONE]
event: metrics
data: {"tokens":{"input":1668,"output":16,"total":1684,"cached":1664},"cost":0.0776,"currency":"jpy","ttft_ms":245,"inference_ms":529}

04-streaming-usage.png

つまり入力トークン数もキャッシュヒット数も実課金額も、OpenAI互換面には全部あって、Anthropic互換面のストリーミングだけが落としている。 変換アダプタの実装漏れです。

そしてClaude Codeはストリーミングでしか喋りません。 帰結はこうです。

  • /cost の表示 → 入力ぶんが丸ごと抜けた金額
  • stream-jsonresult.usage / modelUsageinputTokens: 0
  • X-Aiand-Metrics: true を送っても、Anthropic互換面のストリーミングでは event: metrics が来ない

先行記事や、これから書かれるであろう「ai&でClaude Codeを動かしてみた、コストは/costでこれだけ」という記事の数字は、出力トークンぶんしか数えていません。 私も最初これで表を作りかけました。

では正解はどこにあるか

ai&自身の請求ログです。GET /logs がリクエスト単位で、入力・キャッシュ・出力の各トークン数と、実際に課金された金額を返します。

(Invoke-RestMethod "https://api.aiand.com/logs?range=1h&limit=5" `
  -Headers @{ Authorization = "Bearer $env:AIAND_API_KEY" }).data |
  Select-Object created_at, model, input_tokens, cached_tokens, output_tokens, cost, currency
created_at                      model            input_tokens cached_tokens output_tokens cost       currency
2026-07-09 10:07:08.51414+00    zai-org/glm-5.2  33927        32512         148           1.62308000 jpy
2026-07-09 10:07:02.847886+00   zai-org/glm-5.2  31890        31744         37            1.31717000 jpy

1リクエストで入力3.3万トークン。Claude Codeのシステムプロンプト+ツール定義+会話履歴です。これが /cost では0として扱われていた。

本記事のコスト表は、すべてこの GET /logs を実行時刻の窓で突き合わせて集計しています(bench/cost_from_logs.ps1)。

prompt caching:cache_controlは要らない。勝手に効く

ここが下書き段階で一番読み違えていたところです。私は「互換エンドポイントは cache_control を無視して全額課金するのか、エラーで弾くのか」を見どころだと思っていました。どちらでもありませんでした。

ai&のキャッシュは自動プレフィックス検出です。公式Pricingページの言葉を借りると「繰り返されるプロンプトのプレフィックスは自動的に検出され、再利用された先頭部分は割引後のcached input rateで課金される。cache_control マーカーもコード変更も不要」。

実測します。1024トークン超のシステムプロンプトを固定して、同じリクエストを2回投げる。

input_tokens cache_read_input_tokens X-Cost
cache_control あり・1投目 1189 0 ¥0.20064
cache_control あり・2投目 37 1152 ¥0.06240
cache_control なし・1投目 37 1152 ¥0.06240
cache_control なし・2投目 37 1152 ¥0.06240

読み取れることが3つあります。

  1. cache_control は付けても弾かれない(黙って無視される)。だからClaude Codeがどれだけcache_controlを撒いても壊れない。
  2. cache_control を外してもキャッシュは効く(3行目が1投目なのにいきなりcache_read=1152)。プレフィックスが一致していればマーカーは関係ない。
  3. 課金額が理屈と一致する。2投目は 37×¥160/M + 1152×¥40/M + 16×¥650/M = ¥0.0624cached_input_per_1m(GLM-5.2で¥40、通常入力の1/4)が効いている。

条件は「1024トークン以上」「128トークン刻み」「org単位」「無操作から約10分」。Claude Codeは毎ターン同じシステムプロンプト+ツール定義を先頭に積むので、2ターン目以降はほぼ全部キャッシュに乗ります。 実際、上の請求ログの行では入力33,927トークンのうち32,512がキャッシュヒット(96%)。後述するタスクAの実行では、入力219万トークンの**97.2%**がヒットしました。

03-cache.png

これは重要で、「非Claudeモデルを互換APIで動かすとキャッシュが効かないから高くつく」という直感は、ai&に関しては間違いです。むしろ、このキャッシュが無ければこの構成の経済性は成立しません。

見えないのに課金される:reasoningトークン

もうひとつ、Anthropic互換面の変換で落ちるものがあります。reasoningトークンです。

GLM-5.2は推論モデルで、内部推論を出力トークンとして吐きます。OpenAI互換面では delta.reasoning として流れてきます。ところがAnthropic互換面では、返ってくる content ブロックは text だけ。thinkingブロックはありません。

POST /v1/messages  "27*43をよく考えて計算し、最後に数字だけ答えて。"

usage: {"input_tokens":21, "output_tokens":341}
content: [{ "type": "text", "text": "27 * 43 = **1,161**" }]
X-Cost: 0.22501 jpy

出力341トークンぶん課金されて、返ってきたのは19文字です。 残り330トークンぶんの推論は、アダプタが捨てています。掛け算1回に¥0.225。出力単価は入力の4倍(¥650対¥160)なので、ここが効きます。

Claude Codeで /think 相当の深い思考が表示されないのも同じ理由です。推論の中身は見られないが、代金は払う。 推論モデルを互換API越しにエージェントで回すときの、地味だが効く税金です。

Step 3:実タスクで回す

「Hello Worldが返った」はエージェントの検証になりません。ツール呼び出しが連鎖する実タスクを2本用意しました。タスク・計測スクリプト・生ログ全部をここに置いてあります。

github.com/Subara3/aiand-claude-code-bench

以下の数字はすべて results/*.stream.jsonlresults/*.meta.json から再現できます。「本当か?」と思ったら手元で回してください。

タスクA(リファクタ): 約300行のPHP集計スクリプトを「型付きの名前付き関数に分割し、PHPStanレベル6を通し、既存の挙動をバイト単位で変えない」よう指示。合格条件は3本のスクリプトで機械判定します(phpstan --level=6 がエラー0 / verify_behavior.php がgolden一致 / verify_structure.php が型付き関数5個以上を検出)。

タスクB(バグ修正): pytestが2件落ちるPythonリポジトリを渡して「テストを変更せずに全部通して」とだけ指示。cart/pricing.py に境界条件バグが2つ(qty > VOLUME_THRESHOLD は正しくは >=discounted_subtotal > FREE_SHIPPING_AT も同様)。docstringに「ちょうど10個/ちょうど8000円も対象」と書いてあるので、コードを読めば直せます。

これを 本家Claude(比較基準) / GLM-5.2 / Kimi-K2.7-code の3構成で、それぞれ新規セッションで claude -p のヘッドレス実行します。

余談:ベンチマークの合格条件は機械化しないと成立しない

タスクAの初版は、合格条件を「phpstanが通る+挙動が変わらない」だけにしていました。すると本家Claudeは両方すでに満たしていることを見抜いて、何も変更せずに完了し、逆に質問してきました。 ヘッドレスなので誰も答えられません。正しい振る舞いです。

そこで verify_structure.php(型宣言付きの名前付き関数が5個以上あるかを機械判定)を合格条件に足しました。「リファクタしろ」を検証可能な述語に翻訳しないと、賢いエージェントほど「やる必要がない」と正しく結論します。

余談その2:PowerShellが日本語プロンプトを殺した

これは自分の計測harnessのバグですが、同じ罠を踏む人が多いはずなので書きます。

最初、プロンプトはパイプで渡していました。

$prompt | claude -p --output-format stream-json ...

Kimi-K2.7-codeがこれでタスクを1回も読まずに終了しました。ツール呼び出し0回、20秒で終了。ログを見るとこう返してきます。

I see your message got garbled (lots of ? characters). Could you retype your question?

原因はPowerShell 5.1の $OutputEncoding が既定で US-ASCII であることです。ネイティブexeへのパイプはこのエンコーディングを通るので、日本語のプロンプトが全部 ? に潰れて子プロセスに届いていました。

b'\xef\xbb\xbf??????pytest ?2??????\r\n'

06-mojibake.png

危ないのはここからです。本家Claudeも、GLM-5.2も、この文字化けしたプロンプトでタスクを解いてしまいました。 pytest2 という英数字の断片と、リポジトリの中身から意図を復元したのです。つまり文字化けに気づかないまま「Kimiだけ失敗した」ということになってしまいます。

引数で渡すよう直したら(claude -p $prompt)、Kimiは7回のツール呼び出しで一発合格しました。モデルの優劣ではなく、計測系のバグでした。 この記事の全数値は修正後に取り直しています。

結果

6本すべて合格。介入回数は全部0です(ヘッドレスなので介入しようがない、とも言えます)。

比較基準の「本家」は **Claude Opus 4.8(1Mコンテキスト)**+雑務枠に Haiku 4.5。ai&側は各モデル+雑務枠に deepseek-ai/deepseek-v4-flash です。

構成 タスク 合否 所要 うちAPI ツール呼出 ターン 入力tok キャッシュ率 出力tok 実課金
本家 Opus 4.8 A 576.0s 317.3s 25 26 922,899 23,497 $1.4847(≈¥230)
本家 Opus 4.8 B 154.4s 33.7s 7 8 165,191 1,849 $0.2874(≈¥44.5)
GLM-5.2 A 1,483.8s 908.6s 53 54 2,199,326 97.2% 33,826 ¥117.24
GLM-5.2 B 113.9s 33.9s 10 11 202,365 84.4% 2,105 ¥13.11
Kimi-K2.7-code A 765.7s 178.0s 38 39 1,085,603 95.6% 14,753 ¥45.32
Kimi-K2.7-code B 72.3s 14.1s 7 8 136,386 79.0% 480 ¥7.03

ai&側の「実課金」は GET /logs の実額合計です(/cost の表示ではありません)。本家の円換算は1USD=155円と仮定した参考値。

1タスク完走あたりの実課金額。タスクA: 本家¥230.1 / GLM-5.2 ¥117.24 / Kimi-K2.7-code ¥45.32。タスクB: 本家¥44.5 / GLM ¥13.11 / Kimi ¥7.03

読みどころを4つ。

Kimi-K2.7-code がはっきり本命です。 タスクBは本家より2.1倍速く、6.3倍安い(72.3秒/¥7.03 対 154.4秒/¥44.5)。しかもツール呼び出し7回・8ターンで、本家とまったく同じ手数です。タスクAは1.3倍遅いものの5.1倍安い。coding特化を名乗るだけのことはあります。

GLM-5.2は「合格したが高くつく」。 タスクAで53回ツールを呼び、24.7分かけました。本家は25回・9.6分。手数が2倍なら入力トークンも2倍積み上がるので、安いはずの単価が食われます。結果、本家比たった1.96倍安。単価表だけ見て選ぶと外すという良い実例です。同じ「✅」でも中身が違う。

05-tool-calls.png

キャッシュが無ければ経済性は崩れていました。 GLMのタスクAは、キャッシュ無しなら¥373.80のところ¥117.24¥256.57、率にして69%をキャッシュが削っています。 入力219万トークンの97.2%がキャッシュヒット。Claude Codeは毎ターン同じシステムプロンプトとツール定義を先頭に積むので、この構成と自動プレフィックスキャッシュの相性が異常に良い。「互換APIだとキャッシュが効かないから高い」は、ai&に関しては完全に的外れです。

そして /cost はどのくらい嘘をつくのか。 GLMのタスクAで、Claude Codeの表示は¥24.10(=出力33,826トークンぶんだけ)。実際の請求は¥117.244.9倍の過少申告です。入力219万トークンが丸ごと0として数えられていました。この記事を書き始めたとき、私はこの¥24.10を表に載せるつもりでした。

壊れるポイント(予測と実際)

事前に「ここで壊れるはず」と当たりをつけていた箇所と、実際どうだったかを並べます。

予測 実際
anthropic-beta ヘッダーで落ちる ❌ 落ちない。未知パラメータは黙って無視される。CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS は不要
cache_control を弾かれる or 全額課金 ❌ どちらでもない。マーカー不要の自動プレフィックスキャッシュ。実測79〜97%ヒット
tool_useブロックの形が崩れる ❌ 崩れない(idchatcmpl-tool-* になるだけ)
⚠️ ストリーミングで input_tokens が0/cost が機能しない
⚠️ reasoningトークンが不可視のまま課金される
⚠️ OAuthキャッシュが env を黙って上書きする/logout 必須)

WebSearchツール。 Claude CodeのWebSearchは本家API側の機能なので、非Anthropicエンドポイントでは使えません。これは互換の問題ではなく仕様です。検索が要るワークフローでは、検索系MCPサーバー(Tavily等)を挿すのが定石。今回のタスクA/Bは検索不要な設計にして影響を切り離しました。

MCPツール検索。 ANTHROPIC_BASE_URLを非Anthropicホストに向けると、MCPのツール検索がデフォルトで無効になります。プロキシ側がtool_referenceブロックを転送できるならENABLE_TOOL_SEARCH=trueで戻せますが、今回のタスクはMCPを使わない設計なので未検証です。

ついでに見えたこと。 OpenAI互換面のストリーミングチャンクは、実際に配信されているモデル名を返します。zai-org/GLM-5.2-FP8FP8量子化されたウェイトです。system_fingerprintvllm-0.23.0-tp8。8-way tensor parallelのvLLMで、FP8で回っている。Anthropic互換面はこの情報を落とします(model はリクエストで指定したIDをそのまま返す)。品質を評価するときの前提として知っておく価値があります。

Plan B:LiteLLMを挟む構成

今回は直結が通ったのでPlan Bは不要でしたが、input_tokens が0になる問題をクライアント側から回避したいなら、LiteLLMを変換層として挟む手はあります。

pip install 'litellm[proxy]'
cat > litellm_config.yaml << 'EOF'
model_list:
  - model_name: glm-5.2
    litellm_params:
      model: openai/zai-org/glm-5.2          # OpenAI互換として扱う
      api_base: https://api.aiand.com/v1     # ai&のOpenAI互換エンドポイント
      api_key: os.environ/AIAND_API_KEY
EOF
litellm --config litellm_config.yaml --port 4000

Claude Code側はANTHROPIC_BASE_URL=http://localhost:4000に向けるだけ。ミソはai&のOpenAI互換面を叩かせることで、あちらはストリーミングでも prompt_tokenscached を返します。LiteLLMがそれをMessages APIのusageに詰め直すので、/cost が復活します。翻訳レイヤーが1枚増える分のレイテンシが対価です。

実はこれ、Anthropicの公式ドキュメントが「LLMゲートウェイ」として案内している構成そのもので、直結よりむしろ正攻法だったりします。

で、どう使うか

「全部置き換えられたらな」と思いますが、そうもいきません。今回のタスクは合格条件が機械判定できる、境界のはっきりしたものでした。

仕様が曖昧な設計判断や、大規模な横断リファクタで同じ結果が出るとは限りません。それでも手数と時間を許容できるなら、実務的な選択肢になることは分かってきたかなと思っています。

こういう工夫が出来そうです。

雑務だけ逃がす構成。ANTHROPIC_DEFAULT_HAIKU_MODELCLAUDE_CODE_SUBAGENT_MODEL だけai&の軽量モデル(deepseek-ai/deepseek-v4-flash、入力¥25/出力¥40)に向けて、メインは本家のまま。タイトル生成やサブエージェントの雑リクエストが国内の安い推論に流れ、本体の品質は落ちません。設定2行で今日からできます。

タスクで切り替える構成。今回のタスクBのような「テストが落ちている、直せ」という閉じたループは、Kimi-K2.7-codeが本家と同じ手数で、2.1倍速く、6.3倍安く片付けました。落ちるテストが正解を教えてくれる作業は、ここに逃がして良い。逆にタスクAのような「良い設計に直せ」は、本家の25手に対してGLMが53手かかったように、手数=入力トークンが膨らんで単価差を食い潰します。

案件で切り替える構成。コードを国外APIに送れない案件・予算が厳しい案件だけclaude-aiandで起動する。案件ディレクトリごとに.claude/settings.local.jsonで固定してしまうのが運用としては堅いです(*.local.json はClaude Codeの慣習でgit ignoreされるので、キーがコミットされません)。

モデルの選び方については、単価表を信じないでください。 GLM-5.2(¥160/¥650)とKimi-K2.7-code(¥125/¥560)の単価差は3割弱ですが、実際に払った額は2.6倍開きました(タスクA: ¥117.24 対 ¥45.32)。差を作るのは単価ではなく手数です。

エージェントのコストは「1タスク完走あたりいくらか」でしか測れません。

まとめ

  • 接続は ANTHROPIC_BASE_URL=https://api.aiand.comANTHROPIC_AUTH_TOKENANTHROPIC_API_KEY="" を明示。モデルは4スロット(Opus/Sonnet/Haiku/Subagent)あり、下2つを忘れると円が漏れる
  • 繋いだあと必ず /logout する。 OAuthキャッシュが環境変数を黙って上書きし、ai&に繋いだつもりで本家に課金される
  • /cost を信じない。 Anthropic互換面のストリーミングは input_tokens を0で返す。実額は GET /logs にしかない(実測で4.9倍の乖離)
  • prompt cachingは自動cache_control は不要で、付けても無害。実測79〜97%ヒット、キャッシュ入力は通常の1/4単価。これが無ければ経済性は成立しない
  • 推論モデルのreasoningトークンは見えないが課金される
  • ツール連鎖は壊れなかった。GLM-5.2もKimi-K2.7-codeも2タスクを介入ゼロで完走。Kimi-K2.7-codeは本家Opus 4.8と同じ手数で6.3倍安い
  • 単価表でモデルを選ばない。 コストを決めるのは単価ではなく手数(=積み上がる入力トークン)
  • 非Claudeモデル接続は公式サポート外。その線引きを理解した上で、「国内完結・円建て」が要る場面の選択肢として持っておく価値は実測の通り

Claude Codeは本家モデル前提で調律されたツールです。

しかし、それでも接続の口が環境変数2つ分だけ開いていて、その隙間にai&のような国内推論を差し込めるわけですね。いやー、楽しい楽しい、繋ぎかえは楽しい。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?