Codex SDK 如何按模型校验 xhigh、max 与 ultra 推理强度?

作者:袖梨 2026-09-13

Codex SDK 校验 xhighmaxultra 时,不应只判断字符串是否属于 ReasoningEffort 枚举,而应先取得精确模型的能力元数据,再按语义分流:xhigh 和 max 作为模型级推理强度,与模型公布的 supported_reasoning_efforts 求交集;ultra 作为 Codex 高级编排模式,还要检查客户端是否支持主动多代理、当前任务是否允许该行为以及底层模型如何映射。

一个可靠 SDK 至少需要三道校验:输入格式校验、模型能力校验和运行时服务端校验。任何一道都不能替代另外两道。这样既能在调用前阻止明显无效组合,也能处理模型目录与后端短暂不同步的情况。

先定义清晰的数据边界

SDK 不应把模型 ID、effort 和产品模式塞进一个没有约束的字符串字典。可以先建立以下概念:

ModelDescriptor {
  id: string
  default_reasoning_effort: string | null
  supported_reasoning_efforts: string[]
}

CodexCapabilities {
  proactive_multi_agent: boolean
  ultra_mode: boolean
}

RunOptions {
  model: string
  reasoning_effort: string | null
}

ModelDescriptor 来自模型目录,描述模型级能力;CodexCapabilities 描述客户端或任务运行器能力;RunOptions 是用户请求。将这些数据分开后,max 与 ultra 就不会被误放在同一条无条件排序链里。

第一道:输入格式校验

输入格式校验只负责拒绝空值、非法类型和明显拼写错误。例如 effort 必须是非空字符串,模型 ID 必须存在。SDK 可以识别已知值,也可以保留未来自定义值,以避免协议升级时旧客户端崩溃。

function parseEffort(value) {
  if (value == null) return null
  if (typeof value !== "string" || value.trim() === "") {
    throw new InvalidEffortFormat(value)
  }
  return value.trim().toLowerCase()
}

这一层通过只表示值能被解析。它不能告诉调用方当前模型支持 xhigh 或 max,也不能证明 ultra 可以启用。

第二道:按模型能力校验 xhigh 与 max

对于模型级 effort,判断标准是当前模型的支持列表:

function validateModelEffort(model, effort) {
  if (effort == null) return model.default_reasoning_effort

  if (!model.supported_reasoning_efforts.includes(effort)) {
    throw new UnsupportedReasoningEffort({
      model: model.id,
      requested: effort,
      supported: model.supported_reasoning_efforts
    })
  }

  return effort
}

这个函数对 xhigh、max 以及其他普通档位一视同仁。只要目标模型明确列出,就允许;没有列出,就拒绝。不要写成“所有 GPT 模型都支持 xhigh”或“Codex 新版本都支持 max”,因为能力属于精确模型,而不是品牌或客户端版本。

第三道:单独校验 Ultra

Ultra 不能直接套用上面的模型列表逻辑。它可能是 Codex 产品层的高级模式,会启用主动任务拆分和多代理协作。SDK 应把它转换为一个明确的执行计划:

function resolveUltra(model, codexCapabilities, taskPolicy) {
  if (!codexCapabilities.ultra_mode) {
    throw new UnsupportedCodexMode("ultra")
  }
  if (!codexCapabilities.proactive_multi_agent) {
    throw new MissingCapability("proactive_multi_agent")
  }
  if (!taskPolicy.allow_parallel_agents) {
    throw new TaskPolicyViolation("parallel agents disabled")
  }

  return {
    codex_mode: "ultra",
    model_effort: chooseUltraModelEffort(model)
  }
}

chooseUltraModelEffort 必须由当前 Codex 版本或 provider 的明确规则提供,不能由 SDK 作者凭名称猜测。某个版本可能映射到 max,另一个环境可能要求 xhigh,未来还可能使用新的后端表示。

不要用全局枚举生成下拉框

常见错误是读取 SDK 的 ReasoningEffort 枚举,然后把所有成员展示给每个模型。这样用户会看到模型实际上不支持的 max,或者把 ultra 当作普通模型参数。

正确界面应根据模型动态生成:

modelOptions = descriptor.supported_reasoning_efforts

