20分钟跑通 Jev:从 Playground 到首个 /v1/systemone 调用

先说结论

  • Jev 是 TypeSafe 的旗舰模型,定位不是“生成文本”,而是“对 state 做结构化判断”,它接收 state 和一组问题,直接返回 typed answers。
  • 最小可跑路径一共四步:打开 Playground 看一次判断 → 在控制台拿 API key → 用 cURL 调一次 HTTP API → 用 Python SDK 调一次。
  • API 端点是 POST https://api.typesafe.ai/v1/systemone,请求体核心字段是 state、model、questions,其中 model 当前写 "jev-latest"。
  • questions 只有三种 primitive:Noul、Choice、Score,可以在一次调用里混用,每个问题独立评估,返回值形状不同。
  • 响应里 answers 是每个问题 ID 对应的结果,choice 返回选项 + confidence + probabilities,score 返回分数 + legend + confidence + probabilities,noul 只返回一个 0~1 的 noul 值(没有 confidence);顶层还有 usage.input_tokens 和 usage.output_tokens。

证据与过程

1. 先在 Playground 免费试一次,不需要写代码

最直接的入口是官方控制台的 Playground:https://console.typesafe.ai/playground。按照官方 Quick start 的建议,第一步就是打开它并登录。

你可以把任意一段文本当作 state 粘进去。官方文档给的示例是一段客户支持消息,比如:“Hi, I’ve been trying to connect my Stripe account for 3 days and the integration keeps failing. I’m losing sales. Please help ASAP.” 这段文本就是待评估的原始材料。

然后添加一个问题。官方建议先试一个 Noul 问题,例如:"Does this message express urgency?"。Noul 是 yes/no 判断,返回一个 0 到 1 的数值,数值接近 1 表示“是”的概率更高。在 Playground 里,你可以马上看到结果。这是理解 Jev 工作方式的最快路径:你不是让模型写一段分析,而是让它对一个明确问题给出可被代码消费的判断。

这一步的官方来源是 TypeSafe 的 Quick start 页面。它强调可以先从 Playground 试 Noul,再混合 Choice 和 Score 一起看结果。这个混合能力是 Jev 的重要特点:一次调用可以同时获得多个不同类型的判断。

2. 拿 API key:去官方控制台,别把 key 写死

要从本地调 API,需要 key。官方控制台的 key 管理页是 https://console.typesafe.ai/keys,这是 TypeSafe 自己提供的控制台页面。

登录后生成 API key。强烈建议把它放进环境变量里,比如 TYPESAFE_API_KEY。这样你在 cURL 和 Python SDK 里都可以直接引用环境变量,避免把 key 明文写进脚本或仓库。Quick start 的示例 cURL 命令里写的是 $TYPESAFE_API_KEY,那是一个环境变量引用,不是让你把字面量 $TYPESAFE_API_KEY 填进去。新手容易在这里犯迷糊,所以我们明确说明:先在终端执行 export TYPESAFE_API_KEY=你的真实key,再运行后续命令。

3. 用 cURL 调一次 HTTP API

官方 API Reference 给出的评估端点是:

POST https://api.typesafe.ai/v1/systemone
Authorization: Bearer <API_KEY>
Content-Type: application/json

请求体是一个 JSON 对象,三个核心字段:state 是待评估内容;model 必须写 "jev-latest";questions 是一个映射,键是你自己起的 ID,值是一个 typed question 对象。

下面是一个可直接复制的 cURL 示例。注意,$TYPESAFE_API_KEY 是环境变量,不要直接替换成字面量:

curl -X POST https://api.typesafe.ai/v1/systemone 
  -H "Authorization: Bearer $TYPESAFE_API_KEY" 
  -H "Content-Type: application/json" 
  -d '{
    "state": "Help! My payouts have been failing for 3 days.",
    "model": "jev-latest",
    "questions": {
      "is_urgent": {
        "type": "noul",
        "instructions": "Does this convey urgency?"
      },
      "department": {
        "type": "choice",
        "instructions": "Which team should handle this?",
        "criteria": {
          "billing": "Payments, invoicing, refunds",
          "technical": "Bugs, outages, integrations",
          "sales": "Pricing, upgrades, new accounts"
        }
      },
      "frustration": {
        "type": "score",
        "instructions": "How frustrated does the customer appear?",
        "criteria": [
          "Calm, just stating facts",
          "Frustrated but civil",
          "Very angry, strong language"
        ]
      }
    }
  }'

