返回全部文章

开发者指南

Jev TypeSafe:在 TypeScript 中接入类型化决策

了解 Jev TypeSafe 工作流:定义类型化问题,在运行时校验 API 响应,并在 TypeScript 应用中安全处理不确定决策。

文 / Jev AI2026年9月24日15 分钟阅读
Jev TypeSafe:在 TypeScript 中接入类型化决策

搜索 jev typesafe 时,常会遇到两个相关但不同的概念:Jev 的类型化 AI 决策,以及 TypeScript 的类型系统。本文说明如何把两者接起来,同时避免把编译期类型误当成可信的运行时结果。Jev 会根据一段状态评估问题并返回结构化答案;你的 TypeScript 服务仍要校验网络响应,再决定应用可以执行什么操作。

Jev 官网将其介绍为面向软件团队的独立决策工具。Jev 与 TypeSafe 无隶属、运营或背书关系。可以先访问 Jev AI 官网在线 Playground 了解产品。

数学草图:API 请求经过服务端边界并返回经过校验的决策数据

在 TypeScript 应用中,“Jev 类型安全”意味着什么

TypeScript 会在代码运行前检查程序,可以发现属性拼写错误、函数参数类型不匹配等问题。但它无法证明远程服务返回的 HTTP 响应符合你声明的类型。像 const answer: Decision = await response.json() 这样的声明,只是告诉编译器你希望服务器返回什么,并没有检查实际收到的字节。

response.json() 的结果应先视为 unknown,直到应用检查过它的结构。这个边界包含两个部分:

  1. 构造请求: TypeScript 可以帮助保持状态、问题定义和内部标识的一致性。
  2. 解析响应: 运行时检查确认远程数据包含代码准备读取的字段。

这一区别很重要:模型响应是决策信号,不是执行操作的授权。身份验证、业务约束、确定性检查以及必要的人工审批仍由你的应用负责。

数学草图:对比编译期 TypeScript 检查与运行时响应校验

Jev 的三种问题类型

Jev 官方文档介绍了三种问题形态。应根据决策结果的结构选择类型,不要让一个宽泛的问题同时处理几个无关任务。

类型 适用场景 应检查的结果
Choice 团队、队列、分类或模型选择 选项、概率、置信度
Score 严重程度、质量、紧急度等有序量表 加权分数、各级概率、置信度
Noul 判断一个明确命题是否成立 答案为“是”的 0 到 1 概率

当可能结果已知时使用 Choice。例如,支持工单可以交给账务、技术支持或销售团队,就应说明每个选项的判断标准。如果选项并不穷尽所有情况,应加入安全的 otherneeds_review,否则分类器只能被迫选择一个不完全匹配的结果。

当答案存在顺序时使用 Score。等级应从低到高排列,并给出具体描述。例如,严重程度量表可以明确每一级对应的响应时间或升级方式。Jev 会返回概率分布和按概率加权的分数,因此结果可能落在两个命名等级之间。你的代码仍需把分数映射到明确的业务策略。

Noul 适合一个是非命题,例如消息是否明确提出退款请求。它的 noul 值表示答案为“是”的概率,不是第二个置信度字段。如果流程既要分类又要判断某个条件,可以使用两个问题 ID,并分别读取答案。

数学草图:Choice 分支、有序 Score 量表和 Noul 概率弧线

先设计决策边界,再编写请求

可靠的集成始于一个小而明确的决策约定。先写下应用可能采取的确切操作、所有可能答案、输入需要包含哪些证据,以及结果不确定时应该怎么办。这样往往会发现,所谓的一个“AI 任务”其实包含两三个彼此独立的问题。

例如,客服流程可以让 Jev 选择处理团队、评估紧急程度,并判断消息是否明确提出退款请求。这些问题可以共享同一份状态,并在一次请求中评估。为每个问题分配稳定的键,例如 departmenturgencyrefund_requested;代码会通过这些键找到对应答案。即使界面上的标签改名,也应保持键稳定。

