Jev Recipe / 架构对比

Jev vs Instructor:结构化输出的两种工程路线

最近更新: 2026-11-12

有限决策交给 Jev、丰富结构交给 Instructor。端点、官方 SDK 代码与取舍,按 2026-11-12 核验的官方资料并列。

广告

从 Instructor 迁移到 Jev 的 3 个步骤

01先把 Instructor 的 Pydantic / Zod 模型翻译成等价的 Jev 契约:队列用 Choice,程度用 Score,是/否用 Noul。
02用一次 client.systemOne() 替换「生成 → 解析 → 校验 → 重试」循环,一次前向推理就返回每个问题的类型化答案与概率。
03把 confidence 阈值写进业务代码做分流,而不是用指数退避的重试风暴去掩盖格式错误。
schema / 共享决策契约
{
  "routing": {
    "type": "choice",
    "instructions": "把工单分到正确队列",
    "criteria": {
      "billing": "付款、退款或发票",
      "technical": "产品故障或使用问题",
      "sales": "购买、升级或报价",
      "abuse": "安全或滥用举报"
    }
  }
}
此 schema 与 Instructor Pydantic 模型等价,可直接在 Playground 验证零重试结果。

架构对比:契约优先 vs 先生成再校验

METRIC
Jev
Instructor / Guardrails
生成什么
不生成文本。模型给你的候选选项打分,每个问题返回一个类型化答案。
生成 JSON 文本,再用你的 Pydantic / Zod 模型校验。
输出 token 成本
没有。TypeSafe 只按输入 token 计费(2026-11-12 为 $0.042/M)。
完整生成计费:每个输出 token 都要付钱,包括被丢弃的重试。
延迟
单次前向推理,没有自回归解码。TypeSafe 没有公布延迟 SLA,请自己实测链路。
完全取决于模型和响应长度。请用自己的 schema 与供应商实测。
失败模式
答案永远来自你声明的选项。需要设计的失败是「置信度低」。
校验可能失败并触发重试循环;输出长度与嵌套深度直接决定失败率。
置信度
每个问题都有概率分布和 confidence,但仍需用你自己的标注数据校准。
模型自报(如果有的话),不是可以直接拿来当门限的概率。

代码对比:重试循环 vs 零重试 typed 调用

python / instructor 重试循环
import time
import instructor
from openai import OpenAI
from pydantic import BaseModel, ValidationError

class RouteDecision(BaseModel):
    queue: str

client = instructor.from_openai(OpenAI())
MAX_RETRIES = 3

def route_ticket(ticket_text: str) -> RouteDecision:
    for attempt in range(MAX_RETRIES):
        try:
            return client.chat.completions.create(
                model="gpt-4o-mini",
                response_model=RouteDecision,
                messages=[{"role": "user", "content": ticket_text}],
            )
        except ValidationError:
            if attempt == MAX_RETRIES - 1:
                raise
            time.sleep(2 ** attempt)  # 指数退避

# 典型路径:1800ms+ 延迟,每次尝试计费 20~50 output tokens
python / typesafe-sdk 类型化判定
from typesafe_sdk import Choice, TypeSafeClient

client = TypeSafeClient(model="jev-latest")

def route_ticket(ticket_text: str) -> dict:
    response = client.system_one(
        state=ticket_text,
        questions={
            "routing": Choice(
                instructions="把工单分到正确队列",
                criteria={
                    "billing": "付款、退款或发票",
                    "technical": "产品故障或使用问题",
                    "sales": "购买、升级或报价",
                },
            ),
        },
    )

    routing = response.choices["routing"]
    return {
        "queue": routing.choice,           # 永远是你声明的选项之一
        "confidence": routing.confidence,  # 拿来当门限前先校准
        "probabilities": routing.probabilities,
    }
typescript / instructor 重试循环
import OpenAI from "openai";
import Instructor from "@instructor-ai/instructor";
import { z } from "zod";

const RouteDecision = z.object({ queue: z.string() });
const client = Instructor({ client: new OpenAI(), mode: "TOOLS" });

async function routeTicket(ticket: string, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await client.chat.completions.create({
        model: "gpt-4o-mini",
        response_model: RouteDecision,
        messages: [{ role: "user", content: ticket }],
      });
    } catch {
      if (attempt === maxRetries - 1) throw new Error("schema validation failed");
      await new Promise((r) => setTimeout(r, 2 ** attempt * 1000));
    }
  }
}
typescript / @typesafe-ai/sdk 类型化判定
import { choice, TypeSafeClient } from "@typesafe-ai/sdk";

const client = new TypeSafeClient({ defaultModel: "jev-latest" });

export async function routeTicket(ticket: string) {
  const { answers } = await client.systemOne({
    state: ticket,
    questions: {
      routing: choice("把工单分到正确队列", {
        billing: "付款、退款或发票",
        technical: "产品故障或使用问题",
        sales: "购买、升级或报价",
      }),
    },
  });

  return {
    queue: answers.routing.choice,           // 永远是你声明的选项之一
    confidence: answers.routing.confidence,  // 拿来当门限前先校准
  };
}

继续评估成本与接入