概要 / input

State と質問設計

リクエストは「state(評価してほしい材料)」と「questions(下してほしい判断)」の2つでできています。専門家パネルに資料を配ってから質問する——その資料が state、質問票が questions です。

state は文字列・オブジェクト・配列のどれでもよい

ほとんどの場合はオブジェクトがおすすめです。各部分に名前が付き、関係が明確になります。

形式向いている用途
文字列1つのメッセージ、記事、文章"My card was charged twice."
オブジェクト名前付きフィールド、関連レコード、アプリの状態{"message": "My card was charged twice.", "order_id": "A-104"}
配列メッセージやレコードの並び["Hi", "My customer number is TS1337.", "My card was charged twice."]
DOその質問に必要な文脈だけを入れる。余計な情報は注意を散らし、文脈の劣化(context rot)を招く。
DO最新の事実は自分のナレッジベースから state に入れる。モデルの重みに記憶された知識に頼らない。
DO判断に「比較」が必要なもの(会話・注文・ポリシー)は、1つの state にまとめて入れる。
DON'Tstate に質問を書かない。材料は state、判断の定義は questions、と分ける。

バッククォートのパスで、state の一部を指す

state が構造化されているとき、質問文の中で `ticket.messages[0].text` のようにドットと添字のパスをバッククォートごと書くと、モデルは state のどの部分を判断すべきかを理解します。

state会話・注文・ポリシーを1つに
{
  "ticket": {
    "subject": "Duplicate charge",
    "messages": [
      {"from": "customer", "text": "I was charged twice for order A-104. Please refund the duplicate."},
      {"from": "support", "text": "We are checking the charges."}
    ]
  },
  "order": {
    "id": "A-104",
    "charges": [
      {"amount_usd": 49, "status": "captured"},
      {"amount_usd": 49, "status": "captured"}
    ]
  },
  "refund_policy": "Duplicate charges are eligible for a refund."
}
質問 → 参照先
refund_requested · noulDoes `ticket.messages[0].text` request a refund?→ 顧客の最初のメッセージだけを見る
policy_supports_refund · noulDoes `refund_policy` support the refund requested in `ticket.messages[0].text`, given `order.charges`?→ ポリシー・メッセージ・請求データの3か所を突き合わせる
1つ目は単一の場所、2つ目は3か所の比較。どちらも同じ state に対して同時に評価されます。

質問の4要素

questionsJSON
{
  "department": {
    "type": "choice",
    "instructions": "Which team should handle this?",
    "criteria": {
      "returns": "Exchanges, refunds, wrong or damaged items",
      "shipping": "Delivery status, delays, lost packages",
      "billing": "Charges, invoices, payment problems"
    }
  }
}
  • IDdepartment の部分。あなたが決めるキーで、答えは同じキーで返る。モデルには送られないので、ID が自明でも質問の意味は instructions に完全に書く。
  • typechoice / score / noul のいずれか。
  • instructionsstate について聞きたい問い。評価ロジックの本体。明確で具体的な疑問文、またはモデルに真偽を判定させる平叙文で書く。
  • criteriaありうる答えの定義。Choice は「選択肢→説明」のマップ、Score は順序付きのレベル配列、Noul は任意で true / false の説明。

型の選び方:答えの「形」で決める

迷ったら、コードがそのまま行動に移せる型を選びます。

Choice

順序のない既知の選択肢から1つ

チケットの振り分け、文書種別、プログラミング言語の判定。リストが入力を網羅しないかもしれないなら other / none of the above を足す。

refund / rebook / information → 3本のコードパス

Score

スペクトラム上の位置

各点の意味を言葉で説明できるとき。バグの重大度、いら立ち、スキルレベル。

いら立ちスコア → しきい値

Noul

きれいな Yes/No

確率そのものが有用なシグナルになるとき。バグ報告か、返金要求か、履歴書に分散システムの記載があるか。

Noul → if 文
よくある間違い:程度を Noul で聞く 「この候補者は Python に強いか?」を Noul で聞いて 0.5 が返っても、それは「中級」ではなく「Yes と No が五分五分」という意味です。程度を測りたいなら Score(経験なし/触ったことがある/日常的に使用/深い専門性)。Yes/No が欲しいなら「履歴書に、業務で Python を使ったと書かれているか?」のように条件を明確に。

