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

搜索 jev typesafe 时,常会遇到两个相关但不同的概念:Jev 的类型化 AI 决策,以及 TypeScript 的类型系统。本文说明如何把两者接起来,同时避免把编译期类型误当成可信的运行时结果。Jev 会根据一段状态评估问题并返回结构化答案;你的 TypeScript 服务仍要校验网络响应,再决定应用可以执行什么操作。
Jev 官网将其介绍为面向软件团队的独立决策工具。Jev 与 TypeSafe 无隶属、运营或背书关系。可以先访问 Jev AI 官网 和在线 Playground 了解产品。

在 TypeScript 应用中,“Jev 类型安全”意味着什么
TypeScript 会在代码运行前检查程序,可以发现属性拼写错误、函数参数类型不匹配等问题。但它无法证明远程服务返回的 HTTP 响应符合你声明的类型。像 const answer: Decision = await response.json() 这样的声明,只是告诉编译器你希望服务器返回什么,并没有检查实际收到的字节。
response.json() 的结果应先视为 unknown,直到应用检查过它的结构。这个边界包含两个部分:
- 构造请求: TypeScript 可以帮助保持状态、问题定义和内部标识的一致性。
- 解析响应: 运行时检查确认远程数据包含代码准备读取的字段。
这一区别很重要:模型响应是决策信号,不是执行操作的授权。身份验证、业务约束、确定性检查以及必要的人工审批仍由你的应用负责。

Jev 的三种问题类型
Jev 官方文档介绍了三种问题形态。应根据决策结果的结构选择类型,不要让一个宽泛的问题同时处理几个无关任务。
| 类型 | 适用场景 | 应检查的结果 |
|---|---|---|
| Choice | 团队、队列、分类或模型选择 | 选项、概率、置信度 |
| Score | 严重程度、质量、紧急度等有序量表 | 加权分数、各级概率、置信度 |
| Noul | 判断一个明确命题是否成立 | 答案为“是”的 0 到 1 概率 |
当可能结果已知时使用 Choice。例如,支持工单可以交给账务、技术支持或销售团队,就应说明每个选项的判断标准。如果选项并不穷尽所有情况,应加入安全的 other 或 needs_review,否则分类器只能被迫选择一个不完全匹配的结果。
当答案存在顺序时使用 Score。等级应从低到高排列,并给出具体描述。例如,严重程度量表可以明确每一级对应的响应时间或升级方式。Jev 会返回概率分布和按概率加权的分数,因此结果可能落在两个命名等级之间。你的代码仍需把分数映射到明确的业务策略。
Noul 适合一个是非命题,例如消息是否明确提出退款请求。它的 noul 值表示答案为“是”的概率,不是第二个置信度字段。如果流程既要分类又要判断某个条件,可以使用两个问题 ID,并分别读取答案。

先设计决策边界,再编写请求
可靠的集成始于一个小而明确的决策约定。先写下应用可能采取的确切操作、所有可能答案、输入需要包含哪些证据,以及结果不确定时应该怎么办。这样往往会发现,所谓的一个“AI 任务”其实包含两三个彼此独立的问题。
例如,客服流程可以让 Jev 选择处理团队、评估紧急程度,并判断消息是否明确提出退款请求。这些问题可以共享同一份状态,并在一次请求中评估。为每个问题分配稳定的键,例如 department、urgency 和 refund_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 返回答案前失败:网络超时、服务返回非成功状态,或响应正文无法解析。应显式建模这些情况。内部结果类型可以区分 decision、review 和 unavailable,避免传输故障意外表现成普通的否定答案。
根据操作风险设置有限的超时和重试。只对可能暂时恢复的错误重试,遵守服务器提供的重试提示,并限制尝试次数。不要把响应校验失败当成网络抖动反复重试。如果用完重试额度后仍无法得到决策,应回到已知的安全队列或人工复核流程,而不是默默选择第一个选项。
记录足以诊断流程的信息:可用时记录请求关联 ID、问题 ID、响应状态、校验结果和最终采用的策略分支。避免保存 API 密钥,也不要把不必要的个人数据复制到日志。如果审计确实需要原始状态,应明确设置保留时间、访问控制和脱敏规则。
分阶段上线
渐进式上线有助于区分模型质量问题和集成错误:
- 探索: 在 Jev 在线 Playground 中试用有代表性的状态和类型化问题,先完善含糊的标准。
- 离线评估: 用已标注样本运行请求流程,检查混淆模式和业务代价最高的错误。
- 影子运行: 仅记录建议,让现有流程继续作为权威结果;对比建议和实际结果,不采取自动操作。
- 辅助处理: 把建议展示给操作人员并收集修正,找出遗漏选项和难以理解的量表。
- 选择性自动化: 只启用达到实测阈值且有安全回退路径的分支。输入模式变化时,应能暂停自动化。
将问题措辞和标准与解释结果的代码一起版本管理。如果改变 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 让自有代码更容易推理;运行时检查则保护它免受不可信数据影响。