文档目录

题型总览

TypeSafe 的题型、它们返回的带类型答案、如何在它们之间选择,以及如何一次性提出多个问题。

非官方中文翻译 · 内容以 英文原文 ↗ 为准 · 同步于 2026-10-15

TypeSafe 的题型是你在代码中组合使用的小型、带类型的积木。它们成对出现:一个**问题(question)**定义了一个由 System One 模型针对某个 **state(上下文状态)做出的判断,而它的答案(answer)**就是返回的那个带类型的值。你在代码中组合这些答案来做出决策。每种题型返回不同形态的答案。

类型 它回答的问题 返回值
Choice(选择题) 这些选项中哪一个? choice、probabilities、confidence
Score(打分题) 处于哪一档? score、legend、probabilities、confidence
Noul(是非题) 这是真的吗? noul(取值 0 到 1)

你可以一次提一个问题,也可以一次性发送多个。同一请求中的每个问题看到的 state 相同,互相独立地评估,并按你选择的 ID 返回带类型的答案。

一次提问一个问题,获得单一快速判断

System One 模型专为快速、聚焦的判断而设计。请让它做出的判断,应当是「具备合适上下文的内行人花一秒钟就能给出」的那种。「这条消息是否传达了紧迫感?」是一个好问题。「分析这条消息并确定最佳行动方案」就不是。那需要慢速推理,这也是一个把任务拆分成小问题并在代码中组合答案的信号。

如果你想要的判断依赖于若干独立因素,那就分别针对每个因素提问,然后用你自己的逻辑组合答案。不要问「给这个创业路演打分」,而是分别就市场规模、技术可行性和差异化提问,然后根据它们的相对重要性在代码中加权。当优先级变化时,调整权重值,而不是重写提示词。一次性提出多个问题 展示了如何做到这一点。

定义一个问题

每个问题都有一个 ID、一个 type 和 instructions。Choice 和 Score 类型的问题还需要 criteria,它定义了 Choice 问题的选项或 Score 问题的档位。Noul 类型的问题接受 criteria 作为对 yes 与 no 含义的可选澄清。

  • ID。 你选择的键,比如 refund_requested。它在响应中标识该答案。
  • type。 choice、score 或 noul 之一。
  • instructions。 你就 state 提出的问题。这是放置你的评估逻辑的地方。把它写成一个清晰、具体的问题,或者写成一个供模型判断的陈述。对于大多数问题来说,一个字符串就够了。它也可以是一个对象或数组,把问题放在一个字段中,把它所引用的数据放在其他字段中;参见在问题中使用结构。
  • criteria。 可能的答案:Choice 问题的选项 map,Score 问题的有序档位列表,以及 Noul 问题的 yes 和 no 的可选描述。每个题型的页面都会介绍其形态。

这个问题询问客户是否请求了退款:

python
from typesafe_sdk import Noul

questions = {
    "refund_requested": Noul(
        instructions="Does the customer request a refund?",
    ),
}

选择问题类型

选择与所需答案形态匹配的类型。

  • Choice(选择题) 适合答案是一组已知选项、且彼此之间没有顺序关系的情况:把工单路由到一个部门、对文档类型进行分类、检测编程语言。给出完整的选项列表,并在列表可能无法覆盖所有输入时添加一个 other 或 none of the above 选项。

  • Score(打分题) 适合答案落在一个连续谱上、并且你能描述该连续谱上每个点含义的情况:缺陷严重度、客户挫败感、技能水平。档位由你定义,模型返回沿档位的一个位置。

  • Noul(是非题) 适合一个干净的 yes/no 问题,并且概率本身就是有用的信号:这条消息是否包含个人可识别信息,客户是否在请求退款,这份简历是否提到分布式系统。

如果两种类型看起来都合适,选择你的代码可以直接采取行动的那一种。refund、rebook、information 之间的 Choice 直接对应三条代码路径。客户挫败感的 Score 对应一个阈值。Noul 对应一个 if。

