Skip to main content

Noul

机器翻译译自英文(docs.typesafe.ai)本页 48 段已全部译完

文档索引

在以下地址获取完整的文档索引:https://docs.typesafe.ai/llms.txt 使用此文件可以在进一步探索之前发现所有可用页面。

Noul

Noul 问题要求 TypeSafe 模型评估一个是否问题,并返回答案为“是”的概率。

当答案是“是”或“否”时使用 Noul。例如,此消息是否要求退款,此简历是否提到分布式系统,此评论是否包含个人数据。如果答案是几个选项之一,请使用 Choice。如果在是一个光谱上的位置,请使用 Score。选择问题类型 比较了这三种类型。

Noul 答案是 0 到 1 之间的单个数字,代表答案为“是”的概率,其中 0 代表“否”,1 代表“是”。

请求结构

发送至 TypeSafe API 的 POST 请求主体与其他问题类型具有相同的三个顶级字段:state(即待评估内容)、model 和 questions。每个 Noul 问题具有以下字段:

  • type:始终为 "noul"。
  • instructions:模型需回答的是/非问题,或需其判断的陈述句。
  • criteria:可选。一个包含 true 和 false 的描述对象,说明“是”与“否”的含义。

下方是一个请求示例,其中 state 是一条客服信息,两个问题分别是客户是否需要真人介入,以及他们是否曾联系过客服:

{
  "state": "I have asked three times now. Can I please just talk to a real person?",
  "questions": {
    "is_human_escalation": {
      "type": "noul",
      "instructions": "Is the customer asking for a human agent?"
    },
    "is_repeat_contact": {
      "type": "noul",
      "instructions": "Has the customer contacted support about this before?",
      "criteria": {
        "true": "Mentions a prior attempt, ticket, or that they have asked before",
        "false": "No sign of any previous contact"
      }
    }
  }
}

问题 ID 由你选择,此处为 is_human_escalation 和 is_repeat_contact。ID 不会发送给模型。每个答案都以相同的 ID 返回。第一个问题仅依赖 instructions。第二个问题添加了 criteria 来说明什么情况算“是”,什么情况算“否”。

使用 Python SDK 时,相同的问题表示为 Noul 对象:

from typesafe_sdk import Noul, NoulCriteria, TypeSafeClient

with TypeSafeClient() as client:
    response = client.system_one(
        model="jev-latest",
        state="I have asked three times now. Can I please just talk to a real person?",
        questions={
            "is_human_escalation": Noul(
                instructions="Is the customer asking for a human agent?",
            ),
            "is_repeat_contact": Noul(
                instructions="Has the customer contacted support about this before?",
                criteria=NoulCriteria(
                    true="Mentions a prior attempt, ticket, or that they have asked before",
                    false="No sign of any previous contact",
                ),
            ),
        },
    )

    print(response.answers["is_human_escalation"].noul)
    print(response.answers["is_repeat_contact"].noul)

system_one 方法和 https://api.typesafe.ai/v1/systemone 端点均以 TypeSafe 的 AI 模型 System One 命名。如何使用 TypeSafe 构建 介绍了在代码中何处使用它。

如果你正在使用编码 agent,请首先安装 TypeSafe agent skill,以便它了解请求和响应的结构。

Note

instructions 可以是字符串、对象或数组。从字符串开始。当问题需要附带数据时使用对象,例如需要比较状态的记录,或者当问题的一部分由你的代码构建时。在问题中使用结构 解释了结构何时有帮助,下面的示例 展示了使用代码构建问题时的结构。

响应结构

响应在 answers 中包含每个问题的条目,使用请求中的 ID:

{
  "model": "jev-1.13.0",
  "answers": {
    "is_human_escalation": {
      "type": "noul",
      "noul": 0.99
    },
    "is_repeat_contact": {
      "type": "noul",
      "noul": 0.93
    }
  },
  "usage": {
    "input_tokens": 360,
    "output_tokens": 39
  }
}