这个示例同时展示了三种 primitive 的混用:noul、choice、score。根据官方 Primitives 文档,每种类型对应不同的返回结构。choice 需要 criteria 是映射,键是选项,值是该选项的描述;score 的 criteria 是一个有序数组,代表从低到高的等级;noul 的 criteria 是可选的,用来补充说明 yes/no 的边界。

4. 用 Python SDK 调一次

官方提供了 Python SDK。安装条件是 Python 版本大于等于 3.10。可以用 pip 安装:

pip install typesafe-sdk

也可以使用 uv:

uv add typesafe-sdk

SDK 的使用方式比直接拼 HTTP 请求更顺手。官方 Quick start 里的最小代码如下:

from typesafe_sdk import Choice, Noul, Score, TypeSafeClient

client = TypeSafeClient()

ticket = "Hi, I've been trying to connect my Stripe account for 3 days and the integration keeps failing. I'm losing sales. Please help ASAP."

response = client.system_one(
    state=ticket,
    questions={
        "department": Choice(
            instructions="Which team should handle this",
            criteria={
                "billing": "Payment or subscription issues",
                "technical": "Bugs or integration problems",
                "sales": "Pricing or account questions",
            },
        ),
        "frustration": Score(
            instructions="How frustrated the customer appears",
            criteria=[
                "Calm, just stating facts",
                "Frustrated but civil",
                "Very angry, strong language",
            ],
        ),
        "is_urgent": Noul(
            instructions="The message conveys urgency or time-sensitivity",
        ),
    },
)

print(response.answers["department"].choice)   # 示例输出 "technical"
print(response.answers["frustration"].score)   # 示例输出 1.0
print(response.answers["is_urgent"].noul)      # 示例输出 1.0

几个关键点:TypeSafeClient() 默认从环境变量 TYPESAFE_API_KEY 读取 API key,不需要显式传入;默认调用的模型就是 jev-latest;client.system_one 接收 state 和 questions,返回一个结构化响应对象。三个问题 ID 是自定义的,回答也会按同样的 ID 返回。官方 Quick start 里的注释输出值是文档示例,不代表你运行时会得到完全相同的结果,但结构是固定的。

5. 一次真实响应的结构:别只关心那个值

调用结束后,很多人只打印一个 response.answers["xxx"].choice 就结束了。但理解完整响应结构能帮你写出更可靠的业务逻辑。根据官方 API Reference,响应顶层的 answers 是一个映射,每个问题 ID 对应一个 answer 对象。answer 对象里有 type,以及根据类型不同而不同的字段:

  • Noul 返回 noul,是一个 0 到 1 的数值,表示 yes 的概率;
  • Choice 返回 choice,是选中的选项字符串,同时还有 probabilities,给出所有选项的概率分布;
  • Score 返回 score,是概率加权后的评分,同时还有 legend,对应你定义的有序等级,以及 probabilities,表示各个等级的概率分布。

除了具体的值,Choice 和 Score 还会返回 confidence。这个 confidence 是模型对自己判断的置信度,用于判断是否要相信这个答案,但它不是“准确率”。官方在 Introduction 里也强调,Choice 和 Score 除了返回对应值,还返回 confidence,你的代码可以用它来决定是否继续往下走、是否转人工、是否忽略结果。

顶层还有 usage 字段。官方 Quick start 文档给出的响应示例中,usage.input_tokens 为 392,usage.output_tokens 为 65(API Reference 只定义了这两个字段的类型,没有具体数值)。这是官方文档示例值(官方数据),不是你的账号额度,也不是任何计费承诺。实际调用的 token 数量取决于你的 state 长度和 questions 复杂度。