if (codexCapabilities.ultra_mode && taskPolicy.allow_parallel_agents) {
  productModes.push("ultra")
}

模型 effort 和产品模式最好使用两个控件或清晰分组。即使产品希望在同一个选择器里展示,也应在内部保留两种类型,并显示 Ultra 会启用额外代理行为。

模型目录从哪里来

SDK 应优先读取当前 Codex 运行环境提供的模型目录或模型元数据,而不是从博客复制静态表。模型目录至少应包含模型 ID、显示名、默认 effort 与支持列表。若 SDK 有缓存,还需要版本或过期策略。

缓存模型目录时要考虑三种变化:

  • 后端为现有模型增加或删除 effort;
  • 用户切换 provider 或组织;
  • 远程 Codex 主机与本地应用版本不同。

模型或 provider 变化时,应使相关缓存失效。不能把官方服务的能力列表继续用于兼容网关。

默认值的解析顺序

如果用户没有指定 effort,SDK 应使用模型目录的默认值,而不是固定 medium:

requested = options.reasoning_effort
effective = requested ?? model.default_reasoning_effort

if (effective != null) {
  effective = validateModelEffort(model, effective)
}

用户显式值可能来自会话、命令行、profile、项目配置和用户配置。SDK 需要先按 Codex 配置优先级解析出一个候选值,再做模型校验。若先校验低优先级值,可能误报一个最终不会生效的错误。

模型切换必须重新校验

假设当前模型支持 max,用户随后切换到只支持 xhigh 的模型。SDK 不能沿用旧的有效结果缓存,必须使用新模型描述重新验证。推荐返回一个结构化决策:

EffortDecision {
  requested: "max"
  effective: "xhigh"
  reason: "requested effort unsupported by selected model"
  model: "<new-model-id>"
}

是否允许自动回退应由调用方策略决定。交互式界面可以提示用户选择;无人值守任务可以按预先配置的策略回退;高风险工作流则应直接失败,避免悄悄降低推理投入。

设计可解释的回退策略

模型级 effort 可以维护偏好顺序,但必须与支持列表求交集:

function highestSupported(model, preferred) {
  for (const effort of preferred) {
    if (model.supported_reasoning_efforts.includes(effort)) {
      return effort
    }
  }
  return model.default_reasoning_effort
}

selected = highestSupported(model, ["max", "xhigh", "high"])

这里不要把 ultra 放进 ["ultra", "max", "xhigh"],因为 Ultra 不是单纯的模型级上一档。Ultra 校验失败时,是回退到普通单代理模式,还是终止任务,应由任务策略明确决定。

运行时校验仍然不可省略

即使模型目录声称支持 max,服务端仍可能因灰度发布、区域差异或 provider 配置拒绝。SDK 必须保留运行时错误处理,并识别 invalid value、unsupported parameter 或模型不可用等错误。

try {
  return await client.startThread(resolvedOptions)
} catch (error) {
  if (isUnsupportedEffort(error)) {
    throw new RuntimeCapabilityMismatch({
      model: resolvedOptions.model,
      effort: resolvedOptions.reasoning_effort,
      server_message: error.message
    })
  }
  throw error
}

默认不要在没有记录的情况下自动重试。若允许回退重试,应限制次数,并同时返回最初请求值、实际值和回退原因。

怎样处理服务端返回的允许列表

部分错误会直接列出当前支持值。SDK 可以把它作为临时运行时证据,更新本次会话的能力缓存,但不应永久覆盖官方模型目录,除非有明确版本信息。

一个安全流程是:

  1. 记录原始错误和请求标识。
  2. 解析允许列表,但保留原始文本。
  3. 让策略决定失败、提示或回退。
  4. 若回退,只重试一次。
  5. 在结果中标记实际 effort。
  6. 触发模型目录刷新或遥测告警。

Ultra 的额度保护

Ultra 可能产生多个子代理会话,因此只校验“功能存在”还不够。SDK 应允许调用方设置资源边界:

UltraPolicy {
  allow_parallel_agents: true
  max_agents: 4
  max_total_tokens: 500000
  max_wall_time_seconds: 1800
  require_explicit_request: true
}

