プロンプトと文書処理

ClaudeのJSON出力を業務で使う:形式の正しさと意味の正しさを分ける

JSONとして読み込めたことは、業務処理を実行してよいことを意味しません。構造化出力はアプリ連携を助けますが、値の正しさや操作の権限まで証明するものではありません。データを受け取った後の判断を分けて設計します。

編集・運営:株式会社ノースネスト · AI支援の学習解説

公開: · 更新: · 約4分(目安)

この記事の目的

JSONの形、業務制約、認可をそれぞれ異なる検証として設計できる。

こんな方へ

Claudeの出力をデータベースや他の処理へ渡す開発者。

三段階の検証を用意する

最初は構文と型、次は業務上の意味、最後は利用者の権限を確認します。商品IDが文字列であっても実在するとは限らず、実在してもその利用者が変更できるとは限りません。これらの失敗を一つの「JSONエラー」にすると、改善先が分からなくなります。

APIの構造化出力機能を使う場合は、対応するスキーマや利用条件を公式仕様で確認します。対応していない形式を自然文の指示だけで厳密に強制できると考えないようにします。

不明を表す値を決める

必須項目を増やすだけでは、情報がない場面で無理に埋める誘因になります。不明、該当なし、取得失敗を区別できる表現を設計します。空文字、null、未設定が同じ意味かどうかを、出力を消費する側と合わせる必要があります。

数値には単位や基準日も必要です。金額だけを受け取って通貨を推測したり、期限の日付から時区を推測したりすると、型の検証を通っても誤った処理になります。

検証エラーを安全な修復へ返す

形式を直す再生成と、情報が不足したときの確認質問は別です。存在しないIDを何度も作り直させるより、候補一覧から選ばせるか利用者へ確認します。修復にも回数制限を置き、失敗し続ける入力を無限に再試行しないようにします。

意味の制約をアプリの契約へ戻す

日付の文字列が形式を満たしても、終了日が開始日より前という結果はあり得ます。数量が整数でも、対象の上限や在庫状態によって許可できない場合があります。こうした関係は、スキーマへ表せる範囲と、実データを読んで検証する範囲へ分けます。形式の検証が通ったという単一のフラグを、そのまま業務の承認へ使わないことが要点です。

検証で失敗した出力を修正させる際は、何を直せばよいかを限定します。元資料がない金額を適当な値へ変えるのは修復ではありません。形式の補正、候補からの再選択、利用者への確認を分け、必要な情報がない場合は保留します。修復の回数と、途中で保持するデータも制御し、失敗の反復が別の負荷を生まないようにします。

再現例で判断の境界を確認する

架空の請求額に対して、合計一致、不一致、通貨欠落、文字列の金額を実際に検査します。JSONの構文が通るだけでは、業務条件は保証されません。

この検査は円建ての非負整数と加算だけです。税計算、為替、丸め、実際の請求書の正しさ、モデルによる抽出精度を検証したものではありません。

値を一つ変え、失敗の理由を分けて観察する

このページの入力欄では、下の公開コードと同じ金額ルールをブラウザー内で試せます。最初は合計が一致する架空データから始め、totalだけを変更してください。JSONとして読めても明細と合わない場合は拒否されます。次に文字列の金額や通貨の欠落を試すと、どの条件を直す必要があるかを分けて確認できます。

入力を編集した後は、前の結果を新しい入力の確認済み表示として残しません。再度検証してから判断してください。この教材の条件を通ったことは、元の請求書が正しいことや処理を承認してよいことの証拠ではありません。入力は送信・保存せず、実在する顧客や請求書の情報を使う必要もありません。

具体例で考える

架空の在庫更新候補を受け取りました。JSONの形は正しいが、数量の符号と操作権限が問題になっています。

説明用の入力・判断例
{"item_id":"SKU-7","quantity":-3,"action":"replace"}
形式:文字列・数値としては読める。
意味:replaceで負在庫を許すか要確認。
認可:SKU-7を更新できる利用者かサーバー側で確認。

この候補をそのまま保存せず、在庫ルールと権限を検証します。「正しいJSONだけを返して」という指示はこの判断を代替できません。検証の理由をログへ残す場合も、元の顧客情報を不要に保存しないようにします。

JSONを書き換えて、判断の違いを試す

合計だけを1700へ変える、通貨を消す、金額を文字列にする。小さな変更で、書式と意味の違いを確かめましょう。