質問を分解する — いちばん大事な考え方

広い質問は、いくつもの判断を1つの答えの裏に隠してしまいます。原子的な質問にすれば、それぞれを点検し、調整し、コードで組み合わせられます。並列評価なので、分解しても往復回数は増えません。

例:スパム判定
✕ 広い1問 bad
is_spam · noulIs `message` spam?何を根拠にスパムと言ったのか分からない。閾値を調整しようにも、どの要素が効いているのか見えない。
○ 分解した6問 good
本文はパスワード等の認証情報を求めているか?
本文は予期しない賞金・支払い・報酬を主張しているか?
件名や本文は急いで行動するよう圧力をかけているか?
表示名の組織と、メールアドレスのドメインは食い違うか?
リンク先ドメインと、表示名の組織は食い違うか?
リンクの文言は、実際の遷移先を隠している/偽っているか?
分解した答えは、コード側で重み付けして合成します(例:0.45×認証情報の要求 + 0.30×送信者の不一致 + 0.25×予期しない報酬)。重みはあなたのもの。チームの判断と合わなければ係数を変えて再実行するだけです。
同じ考え方はエージェントの検証にも使える 「このツール呼び出しトレースは正しいか?」と1問で聞く代わりに、「ツールの選択は適切か」「引数はスキーマに合っているか」「日付はリクエストと一致するか」「単位(華氏/摂氏)は一致するか」と1項目ずつ聞く。どこが間違っているかまで分かります。

構造化した instructions と criteria

instructionscriteria の各エントリも、文字列だけでなくオブジェクトや配列で書けます。まずは文字列で始め、似た選択肢をモデルが取り違えるときに構造を足します。

対比的な Choice の criteriaJSON
{
  "return_topic": {
    "type": "choice",
    "instructions": {
      "question": "Which returns topic is the customer asking about?",
      "focus": "Classify the information the customer wants."
    },
    "criteria": {
      "return_policy": {
        "what": "Whether and how an item can be returned",
        "not_for": "Progress of a return already sent",
        "examples": [
          "Can I return shoes I've worn once?",
          "How long do I have to return an order?"
        ]
      },
      "return_status": {
        "what": "Progress of a return already sent",
        "not_for": "Whether and how an item can be returned",
        "examples": [
          "Has my return arrived yet?",
          "When will my refund be paid?"
        ]
      }
    }
  }
}
  • whatその選択肢が何をカバーするか
  • not_for隣の選択肢に属するもの(=これではないもの)
  • examples代表的な入力例をいくつか
フィールド名は自由question / focus / what / not_for / examples は API の予約語ではありません。選択肢名と同じく、あなたが決める名前です。モデルは名前と値の両方を見るので、後に続く内容を示す短い名前にします。全選択肢で同じフィールド名を使うと、モデルが横並びで比較しやすくなります。
既に JSON のデータはそのまま渡すスキーマ、タクソノミー、DB の行などは、文字列テンプレートに流し込まず JSON のまま(または必要なサブフィールドだけ)instructions に入れられます。抽出したい項目ごとに質問をループで組み立て、1回のリクエストで全部送る、という使い方ができます。

ある質問が別の質問に依存するとき

同じリクエスト内の質問は独立で、ある答えが別の質問の文脈になることはありません。後の判断が前の答えに依存するなら、コードで2回目のリクエストを作ります。ただし、それは例外です。

1回で聞く(原則)2回目の質問が、元の state に対してそのまま聞けるものなら、最初のリクエストに入れてしまう。不要な答えはコードが無視すればよい。
2回に分ける(本当に依存するとき)最初の答えがないと、①state に入れる追加データを取りに行けない、②state の構成自体が決まらない、③次の質問の選択肢が決まらない、のいずれか。
公式クックブックなぜ2回必要か
スキル提案182個のスキルを1回でランク付け → 上位3件の全文を取得して、より良い証拠で再判定する
構造の復元各改行が文を分断したかを聞く → その答えで行をブロックに結合 → できたブロックを分類する(ブロックは1回目が終わるまで存在しない)
階層分類各 Choice の答えが、次のリクエストで提示する選択肢を決める(確率を使ったビームサーチ)

1リクエストに入れられる質問数の上限は、state と質問が共有するトークン予算だけで決まります。予算はおよそ 32,000 トークン(英語でおよそ15万文字)です。