文件導航

進階:結構

instructions、Choice 選項、Score 檔位和 Noul 判定標準,都接受 JSON 結構。

System One 模型經過訓練,能理解結構。

哪些欄位允許使用結構

下面這些欄位,每一個都是 EntryType。

欄位 適用於 接受的形態
instructions Choice、Score、Noul string、object、array 或 null
criteria 的值(選項描述) Choice string、object、array 或 null
criteria 的條目(檔位描述) Score string、object、array 或 null
criteria.true 和 criteria.false Noul string、object、array 或 null

什麼時候該給問題加結構

  • 當結構有助於說清楚時。 一個問題包含多個部分時,把它們寫成 JSON 的形式會更清楚,因為每個鍵都有名字。
  • 當問題需要附帶資料時。 一份 schema、一套分類法、一行資料庫記錄,本身就是 JSON。直接整份用,或者只傳相關的子欄位,不要把它們序列化進字串模板裡。

結構化的 instructions

一個 field 物件描述被檢查的那個欄位,每個問題通過鍵來引用它。同一套結構既能驅動一個校驗取值的 Noul,也能驅動一個從候選中挑一個的 Choice,還能驅動兩個把取值放到刻度上的 Score。

request
{
  "state": {
    "source_text": "Invoice #4471 issued March 3, 2026 to Beaver Dam Logistics for $12,840.00, net 30."
  },
  "questions": {
    "invoice_number_is_correct": {
      "type": "noul",
      "instructions": {
        "field": {
          "name": "invoice_number",
          "type": "string",
          "description": "The identifier printed on the invoice."
        },
        "extracted_value": "4471",
        "question": "Does `extracted_value` match the `field` as it appears in `source_text`?"
      }
    },
    "customer_name": {
      "type": "choice",
      "instructions": {
        "field": {
          "name": "customer_name",
          "type": "string",
          "description": "The organization the invoice was issued to."
        },
        "question": "Which option is the value of `field` in `source_text`?"
      },
      "criteria": {
        "Beaver Logistics": null,
        "Dam Logistics": null,
        "Beaver Dam Logistics": null,
        "Beaver": null,
        "Dam": null
      }
    },
    "amount_due": {
      "type": "score",
      "instructions": {
        "field": {
          "name": "amount_due",
          "type": "number",
          "unit": "USD",
          "description": "The total the invoice asks to be paid."
        },
        "question": "How large is the `field` value in `source_text`?"
      },
      "criteria": [
        "Under $1,000",
        "$1,000 to $10,000",
        "$10,000 to $100,000",
        "$100,000 to $1,000,000",
        "Over $1,000,000"
      ]
    },
    "payment_terms": {
      "type": "score",
      "instructions": {
        "field": {
          "name": "payment_terms",
          "type": "integer",
          "unit": "days",
          "description": "Days allowed for payment, from terms such as \"net 30\"."
        },
        "question": "How many days does the `field` in `source_text` allow for payment?"
      },
      "criteria": [
        "Due on receipt",
        "Net 10",
        "Net 30",
        "Net 60",
        "Net 90"
      ]
    }
  }
}

在程式碼裡,你可以遍歷可能存在的記錄,為每個欄位各構建一個這樣的問題,全部放進一次呼叫裡發出。SDE 級聯 cookbook 做的就是類似的事。

陣列也可以。當指令是一串要檢查或要比較的東西時,就用陣列:

"instructions": {
  "question": "Does the claimed sender identity conflict with the sending domain?",
  "compare": ["ticket.sender.display_name", "ticket.sender.email"],
  "focus": "Compare the named organization with the email domain."
}

結構化的 Choice 選項

Choice 的選項描述,也可以是一個結構化物件。

用 JSON 量規澄清邊界

request
{
  "state": "I ordered the standing desk two weeks ago and tracking still says label created. Was I even charged?",
  "questions": {
    "department": {
      "type": "choice",
      "instructions": {
        "question": "Which team should handle this message?",
        "focus": "Classify the customer's primary request, not every topic mentioned."
      },
      "criteria": {
        "billing": {
          "what": "Charges, invoices, refunds, or subscriptions",
          "not_for": "Order tracking or account access",
          "examples": [
            "I was charged twice",
            "Where is my refund?"
          ]
        },
        "orders": {
          "what": "Order status, delivery, cancellation, or returns",
          "not_for": "Charges or account access",
          "examples": [
            "Where is my package?",
            "Cancel my order"
          ]
        },
        "account": {
          "what": "Login, password, profile, or security",
          "not_for": "Charges or delivery",
          "examples": [
            "I can't log in",
            "Change my email"
          ]
        }
      }
    }
  }
}

