🛡️TypeSafe 中文文档
原文档 ↗

Score

Score 是一种 System One 问题类型,用于按照有序的、描述性的级别对内容进行评级。答案包含一个分数、每个级别的概率以及置信度。

当答案是你可以用步骤描述的一个光谱上的位置时,使用 Score。例如,bug 有多严重、客户有多满意,或者候选人有多少 Python 经验。如果答案是固定选项集合之一且选项之间没有顺序,请使用 Choice。如果是“是”或“否”,请使用 Noul。选择问题类型对这三种类型进行了比较。

Score 的答案是在 score 中沿你的级别的一个位置,它可能落在两个级别之间。模型还会在 probabilities 中返回每个级别的概率,并为答案返回一个 confidence 值。

🔭 本页原本包含一个交互式组件(ScoreExplorer),为动态应用;请移步原站对应页面体验。

每个步骤前面的数字是位置,相关说明见 级别。

请求结构

发送到 TypeSafe API 的 POST 请求体与任何其他问题类型一样具有相同的三个顶层字段:state(要评估的内容)、model 和 questions。每个 Score 问题包含以下字段:

  • type:始终为 "score"。

  • instructions:模型要回答的问题,即它要评级的内容。

  • criteria:一个有序的级别描述数组,从量表的低端到高端。应至少包含两个级别;API 最多接受 10 个。

下面是一个请求示例,其中状态是一份 bug 报告,问题是这个 bug 有多严重:

request
{
  "state": "The export button crashes the settings page in Safari. It works in Chrome, but a few of our customers only use Safari.",
  "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"
      ]
    }
  }
}

问题 id 由你选择,此处为 bug_severity。该 id 不会发送给模型。答案会以相同的 id 返回。

级别

criteria 中的每一项都是一个级别:可能答案光谱上的一个点,用文字描述。级别的编号是它在 criteria 数组中的位置,从 0 开始,因此上面的三项分别是级别 0、1 和 2。数组的顺序就是编号。

模型只拿到这些描述,别的什么都拿不到,而且每个级别都是独立地针对状态进行判断的。

响应中的 score 是级别光谱上的一个位置。对于三级量表,它从 0 到 2,并且可能落在两个级别之间。

我们的客户端 SDK 提供类型化的问题。在 Python 中,同样的问题是一个 Score:

python
from typesafe_sdk import Score, TypeSafeClient

with TypeSafeClient() as client:
    response = client.system_one(
        state="The export button crashes the settings page in Safari. It works in Chrome, but a few of our customers only use Safari.",
        questions={
            "bug_severity": 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",
                ],
            ),
        },
    )

    print(response.answers["bug_severity"].score)

使用 system_one 方法或 https://api.typesafe.ai/v1/systemone 端点调用 System One 模型。model 字段选择由哪个模型处理该请求。如何使用 TypeSafe 构建介绍了在代码中的什么位置调用它。

使用我们的客户端 SDK 之一,或直接调用 TypeSafe API。如果编码 agent 正在为你编写集成,请先安装 TypeSafe agent 技能,这样它就了解请求和响应的结构。

📝注意

instructions 和 criteria 中的每个级别可以是字符串、对象或数组。先从字符串开始。当某个级别需要一段描述加上几个示例情境时,使用对象。参见下文的结构化级别描述以及 API 参考。

响应结构

响应中的 answers 为每个问题包含一个条目,以请求中的 id 为键。以下是对上面示例请求的响应:

json
{
  "model": "jev-1.13.0",
  "answers": {
    "bug_severity": {
      "type": "score",
      "score": 1.43,
      "confidence": 0.35,
      "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.57,
        "2": 0.43
      }
    }
  },
  "usage": {
    "input_tokens": 332,
    "output_tokens": 18
  }
}

每个 Score 答案包含五个值:

  • type:TypeSafe 问题的类型。

  • probabilities:每个级别的概率,以字符串形式的级别编号为键。所有值之和为 1。

  • score:级别编号轴上的位置,从 0 到最高级别编号(此处为 2)。它是每个级别编号乘以其概率后相加的结果:0 x 0.0 + 1 x 0.57 + 2 x 0.43 = 1.43。

  • legend:每个级别编号映射回其描述。

  • confidence:一个 0 到 1 之间的数字,根据 probabilities 的分布情况计算得出。概率集中在单个级别上意味着高置信度;概率分散在多个级别上意味着低置信度。