这里的两个答案都接近 1。客户说“跟真人说话”,所以 is_human_escalation 是 0.99。“我已经问了三次了”与 is_repeat_contact 的 true 描述匹配,所以它是 0.93。

读取 Noul

这个数字将答案和确定性合二为一。接近 1 的值是强烈的“是”。接近 0 的值是强烈的“否”。接近 0.5 的值意味着模型认为“是”和“否”的概率相似。

下表显示了 jev-1.13.0 针对不同的客户消息记录的 is_human_escalation 问题的回答:

状态noul
谢谢,修好了!0.02
如何重置我的密码?0.07
我今天需要解决这个问题,不惜一切代价。0.26
你是机器人吗?0.40
有什么办法可以找人谈谈我的发票吗?0.84
我已经问了三次了。我可以跟真人说话吗?0.99

前两个和后两个是明确的。“我今天需要解决这个问题”很紧急,但从未要求人工,得分为 0.26。“你是机器人吗?”暗示想要人工但并未提出要求,模型以 0.40 的概率几乎平均分摊。这两种都是需要根据你代码中的阈值做出决定的消息类型。

与 Choice 或 Score 不同,Noul 没有单独的 confidence 值。Noul 的概率分布只有两个结果:是 (yes) 和 否 (no),因此单个 noul 值就能完整描述它。Choice 或 Score 将概率分布在多个选项或等级上,而 confidence 则汇总了这种分布情况。

大多数情况下,你的代码会用阈值把 noul 转成布尔值:

wants_human = response.answers["is_human_escalation"].noul > 0.9

if wants_human:
    route_to_agent(ticket)
else:
    route_to_bot(ticket)

阈值的设置取决于出错的代价。如果“是”和“否”的处理难度相当,请使用 0.5。当处理“假阳”(false yes)代价高昂时(例如呼叫某人或发放退款),请提高阈值。当漏掉“真阳”(true yes)代价高昂时(例如未能标记安全问题),请降低阈值。中间的值可以转由人工处理,而不是走任何一条代码路径。这与 Confidence 页面中针对 Choice 和 Score 答案描述的三路分流相同。

Noul 值从 0 到 1,但它不是你所问事物的刻度。它是答案为“是”的概率。如果问题实际上是关于程度的,该数值并不衡量程度。下面,“候选人在 Python 方面是否强大?”这个问题针对四名候选人,旁边是一个具有四个等级(无经验、有些熟悉、在工作中经常使用、深厚专业知识)的 Score。

候选人Noul: “候选人在 Python 方面是否强大?”Score: “候选人的 Python 经验如何?”
我的经验在于 Java 和 Go。我没有用过 Python。0.030.0(无经验)
我在主要的 Java 工作之外偶尔使用 Python 编写小脚本。0.141.0(有些熟悉)
我在上一份工作中每天使用 Python,为期两年,主要从事数据管道工作。0.812.05(在工作中经常使用)
我写了八年的 Python,包括维护一个大型的 Django 代码库。0.922.89(深厚专业知识)

Noul 判断一个命题(“强大”),值是可能性程度。你可以在代码中将 0 到 1 的范围划分为等级,例如 0.3 到 0.7 代表“有些经验”,但模型看不到它们,因此答案中没有针对这些等级进行判断。中间值可能意味着中等经验或不确定的情况,并且候选人之间的间距不是你自己选择的。Score 会独立判断每个等级描述,因此每个候选人都落在你编写的等级上或附近,返回的概率显示模型如何在等级之间分配其判断。如果你不同意,可以重新措辞某个等级并再次运行。选择问题类型 解释了这种区别。

编写 Noul 问题

每个 Noul 只问一个是否问题。若问题包含两个条件,例如“客户是否生气且要求退款?”,模型必须同时判断两者,导致数值含义不清晰。请提出两个 Noul 并在代码中组合结果。

