基本の形
バグ報告の重大度を3段階で評価する例です。
{
"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"
]
}
}
}
{
"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レベルに集中すれば高く、複数レベルに割れれば低い。
スコアの読み方
同じ質問に、異なるバグ報告を入れるとどうなるか。報告を切り替えて、確率分布とスコアの位置の関係を確かめてください(値はすべて公式ドキュメント掲載のもの)。
—
小数のスコアは「位置」
1.30 は「ほぼレベル1、少しレベル2寄り」。重大度順の並べ替えに使えるし、1つの結論が必要なら最も近いレベルに丸めてもよい。「回避策のない顧客の割合」を測っているわけではない。
同じスコアでも分布は違いうる
スコア 1.0 は「全確率がレベル1」かもしれないし、「レベル0と2に半々」かもしれない。probabilities と confidence を一緒に見て区別する。
confidence 1.0 ≠ 正解の保証
confidence 1.0 は「分布が1レベルに集中している」というモデルの回答の性質であって、その答えが正しい保証ではない。
よいレベルの書き方:「程度」ではなく「状況」を書く
各レベルは単独で評価されます。モデルはレベルの番号も、隣のレベルも見ていません。だから「前のレベルより悪い」といった相対表現や、説明文中の数字は意味を持ちません。
instructions: "Rate severity from 0 to 2, where 2 is worst"criteria: ["0", "1", "2"]
「ボタンが数ピクセルずれている」報告が score 0.57、confidence 0.35。照合する手がかりがなく、確率が割れる。
"Cosmetic; no impact to functionality" ほか、具体的な状況を書いた3レベル
同じ報告が score 0.0、confidence 1.0。
1つの質問で測るのは1次元
「時間に正確で、賢く、経験豊富」のように3つを混ぜると、片方が高く片方が低い入力を置けなくなる。confidence が下がり、スコアの意味も薄れる。次元ごとに質問を分けてコードで合成する。
レベル数は、区別して書ける数だけ
最大10まで使えるが、3で十分なことも多い。区別して説明できないレベルは足さない。
特別扱いしたい極端なケースには専用レベルを
感情スケールが「非常に怒っている」で終わると、脅迫的なメッセージも同じく最上位付近になる。別の対応が必要なら「暴言・脅迫」のレベルを足す。
複雑な評価は、複数の Score に分けて合成する
チケットの優先度を「バグの重大度」「顧客のいら立ち」「報告の質」の3つの Score から作る例です。3問は1リクエストで並列に評価されます。
| 質問 | score | conf | 最上位レベル | 正規化 (score ÷ 最上位) | 重み | 寄与 |
|---|---|---|---|---|---|---|
severity 重大度 | 1.24 | 0.63 | 2 | 0.62 | 0.6 | 0.372 |
frustration いら立ち | 1.45 | 0.33 | 2 | 0.725 | 0.3 | 0.2175 |
report_quality 報告の質 | 3.0 | 1.00 | 3 | 1.0 | 0.1 | 0.1 |
| priority | 0.69 | |||||
score / (len(criteria) - 1) で 0〜1 に正規化します。そうして初めて「重大度 0.6・いら立ち 0.3 なら重大度が2倍効く」と重みが言葉どおりの意味になります。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つのレベルが重なっています。
重みはあなたのコードの中にあります。数値がどう作られたかが完全に見え、並び順がチームの判断と合わなければ係数を変えて再実行できます。これがコンポジット・スコアリングのパターンです。
レベルに例を足して精度を上げる
明確だと思う入力でも隣接レベルの間にスコアが落ち続けるなら、各レベルを文字列からオブジェクトに変え、what と examples を持たせます。全レベルで同じフィールド名を使います。
| Safari クラッシュの報告に対するレベル記述 | score | confidence |
|---|---|---|
| 文字列のみ(例なし) | 1.30 | 0.54 |
| 入力に似た例を追加:"export fails in one browser but works in another" | 1.07 | 0.90 |
| ブラウザと無関係な例を追加:"search fails, but browsing categories still works" | 1.28 | 0.57 |