状态要足够完整,但不要塞入无关信息。工单路由可能需要消息内容、产品区域和账户等级,却通常不需要完整客户档案、无关历史对话或机密信息。精简且相关的状态更易审查、传输成本更低,也能减少无关数据泄露的可能。

Jev 支持将文本、JSON 对象和文本数组作为状态。简单场景可以用字符串;当字段代表不同含义时,使用结构化对象。数组可以放入彼此相关的文本,例如当前消息和一小段政策内容。官方文档当前列出的输入边界不包括图像、音频和视频;需要这些材料时,先用 OCR、转录或其他服务预处理,再传入相关文本。

构造类型化请求并校验响应

下面的示例从服务端 TypeScript 代码调用文档列出的 System One 端点。请求类型可以在编写代码时发现错误;响应仍先保持为 unknown,直到运行时检查确认本流程实际使用的字段。

type Question =
  | { type: "choice"; instructions: string; criteria: Record<string, string> }
  | { type: "score"; instructions: string; criteria: string[] }
  | { type: "noul"; instructions: string };

type EvaluationRequest = {
  model: "jev-latest";
  state: string | Record<string, unknown> | string[];
  questions: Record<string, Question>;
};

function asRecord(value: unknown): Record<string, unknown> | null {
  return typeof value === "object" && value !== null && !Array.isArray(value)
    ? (value as Record<string, unknown>)
    : null;
}

function isProbabilityMap(value: unknown): value is Record<string, number> {
  const map = asRecord(value);
  if (!map) return false;
  const entries = Object.values(map);
  if (entries.length === 0) return false;
  if (!entries.every(
    (item) => typeof item === "number" && Number.isFinite(item) && item >= 0 && item <= 1
  )) return false;
  const total = entries.reduce((sum, item) => sum + item, 0);
  return Math.abs(total - 1) < 0.02;
}

async function classifyTicket(ticket: string) {
  const apiKey = process.env.JEV_API_KEY;
  if (!apiKey) throw new Error("JEV_API_KEY is not configured");

  const request = {
    model: "jev-latest",
    state: { message: ticket },
    questions: {
      department: {
        type: "choice",
        instructions: "这张工单应由哪个团队处理?",
        criteria: {
          billing: "付款、发票、退款或打款",
          technical: "缺陷、服务中断或集成故障",
          sales: "价格、升级或新账户"
        }
      }
    }
  } satisfies EvaluationRequest;

  const response = await fetch("https://thejevai.com/v1/systemone", {
    method: "POST",
    headers: {
      Authorization: "Bearer " + apiKey,
      "Content-Type": "application/json"
    },
    body: JSON.stringify(request)
  });

  if (!response.ok) throw new Error("Jev 请求失败:" + response.status);

  const payload: unknown = await response.json();
  const root = asRecord(payload);
  const answers = asRecord(root?.answers);
  const answer = asRecord(answers?.department);

  if (
    answer?.type !== "choice" ||
    typeof answer.choice !== "string" ||
    typeof answer.confidence !== "number" ||
    !Number.isFinite(answer.confidence) ||
    answer.confidence < 0 ||
    answer.confidence > 1 ||
    !isProbabilityMap(answer.probabilities)
  ) {
    throw new Error("Jev 响应结构不符合预期");
  }

  const allowedTeams = ["billing", "technical", "sales"];
  if (!allowedTeams.includes(answer.choice)) {
    throw new Error("Jev 返回了未配置的团队");
  }

  return {
    team: answer.choice,
    confidence: answer.confidence,
    probabilities: answer.probabilities
  };
}

const result = await classifyTicket("连续三次部署失败,生产环境正在返回 500 错误。");
if (result.confidence >= 0.8) {
  // 路由前应用允许列表和业务规则。
} else {
  // 将不确定的情况送入人工复核队列。
}

