Claude API Structured Outputs実践ガイド — JSON出力モードとstrict tool useの使い分けから併用パターンまで
「JSONで返して」とプロンプトに書いても、閉じ括弧が欠けたり余計なマークダウンが混入したり——LLMアプリ開発者なら一度は経験する"パース地獄"。Claude APIのStructured Outputsは、constrained decodingによってスキーマ準拠を100%保証する機能だ。2026年1月のGA化で本番投入の準備が整った今、output_config.formatとstrict tool useという2つのアプローチを実装コード付きで比較し、両者を組み合わせたエージェントワークフローまで解説する。
Structured Outputsとは何か — なぜ「プロンプトでJSON指定」では不十分なのか
従来のJSON出力の3つの落とし穴
プロンプトに「JSONで出力してください」と指示する従来の方法には、3つの典型的な問題がある。
- 閉じ括弧の欠損 — 出力が長くなると
max_tokensに達し、JSONが途中で切れる - マークダウン混入 —
`jsonで囲んだり、前後に説明テキストを付けたりする - スキーマ逸脱 — 指定したフィールド名と異なるキーを返す、型が合わない
// Before: プロンプト頼みのJSON出力 — パースが壊れるリスクが常にある
const response = await client.messages.create({
model: "claude-sonnet-4-5-20250929",
max_tokens: 1024,
messages: [{ role: "user", content: "ユーザー情報をJSON形式で返して" }],
});
const text = response.content[0].text;
const data = JSON.parse(text); // ここで落ちる可能性がある
constrained decodingによるスキーマ保証の仕組み
Structured Outputsは、トークン生成時にスキーマに違反する選択肢を排除するconstrained decodingで動作する。モデルの後処理ではなく生成プロセス自体に制約をかけるため、stop_reasonがend_turnであればスキーマ準拠が保証される。
2025年11月にPublic Betaとして登場し、2026年1月29日にClaude APIでGA化された。ベータ期間中に必要だったヘッダー(structured-outputs-2025-11-13)はもう不要だ。対応モデルはClaude Opus 4.6、Sonnet 4.6、Sonnet 4.5、Opus 4.5、Haiku 4.5。Amazon Bedrockでは2026年2月4日にGA化されており、Microsoft Foundryではpublic betaとなっている。
アプローチ1: output_config.formatによるJSON出力モード
基本実装 — TypeScript SDK + Zodスキーマ
output_config.formatにJSON Schemaを渡すと、モデルの最終出力が必ずそのスキーマに準拠したJSONになる。TypeScript SDKではzodOutputFormatヘルパーを使えば、Zodスキーマから自動でJSON Schemaに変換される。
import Anthropic from "@anthropic-ai/sdk";
import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";
import { z } from "zod";
const client = new Anthropic();
// Zodでスキーマを定義
const UserSchema = z.object({
name: z.string(),
email: z.string(),
age: z.number(),
interests: z.array(z.string()),
});
const response = await client.messages.parse({
model: "claude-sonnet-4-5-20250929",
max_tokens: 1024,
output_config: { format: zodOutputFormat(UserSchema) },
messages: [
{
role: "user",
content:
"田中太郎、30歳、メールはtanaka@example.com、趣味はプログラミングと登山",
},
],
});
// response.parsed_output は型安全(z.infer<typeof UserSchema> 型)
console.log(response.parsed_output.name); // "田中太郎"
console.log(response.parsed_output.interests); // ["プログラミング", "登山"]
client.messages.parse()を使うと、レスポンスのparsed_outputプロパティから型付きのオブジェクトを直接取得できる。JSON.parseを自分で呼ぶ必要はない。
outputformatからoutputconfig.formatへの移行
GA化に伴い、パラメータ名がoutput_formatからoutput_config.formatに変更された。旧パラメータは移行期間中は動作するが、Amazon Bedrockでは既に旧パラメータを拒否するため、早めの移行を推奨する。SDKを最新版に更新すれば、内部的に新パラメータが使われる。
注意すべき点として、stop_reasonがmax_tokensの場合はJSONが途中切断されている可能性がある。必ずチェックしよう。
if (response.stop_reason !== "end_turn") {
throw new Error("JSON出力が途中で切断された可能性があります");
}
また、JSON Schemaの制約として、全てのobjectレベルでadditionalProperties: falseが必須だ。Zodスキーマ経由なら自動付与されるが、手書きのJSON Schemaでは省略するとエラーになる。
アプローチ2: strict tool useによるツールパラメータの型保証
strict: trueの基本実装
ツール定義にstrict: trueを追加するだけで、モデルが生成するツール呼び出しのパラメータがスキーマ準拠になる。正直、この手軽さには驚いた。
const response = await client.messages.create({
model: "claude-sonnet-4-5-20250929",
max_tokens: 1024,
tools: [
{
name: "extract_user_info",
description: "テキストからユーザー情報を抽出する",
strict: true, // これだけで型保証が有効になる
input_schema: {
type: "object" as const,
properties: {
name: { type: "string", description: "氏名" },
email: { type: "string", description: "メールアドレス" },
age: { type: "number", description: "年齢" },
},
required: ["name", "email", "age"],
additionalProperties: false,
},
},
],
tool_choice: { type: "any" },
messages: [
{
role: "user",
content: "山田花子さん(25歳)の連絡先はhanako@example.comです",
},
],
});
tool_choice: 'any'との組み合わせ
tool_choice: { type: "any" }を指定すると、モデルは必ずツール呼び出しで応答する。テキスト応答を防げるため、構造化データの抽出用途ではoutput_config.formatの代替として使える。既存のtool useコードからの移行はstrict: trueを1行追加するだけだ。
2つのアプローチの使い分け判断フロー
| 観点 | output_config.format | strict tool use |
|---|---|---|
| 保証対象 | モデルの最終テキスト出力 | ツール呼び出しのパラメータ |
| 主なユースケース | データ抽出・分類・構造化レスポンス | 関数呼び出し・外部API連携 |
| レイテンシ | 初回にgrammar compilationあり | 同様 |
| 適用場面 | 会話の最終応答をJSONで受け取りたい | エージェントのアクション入力を保証したい |
両者は排他ではなく併用可能だ。次節でその実装パターンを解説する。
併用パターン — エージェントワークフローでツール呼び出しも最終出力も型保証する
実装例: 調査→レポート生成エージェント
個人的に最も実用的だと感じるのが、strict tool useで中間のツール呼び出しを保証しつつ、output_config.formatで最終出力もJSON保証する併用パターンだ。
import Anthropic from "@anthropic-ai/sdk";
import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";
import { z } from "zod";
const client = new Anthropic();
// 最終出力のスキーマ
const ReportSchema = z.object({
title: z.string(),
summary: z.string(),
findings: z.array(
z.object({
topic: z.string(),
detail: z.string(),
confidence: z.number(),
})
),
recommendation: z.string(),
});
// ツール定義(strict: trueで入力パラメータも保証)
const tools: Anthropic.Messages.Tool[] = [
{
name: "web_search",
description: "Webで情報を検索する",
strict: true,
input_schema: {
type: "object" as const,
properties: {
query: { type: "string", description: "検索クエリ" },
max_results: { type: "number", description: "最大件数" },
},
required: ["query", "max_results"],
additionalProperties: false,
},
},
];
// エージェントループ
let messages: Anthropic.Messages.MessageParam[] = [
{ role: "user", content: "2026年のTypeScriptフレームワーク動向を調査して" },
];
while (true) {
const response = await client.messages.parse({
model: "claude-sonnet-4-5-20250929",
max_tokens: 4096,
tools,
output_config: { format: zodOutputFormat(ReportSchema) },
messages,
});
// ツール呼び出しがあれば処理して続行
const toolUse = response.content.find((b) => b.type === "tool_use");
if (toolUse && toolUse.type === "tool_use") {
const result = await executeSearch(toolUse.input); // 実際のツール実行
messages.push({ role: "assistant", content: response.content });
messages.push({
role: "user",
content: [
{ type: "tool_result", tool_use_id: toolUse.id, content: result },
],
});
continue;
}
// 最終出力 — ReportSchema準拠が保証されている
console.log(response.parsed_output.title);
console.log(response.parsed_output.findings);
break;
}
この構成では、web_searchツールのパラメータ(queryとmax_results)はstrict: trueで型保証され、最終的なレポート出力はReportSchemaで構造が保証される。エージェントワークフロー全体で型安全性を担保できる。
本番投入時の注意点 — レイテンシ・ZDR・スキーマ制約
grammar compilation latencyとキャッシュ戦略
Structured Outputsでは、初回リクエスト時にJSON Schemaからgrammarをコンパイルする処理が入る。このコンパイル結果は24時間キャッシュされるため、2回目以降は追加レイテンシが発生しない。コンパイルのタイムアウトは180秒で、巨大なスキーマを使う場合は分割を検討すべきだ。
JSON Schemaの制約事項
| 制約 | 詳細 |
|---|---|
| トップレベル型 | objectのみ(配列やプリミティブは不可) |
| additionalProperties | 全objectでfalseが必須 |
| $ref | 内部参照($defs)は利用可能。外部URLの参照は不可 |
| 再帰スキーマ | $ref/$defsによる限定的な再帰は対応。循環的(cyclic)な再帰定義は非対応 |
| 数値制約 | minimum/maximum/multipleOfは非対応 |
| 文字列制約 | minLength/maxLengthは非対応 |
ZDR環境での挙動
Zero Data Retention環境では、プロンプトとレスポンスは保持されない。ただし、JSON Schema自体はgrammar compilationの最適化目的で最大24時間キャッシュされる点に留意が必要だ。スキーマにセンシティブな情報(社内用語のenum値など)を含める場合は、このキャッシュ挙動を把握しておこう。
Structured Outputsの登場により、LLMアプリ開発における「パース不安定性」は解決済みの問題になった。output_config.formatで最終出力を、strict tool useでツール呼び出しを、それぞれ型保証できる。両者を併用すれば、エージェントワークフロー全体で入出力の型安全性を担保できる。GA化済みの今こそ、本番コードのJSON処理を正規表現やリトライ頼みからStructured Outputsへ移行するタイミングだ。