1.43 的分数意味着模型在级别 1 和级别 2 之间有所摇摆,偏向级别 1。这与该报告相符:导出功能坏了,对大多数客户来说切换到 Chrome 是一种变通办法,但对只用 Safari 的客户来说不是。模型给“存在变通办法”分配了 0.57,给“没有变通办法”分配了 0.43,置信度为 0.35,因为它在两者之间摇摆。

使用 Python SDK 时,ScoreAnswer 以类型化字段的形式提供 score、confidence、probabilities 和 legend。SDK 以整数级别(而非字符串)作为 probabilities 和 legend 的键。

解读 Score

我们来看看分数如何随不同输入而变化。例如,使用上面请求中的问题及其级别:

"How severe is the reported issue?"
  → 0: Cosmetic; no impact to functionality
  → 1: Broken or degraded feature, but workaround exists
  → 2: Blocking issue; no workaround exists

我们可以看到不同的 bug 报告如何改变分数:

probabilities
状态 score confidence 级别 0 级别 1 级别 2
设置页面上导出按钮的位置偏差了几个像素。 0.01.01.00.00.0
PDF 导出按钮点击后没有任何反应。我仍然可以导出 CSV 然后自己转换,但这太花时间了。 1.01.00.01.00.0
导出为 PDF 失败,转圈图标永远转不完。我们团队有些人说 CSV 导出对他们仍然有效,另一些人说也失败了。 1.110.840.00.890.11
导出按钮会让 Safari 中的设置页面崩溃。在 Chrome 中正常,但我们有少数客户只用 Safari。 1.430.350.00.570.43
从今天早上开始,我们团队没有人能登录。每次尝试都会收到 500 错误。 2.01.00.00.01.0

在这些示例中,置信度 1.0 表示返回的分布把全部概率都放在了一个级别上。这描述的是模型的答案,并不保证答案一定正确。

分数是级别编号的概率加权平均值。在第三和第四个示例中,概率分布在级别 1 和级别 2 之间。级别 2 上的权重越大,分数越高。它并不衡量没有变通办法的客户比例。

不同的分布可能产生相同的分数。分数 1.0 可能意味着全部概率都在级别 1 上,也可能是一半在级别 0、一半在级别 2。要区分这些情况,需要结合 probabilities 和 confidence 一起解读分数。

小数分数是一个位置。你可以用它按严重程度对报告排序,或者在代码只需要一个结果时把它四舍五入到最接近的级别。我们的实体对齐实战指南展示了四舍五入到最接近级别以做出决策的示例。

Score 上的低置信度通常意味着三种情况之一:级别对这个状态来说相互重叠,问题在衡量不止一件事,或者状态提供的信息不足以定位它。我们的 Confidence 文档介绍了如何在代码中使用置信度。

编写好的级别

描述情境,而不是程度。“功能损坏或降级,但存在变通办法”给了模型可以与状态匹配的具体内容。“中等严重”则没有。具体的描述可以帮助模型区分各个级别。用已知示例核对答案;仅凭更高的置信度并不能说明某个描述更好。

每个级别都是单独评估的。模型看不到级别的编号或相邻级别,所以“比上一个级别更严重”对它毫无意义,描述或 instructions 中出现数字也没有帮助。下面是上表中按钮错位报告在级别只有数字时的情况:

instructions: "Rate severity from 0 to 2, where 2 is worst"
criteria: ["0", "1", "2"]
→ score 0.55, confidence 0.33, probabilities 0: 0.45, 1: 0.55, 2: 0.0

同一份报告使用那三个描述性级别时,得分为 0.0,置信度 1.0。只有数字时,模型没有可以匹配的内容,于是把概率在 0 和 1 之间分摊。

有多少个能清晰区分描述的级别,就使用多少个,最多 10 个。三个就很好。不要添加你无法清晰描述的级别。

每个 Score 问题只保留一个维度。如果某个描述写的是“守时、聪明又有经验”,那这个问题就在衡量三件事,而一个在某方面高、另一方面低的输入就无法被定位。置信度会下降,分数的意义也随之减弱。把它拆成每个方面一个 Score 问题,然后在代码中组合,下一节会展示这种做法。

如果量表的顶端有一个需要区别处理的罕见极端情形,就为它单设一个级别。一个以“非常愤怒”结尾的情感量表可以加上“辱骂或威胁”。没有这个级别时,这两类消息都可能得到接近顶端的分数。单凭分数可能无法区分它们。