这个示例有意保持聚焦。它校验团队路由分支所使用的响应结构,确认概率是有限数值且范围合理,并拒绝应用允许列表之外的选项。更大的应用应校验所有会影响操作的字段。如果某个字段仅供展示,也不要让它在不知不觉中成为控制信号。

JEV_API_KEY 保存在服务端密钥存储中。不要把它放进浏览器包、公开仓库、错误消息或分析事件属性里。如果包含密钥的代码被发送到浏览器,TypeScript 类型无法替用户隐藏它。

应用流程草图:状态进入并行问题,再由应用决定路由或人工复核

读取答案结构,但不要过度信任结果

响应按你发送的问题 ID 组织。对于 Choice,应检查选中项、各选项概率和置信度;对于 Score,应检查按概率加权的分数、等级说明、各级概率和置信度;对于 Noul,应读取“是”的概率。要按问题类型校验对应结构,不要假设所有答案都有相同字段。

响应结构有效,不代表它一定适合某个业务场景。运行时校验回答的是“代码能否安全读取这个值?”,而评估回答的是“它能否在有代表性的输入上做出有用决策?”这是两种不同检查,都不可省略。

不要把所有信号压成一个通用的 success 布尔值。保留类型化结果,让应用策略使用正确字段。路由可能依赖 Choice 的选项和置信度阈值;优先级队列可能使用 Score;确认流程可能依赖 Noul。明确映射能让后续审查和调试更容易。

把置信度当作信号,而不是保证

Jev 会为 Choice 和 Score 返回概率以及置信度信号;Noul 返回“是”的概率。这些数值有助于比较或分流,但不能证明判断正确。置信度不等于实测准确率,高数值也不是安全保证。

用接近真实输入分布的已标注样本建立评估集。样本应包含常见情况、边界情况、低频但代价高的情况,以及应该归入 other 或人工复核的案例。要按结果评估错误,而不只是看一个总体准确率。把紧急故障送入低优先级队列,可能比把普通请求升级处理代价更高。

根据实际表现和错误成本选择阈值。对于低影响的建议,如果人员很容易纠正,较低阈值或许可以接受。对于付款、账号限制或破坏性操作,即使模型信号很强,也应保留确定性检查和人工审批。不要直接照搬教程里的阈值,把它当成通用默认值。

数学草图:窄分布与宽分布概率曲线穿过人工复核阈值

把失败和不确定结果当作正常分支处理

生产请求可能在 Jev 返回答案前失败:网络超时、服务返回非成功状态,或响应正文无法解析。应显式建模这些情况。内部结果类型可以区分 decisionreviewunavailable,避免传输故障意外表现成普通的否定答案。

根据操作风险设置有限的超时和重试。只对可能暂时恢复的错误重试,遵守服务器提供的重试提示,并限制尝试次数。不要把响应校验失败当成网络抖动反复重试。如果用完重试额度后仍无法得到决策,应回到已知的安全队列或人工复核流程,而不是默默选择第一个选项。

记录足以诊断流程的信息:可用时记录请求关联 ID、问题 ID、响应状态、校验结果和最终采用的策略分支。避免保存 API 密钥,也不要把不必要的个人数据复制到日志。如果审计确实需要原始状态,应明确设置保留时间、访问控制和脱敏规则。

分阶段上线

渐进式上线有助于区分模型质量问题和集成错误:

  1. 探索: 在 Jev 在线 Playground 中试用有代表性的状态和类型化问题,先完善含糊的标准。
  2. 离线评估: 用已标注样本运行请求流程,检查混淆模式和业务代价最高的错误。
  3. 影子运行: 仅记录建议,让现有流程继续作为权威结果;对比建议和实际结果,不采取自动操作。
  4. 辅助处理: 把建议展示给操作人员并收集修正,找出遗漏选项和难以理解的量表。
  5. 选择性自动化: 只启用达到实测阈值且有安全回退路径的分支。输入模式变化时,应能暂停自动化。

