AI活用

Claude Structured Outputsの使い方|TypeScriptでJSON Schema準拠の出力を得る

Claude Structured OutputsをTypeScriptから使い、output_config.format、JSON解析、strict tool use、拒否・max_tokens・スキーマ制限への対処を解説します。

この記事の目次
  1. 結論:JSON出力とstrict tool useを使い分ける
  2. TypeScriptプロジェクトを準備する
  3. output_config.formatでJSON Schemaを指定する
  4. stop_reasonとcontentを確認して解析する
  5. 業務ルールは別に検証する
  6. 成功・400・例外出力を切り分ける
  7. strict tool useで関数入力を制約する
  8. スキーマの初回コンパイルとキャッシュ
  9. ストリーミング時の扱い
  10. スキーマを簡潔に保つ
  11. 他方式との比較
  12. 導入チェックリスト
  13. よくある質問
  14. JSON.parseのtry-catchは不要ですか?
  15. スキーマ準拠なら内容も正しいですか?
  16. Zodを使えますか?
  17. まとめ

Claude Structured Outputsは、自由文へ「JSONで答えて」と頼む方法ではなく、JSON Schemaに従う出力を制約付きで生成する機能です。TypeScriptではMessages APIへ output_config.format を渡し、返されたテキストを JSON.parse() して利用できます。

JSON構文、必須項目、型を安定させられるため、問い合わせ分類、文書からの項目抽出、APIレスポンス整形に向いています。一方、出力がスキーマに合っていても、内容が事実であることや業務ルールに適合することまでは保証しません。

この記事では、TypeScriptの最小実装、レスポンスの検査、strict tool useとの違い、ストリーミング、スキーマ制限、失敗時の扱いを解説します。

情報確認日:2026年7月18日(日本時間)

結論:JSON出力とstrict tool useを使い分ける

機能 制約する対象 主な用途
JSON outputs Claudeが返す最終回答 抽出、分類、レポート、API用データ
Strict tool use Claudeがツールを呼ぶときの名前と入力 外部関数、データ取得、エージェント処理

2つは併用できます。たとえば、厳密な引数で在庫検索ツールを呼び、その結果を決まったJSON形式で返す構成です。構造化出力の共通概念はStructured Outputsの基本でも解説しています。

スポンサーリンク

TypeScriptプロジェクトを準備する

mkdir claude-structured-sample
cd claude-structured-sample
npm init -y
npm pkg set type=module
npm install @anthropic-ai/sdk
npm install -D typescript tsx @types/node

APIキーと利用可能なモデルIDを環境変数へ設定します。

export ANTHROPIC_API_KEY="your_api_key_here"
export ANTHROPIC_MODEL="your_supported_model_id"

Claude APIの最初の呼び出しから確認したい場合は、Claude APIをTypeScriptから使う方法を先に参照してください。対応モデルは変わるため、公式のモデル一覧でStructured Outputs対応状況を確認します。

output_config.formatでJSON Schemaを指定する

問い合わせ文からカテゴリ、緊急度、要約、担当部署を抽出する例です。

import Anthropic from "@anthropic-ai/sdk";

const client = new Anthropic();
const model = process.env.ANTHROPIC_MODEL;

if (!model) {
  throw new Error("ANTHROPIC_MODEL is not set");
}

const ticketSchema = {
  type: "object",
  properties: {
    category: {
      type: "string",
      enum: ["billing", "account", "bug", "other"],
    },
    urgency: {
      type: "string",
      enum: ["low", "normal", "high"],
    },
    summary: { type: "string" },
    assignedTeam: {
      type: "string",
      enum: ["support", "finance", "engineering"],
    },
  },
  required: [
    "category",
    "urgency",
    "summary",
    "assignedTeam",
  ],
  additionalProperties: false,
} as const;

const message = await client.messages.create({
  model,
  max_tokens: 500,
  messages: [
    {
      role: "user",
      content:
        "問い合わせを分類してください:" +
        "請求書を二重に受け取りました。今週中に確認希望です。",
    },
  ],
  output_config: {
    format: {
      type: "json_schema",
      schema: ticketSchema,
    },
  },
});

オブジェクトでは additionalProperties: false を付け、許可していないフィールドが増えないようにします。必須項目は required へ列挙します。

旧ベータ版からの変更:公式ドキュメントでは、以前の output_formatoutput_config.format へ移動し、ベータヘッダーが不要になったと案内されています。古い記事のコードを使う場合はAPI形状を確認してください。

stop_reasonとcontentを確認して解析する

Structured Outputsでも、最初から content[0].text が存在すると決めつけず、停止理由とコンテンツ種別を確認します。

if (message.stop_reason === "refusal") {
  throw new Error("Claude refused the request");
}

if (message.stop_reason === "max_tokens") {
  throw new Error("Structured output was truncated");
}

const textBlock = message.content.find(
  (block) => block.type === "text"
);

if (!textBlock) {
  throw new Error("Text output was not returned");
}

const ticket = JSON.parse(textBlock.text);

console.log(ticket.category);
console.log(ticket.assignedTeam);

公式ドキュメントでは、安全上の拒否で stop_reason: "refusal" になった場合と、出力上限へ達して stop_reason: "max_tokens" になった場合は、スキーマに合わない可能性があると説明されています。

業務ルールは別に検証する

const allowedTeams = new Set([
  "support",
  "finance",
  "engineering",
]);

if (!allowedTeams.has(ticket.assignedTeam)) {
  throw new Error("Unknown assigned team");
}

if (
  ticket.category === "billing" &&
  ticket.assignedTeam !== "finance"
) {
  throw new Error("Billing tickets must go to finance");
}