如果完全没有中间地带,且答案是几个离散类别之一,请改用 Choice,或者把问题拆成几个 Noul 问题。用你自己的数据测试级别非常重要。同一量表的两种措辞在你的数据上可能表现不同。

将复杂判断拆分为多个 Score 问题

复杂判断——即依赖于多个因素的判断——最好拆成每个因素一个 Score 问题。然后你可以在代码中组合 TypeSafe 返回的各个 Score 来做出判断。有些 Score 问题可能比其他的更重要,因此为每个 Score 问题赋予一个表示相对重要性的权重。权重由你决定。当组合结果与你的团队会做出的决定不符时,在代码中修改权重并重新运行。把 Score 问题放在一个请求中发送。它们会被并行评估。增加问题几乎不会改变响应时间,只多消耗几个问题 token;参见一次提出多个问题。

下面的请求是上表中那个转圈工单,并补充了一些上下文。它提出三个 Score 问题:bug 有多严重、客户有多沮丧,以及报告给工程师提供了多少可用信息。

request
{
  "state": "Export to PDF fails with a spinner that never finishes. Some of our team say CSV export still works for them, others say it fails too. This is the third time I'm writing in and honestly I'm done. Steps: open any report, click Export, choose PDF. Chrome 128 on macOS.",
  "questions": {
    "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"
      ]
    },
    "frustration": {
      "type": "score",
      "instructions": "How frustrated is the customer?",
      "criteria": [
        "Calm, just stating facts",
        "Frustrated but civil",
        "Very angry, strong language or threatening to leave"
      ]
    },
    "report_quality": {
      "type": "score",
      "instructions": "How much does the report give an engineer to work with?",
      "criteria": [
        "No detail; just says something is broken",
        "Names the feature but no steps or environment",
        "Steps to reproduce or environment, but not both",
        "Steps to reproduce and environment"
      ]
    }
  }
}

TypeSafe 的响应:

json
{
  "model": "jev-1.13.0",
  "answers": {
    "severity": {
      "type": "score",
      "score": 1.24,
      "confidence": 0.64,
      "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.76,
        "2": 0.24
      }
    },
    "frustration": {
      "type": "score",
      "score": 1.28,
      "confidence": 0.58,
      "legend": {
        "0": "Calm, just stating facts",
        "1": "Frustrated but civil",
        "2": "Very angry, strong language or threatening to leave"
      },
      "probabilities": {
        "0": 0.0,
        "1": 0.72,
        "2": 0.28
      }
    },
    "report_quality": {
      "type": "score",
      "score": 3.0,
      "confidence": 1.0,
      "legend": {
        "0": "No detail; just says something is broken",
        "1": "Names the feature but no steps or environment",
        "2": "Steps to reproduce or environment, but not both",
        "3": "Steps to reproduce and environment"
      },
      "probabilities": {
        "0": 0.0,
        "1": 0.0,
        "2": 0.0,
        "3": 1.0
      }
    }
  },
  "usage": {
    "input_tokens": 468,
    "output_tokens": 43
  }
}

每个问题都独立地针对工单作答并获得一个分数:

  • severity 为 1.24,置信度 0.64。与开头示例的解读相同:导出功能坏了,但一些人有变通办法。

  • frustration 为 1.28,置信度 0.58。措辞仍然礼貌,但“第三次”和“我受够了”把部分分数推向了最高级别,因此模型在“沮丧但礼貌”和“非常愤怒”之间按 0.72 和 0.28 分配。对这个工单来说两个级别有所重叠,这就是置信度中等的原因。

  • report_quality 为 3.0,置信度 1.0。复现步骤和浏览器版本都有说明。

这三个量表长度不同,因此在组合之前,先对每个分数做归一化。四级量表返回 0 到 3,三级量表返回 0 到 2,所以一个量表上的最高分数比另一个上的大。将每个分数除以其最高级别编号,即 len(criteria) - 1,就能把每个分数都归到 0 到 1。这样权重才有其字面含义:severity 权重 0.6、frustration 权重 0.3,意味着 severity 的分量是 frustration 的两倍。

下面的 TypeSafe Python SDK 代码提出这三个问题,对每个分数做归一化,并用一个示例优先级计算将它们组合起来:

python
from typesafe_sdk import Score, TypeSafeClient