措辞应使高值代表“是”。“信息是否包含个人数据?”表述清晰。“信息是否不含个人数据?”则颠倒了含义,后续读取该值的代码会得出相反结论。

陈述句与问句效果相同。对于“客户要求退款”这一陈述,数值接近 1 表示该陈述为真。请尝试两种措辞方式与你的数据,以查看哪种效果更好。

明确“是”与“否”之间的界限。“该候选人是否有任何 Python 经验?”就是一个很好的例子,因为“任何 (any)”一词消除了中间地带。当界限模糊时,请添加带有 true 和 false 描述的 criteria,就像上面的 is_repeat_contact 问题所做的那样。对于大多数 Noul,指令就足够了,因此请尝试在有 criteria 和没有 criteria 的情况下测试你的问题,并保留在文档中给出更好答案的那个。

最佳实践:每次调用询问多个问题

对于条件清单,请在一次请求中提出多个 Noul 问题:每个条件一个问题,由代码决定组合含义。问题是并行评估的,因此添加 Noul 几乎不会改变响应时间。一次性询问多个问题 对此有更详细解释。

在代码中处理多个 Noul 答案

上述双问题请求为代码提供了足够的信息来路由消息。下面的示例在客户要求时升级至人工,并在他们之前有过联系时提高优先级。任何一个问题的中间值都会转交给审核者,而不是代码路径:

from typesafe_sdk import Noul, NoulCriteria, TypeSafeClient

SUPPORT_QUESTIONS = {
    "is_human_escalation": Noul(
        instructions="Is the customer asking for a human agent?",
    ),
    "is_repeat_contact": Noul(
        instructions="Has the customer contacted support about this before?",
        criteria=NoulCriteria(
            true="Mentions a prior attempt, ticket, or that they have asked before",
            false="No sign of any previous contact",
        ),
    ),
}

YES = 0.8
NO = 0.2


def route(message: str) -> None:
    with TypeSafeClient() as client:
        response = client.system_one(
            model="jev-latest",
            state=message,
            questions=SUPPORT_QUESTIONS,
        )
    answers = response.answers

    wants_human = answers["is_human_escalation"].noul
    repeat = answers["is_repeat_contact"].noul

    if NO < wants_human < YES or NO < repeat < YES:
        # The model isn't sure either way. Let a person decide.
        send_to_review(message)
        return

    priority = "high" if repeat > YES else "normal"
    if wants_human > YES:
        route_to_agent(message, priority=priority)
    else:
        route_to_bot(message, priority=priority)

对于上面的消息,is_human_escalation 的 Noul 答案值为 0.99,is_repeat_contact 为 0.93,因此代码将其以高优先级路由到 agent。消息“如何重置我的密码?”在两个问题上均为 0.07,并路由到机器人。

阈值存在于你的代码中。如果审核者看到太多消息,请缩小 NO 和 YES 之间的差距。如果太多错误的路由通过,请扩大差距。如果你以后需要知道消息是否提及付款,或者是否包含个人数据,请在 SUPPORT_QUESTIONS 中添加另一个 Noul。请求次数仍保持为一次。

结构化指令

指令可以是对象而不是字符串,其中一个问题在一个字段中,其他字段包含补充数据。在问题中使用结构 涵盖了何时使用这种方法有帮助。这里它用于通过代码构建的问题:刚收到的简历与可能为同一人的候选人数据库中的记录进行比较。每条记录按其原样进入 potential_duplicate 字段,question 对每条记录都相同,并且所有记录都在一次请求中进行检查。代码生成的问题键包含每条记录的数据库 ID:

