概要 / primitive

Score

「どの程度?」に答えるプリミティブ。言葉で定義した順序付きのレベル(2〜10段階)に対して、state がどの位置にあるかを返します。位置はレベルとレベルの間に落ちることもあります。

基本の形

バグ報告の重大度を3段階で評価する例です。

requestPOST /v1/systemone
{
  "state": "The export button crashes the settings page in Safari. It works in Chrome, but a few of our customers only use Safari.",
  "model": "jev-latest",
  "questions": {
    "bug_severity": {
      "type": "score",
      "instructions": "How severe is the reported issue?",
      "criteria": [
        "Cosmetic; no impact to functionality",
        "Broken or degraded feature, but workaround exists",
        "Blocking issue; no workaround exists"
      ]
    }
  }
}
response200 OK
{
  "model": "jev-latest",
  "answers": {
    "bug_severity": {
      "type": "score",
      "score": 1.3,
      "confidence": 0.54,
      "legend": {
        "0": "Cosmetic; no impact to functionality",
        "1": "Broken or degraded feature, but workaround exists",
        "2": "Blocking issue; no workaround exists"
      },
      "probabilities": { "0": 0.0, "1": 0.7, "2": 0.3 }
    }
  },
  "usage": { "input_tokens": 332, "output_tokens": 18 }
}
  • criteria低い側から高い側へ並べたレベル記述の配列。配列内の位置がそのままレベル番号(0 始まり)。最低2、最大10レベル。
  • probabilities各レベルの確率。キーはレベル番号(JSON では文字列、Python SDK では整数)。合計は 1。
  • scoreレベル番号の確率加重平均。0 から最上位レベル番号までの値をとる。
  • legendレベル番号から記述への対応表。レスポンスだけ見ても意味が分かるようにするためのもの。
  • confidence確率の広がりから計算される 0〜1 の値。1レベルに集中すれば高く、複数レベルに割れれば低い。

スコアの読み方

同じ質問に、異なるバグ報告を入れるとどうなるか。報告を切り替えて、確率分布とスコアの位置の関係を確かめてください(値はすべて公式ドキュメント掲載のもの)。

バグ報告を選ぶ
0: 見た目だけ0.00
1: 回避策あり0.70
2: 回避策なし0.30
012 見た目だけ壊れているが回避策ありブロッキング score 1.30
0×0.00 + 1×0.70 + 2×0.30 = 1.30confidence 0.54 — スコアは各レベル番号に確率を掛けて足したもの。

小数のスコアは「位置」

1.30 は「ほぼレベル1、少しレベル2寄り」。重大度順の並べ替えに使えるし、1つの結論が必要なら最も近いレベルに丸めてもよい。「回避策のない顧客の割合」を測っているわけではない。

同じスコアでも分布は違いうる

スコア 1.0 は「全確率がレベル1」かもしれないし、「レベル0と2に半々」かもしれない。probabilitiesconfidence を一緒に見て区別する。

confidence 1.0 ≠ 正解の保証

confidence 1.0 は「分布が1レベルに集中している」というモデルの回答の性質であって、その答えが正しい保証ではない。

Score の confidence が低いとき、原因はたいてい3つのどれか① その state に対してレベル同士が重なっている ② 質問が2つ以上のことを同時に測っている ③ state に位置を決めるだけの情報がない

よいレベルの書き方:「程度」ではなく「状況」を書く

各レベルは単独で評価されます。モデルはレベルの番号も、隣のレベルも見ていません。だから「前のレベルより悪い」といった相対表現や、説明文中の数字は意味を持ちません。

DON'T — 数字だけのレベル instructions: "Rate severity from 0 to 2, where 2 is worst"
criteria: ["0", "1", "2"]
"0"0.43
"1"0.57
"2"0.00
「ボタンが数ピクセルずれている」報告が score 0.57、confidence 0.35。照合する手がかりがなく、確率が割れる。
DO — 状況を記述したレベル "Cosmetic; no impact to functionality" ほか、具体的な状況を書いた3レベル
見た目だけ1.00
回避策あり0.00
回避策なし0.00
同じ報告が score 0.0、confidence 1.0

