Jev 常见报错排查与修复
每个 Jev 集成都会撞上同一小撮故障,而几乎每一个都有很短的修复路径。这篇是排查手册:HTTP 报错的”症状→原因→修复”表、占了大部分”API 坏了”报告的 JSON 解析陷阱,以及 confidence 数值不对劲时怎么办。从上往下过一遍,绝大多数问题不用提工单就能解决。
TL;DR: 401 找 Key;404 找模型 slug;429 退避减速;解析失败说明 message content 没做 JSON 加载;confidence 奇怪多半是问题太含糊。动手前先查下面的表,并第一时间到 OpenRouter 模型页确认确切的 model slug——它是最常见的元凶。
HTTP 报错:症状、原因、修复
| 报错 | 常见原因 | 修复 |
|---|---|---|
401 Unauthorized | 环境里没有 Key、变量读错、或 Key 已作废 | 在调用所在的同一 shell 打印变量;必要时到 OpenRouter 重建 Key;确认带了 Bearer 前缀 |
404 / 未知模型 | 模型 slug 写错或过期 | 到 OpenRouter 模型页确认确切 slug(写作本文时列表值为 typesafe/jev-1.13;slug 随版本变化)后重发 |
429 Too Many Requests | 触发限流或消费上限 | 指数退避加抖动;检查 Key 级限额与余额;对并发调用做批处理或节流 |
400 Bad Request | JSON 请求体格式错或缺必填字段 | 本地先校验请求体能解析;核对 OpenAI 兼容字段名(model、messages) |
5xx 服务端错误 | 渠道上游问题 | 退避重试 2–3 次,然后排队并告警;不要硬刚 |
一个最小重试封装,把 429 和 5xx 的处理收在一处:
import json, os, time, requests
def ask_jev_with_retry(prompt: str, max_retries: int = 3) -> dict:
# Confirm the exact model slug on the OpenRouter model page
for attempt in range(max_retries):
resp = requests.post(
"https://openrouter.ai/api/v1/chat/completions",
headers={"Authorization": f"Bearer {os.environ['OPENROUTER_API_KEY']}"},
json={
"model": "typesafe/jev-1.13",
"messages": [{"role": "user", "content": prompt}],
},
timeout=30,
)
if resp.status_code == 200:
return json.loads(resp.json()["choices"][0]["message"]["content"])
if resp.status_code in (429, 500, 502, 503):
time.sleep(2 ** attempt) # 指数退避
continue
resp.raise_for_status() # 401/404/400 在这里带着上下文抛出
raise RuntimeError("Jev call failed after retries")
封装在成功时返回 fixture 形态的答案;响应形态为示例数据(example fixture),正式字段名以官方文档为准。官方 TypeSafe AI 渠道自己的错误码与重试建议,以 typesafe.ai 官方文档为准。
解析失败:常见嫌疑人
大部分”API 坏了”的报告,最后都发现是客户端这边的坑:
- 把 content 当字典用。 message content 是 JSON 字符串。
resp.json()["choices"][0]["message"]["content"]拿到的是字符串;访问answer、confidence之前先json.loads。 - 把响应壳当答案。
answer在解析后的 content 里,不在 HTTP 响应的顶层。HTTP 壳是 OpenAI 兼容的,判断载荷在里面——示例数据(example fixture),正式字段名以官方文档为准:
{
"answer": "billing",
"confidence": 0.94,
"rationale": "The message disputes a charge amount rather than reporting a bug."
}
- 对坏载荷零防御。 罕见,但解析要包 try,先重试一次再叫人:
try:
result = json.loads(resp.json()["choices"][0]["message"]["content"])
except (json.JSONDecodeError, KeyError):
result = ask_jev_with_retry(prompt) # 重试一次,然后告警
重试返回新的 fixture 形态答案;响应形态为示例数据(example fixture),正式字段名以官方文档为准。
confidence 异常
confidence 看起来不对时,按这个清单往下查:
- 所有答案都特别高。 问题多半没有真实边界——标准太宽,什么都是显然的 yes 或 no。收紧判定标准。
- 所有答案都特别低。 问题欠约束,或输入比问题假设的更嘈杂;按《问题设计》补边界和 few-shot 示例。
- 相似输入的 confidence 大幅摆动。 判分前先归一化输入(去 HTML、确定性地截断长文本),并确保每次调用用同一量表和同一选项列表。
- confidence 高但和你的标注对不上。 回查选项定义是否有重叠;互斥的定义方法在《Jev 问题设计与状态管理》里。
这些都不奏效时,取一条输入,用《第一次调用》教程的最小请求单独跑一遍——剩余问题多半是意外混进 prompt 的状态。product FAQ 打分那个 case 展示了把这套检查做成自动化环节的完整管线。
本文适用版本 Jev 1.13。