← 返回博客
APIOpenRouter教程TypeScript错误处理

Jev API 教程:从 OpenRouter 起步(密钥、请求、错误、重试)

·约 3 分钟

Jev API 教程:从 OpenRouter 起步

这份 Jev API 教程带你五分钟从零拿到类型化响应。Jev 通过 OpenRouter 提供服务,一把密钥可同时调用其他模型。模型 ID:typesafe/jev-1.13。

一句话总结:POST /api/v1/chat/completions,content = { state, questions }。读 answers[].selected、confidence、distribution。429 要重试;发送前先校验 questions。


1. 获取 OpenRouter API Key

  1. 在 OpenRouter 注册账号。
  2. 生成 API 密钥(sk-or-...)。
  3. 放进环境变量——不要写进前端或 git 仓库。
export OPENROUTER_API_KEY="sk-or-..."

这条路不需要单独注册 TypeSafe。


2. 发出第一个请求

curl https://openrouter.ai/api/v1/chat/completions \
  -H "Authorization: Bearer $OPENROUTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "typesafe/jev-1.13",
    "messages": [{
      "role": "user",
      "content": {
        "state": "同一笔订单 #4821 扣了我两次钱,麻烦尽快处理。",
        "questions": [{
          "type": "choice",
          "text": "这条工单该由哪个团队处理?",
          "options": ["billing", "tech_support", "sales"]
        }]
      }
    }]
  }'

请求结构

字段 类型 说明
state string 非结构化文本(邮件、工单、DOM 快照…)
questions[].type choice | score | noul 问题类型
questions[].text string 要分支的那句判断
questions[].options string[] choice / score 需要

一次调用可带多个问题(例如意图 + 紧急度)。


3. 读取类型化结果

{
  "answers": [{
    "type": "choice",
    "text": "这条工单该由哪个团队处理?",
    "selected": "billing",
    "confidence": 0.94,
    "distribution": {
      "billing": 0.94,
      "tech_support": 0.04,
      "sales": 0.02
    }
  }]
}
  • selected — 最高选项(score 则为加权结果)。
  • confidence — 最高选项的概率。
  • distribution — 完整概率质量。
type JevAnswer = {
  type: 'choice' | 'score' | 'noul';
  text: string;
  selected?: string;
  confidence: number;
  distribution?: Record<string, number>;
};

TypeScript 客户端示意

export async function runJev(state: string, questions: unknown[]) {
  const res = await fetch('https://openrouter.ai/api/v1/chat/completions', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.OPENROUTER_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      model: 'typesafe/jev-1.13',
      messages: [{ role: 'user', content: { state, questions } }],
    }),
  });
  if (!res.ok) throw new Error(`Jev HTTP ${res.status}`);
  const json = await res.json();
  return json.choices?.[0]?.message?.content?.answers ?? json.answers ?? json;
}

热路径上发送前先本地校验 questions——缺 options 是常见 400。


错误处理与重试

状态码 含义 动作
401 密钥错误/缺失 快速失败;查环境变量
400 问题负载非法 改请求;不要盲目重试
429 限流 指数退避 + 抖动
5xx 上游异常 有限重试;走兜底路径
超时 网络 / 过慢 预算约 2s;转人工或队列
async function withRetry<T>(fn: () => Promise<T>, tries = 3): Promise<T> {
  let lastErr: unknown;
  for (let i = 0; i < tries; i++) {
    try { return await fn(); } catch (e) {
      lastErr = e;
      await new Promise((r) => setTimeout(r, 2 ** i * 200 + Math.random() * 100));
    }
  }
  throw lastErr;
}

Jev API 的安全注意

  • 密钥保密时,在服务端 / Worker 调用,不要直接打浏览器。
  • 必须走前端时,加带鉴权与限流的代理。
  • 政策要求时对 state 做 PII 脱敏。
  • 设总超时(约 2s),产品交互不能挂死。

日志与可观测性

每次调用记录:模型 ID、延迟、selected、confidence、distribution 摘要、工单/消息 ID。
对错误率与 p95 延迟告警;对平均 confidence 与熵做漂移看板。


下一步


FAQ

有官方 SDK 吗?
社区有 TypeScript / Python 封装;HTTP 契约与 OpenRouter 兼容。

能流式输出吗?
分类返回小类型化载荷,流式几乎无用。

如何锁定模型版本?
使用完整模型 ID typesafe/jev-1.13,升级前看发布说明。


相关阅读