ローカルで messages.create がうまく動いた瞬間と、それを本番ユーザーに開放した瞬間の間には、想像以上に大きな谷があります。私自身、夜中に「Claude が応答しない」「課金が爆発した」「ユーザーが空のレスポンスを受け取っている」といった通知で叩き起こされて、慌てて対応した経験が何度もあります。
そのたびに思うのは、本番でつまずく原因の大半は「Claude そのもの」ではなく「Claude を支えるインフラ」が欠けていることだ、という現実です。今日はその経験から逆算して、Claude API を本番に投入するときに最低限揃えておきたい8つの実装を整理します。
なぜ「動くコード」と「本番で持つコード」は別物なのか
ローカルで成功する messages.create の呼び出しは、たいてい以下の前提が暗黙に成立しています。
- ネットワークが安定している
- 自分以外に同じ API キーを叩いている人がいない
- レスポンスが数秒以内に返ってくる
- 失敗した瞬間にあなたが画面を見ている
本番では、この4つすべてが崩れます。AWS のリージョン切替、同僚の検証スクリプトと並走、Streaming の途中接続切れ、深夜帯の障害 — どれも一度は経験するはずです。だからこそ、コードよりもまず「壊れたとき何が起きるか」を先に設計することが必要になります。
1. レート制限を「自分側」でも見張る
Anthropic のレート制限はサーバー側で 429 を返してくれますが、それを待つだけではユーザー体験は守れません。アプリ側で同時実行数とトークン消費を見張る軽量なリミッターを入れておくと、429 が返る前にキューイングできます。
// lib/claude-limiter.ts
import Anthropic from "@anthropic-ai/sdk";
import pLimit from "p-limit"; // npm i p-limit
const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY! });
const limit = pLimit(8); // 同時実行 8 本に制限
export async function safeCreate(params: Anthropic.MessageCreateParams) {
return limit(async () => {
const start = Date.now();
try {
const res = await client.messages.create(params);
// 成功時のレイテンシを記録
console.log(JSON.stringify({ event: "claude_ok", ms: Date.now() - start }));
return res;
} catch (err: any) {
console.log(JSON.stringify({
event: "claude_err",
ms: Date.now() - start,
status: err.status,
type: err.error?.type
}));
throw err;
}
});
}期待する出力は1行のJSONログで、後段の集計に流せる形です。レート制限の詳細な設計はレートリミットのベストプラクティスで踏み込んでいるので、合わせて確認していただくと理解が深まります。
2. タイムアウトと指数バックオフを必ず重ねる
Claude のレスポンスは数十秒かかる場合があります。本番では「レスポンスが返ってこない」のではなく「クライアント側が先にタイムアウトしている」ことが大半です。SDK 既定のタイムアウトを過信せず、明示的に設定してください。
import { setTimeout as wait } from "node:timers/promises";
async function withRetry<T>(fn: () => Promise<T>, max = 3): Promise<T> {
for (let attempt = 0; ; attempt++) {
try {
return await fn();
} catch (err: any) {
const isRetriable = [429, 500, 502, 503, 504, 529].includes(err.status);
if (!isRetriable || attempt >= max) throw err;
const backoff = Math.min(2 ** attempt * 500 + Math.random() * 200, 8000);
await wait(backoff);
}
}
}529 は Anthropic 側の過負荷を表すコードです。これを再試行リストに入れておかないと、混雑時に大量のエラーをそのままユーザーに見せてしまいます。
3. プロンプトキャッシュを必ず張る
長いシステムプロンプトを毎回送信していると、月末の請求書を見て凍りつきます。プロンプトキャッシュを正しく張れば、同じシステムプロンプトの2回目以降は最大90%のコスト削減になります。
書き方は単純で、システムブロックに cache_control: { type: "ephemeral" } を付けるだけです。実装の詳細とトラップはプロンプトキャッシュで月額を半減させた設計記録で解説していますので、課金が気になる方は併せてどうぞ。
4. フォールバック先を一つは用意する
Anthropic のステータスページが赤くなる日は、年に数回必ず来ます。そのとき「Claude が止まったのでサービスも止まりました」では商売になりません。Sonnet → Haiku、あるいは Bedrock 経由 / Vertex AI 経由のフォールバックなど、最低でも1経路は用意しておきます。
完璧な多重化までは要りません。重要なのは「劣化して動き続ける」ことで、品質が落ちてもユーザーが何かしら受け取れる状態を作ることです。マルチモデル設計の指針はClaude API マルチモデルフォールバックで高可用性を作るに、Cloudflare AI Gateway を使う場合はAI Gateway で本番運用を一段引き上げるにまとめています。
5. 構造化ログとトレーシングを最初から入れる
「ログがあとで欲しい」と思った瞬間、それはもう遅いタイミングです。本番1日目から構造化ログを出してください。私が必ず仕込むのは下記4項目です。
- リクエスト ID(自前の UUID とAnthropic の
request_idの両方) - 入出力トークン数
- 使用モデル
- 終端ステータス(成功 / リトライ何回 / 最終エラーコード)
OpenTelemetry を使ったトレーシングまで入れる余裕があれば、Token-by-Token のレイテンシ分布まで取れて運用が楽になります。具体的な計装はClaude API の OpenTelemetry 観測ガイドにコード付きで載せていますので、設計に迷ったら参考にしてみてください。
6. 課金アラートと予算上限を二重で設定する
Anthropic Console には「Spend Limit」と「Spend Alert」の二段階があります。両方を必ず設定してください。アラートだけだと深夜の暴走を止められず、リミットだけだと気付けない状態が続きます。
加えて、自前の集計でも「直近1時間のトークン消費」をダッシュボード化しておくと、アラート発火前に異常を察知できます。社内ツールに繋がる場合は Slack の Webhook に送って、本番障害用チャンネルを作るのが手っ取り早い実装です。
7. 出力検証を必ず一段挟む
LLM の出力をそのまま下流に流すのは、パイプの外し忘れと同じくらい怖い行為です。JSON を期待しているなら zod で検証し、HTML を生成しているなら DOMPurify を通し、SQL を組み立てるなら絶対にパラメータ化する — これが最低ラインです。
import { z } from "zod";
const ClaudeOutputSchema = z.object({
intent: z.enum(["summarize", "translate", "search"]),
query: z.string().min(1).max(500),
language: z.string().length(2).optional(),
});
function parseClaude(text: string) {
const parsed = JSON.parse(text); // ここで失敗したらリトライ
return ClaudeOutputSchema.parse(parsed); // ここで失敗したらフォールバック
}response_format: { type: "json_object" } を使っていても、稀に壊れた JSON が返ります。「絶対に来ないと思っていたケース」が来たときの挙動を先に決めておくのが本番運用の作法です。
8. 障害時の「人間に届ける」経路を持つ
最後の一つは、技術ではなく運用の話です。本番で何かが壊れたとき、誰が、どの経路で、何分以内にそれを知るのか — これを決めていないインフラは、どれだけ精巧でも持ちません。私は最低限、以下の3つを揃えています。
- エラーレートが閾値を超えたら Slack にメンション付きで通知
- ユーザー側のエラー画面に「再試行」と「サポート連絡」の両方を出す
- 当番制の対応時間を1人月あたり数時間でも確保する
完璧な SRE 体制を1日で作る必要はありません。ただ「無音で壊れている時間がゼロに近い」状態は、最初から作っておく価値があります。
今日できる、最初の一歩
8項目を一気にやろうとすると挫折します。まずは「構造化ログ」と「タイムアウト+リトライ」の2つだけ、今日のうちに入れてみてください。この2つが入っていれば、たとえ他のインフラが未整備でも、障害時に「何が起きたか」をあとから追えます。追えるなら直せます。
もう少し踏み込んで、ハルシネーション対策や本番品質の検証パターンまで一気に見ていきたい方には、同じシリーズのハルシネーションを本番で抑える Claude API 多層防御アーキテクチャが踏み込んだ実装の参考になるはずです。
LLM 固有の運用知見というより、分散システム全般の設計言語を身につけられる一冊です。