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か所を突き合わせる質問の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"
}
}
}
- ID
departmentの部分。あなたが決めるキーで、答えは同じキーで返る。モデルには送られないので、ID が自明でも質問の意味はinstructionsに完全に書く。 - type
choice/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
instructions も criteria の各エントリも、文字列だけでなくオブジェクトや配列で書けます。まずは文字列で始め、似た選択肢をモデルが取り違えるときに構造を足します。
対比的な 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万文字)です。