スキーマは「文字列である」「列挙値のどれか」といった形を保証するためのものです。担当部署の割当ルール、金額、権限、実在性はアプリ側で検証します。

成功・400・例外出力を切り分ける

状況 結果 アプリの対応
通常完了 スキーマ準拠のJSONテキスト 解析後、業務ルールを検証
複雑すぎるスキーマ コンパイル時に400 任意項目、union、深い入れ子、ツール数を減らす
拒否 200でもスキーマ外の可能性 stop_reasonを見て通常データと分ける
max_tokens 途中で切れたJSONの可能性 上限と要求量を見直し、安全に再試行
enumの大文字小文字 指定値と大小文字だけ異なる可能性 公式注意に従い大小文字を正規化して比較

enum値は、大文字小文字だけが異なる候補を並べないようにします。データベースへ保存する前に、小文字化などの正規化と許可リスト照合を行います。

strict tool useで関数入力を制約する

const toolMessage = await client.messages.create({
  model,
  max_tokens: 600,
  messages: [
    {
      role: "user",
      content: "注文A-100の配送状況を確認してください。",
    },
  ],
  tools: [
    {
      name: "get_delivery_status",
      description: "注文番号から配送状況を取得する",
      strict: true,
      input_schema: {
        type: "object",
        properties: {
          orderId: { type: "string" },
        },
        required: ["orderId"],
        additionalProperties: false,
      },
    },
  ],
});

strict: true はツール名と入力のスキーマ準拠を強化しますが、ツールを実行する権限や注文へのアクセス権は保証しません。アプリ側で関数名を許可リスト化し、入力と利用者権限を再検証します。

JSON出力とstrict tool useを同じリクエストで使う場合、ツール結果を受けた後の最終回答も指定スキーマへ整えられます。ただし、スキーマとツールの組合せが変わるとコンパイルキャッシュへ影響します。

スキーマの初回コンパイルとキャッシュ

Structured Outputsはスキーマから文法をコンパイルするため、新しいスキーマの初回リクエストは追加の待ち時間が発生します。公式ドキュメントでは、コンパイル結果は最終利用から24時間キャッシュされると説明されています。

  • スキーマ構造を変えるとキャッシュが無効になる
  • 併用するツール集合を変えても無効になる
  • 名前や説明だけの変更は無効化しない
  • リリース後の初回遅延を監視する
  • 利用者ごとに動的な巨大スキーマを生成しない

ストリーミング時の扱い

Structured Outputsはストリーミングと組み合わせられますが、途中の断片は完成したJSONではありません。断片ごとに JSON.parse() せず、SDKのイベントを連結し、完了と停止理由を確認してから解析します。

text delta受信
  ↓ 文字列バッファへ追加
message完了
  ↓ stop_reasonを確認
完成した文字列をJSON.parse
  ↓ 業務ルールを検証
保存・画面表示

画面へ進捗を出したい場合は、「解析中」と表示し、JSONの途中断片をそのまま利用者へ見せないほうが実装しやすくなります。

スキーマを簡潔に保つ

公式ドキュメントでは、strictツール数、任意パラメータ、union型などに明示的な上限があり、組み合わせによる内部の複雑性上限もあると説明されています。数値は更新される可能性があるため、実装時に最新の制限表を確認してください。

  1. 本当に必要な項目だけを残す
  2. デフォルトを決められる値は必須項目にする
  3. 深い入れ子を平坦化する
  4. 巨大な1スキーマを用途別に分ける
  5. 重要なツールだけをstrictにする

互換性:Structured Outputsは、引用機能やメッセージのprefillなど一部機能と組み合わせられない場合があります。必要機能を一覧にし、利用モデルとAPIの最新対応表を確認してください。

他方式との比較

方式 JSON構文 スキーマ 向く用途
プロンプトだけ 崩れる可能性 保証なし 試作、自由文中心
JSON mode相当 有効JSONを重視 項目保証は方式による 形式だけ固定したい処理
Structured Outputs 有効JSON 対応範囲で準拠 抽出、分類、後続処理
Strict tool use ツール入力 対応範囲で準拠 外部関数の引数

JSON出力の一般的な検証方法はAIからJSONを安定して受け取る方法も参考になります。

導入チェックリスト

  • 対応モデルと最新SDKを確認した
  • output_config.format の現行形を使っている
  • すべてのobjectに追加項目の方針がある
  • 拒否とmax_tokensを通常データから分けている
  • JSON解析後に業務ルールを検証している
  • 初回コンパイル遅延を監視している
  • スキーマを変更したとき回帰テストを実行している
  • ツール実行では認可と承認を別途行っている

よくある質問

JSON.parseのtry-catchは不要ですか?

Structured Outputs利用時も、ネットワーク、SDK変更、拒否、出力上限、アプリ側の取得ミスに備えて例外処理を残します。停止理由を確認してから解析します。

スキーマ準拠なら内容も正しいですか?

いいえ。型と項目が合っていても、抽出値や分類が誤っている可能性があります。代表データで評価し、重要処理では原文との照合や人の確認を行います。

Zodを使えますか?

公式ドキュメントでは、TypeScript SDKの zodOutputFormat() などのヘルパーが案内されています。SDKの現行バージョンに合わせ、変換後にもアプリ側で元の制約を検証してください。

まとめ

Claude Structured Outputsでは、Messages APIの output_config.format へJSON Schemaを渡し、スキーマ準拠のJSONテキストを取得できます。外部関数の引数を固定したい場合は strict: true を使います。

拒否、max_tokens、enumの大文字小文字、スキーマ複雑性、初回コンパイルには例外があります。停止理由を確認し、JSON解析後も業務ルールと権限を検証してから後続処理へ渡してください。

スポンサーリンク