返回结果

答案也是题型。每个问题类型返回一个带类型的值,你的代码可以比较、设置阈值、排序、传入进一步的逻辑,或放入后续请求的 state 中(参见一个问题依赖于另一个问题时)。

类型 答案字段 如何解读
Choice choice、probabilities、confidence choice 是所选选项。probabilities 是跨所有选项的分布。confidence 概括了该分布的尖锐程度。
Score score、legend、probabilities、confidence score 是沿你的档位上的一个位置,可以落在两档之间。legend 按编号重复档位。probabilities 是跨档位的分布。
Noul noul 答案为 yes 的概率。接近 1 是强烈的 yes,接近 0 是强烈的 no,接近 0.5 表示不确定。Noul 没有独立的 confidence。

这些答案有两个使其可组合的特性:

  • 每个答案都被约束在你提供的选项内。 模型返回的是你的选项或档位上的概率分布,绝不会返回超出它们的值。你的代码永远不需要从生成的散文中恢复一个值。
  • 每个答案都是独立的。 一个问题的答案不会成为另一个问题的隐藏上下文。你可以添加或删除问题,而不会改变其他问题的结果。

某些题型还会返回 confidence;每个题型的页面会说明其答案是否包含它。置信度 一节解释了如何用它来决定何时自动执行、何时升级到人工处理。

引用具体字段

被评估的内容——也就是 state ——通常是一个包含多个部分的 JSON 对象:一次对话、一条记录、一份策略。当问题针对的是其中某一部分时,用点-索引路径在 instructions 中指明它的键,包括反引号。模型于是就知道要判断 state 的哪一部分。

拿 State 页面中的支持对话为例:

json
{
  "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."
}

下面这两个问题按路径指向客户的消息、策略以及收费记录:

python
questions = {
    "refund_requested": {
        "type": "noul",
        "instructions": "Does `ticket.messages[0].text` request a refund?",
    },
    "policy_supports_refund": {
        "type": "noul",
        "instructions": (
            "Does `refund_policy` support the refund requested "
            "in `ticket.messages[0].text`, given `order.charges`?"
        ),
    },
}

显式的路径能清楚地表明结构化 state 中的哪些部分应当影响每次判断。参见 State 以了解如何组织输入。

一次性提出多个问题

把所有使用同一个 state 的问题放在一个请求中发送。你可以自由地混合题型。System One 模型并行评估同一请求中的所有问题。增加问题几乎不会改变响应时间,也只会增加少量额外问题的 token 成本,而这些成本是很低的。问一个你可能不需要的问题几乎是不花钱的。

这个请求一次性完成了客户消息的分类、紧迫性的判断以及挫败感的打分:

json
{
  "model": "jev-latest",
  "state": "Our API integration started returning 500 errors on every request about 20 minutes ago, and we can't process any customer orders until this is fixed.",
  "questions": {
    "department": {
      "type": "choice",
      "instructions": "Which team should handle this",
      "criteria": {
        "billing": "Payment or subscription issues",
        "technical": "Bugs or integration problems",
        "sales": "Pricing or account questions"
      }
    },
    "is_urgent": {
      "type": "noul",
      "instructions": "The message conveys urgency or time-sensitivity"
    },
    "frustration": {
      "type": "score",
      "instructions": "How frustrated the customer appears",
      "criteria": [
        "Calm, just stating facts",
        "Frustrated but civil",
        "Very angry, strong language"
      ]
    }
  }
}

我们的客户端 SDK 提供了带类型的问题和答案。在 Python 中,把 Choice、Noul 和 Score 对象的 questions 字典传递给 client.system_one(...)。下面的请求发送一张工单和一条退款策略一次,并为每个问题获得一个带类型的答案:

python
from typesafe_sdk import Choice, Noul, Score, TypeSafeClient