下面是官方 Quick start 文档里原样给出的响应示例(不是我自己编的形状,逐字段照抄,其中 legend 与 probabilities 在 score 里都是按等级索引的对象,不是数组;noul 类型只返回 noul 值,没有 confidence 字段):

{
  "model": "jev-1.13.0",
  "answers": {
    "department": {
      "type": "choice",
      "choice": "technical",
      "confidence": 0.78,
      "probabilities": {
        "technical": 0.85,
        "sales": 0.0,
        "billing": 0.15
      }
    },
    "frustration": {
      "type": "score",
      "score": 1.0,
      "confidence": 1.0,
      "legend": {
        "0": "Calm, just stating facts",
        "1": "Frustrated but civil",
        "2": "Very angry, strong language"
      },
      "probabilities": {
        "0": 0.0,
        "1": 1.0,
        "2": 0.0
      }
    },
    "is_urgent": {
      "type": "noul",
      "noul": 1.0
    }
  },
  "usage": {
    "input_tokens": 392,
    "output_tokens": 65
  }
}

上面这段就是官方文档示例的原文结构(usage 里的 392 / 65 也是官方示例值,属于官方数据);你自己跑出来的概率分布和 token 数都会不同。要注意的是:noul 只回 noul 一个值,confidence 只出现在 choice 和 score 上——如果你在代码里对 noul 读 confidence,会拿到 undefined。

自己的判断与新手清单

我跑完官方 Quick start 后,最明显的感受是:Jev 的价值不在于“输出一段聪明的话”,而在于把模糊的自然语言判断变成可编程、可路由、可组合的决策原语。对于中文圈的开发者,最容易犯的不是 API 调用错误,而是思维还停留在“写个好 prompt 让大模型分析”。下面是三个我自己观察到、或者从文档推断出的新手坑,带有自己的判断色彩。

坑 1:把大段上下文直接塞进 state。
state 可以是字符串、对象或数组,但不代表你应该把整个工单系统日志、五个会话记录和客户资料全倒进去。官方文档提到的是“当前状态”或“结构化数据”,并且强调每个问题都是一次快判断。我的判断是:先做粗筛或抽取,再把与问题相关的最小上下文放进去。如果 state 太长,token 消耗会上升,判断质量也可能因为噪声而下降。这个观点来自我对文档原则的推断,不是官方给出的严格阈值。

坑 2:一个问题里塞多个判断。
比如 "Analyze this message and determine the best course of action",这不是 Jev 适合的问题。官方 Primitives 页面明确说:如果一个判断依赖多个独立因素,就分解成多个问题,分别评估,再用自己的代码组合。官方 Introduction 给出的示例是:不要问“rate this startup pitch”,而是分别问市场规模、技术可行性、差异化,然后在代码里加权。我自己的清单是:每个问题只问一个可回答是/否、选一个、打一个分的判断;一旦你发现 instructions 里出现“并且”“同时”“然后”,就该拆开。

坑 3:拿 confidence 当准确率。
这是最容易导致业务事故的误解。confidence 表示模型对当前判断的置信度,不代表该判断在实际业务中的准确率。官方文档把它定位为“你的代码用来决定是否行动或转人工”的架构信号,而不是“准确率 90% 就一定能自动处理”。我的清单:把 confidence 作为一个阈值开关,低于某值就人工复核;不要把它当成模型能力的评估指标,更不能拿它直接向业务方承诺准确率。

这次没核实的

  • 未能核实 TypeSafe 的计费方式、免费额度以及速率限制。官方 Quick start 和 API Reference 未提供完整计费细节。
  • 未能核实 Playground 登录后是否需要额外配置或邀请码。官方文档仅说“log in”,没有描述账号体系细节。
  • 未能核实 usage 示例值 392/65 是否对应最新 jev-latest 模型在某个具体输入下的实际统计。该数字来自官方 Quick start 的响应示例,标注为官方数据,但未验证其上下文。
  • 未能核实 Python SDK 的全部异常行为和错误码处理方式。官方文档只覆盖了正常调用路径。

参考来源

评论区

0 条评论

登录后可评论。

我家大橘 210 阅读