幂等键
在 System One API 决策请求中附带幂等键,超时或 5xx 后可安全重试且不重复扣积分。
新的逻辑操作 → 新的随机 UUID。同一操作的重试或恢复 → 相同的已保存键和完整请求。 有意对相同输入重新评估属于新操作,不是重试。在保留窗口内,操作键让 HTTP 或 SDK 客户端在响应丢失后恢复成功结果,不再次评估或扣费。这些规则适用于托管 API 与会话 Playground,不是对独立 SDK 适配器或直连提供方的统一保证。
常见问题
Q: System One API 中的幂等键是什么? A: 客户端生成的令牌,随请求发送;重试返回同一决策结果且不重复扣积分。
Q: 何时应发送幂等键? A: 只要客户端可能在超时、网络错误或 5xx 后重试同一逻辑决策,就应发送。
每个操作保存一个键
为每次操作发送 Idempotency-Key 请求头。在第一次发送前只生成一次 UUID,并将它与完整请求一起保存。不要使用全局环境键、任务间共享的常量或内容哈希:相同内容可以属于不同的计费评估。键长为 1–128 个字符,首位为字母或数字,之后允许字母、数字、点、下划线、冒号和连字符。
| 场景 | 操作键与请求 |
|---|---|
| 新决策或新任务 | 创建新的 UUID 和完整请求。 |
| 有意对相同输入重新评估 | 即使请求体完全相同,也创建新的 UUID;这是新的计费操作。 |
超时、响应丢失或 request_in_progress 后重试 | 复用已有操作的键与未修改的完整请求,遵守重试等待时间。 |
| 应用崩溃或重启后恢复 | 加载第一次发送前已持久保存的完整操作,不要重新执行创建操作的代码。 |
| 使用同一账户的另一个 API 密钥 | 恢复时仍保留原操作键;更换凭据不会创建独立命名空间。 |
重试时使用托管快速开始中创建的同一个 operation 对象。以下代码替换其中的 evaluate() 调用并复用 client,不创建操作,也不生成 UUID:
const result = await client.evaluate(operation.request, {
maxRetries: 0,
timeoutMs: 30_000,
headers: { 'Idempotency-Key': operation.idempotencyKey },
});快速开始示例只在内存中保留操作。每次运行脚本都会创建新的计费操作,不能安全地跨重启恢复。在真实应用中,应在第一次发送前,将完整操作——键与包含所选模型的完整请求——持久保存到应用的任务记录中。崩溃后加载该记录,而不是创建 UUID 或重新构造请求。进程内重试时保留已有对象,按下方已记录的结果处理后,仅重新执行调用。不要在重试代码中生成新键,否则可能产生另一次费用。
HTTP 参考展示了等价的 curl 请求头。SDK 及其 Fetch 传输都不会自动生成幂等键。
保持请求不变
键以账户为范围,同一账户的所有 API 密钥及会话 Playground 共享该范围,不同账户拥有独立命名空间。同一账户的其他有效 API 密钥可以恢复操作;已撤销密钥无法认证。API 密钥也不会因此获得调用会话专用端点的权限。
服务比较验证后的规范化请求,包括实际请求模型、上下文、问题、标准及接受的扩展字段。排版空白与对象键顺序不影响比较;数组顺序、评分档位顺序、字段值,以及 null 与省略字段的区别都会影响。相同键提交不同请求时,返回 409 idempotency_conflict。
请始终明确发送并保存来自当前模型列表的 model ID。省略模型时,在比较前补入服务默认值。显式别名或版本名仍属于请求标识的一部分,即使两个名称最终对应同一模型。恢复时不要将请求中的 ID 换成响应返回的解析后模型名。如果旧请求省略模型,而默认值已变化,应保留原实际模型,并先检查原操作再尝试恢复。
精确十进制标识会保留:9007199254740993 不等于 9007199254740992,1e-400 不等于零。等价数字写法视为相同,重复对象键(包括转义后同名)会被拒绝。这比较的是线上提交的 JSON,无法恢复客户端数值类型已经丢失的精度。必要时保留原始 JSON 文本。更换模型或标准属于新操作,价格也可能不同;成功重放原操作仍然零扣费。
图片属于完整请求标识的一部分,包括每个 base64 字符串、媒体类型、表示形式和数组顺序。同一保留中的键下,调整顺序或替换图片会冲突;将 data URL 改为对象也不是无变化重试。images: [] 对纯文本模型有效,但与省略字段不同。恢复时保留原始正文,不要重新编码文件,或根据当前 Playground 状态重建请求。
按已记录的结果处理
| 已有操作 | 相同键与请求体的响应 | 处理方式 |
|---|---|---|
| 没有保留中的操作 | 可以开始新的评估并收取积分。 | 保存此键,直到结果明确。 |
| 仍在处理 | 409 request_in_progress,附带 Retry-After: 2。 | 等待后用相同键和请求体重试。 |
| 成功 | 返回保存的响应状态、文本和允许的响应头,不再次评估或扣费。 | 使用恢复的结果。 |
| 失败且积分已返还 | 409 idempotency_failed。 | 只有明确决定重新尝试时才使用新键。 |
| 请求体不同 | 409 idempotency_conflict。 | 恢复原请求体,或为确实不同的新操作分配新键。 |
| 答案已不可用 | 410 idempotency_expired。 | 开始新操作前先核对原操作。 |
成功重放包含 X-Idempotency-Replayed: true、X-System-One-Credits: 0 和原始 X-Request-Id,并在可用时保留原模型服务跟踪头。它复用保存的响应,不会重新评估模型、再次舍入数值,或向正文添加 System One 的 replayed、billing、request_id 字段。模型自行提供的同名字段不是 System One 计费信息。不要用两次独立评估比较重放保真度:新操作可以得到不同答案。
重放尝试仍计入账户决策限流。新尝试包含当前账户限流头,因此并非每个 HTTP 头都与原响应完全相同。响应使用 Cache-Control: no-store;普通浏览器或代理缓存不是幂等机制。
24 小时窗口
已完成操作可通过其键恢复,期限为从请求创建起 24 小时。超过此窗口后,相同键可以发起新的计费操作,不一定返回过期错误。这不是永久去重保护。
不提供操作键时,重复请求会发起独立评估。请在自己的应用中保存重要答案,不要依赖重放来长期获取结果。
普通评估失败会在错误响应前返还预扣积分。中断可能使操作停留在处理中;持续超过 15 分钟的处理中请求可以进入对账,但这不是立即完成或退款的期限保证。结果明确前保留原键和请求 ID。未结算的处理中操作不意味着 24 小时后就可以新建替代操作:保留窗口不承诺清除在途请求。
如果请求返回 503 request_reconciliation_pending,表示费用尚未核对完成。此时省略 X-System-One-Credits,缺失不等于零。恢复可能不会立即完成。保留原键和 X-Request-Id,检查控制台用量;状态持续不明时,请联系 support@system-one.dev。不要为了跳过错误而换新键。
SDK 重试设置
SDK 示例通过 maxRetries: 0 关闭自动重试,让应用按状态表处理。SDK 重试策略可以重试网络错误和部分 HTTP 失败,但不会自动处理不同的 409 状态。启用重试仍需提供相同的已保存键与正文;关闭重试也不会取消响应已丢失的服务端操作。服务不会自动重试模型推理。
Core 的 APIError 暴露 statusCode、requestId 和 retryAfterMs,不暴露完整正文或分类响应头。区分冲突、处理中及失败状态时,请用 Fetch 读取 X-System-One-Error-Code。遵守 Retry-After,它可以是秒数或 HTTP 日期。检查 X-System-One-Error-Source:模型服务的 401 不代表 System One 登录过期。详见错误参考。
独立的官方 TypeSafe 客户端有自己的重试配置,调用本服务时也需要显式提供并保存幂等键。独立 SDK 的直连提供方适配器遵循各自服务的契约,不能从 SDK 包版本推断托管服务的积分或重放规则。