{
  "state": {
    "resume": {
      "name": "John Smith",
      "location": "Oakland, CA",
      "summary": "Backend engineer with eight years of Python and Go experience.",
      "experience": [
        {
          "employer": "Google",
          "title": "Senior Backend Engineer",
          "years": "2021-2025"
        },
        {
          "employer": "Microsoft",
          "title": "Software Engineer",
          "years": "2017-2021"
        }
      ]
    }
  },
  "questions": {
    "same_as_record_18": {
      "type": "noul",
      "instructions": {
        "potential_duplicate": {
          "name": "Jon Smith",
          "location": "Oakland, CA",
          "last_employer": "Google"
        },
        "question": "Is the resume for the same person as `potential_duplicate`?"
      }
    },
    "same_as_record_42": {
      "type": "noul",
      "instructions": {
        "potential_duplicate": {
          "name": "John Smith",
          "location": "Austin, TX",
          "last_employer": "Lone Star Freight"
        },
        "question": "Is the resume for the same person as `potential_duplicate`?"
      }
    },
    "same_as_record_77": {
      "type": "noul",
      "instructions": {
        "potential_duplicate": {
          "name": "John Smithers",
          "location": "Oakland, CA",
          "last_employer": "Bay Health Clinic"
        },
        "question": "Is the resume for the same person as `potential_duplicate`?"
      }
    }
  }
}

响应如下:

{
  "model": "jev-1.13.0",
  "answers": {
    "same_as_record_18": {
      "type": "noul",
      "noul": 0.74
    },
    "same_as_record_42": {
      "type": "noul",
      "noul": 0.09
    },
    "same_as_record_77": {
      "type": "noul",
      "noul": 0.08
    }
  },
  "usage": {
    "input_tokens": 535,
    "output_tokens": 58
  }
}

每个答案代表简历属于该记录中人物的概率。记录 18 的姓名拼写不同,但地点和雇主匹配,得分为 0.74。记录 42 姓名相同但位于不同城市且雇主不同,得分为 0.09。记录 77 姓名相似,地点相同但雇主不同,得分为 0.08。请在代码中对每个值设置阈值,如代码中的 处理多个 Noul 答案,并将中间值提交给人工处理。

在 Python SDK 中,问题由候选记录构建。问题文本是固定的,而记录会发生变化:

from typesafe_sdk import Noul, TypeSafeClient

SAME_PERSON = "Is the resume for the same person as `potential_duplicate`?"


def duplicate_questions(candidates: list[dict]) -> dict[str, Noul]:
    """One Noul per candidate record, all asking the same question."""
    return {
        f"same_as_record_{candidate['id']}": Noul(
            instructions={
                "potential_duplicate": {
                    "name": candidate["name"],
                    "location": candidate["location"],
                    "last_employer": candidate["last_employer"],
                },
                "question": SAME_PERSON,
            },
        )
        for candidate in candidates
    }


def find_duplicates(resume: dict, candidates: list[dict]) -> list[str]:
    with TypeSafeClient() as client:
        response = client.system_one(
            model="jev-latest",
            state={"resume": resume},
            questions=duplicate_questions(candidates),
        )
    return [
        question_id
        for question_id, answer in response.answers.items()
        if answer.noul > 0.7
    ]

结构化数据提取级联 cookbook 使用结构化指令来验证提取的记录。每个字段都使用相同的一组问题。每个问题的 instructions 对象在 main_question 属性中包含问题文本。此外还有 field_spec 和 extracted_field 属性,它们随每个字段而变化。

cookbook 中的 Noul

查看我们的 cookbook,了解应用 Noul 问题的应用程序:

  • 并行问题 在单一请求中对一篇文章运行包含 13 个问题的合规清单。
  • 自我一致性:Nouls 依据 15 项标准给保险理赔评分,并衡量多次运行中的数值稳定性。
  • 重新排序 直接使用概率值而非阈值:每对查询-候选对象使用一个 Noul,然后根据数值对候选对象排序。
  • 逐行搜索 结合一个找到匹配行的 Choice 和一个检查文档是否包含答案的 Noul。
  • 结构恢复 对每对行使用一个 Noul,判断换行符是否截断了句子,从而从纯文本重建段落。