這個例子告訴模型:每個選項覆蓋什麼,又不覆蓋什麼。它能讓選項之間的邊界更清晰。

遍歷分類樹

要分進一棵很深的分類樹,就每一層問一個 Choice,在程式碼裡沿樹往下走。每一步的選項,是當前節點的子節點;每個選項的值,則是那個子節點自己的子樹。這樣做能讓模型在選定一個分支之前,先看看分支底下有什麼 —— 當一個條目屬於某個葉子,而這個葉子的名字光看分支名看不出來時,這一點很關鍵。

這裡的狀態是一條商品資訊,第一個問題選出頂層部門。

request
{
  "state": "32oz plastic bottle with a flip straw lid. Fits most bike cages.",
  "questions": {
    "department": {
      "type": "choice",
      "instructions": "Which top-level department does this product belong to?",
      "criteria": {
        "Sporting Goods": {
          "Cycling": [
            "Bike Bottles & Cages",
            "Bike Lights",
            "Helmets"
          ],
          "Fitness": [
            "Yoga Mats",
            "Resistance Bands"
          ],
          "Outdoor": [
            "Tents",
            "Sleeping Bags",
            "Hydration Packs"
          ]
        },
        "Home & Kitchen": {
          "Drinkware": [
            "Water Bottles",
            "Travel Mugs",
            "Tumblers"
          ],
          "Cookware": [
            "Pots & Pans",
            "Bakeware"
          ]
        },
        "Baby & Toddler": [
          "Sippy Cups",
          "Bottle Warmers",
          "Bibs"
        ]
      }
    }
  }
}

這隻瓶子合理地可以歸進兩個部門。把子樹擺出來,模型就能看到 Sporting Goods > Cycling > Bike Bottles & Cages 和 Home & Kitchen > Drinkware > Water Bottles 都存在,從而在商品描述對車筐的強調和日常飲水杯之間做權衡。這個答案上的 probabilities 會告訴你,兩者的分歧是否小到值得兩條分支都探一探。

選定一個部門後,用它的子節點作為選項、子樹作為取值,問下一個 Choice,如此重複,直到走到葉子。在程式碼裡,這可以是對一個巢狀 dict 的迴圈,每個問題的 criteria 就是當前節點。分層分類 cookbook 給了一個類似遍歷樹的例子,其中還包括一個束搜尋:當機率咬得很近時,讓幾條候選路徑同時活著。

結構化的 Score 檔位

Score 的 criteria 數組裡,每個條目都可以是一個物件。

request
{
  "state": "Fixed the null check in the payment handler. Also refactored the retry loop while I was in there, and bumped the SDK version since the old one had that timeout bug.",
  "questions": {
    "pr_scope": {
      "type": "score",
      "instructions": {
        "question": "How focused is this pull request description on a single change?",
        "note": "Judge the number of independent changes, not the size of any one change."
      },
      "criteria": [
        {
          "summary": "One change, clearly stated",
          "signals": [
            "A single fix or feature",
            "Nothing described as \"also\" or \"while I was in there\""
          ]
        },
        {
          "summary": "One main change plus a small related tweak",
          "signals": [
            "A primary change and one minor adjacent edit",
            "The tweak supports the main change"
          ]
        },
        {
          "summary": "Several independent changes bundled together",
          "signals": [
            "Two or more unrelated fixes or features",
            "Changes that could each be their own PR"
          ]
        }
      ]
    }
  }
}

結構化的 Noul 判定標準

Noul 的 criteria 是可選的。當是/非的邊界很微妙時,結構化的 true 和 false 描述能讓你給每一邊都加上定義和例子,把邊界釘死。

request
{
  "state": {
    "sender": {
      "display_name": "Beaver Dam Builders Ltd.",
      "email": "donotreply@payroll.example"
    },
    "message": "Your Q3 bonus is ready. Reply with your login password so we can verify your identity and release the funds."
  },
  "questions": {
    "requests_credentials": {
      "type": "noul",
      "instructions": {
        "question": "Does the `message` ask the recipient to disclose a sensitive credential?",
        "inspect": "message",
        "focus": "Look for a request to send the credential itself, not a request to change or reset it."
      },
      "criteria": {
        "true": {
          "what": "Asks the recipient to reply with, type, or send a password, PIN, one-time code, or other security sensitive answer",
          "examples": [
            "Reply with your password",
            "Send us the 6-digit code you just received"
          ]
        },
        "false": {
          "what": "No sensitive credential is requested",
          "examples": [
            "Reset your password from the settings page",
            "Your statement is ready"
          ]
        }
      }
    }
  }
}