具体字段取决于运行环境,但原则是明确并发、总资源和超时。不要把单代理的 max token 限制误当成整个 Ultra 任务的总预算。

状态回传必须包含什么

SDK 的结果或事件应至少暴露:

  • 用户请求的模型与 effort;
  • 最终实际模型与模型级 effort;
  • 是否启用了 Ultra 产品模式;
  • 是否发生回退及原因;
  • 模型目录版本或获取时间;
  • 若可用,子代理数量与总用量。

如果只返回一个 success=true,调用方无法知道 max 是否静默变成 high,也无法审计 Ultra 是否真的启用了多代理。

错误类型应该结构化

建议至少区分:

  • InvalidEffortFormat:输入为空或类型错误;
  • UnknownModel:模型目录没有该模型;
  • UnsupportedReasoningEffort:模型未声明该档位;
  • UnsupportedCodexMode:客户端不支持 Ultra;
  • TaskPolicyViolation:任务策略禁止多代理;
  • RuntimeCapabilityMismatch:目录与服务端结果不一致。

结构化错误便于 UI 给出正确提示,也方便无人值守任务选择是否回退。把所有情况压成“请求失败”会让排错非常困难。

单元测试矩阵

SDK 至少应覆盖以下组合:

模型支持 [low, medium, high, xhigh] + 请求 xhigh => 通过
模型支持 [low, medium, high, xhigh] + 请求 max   => 拒绝
模型支持 [low, medium, high, xhigh, max] + 请求 max => 通过
客户端无 Ultra 能力 + 请求 ultra => 拒绝
客户端支持 Ultra、任务禁用并发 + 请求 ultra => 拒绝
客户端支持 Ultra、任务允许并发 + 请求 ultra => 生成编排计划
模型切换后旧 max 失效 => 提示或按策略回退

还要测试模型目录缺失、缓存过期、服务端能力冲突和自定义未来值。未来值能被解析,不代表应自动显示或提交;只有模型目录声明后才进入有效选项。

集成测试不能只 Mock

Mock 模型目录可以验证算法,但无法发现真实后端与客户端发布不同步。集成测试应使用受控账户运行少量低风险任务,确认状态中记录的实际 effort,并覆盖一次服务端拒绝路径。

测试日志需要脱敏,但应保留客户端版本、模型 ID、provider、目录支持列表、请求值和错误代码。这样出现线上差异时,可以判断是 SDK 逻辑、缓存、模型目录还是服务端造成。

一个完整的解析流程

function resolveRunOptions(input, environment) {
  const model = environment.catalog.require(input.model)
  const effort = parseEffort(input.reasoning_effort)

  if (effort === "ultra") {
    return resolveUltra(model, environment.codex, environment.taskPolicy)
  }

  return {
    model: model.id,
    codex_mode: "standard",
    model_effort: validateModelEffort(model, effort)
  }
}

这个流程先确定模型,再解析 effort,随后按 Ultra 与普通模型档位分流。它不会用全局枚举冒充模型能力,也不会把产品模式直接发送给不理解它的后端。

常见实现错误

用客户端版本判断 max

新客户端认识 max,不代表所选模型支持。应查询模型描述。

把 ultra 当成最高模型 effort

Ultra 可能触发多代理编排,需要单独能力和策略检查。

失败后无限降级重试

这会浪费额度并掩盖配置问题。回退次数应受限且结果可审计。

切换模型不清理校验缓存

支持列表属于模型与 provider 组合,不能跨模型复用。

只显示 requested effort

若发生映射或回退,必须同时显示 effective effort。

结论

Codex SDK 校验 xhigh、max 与 ultra 的正确方式,是把协议值、模型能力和产品编排分开处理。xhigh 与 max 必须存在于精确模型的 supported reasoning efforts 中;Ultra 需要额外验证 Codex 客户端、多代理能力和任务资源策略,再由当前实现决定底层模型 effort。SDK 应动态读取模型目录、在模型切换后重新校验、保留服务端运行时验证,并返回 requested 与 effective 两套状态。这样才能既兼容新模型,又避免把宽泛的工具 Schema 误当作当前模型能力。

相关文章

精彩推荐