# Claude構造化出力の失敗設計と再試行の実装

> Claudeの構造化出力はスキーマ準拠を保証するが、refusalやmax_tokensで崩れる。output_config.formatの制約と、中小企業が実装すべき検証・再試行の手順を整理する。

- Canonical: https://kuucorp.com/blog/claude-structured-outputs-failure-handling-smb/
- Date: 2026-10-19
- Last modified: 2026-10-19
- Publisher: Kuu株式会社 (https://kuucorp.com)

---
請求書や問い合わせメールからJSONを取り出して基幹システムに流す。この処理でJSONが一度でも壊れると、夜間バッチが止まり、翌朝に担当者が手で直すことになります。構造化出力はこの問題を大きく減らしますが、「絶対に壊れない」わけではありません。失敗する条件を先に知っておくことが、運用の手間を決めます。

## 構造化出力は何を保証し、何を保証しないのか

> 構造化出力はJSON Schemaへの準拠を文法制約で強制する機能です。内容の正しさは保証せず、refusalやmax_tokensでは準拠が崩れます。

Claude APIの構造化出力には2つの機能があります。`output_config.format` に `json_schema` を渡す **JSON outputs** は、回答そのものの形を縛ります。ツール定義に `strict: true` を付ける **strict tool use** は、ツール呼び出しの引数を縛ります。両者は独立して使え、同じリクエストで併用もできます。ツール定義の基本は[Function callingのツール定義](/blog/function-calling-structured-output-tool-design/)で扱っています。

保証されるのは形式です。たとえば金額欄に数値が入ることは保証されますが、その数値が請求書と合っているかは別問題です。金額の突合や必須項目の業務ルール検証は、アプリ側に残ります。

## どのスキーマ記法が使えないのか

> 数値範囲や文字数の制約は使えず、使うと400エラーになります。上限や桁数の検証はアプリ側のバリデーションで行います。

対応しているのは、object・array・string・integer・number・boolean・null、`enum`、`const`、`anyOf`、`$ref` などです。object には `additionalProperties: false` が必須です。対応していないものの代表は次のとおりです。

- 再帰的なスキーマ
- `minimum` / `maximum` / `multipleOf` といった数値制約
- `minLength` / `maxLength` といった文字列制約
- 0または1以外の `minItems`

「金額は0以上」「品名は50字以内」のような条件は、スキーマに書かず `description` で伝え、受信後にコードで検証します。公式SDKのヘルパー（Pythonの `messages.parse()` など）は、未対応の制約を説明文に移しつつ、返却後にローカルで元の制約を検証します。小規模な内製開発では、このヘルパーを使うのが最短です。

## 出力が壊れるのはどんな時か

> 壊れる主因は2つです。stop_reasonがrefusalまたはmax_tokensの場合、出力はスキーマに合わない可能性があります。

- **`stop_reason: "refusal"`**: HTTPステータスは200で返り、生成されたトークンには課金されます。拒否が優先されるため、スキーマ準拠は保証されません。
- **`stop_reason: "max_tokens"`**: 出力が途中で切れ、不正なJSONになりえます。`max_tokens` を引き上げて再試行します。

もう一つ、見落としやすい挙動があります。`enum` や `const` の値は、先頭の大文字・小文字が変わって返ることがあります。エラーにはならないため、比較は大文字小文字を区別せずに行い、大小文字だけが違う enum 値は作らないでください。また、推論過程をスキーマのプロパティに出させる設計は `reasoning_extraction` の拒否を招くことがあるため、理由欄は短い説明にとどめます。

## 最小限の受け口をどう実装するか

> 受信側で行うのは、stop_reasonの確認、スキーマ外の業務検証、再試行の上限設定の3点です。この3点が運用の安全網になります。

1. `stop_reason` が `end_turn` 以外ならパースせず、`max_tokens` なら上限を上げて1回だけ再試行する
2. パース後に金額の突合や日付の妥当性など業務ルールを検証し、不合格なら人間の確認キューに回す
3. 再試行は上限を決め、超えたら失敗として記録する。無限再試行は費用の暴走につながる

互換性にも制限があります。引用機能（Citations）を有効にしたまま `output_config.format` を使うと400エラーになります。メッセージのプリフィルも使えません。出典付きの回答と構造化抽出は、別のリクエストに分けてください。

## 運用コストと情報管理で押さえる点は何か

> 初回リクエストは文法のコンパイルで遅くなり、スキーマ構造を変えるとキャッシュが無効になります。スキーマは頻繁に変えない運用が前提です。

- コンパイル済みの文法は最終利用から24時間キャッシュされます。スキーマ構造やツール構成を変えると再コンパイルされ、`name` や `description` だけの変更では無効になりません
- `output_config.format` を変えると、その会話のプロンプトキャッシュが無効になります
- 出力形式を伝える追加のシステムプロンプトが入るため、入力トークンがわずかに増えます。単価は[公式の料金表](https://platform.claude.com/docs/en/about-claude/pricing)で確認してください
- 大量の帳票は[バッチAPI](/blog/claude-batch-api-cost-reduction-smb/)と組み合わせられ、構造化出力でもバッチの割引が使えます
- スキーマは文法としてプロンプトとは別に最大24時間保持されます。医療情報などの機微な値を、プロパティ名・enum値・patternに書いてはいけません。保持の扱いは[データ保持とZDRの解説](/blog/claude-api-data-retention-zdr-smb-guide/)も参照してください

## 参考

- [Structured outputs（Claude Platform Docs）](https://platform.claude.com/docs/en/build-with-claude/structured-outputs)
- [Strict tool use（Claude Platform Docs）](https://platform.claude.com/docs/en/agents-and-tools/tool-use/strict-tool-use)
- [Pricing（Claude Platform Docs）](https://platform.claude.com/docs/en/about-claude/pricing)

## まとめ

構造化出力は形式を保証し、業務上の正しさと失敗時の挙動は実装側の責任として残ります。stop_reasonの確認、スキーマ外の検証、再試行の上限という3点を入れるだけで、夜間バッチが止まるリスクは大きく下がります。

Kuu株式会社の[AI Ops](https://kuucorp.com/services/ai-ops/)では、帳票抽出などのAI処理を、検証と例外処理まで含めて導入から定着まで伴走します。まずは自社の処理に合う設計を相談してください。