TRIAGE_QUESTIONS = {
    "severity": 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",
        ],
    ),
    "frustration": Score(
        instructions="How frustrated is the customer?",
        criteria=[
            "Calm, just stating facts",
            "Frustrated but civil",
            "Very angry, strong language or threatening to leave",
        ],
    ),
    "report_quality": Score(
        instructions="How much does the report give an engineer to work with?",
        criteria=[
            "No detail; just says something is broken",
            "Names the feature but no steps or environment",
            "Steps to reproduce or environment, but not both",
            "Steps to reproduce and environment",
        ],
    ),
}


def normalized(answers, question_id: str) -> float:
    """Put a score on 0 to 1 by dividing by its top level number."""
    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")

    # A detailed report helps an engineer investigate, so it raises priority a little.
    return 0.6 * severity + 0.3 * frustration + 0.1 * report_quality

对于上面的示例响应,归一化后的分数为:severity 0.62,frustration 0.64,报告质量 1.0。优先级为 0.6 × 0.62 + 0.3 × 0.64 + 0.1 × 1.0 = 0.664,四舍五入为 0.66。

权重存在于你的代码中,所以你能清楚地看到这个数字是如何得出的,并在排序与团队判断不符时修改它。如果以后需要更多 Score 问题,把它们加入 TRIAGE_QUESTIONS 即可。请求数量保持为一个。这种把复杂判断拆分为多个独立的 Score、然后在代码中用权重组合它们的技术,称为组合评分模式。

结构化级别描述

先为每个级别使用基本的文本描述。当模型在你认为很清晰的输入上持续给出介于两个相邻级别之间的分数时,把每个级别从字符串改为对象,其中一个字段说明该级别涵盖什么,另一个字段给出几个示例情境。每个级别使用相同的字段名,以便模型进行同类比较。

下面的请求是之前用过的那个转圈工单,但每个级别都附带了示例:

request
{
  "state": "Export to PDF fails with a spinner that never finishes. Some of our team say CSV export still works for them, others say it fails too.",
  "questions": {
    "bug_severity": {
      "type": "score",
      "instructions": "How severe is the reported issue?",
      "criteria": [
        {
          "what": "Cosmetic; no impact to functionality",
          "examples": [
            "typo in a label",
            "misaligned icon"
          ]
        },
        {
          "what": "Broken or degraded feature, but workaround exists",
          "examples": [
            "export fails in one browser but works in another"
          ]
        },
        {
          "what": "Blocking issue; no workaround exists",
          "examples": [
            "cannot log in",
            "data loss"
          ]
        }
      ]
    }
  }
}

响应:

json
{
  "model": "jev-1.13.0",
  "answers": {
    "bug_severity": {
      "type": "score",
      "score": 1.09,
      "confidence": 0.87,
      "legend": {
        "0": {
          "what": "Cosmetic; no impact to functionality",
          "examples": [
            "typo in a label",
            "misaligned icon"
          ]
        },
        "1": {
          "what": "Broken or degraded feature, but workaround exists",
          "examples": [
            "export fails in one browser but works in another"
          ]
        },
        "2": {
          "what": "Blocking issue; no workaround exists",
          "examples": [
            "cannot log in",
            "data loss"
          ]
        }
      },
      "probabilities": {
        "0": 0.0,
        "1": 0.91,
        "2": 0.09
      }
    }
  },
  "usage": {
    "input_tokens": 379,
    "output_tokens": 18
  }
}

使用纯字符串时,这份工单得分为 1.11,置信度 0.84。使用示例后,得分为 1.09,置信度 0.87,变化很小,因为纯字符串本来就定位得不错。当纯字符串让模型摇摆不定时,效果会更明显,如下表所示。

示例会引导模型,而且只有当它们看起来像你的真实输入时才有帮助。下表是开头那个 Safari 报告配三组不同的级别对象:

级别描述scoreconfidence
纯字符串:没有带示例的对象1.430.35
添加了 examples 数组,示例切题:“导出在一个浏览器中失败但在另一个浏览器中正常”1.030.96
添加了 examples 数组,但示例与浏览器无关:“搜索失败,但浏览分类仍然正常”1.430.35

在这个对比中,切题的示例几乎把全部概率集中到了一个级别上。无关的示例返回的结果与纯字符串相同。更高的置信度并不能证明哪个答案正确。选择具有已知预期级别的示例,然后在不同的输入上测试修改后的描述,再决定是否采用。

本站为 docs.typesafe.ai 的中文翻译,仅供学习参考;内容版权归原作者所有。