このページの目次

プロンプトエンジニアリングと構造化出力

サンプリングパラメータが削除された後、「創造性/決定論の調整」ノブは2つに置き換わった: prompt(意図を明確にする)と effort(十分な計算リソースを確保する)。そして、出力をプログラムが消費可能にするのは、「JSONを返してください」というお願いではなく、スキーマ制約である。

概要

トークンとサンプリング で触れた分岐点: Claude のマネージド API は Opus 4.7 から temperature/top_p/top_k を削除し、これらを送信すると即座に 400 エラーとなる。旧時代の「温度を調整して創造性を制御し、温度0で決定論を求める」という手触りは失われた。Anthropic は制御権を、より高レベルで制御可能な2つのノブへと移行させた:

  • プロンプティング⁠——決定論を望めば指示を固定し、発散を望めばプロンプト内で明示的に多様性を要求する;
  • effort——どの程度深く推論するか、どのくらいのトークンを消費するかを制御する (推論と思考 参照)。

この章では、この2つのノブをどのように活用するか(前半: プロンプトエンジニアリング)、そして出力をダウンストリームのプログラムに渡す際に、⁠構造化出力を用いて「自由テキスト」を「確実に解析可能な JSON」に制約する方法(後半)について解説する。これら2つはよく組み合わせて用いられる: プロンプトで振る舞いを定め、スキーマで形状を定める。

前半: プロンプトエンジニアリング

モデルが一度に見ているのは、連結された1つのテキストである

コンテキストエンジニアリング でレンダリング順序 tools → system → messages について述べた。プロンプトエンジニアリングとは、このテキストに何を、どのように記述するかを決定することである:

  • system: 安定したロール、ルール、出力規約——これを凍結し、タイムスタンプを挿入しない(キャッシュの維持のため)。
  • user: 本ラウンドの具体的なタスク + 必要な動的コンテキスト(検索スニペット、状態)。
  • few-shot 例⁠: 数個の入力→出力の例は、長い抽象的なルールの羅列よりも、フォーマットやスタイルを「教える」のに効果的である。system と user の間に配置する。

手を動かせば実感できる4つの原則

  • 「〜しなければならない」と積み重ねるのではなく、「いつ/境界」を明確にする。 最近の Opus (4.7/4.8) は指示遵守が非常に強く、旧時代に使われていたモデルを「威圧するための」CRITICAL: YOU MUST過剰にトリガーされる⁠。CRITICAL: 検索ツールを使わなければならない を、答えが会話に含まれていない情報に依存している場合は、まず検索してから回答する のように緩和する。
  • 重要な指示は注意力の強い位置に配置する。 重要な制約は、先頭の system または末尾の user に配置し、XMLタグや区切り文字で囲む(<rules>...</rules>)。中段に埋め込まない(真ん中で失われる現象を防ぐため)。
  • 正例は負例より優れている。 「このように書く: …」は「こうしないで」よりも安定している。簡潔さを求めるなら、1つの簡潔な例を与える方が、「冗長にするな」という10のルールを列挙するよりも効果的である。
  • 意図を与え、指示だけでなく。 「誰のために、何に使うものなのか」を説明すれば、モデルは合理的な詳細を自ら補完できる——特に長期的なタスクで顕著である。

移行レシピ: temperature → prompt + effort

旧コードのサンプリングパラメータを、意図に応じて変換する:

旧書き方 (意図)新書き方
temperature=0 (決定論を求める)effort:"low" + 指示を固定し、出力フォーマットを指定
temperature=高 (発散を求める)プロンプト内で「N個の異なる方向を提示せよ」と明示; フロントエンドで、モデルに「まず4つの異なる方向を提示してから実装する」ようなシナリオを設計
temperature で出力長を制御プロンプト内で長さ/詳しさの要件を直接記述(モデルはタスクの複雑さに応じて自己調整するため、必要であれば固定する)

古い誤解への注意: temperature=0 は旧モデルでも決してバイト単位の完全な再現を保証しない(浮動小数点の累積順序、バッチ処理、MoE ルーティングの揺らぎによるもの。詳細は トークンとサンプリング 参照)。「決定論」はエンジニアリング上の目標(プロンプトの固定 + 低 effort + 微小なドリフトの許容)であり、あるパラメータが保証するものではない。

後半: 構造化出力

モデルの出力をプログラムに渡す(データベースへの保存、ダウンストリームAPIの呼び出し、アサーションの実行)場合、「JSONを返してください」というお願いは信頼できない——モデルは前言語を追加したり、フィールドを漏らしたり、フォーマットがぶれたりする可能性がある。⁠構造化出力は、スキーマを用いてAPIレベルで出力を制約し、確実に有効な形状に保つ。2つのレイヤーがある:

1. JSON出力: 応答全体を制約

messages.createoutput_config.format を渡す(注意: 従来のトップレベルの output_format パラメータは廃止された):

response = client.messages.create(
    model="claude-opus-4-8",
    max_tokens=16000,
    messages=[{"role": "user", "content": "Extract: John (john@co.com), Enterprise plan"}],
    output_config={"format": {
        "type": "json_schema",
        "schema": {
            "type": "object",
            "properties": {
                "name":  {"type": "string"},
                "email": {"type": "string"},
                "plan":  {"type": "string"},
            },
            "required": ["name", "email", "plan"],
            "additionalProperties": False,   # 必須
        },
    }},
)
# 最初の text block が有効な JSON となる