将问题措辞和标准与解释结果的代码一起版本管理。如果改变 critical 的含义或添加处理团队,评估集也应覆盖新行为,再进行上线。监控输入构成、人工修正、升级率和下游结果;响应结构稳定并不代表模型表现始终稳定。

常见集成错误

相信 TypeScript 类型断言。 类型断言改变的是编译器的认知,不是服务器实际发送的数据。应从 unknown 开始解析,校验使用到的字段,并拒绝意外的结果类型。

把多个判断塞进一个问题。 “选择团队、判断紧急度并确认退款资格”混合了不同判断。使用清晰的问题 ID,再用普通代码组合结果。硬性资格规则应保持确定性。

选项不全却强迫选择。 不完整的标准会促使模型选择最接近但不正确的答案。添加兜底选项,或把不确定情况交给人工复核。

把概率当作执行许可。 概率是策略判断的证据,不是策略本身。身份验证、授权、速率限制和不可逆操作检查都应由服务端负责。

发送整个记录。 上下文越多不一定越好。只包含定义问题所需的信息,尤其要谨慎处理个人或机密数据。

Jev TypeSafe 生产集成检查清单

  • 将 API 密钥存放在服务端密钥存储中,并按组织规则轮换。
  • 只发送相关文本或结构化字段;移除无关个人信息和机密数据。
  • 用 Playground 和离线评估集检查常见、含糊、对抗性及范围外的样本。
  • 校验响应类型、必需字段、取值范围、概率总和以及应用允许列表。
  • 定义超时、有限重试、非成功响应处理和安全回退路径。
  • 记录校验和策略结果,但不要记录凭据或不必要的敏感状态。
  • 当输入分布或业务成本变化时,重新审视阈值和问题标准。

当前请求和响应字段请参阅 Jev API 文档。估算生产用量前,也可以查看套餐与用量

常见问题

Jev TypeSafe 和 TypeScript 是一回事吗?

不是。Jev 返回类型化决策结果,例如选项、分数或“是”的概率;TypeScript 在代码运行前检查应用程序。可靠的集成会同时使用两者:为请求和内部结果定义类型,并在运行时校验不可信的网络数据。

TypeScript 类型能校验 API 响应吗?

不能。类型注解在 JavaScript 运行时会被移除。应将解码后的响应视为 unknown,并检查应用在路由、存储或展示前依赖的每个字段。类型守卫可以在校验后安全地缩窄类型。

多个问题能共享同一份状态吗?

可以。Jev 支持针对同一状态并行评估多个类型化问题。每个问题都应保持原子化,并使用稳定键,以便代码独立读取结果。与一个提示词要求返回复杂嵌套决策相比,拆分的问题更容易测试和组合。

Noul 是置信度吗?

不是。Noul 表示某个命题为真的概率;Choice 和 Score 会连同答案概率一起返回置信度。读取与问题类型对应的字段,并依据实测样本设计自己的阈值。

Jev 能在做出决策后直接执行操作吗?

执行操作应由你的应用负责。让 Jev 评估一个边界明确的问题,再由授权校验、允许列表、确定性规则和审批步骤决定是否继续。这样可以避免不确定的模型答案变成未经检查的副作用。

什么适合作为第一个用例?

从重复出现、影响较低且选项少的判断开始,例如建议工单应转给哪个支持团队。准备一组已标注样本,说明每个结果的含义,并确定如何处理未知情况。流程可测量后,再添加运行时校验,并先与现有流程对比,再考虑自动化。

总结

jev typesafe 的实际含义,是在模型的结构化判断和消费该结果的应用逻辑之间建立清晰边界。定义范围明确的问题,只发送相关状态,在运行时校验每个响应,用有代表性的样本衡量表现,并让不确定或影响重大的情况进入明确的复核流程。TypeScript 让自有代码更容易推理;运行时检查则保护它免受不可信数据影响。

© 2026 Jev AI Journal返回首页