state = {
    "ticket_message": "My flight was cancelled. Can I get a refund?",
    "refund_policy": "Cancelled flights are eligible for a full refund.",
}

with TypeSafeClient() as client:
    response = client.system_one(
        state=state,
        questions={
            "refund_requested": Noul(
                instructions="Does `ticket_message` request a refund?",
            ),
            "request_type": Choice(
                instructions="What is the main request in `ticket_message`?",
                criteria={
                    "refund": "The customer wants money returned.",
                    "rebooking": "The customer wants a replacement flight.",
                    "information": "The customer is asking for information only.",
                },
            ),
            "frustration": Score(
                instructions="How frustrated does the customer appear in `ticket_message`?",
                criteria=[
                    "Calm and neutral.",
                    "Concerned but civil.",
                    "Very angry or using strong language.",
                ],
            ),
        },
    )

print(response.answers["refund_requested"].noul)
print(response.answers["request_type"].choice)
print(response.answers["frustration"].score)

参见客户端 SDK 以获取你所使用语言的安装和使用方法。

提出推测式问题

把所有你的代码可能需要的问题都问出来,包括那些答案只对部分输入才有意义的问题,然后让代码决定使用哪些答案。如果一张工单最终不是缺陷报告,那就忽略严重度答案。我们称其为推测式扇出(speculative fan-out) 设计模式。并行问题实战示例 展示了把 13 个问题批量放入一次调用比 13 次单独调用便宜 11.5 倍、快 9.6 倍,而答案没有任何变化。

你可以问多少个问题

请求中的问题数量没有限制。真正限制请求的是其大小:它必须适配模型的单次请求上下文和单题上下文。

与 state 相比,问题通常较短,因此 state 占据了这两者的大部分。如果你的问题无法全部放进一个请求,一种做法是把它们拆分到几个共享同一个 state 的请求中。

把一个复杂判断拆成多个问题

依赖于多件事的判断最好按「一件事一个问题」的方式拆开。在你的代码中组合这些答案,按相对重要性给每个加权。这些权重由你决定。当组合结果与你团队的判断不一致时,在代码中调整它们并重新运行。由于问题在同一个请求内并行运行,增加问题几乎不会改变响应时间。这种拆分只会多花少量问题的 token。

例如,工单优先级可以由三个 Score 问题构建:缺陷有多严重、客户有多挫败、以及这份报告为工程师提供了多少可操作的信息。Score 页面通过把一个复杂判断拆成多个 Score 问题 一节详细介绍了这个请求及其对答案进行归一化和加权的代码。这种技巧被称为组合打分 设计模式。

一个问题依赖于另一个问题时

同一请求中的问题是相互独立的:一个问题的答案不会成为另一个问题的上下文。如果后续的判断依赖于前面的答案,就在代码中发起第二次请求。只有当你的代码在拿到第一个答案之前无法构建第二次请求时,这种依赖才是真实的:它需要该答案来为 state 获取更多数据、决定 state 由什么组成,或者挑选下一个问题的选项。否则,就把这些问题一起提出,并在代码中组合它们的答案。

两次请求是例外,而非通则。如果第二次请求中的问题原本就可以针对原始 state 提出,就把它们放进第一次请求中,让代码忽略它不需要的部分。三个实战示例出于真实的原因发出了第二次请求。技能推荐 在一次请求中排序 182 个技能,然后获取前三名的完整内容,并针对这些更好的证据再次进行判断。结构恢复 询问每个换行是否分隔了句子,根据这些答案把行合并成块,然后对这些块进行分类——而这些块直到第一次请求回答后才存在。层次化分类 用每个 Choice 答案来决定下一次请求提供哪些选项。

参见如何使用 TypeSafe 构建 以获取关于把工作流拆解为聚焦判断的指导。

下一步

要看这些题型如何组合成系统架构,可以前往 设计模式(Patterns)。

本文档由 Mintlify 构建和托管,Mintlify 是一个面向开发者的文档平台。