SDK は client.messages.parse() + Pydantic/Zod も提供しており、手動解析なしで検証済みのオブジェクトを直接取得できる。

2. 厳格なツール呼び出し: ツール引数を制約

モデルがツールを呼び出す際、引数もスキーマに従って厳密に検証されるようにする——ツール定義に strict: true を追加する(tool_choice ではなく、ツール自体に設定)。スキーマには additionalProperties: falserequired を含める必要がある:

tools=[{
    "name": "book_flight",
    "strict": True,
    "input_schema": {
        "type": "object",
        "properties": {
            "destination": {"type": "string"},
            "passengers":  {"type": "integer", "enum": [1,2,3,4]},
        },
        "required": ["destination", "passengers"],
        "additionalProperties": False,
    },
}]

これにより、tool_use.input がスキーマに厳密に適合することが保証される——分類タスクにおいて、enum付きのツールや構造化出力を用いることは、モデルに「ラベルを出力せよ」と言ってから解析するよりも、はるかに安定している。

スキーマの能力の限界(知っておかないと、静かにハマる)

構造化出力はサポート⁠: 基本型、enum/const/anyOf/$ref、文字列フォーマット(date-time/email/uri/uuid…)、additionalProperties: false(各オブジェクトで指定が必要)。⁠サポートしない⁠: 再帰スキーマ、数値制約(minimum/maximum)、文字列長(minLength/maxLength)、複雑な配列制約。Python/TS SDK はサポートされない制約を自動的に除去し、クライアント側での検証に委ねる。

実務上の2点:⁠最初の新しいスキーマには一度限りのコンパイルオーバーヘッドがあり、その後24時間はキャッシュがヒットする。⁠構造化出力と引用(citations)は排他⁠(両方を有効にすると400エラー。RAG 参照)。

構造化出力 × 評価: 検証をプログラム可能にする

これが構造化出力で最も過小評価されている価値である。評価と観測 で述べたように、「コードで判定できるなら、モデルに任せるな」。構造化出力により、多くの検証が「LLM-as-judge」から「コードアサーション」へと格下げされる——スキーマに適合しているか、enum値が有効か、必須フィールドが揃っているか——すべて assert で行え、確実で、ゼロコスト、バイアスがない。⁠まずスキーマで出力をアサート可能な形状に固定し、その上で LLM 裁判官を使うかどうかを検討する。

ベストプラクティス

  • サンプリングパラメータはすべて削除し、意図を prompt + effort に変換する。 temperature=0effort:"low" + 指示の固定; 発散を求めたい場合→プロンプトで「N個の方向を提示」を明示。
  • 指示は「いつ/境界」を記述し、「〜しなければならない」を積み重ねない。 新 Opus は指示遵守が強く、過激な表現は過剰にトリガーされる; 重要な制約は強い位置に配置し、XMLで囲む。
  • few-shot 正例 > 抽象的なルールの羅列。 例を与えてフォーマットやスタイルを教え、簡潔さを求めるなら簡潔な例を与える。
  • 出力をプログラムに渡す場合はスキーマを使用する。 output_config.format で応答全体を制約、strict:true でツール引数を制約; 「JSONを返してください」に頼らない。
  • 各オブジェクトで additionalProperties:falserequired を記述する。 さもないと、厳格モードは機能しない。
  • スキーマを用いて検証をコードアサーションに格下げする。 assert できるものは裁判官に頼らない; 構造化出力は評価のプログラム可能性の前提条件である。
  • スキーマの限界と排他条件を覚える。 再帰/数値/長さの制約はサポートされない; 引用とは排他なので、どちらか一方を選ぶ。

トレードオフと失敗パターン

  • 新モデルでも temperature/top_p/top_k を送信する⁠: 400 → 削除し、prompt + effort に移行。
  • 旧式の CRITICAL: YOU MUST 攻撃⁠: ツール/スキルが過剰にトリガーされる → 「いつ使うか」の条件文に緩和。
  • 「JSONを返してください」に頼る⁠: 偶発的な前言語/フィールド漏れ/フォーマットのぶれ → output_config.format で強く制約。
  • stricttool_choice に設定する⁠: 機能しない → ツール定義に配置し、additionalProperties:false を設定。
  • スキーマに minLength/minimum を記述する⁠: 静かに除去され、制約が機能しない → このような範囲検証はクライアントコードで行う。
  • 構造化出力と同時に citations を有効にする⁠: 400 → どちらか一方を選ぶ; 出典を追いたい場合はプレーンテキスト + 引用を、解析可能にしたい場合はスキーマを使用。
  • 思考を無効化すると冗長になる⁠: thinking:disabled 下で推論が本文に漏れ出る → adaptive を残すか、「最終回答のみを出力せよ」を追加(推論と思考 参照)。

参考

キーワード: プロンプトエンジニアリング、system prompt、few-shot、XMLタグ、指示の配置、temperature の移行、effort、構造化出力、structured outputs、output_config.format、json_schema、厳格なツール使用、additionalProperties、required、messages.parse、Pydantic、Zod、enum、スキーマコンパイルキャッシュ、citations の排他、プログラム可能な検証、決定論