架空データ用です。入力はこのページ内で処理し、保存・送信しません。Claude APIの呼出しや課金はありません。

32,768文字以内 · 値を変更したら、もう一度検証してください。

この判定は下の公開コードと同じ金額ルールを使います。税・為替・丸め、実在する請求書や抽出精度の確認は含みません。

ローカルで再現する:JSONの構文と請求額の意味を分ける

架空の請求額に対して、合計一致、不一致、通貨欠落、文字列の金額を実際に検査します。JSONの構文が通るだけでは、業務条件は保証されません。

以下は公開コードを実行して得た結果です。入力は学習用に作った架空データで、Claude API・外部サービスへの通信は行っていません。モデルの回答品質や実サービスの性能を測定した結果ではありません。

実行:
Node.js v24.22.1 · darwin/arm64 · 4ケース

再現の手順

  1. 実行コードを保存する(.mjs)
  2. Node.js 20以上の環境で、保存したフォルダから次のコマンドを実行します。追加パッケージやAPIキーは不要です。
  3. 出力の各ケースを読み、入力を一つ変えて判断の違いを確かめてください。
node claude-json-semantic-validation.mjs

実際の入力と出力

{
  "cases": [
    {
      "name": "金額と合計が一致",
      "input": {
        "currency": "JPY",
        "lines": [
          1000,
          500
        ],
        "total": 1500
      },
      "expected": "accepted",
      "actual": "accepted",
      "pass": true
    },
    {
      "name": "JSONは正しいが合計が不一致",
      "input": {
        "currency": "JPY",
        "lines": [
          1000,
          500
        ],
        "total": 1700
      },
      "expected": "sum_mismatch",
      "actual": "sum_mismatch",
      "pass": true
    },
    {
      "name": "通貨が欠落",
      "input": {
        "lines": [
          1000,
          500
        ],
        "total": 1500
      },
      "expected": "currency_required",
      "actual": "currency_required",
      "pass": true
    },
    {
      "name": "文字列の金額を暗黙変換しない",
      "input": {
        "currency": "JPY",
        "lines": [
          1000,
          "500"
        ],
        "total": 1500
      },
      "expected": "invalid_amount",
      "actual": "invalid_amount",
      "pass": true
    }
  ]
}
実行したコードを読む
// Learnify local experiment. Fictional invoice values; no model call.
export function validateInvoice(value) {
  if(!value||typeof value!=='object'||Array.isArray(value))return 'invalid_object';
  if(value.currency!=='JPY')return 'currency_required';
  if(!Array.isArray(value.lines)||value.lines.length===0)return 'lines_required';
  if(!Number.isSafeInteger(value.total)||value.total<0||value.lines.some(n=>!Number.isSafeInteger(n)||n<0))return 'invalid_amount';
  const sum=value.lines.reduce((n,v)=>n+v,0);
  if(!Number.isSafeInteger(sum)||sum!==value.total)return 'sum_mismatch';
  return 'accepted';
}
export function run() {
  const fixtures=[
    {name:'金額と合計が一致',input:{currency:'JPY',lines:[1000,500],total:1500},expected:'accepted'},
    {name:'JSONは正しいが合計が不一致',input:{currency:'JPY',lines:[1000,500],total:1700},expected:'sum_mismatch'},
    {name:'通貨が欠落',input:{lines:[1000,500],total:1500},expected:'currency_required'},
    {name:'文字列の金額を暗黙変換しない',input:{currency:'JPY',lines:[1000,'500'],total:1500},expected:'invalid_amount'},
  ];
  return {cases:fixtures.map(c=>{const actual=validateInvoice(c.input);return {...c,actual,pass:actual===c.expected};})};
}
if(typeof process!=='undefined'&&process.argv?.[1]&&import.meta.url===new URL('file://'+process.argv[1]).href)console.log(JSON.stringify(run(),null,2));

コードのSHA-256:f43fd2a5a9d76336a5d0c0745a69125357bc869868f0d70c5aab6809a1fd482e

5実験の実行記録を保存する(JSON)

この検証で分かる範囲

この検査は円建ての非負整数と加算だけです。税計算、為替、丸め、実際の請求書の正しさ、モデルによる抽出精度を検証したものではありません。

確認課題と合格の目安

一つのJSON出力に、形式が正しくても拒否すべき例を三つ作ってください。

  • 型、業務ルール、認可を別に点検できる。
  • 不明と処理失敗の表現が定義されている。
  • 修復で根拠のない値を補っていない。