1つの質問で測るのは1次元

「時間に正確で、賢く、経験豊富」のように3つを混ぜると、片方が高く片方が低い入力を置けなくなる。confidence が下がり、スコアの意味も薄れる。次元ごとに質問を分けてコードで合成する。

レベル数は、区別して書ける数だけ

最大10まで使えるが、3で十分なことも多い。区別して説明できないレベルは足さない。

特別扱いしたい極端なケースには専用レベルを

感情スケールが「非常に怒っている」で終わると、脅迫的なメッセージも同じく最上位付近になる。別の対応が必要なら「暴言・脅迫」のレベルを足す。

中間がないなら Score ではない

答えがいくつかの離散的なカテゴリなら Choice、または複数の Noul に分ける。同じスケールでも言い回しで挙動が変わるので、必ず自分のデータで試す。

複雑な評価は、複数の Score に分けて合成する

チケットの優先度を「バグの重大度」「顧客のいら立ち」「報告の質」の3つの Score から作る例です。3問は1リクエストで並列に評価されます。

正規化 → 重み付け → 合計
質問scoreconf最上位レベル正規化 (score ÷ 最上位)重み寄与
severity 重大度1.240.6320.620.60.372
frustration いら立ち1.450.3320.7250.30.2175
report_quality 報告の質3.01.0031.00.10.1
priority0.69
スケールの長さが違う(3レベルは 0〜2、4レベルは 0〜3)ので、合成の前に必ず score / (len(criteria) - 1) で 0〜1 に正規化します。そうして初めて「重大度 0.6・いら立ち 0.3 なら重大度が2倍効く」と重みが言葉どおりの意味になります。
priority.py公式ドキュメントより
def normalized(answers, question_id: str) -> float:
    """最上位レベル番号で割って 0〜1 に揃える"""
    top_level = len(TRIAGE_QUESTIONS[question_id].criteria) - 1
    return answers[question_id].score / top_level


def priority(ticket: str) -> float:
    with TypeSafeClient() as client:
        response = client.system_one(
            state=ticket,
            questions=TRIAGE_QUESTIONS,
        )
    answers = response.answers

    severity = normalized(answers, "severity")
    frustration = normalized(answers, "frustration")
    report_quality = normalized(answers, "report_quality")

    # 詳しい報告は調査しやすいので、少しだけ優先度を上げる
    return 0.6 * severity + 0.3 * frustration + 0.1 * report_quality

frustration の confidence が 0.33 と低いのは、文面は礼儀正しいのに「3回目の連絡」「もう限界」という表現があり、「不満だが礼儀正しい」と「非常に怒っている」で確率が 0.55 / 0.45 に割れたためです。このチケットに対しては2つのレベルが重なっています。

重みはあなたのコードの中にあります。数値がどう作られたかが完全に見え、並び順がチームの判断と合わなければ係数を変えて再実行できます。これがコンポジット・スコアリングのパターンです。

レベルに例を足して精度を上げる

明確だと思う入力でも隣接レベルの間にスコアが落ち続けるなら、各レベルを文字列からオブジェクトに変え、whatexamples を持たせます。全レベルで同じフィールド名を使います。

Safari クラッシュの報告に対するレベル記述scoreconfidence
文字列のみ(例なし)1.300.54
入力に似た例を追加:"export fails in one browser but works in another"1.070.90
ブラウザと無関係な例を追加:"search fails, but browsing categories still works"1.280.57
例は「実際の入力に似ている」ときだけ効く例はモデルを誘導します。また、confidence が上がったことは、その答えが正しいことの証明にはなりません。期待するレベルが分かっている例を選び、修正した記述を別の入力でテストしてから採用してください。