OpenCode推理模型第三方 API配置排错

OpenCode 报推理档位不支持:先查版本、Variant 和实际请求

遇到 reasoning effort medium not supported,怎样区分模型默认值、Variant 与实际请求,核对 OpenCode V1/V2 配置,并用最小请求验证修改。

查看接入文档
OpenCode 报推理档位不支持:先查版本、Variant 和实际请求

OpenCode 能列出模型,却一发送就报 level "medium" not supported,先保存报错、实际模型和本次选中的 Variant,再查请求里到底带了什么。菜单里有某个档位、模型设置了默认档位、接口收到了这个档位,是三个需要分别核对的事实。

如果接口拒绝了一个确定的参数值,原样重试通常没有帮助。先把失败缩小到一句不调用工具的短对话,再改一个设置验证;暂时停下会反复执行外部动作的长任务。

本文核对于 2026-11-10:配置示例面向已发布的 OpenCode v2.2.6 与当前官方文档。仍在使用 1.x 的读者可以先看原有接入教程,不要把两代配置的字段交叉粘贴。

一份新报告为什么不能直接变成“默认值有 bug”

OpenCode #62592 的原报告来自 Windows 桌面版 2.2.6,同一机器 PATH 中的 CLI 是 2.0.29。作者在图片相关流程中遇到如下拒绝:

level "medium" not supported, valid levels: low, high, max

作者后来把范围收窄到自定义兼容提供方,并报告声明能力与默认设置后可用。但讨论中的机器人回复,用本地模拟接口检查时观察到:没有选 Variant 的请求未带 reasoning_effort;明确选 #medium 才发送 medium 并被模拟接口拒绝。

这条回复没有复现原作者的真实模型服务,也没有证明图片流程的来源。它足以提醒我们:不能由“提供了 medium 选项”推断“每个默认请求都发送 medium”。 截至查阅时,该 Issue 仍为 Open,不能写成已有正式修复。

本文也没有运行完整 OpenCode 或真实 GLM 服务。下面的配置行为来自正式版源码与官方说明,公开脚本只做本地字段检查。

先确认报错来自哪个程序、哪一次调用

先在终端记录:

opencode --version

然后核对桌面应用的版本,以及它实际连接的服务端。终端输出不能单独代表桌面应用或远程服务的版本。把下面几项放在同一条排查记录里即可:

要记录什么用来排除什么误判
桌面、CLI、服务端的版本与运行位置改的是另一套安装或另一台机器
提供方 ID、模型 ID、所选 Variant相同展示名称对应不同请求配置
失败时间与触发动作主对话、标题、子任务或插件请求混在一起
完整错误中的字段和值把参数拒绝当成 Key 或网络故障

只发一句“请回复 OK,不调用工具”,观察是否仍失败。若纯文本能完成,只有附件、某个 Agent 或工具路径失败,应继续追那条路径的模型和设置,不要先给全部模型覆盖同一个档位。

基本地址、Key 和模型 ID 尚未核对时,先用接入基础与排错把连接问题排除。模型出现在列表里,只能证明它可被列出,不能代替推理参数或工具调用验收。

可选档位、默认值和最终请求分别管什么

提供方设置、模型设置与所选 Variant 依次应用,能力声明决定可生成的档位

按当前官方 Models 文档,V2 中相关字段分工如下:

配置作用
capabilities.reasoning.effort描述模型的推理控制,供 OpenCode 生成相应 Variant
模型的 settings.reasoningEffort给模型设置默认请求选项
所选 Variant 的 settings在提供方、模型设置之后应用,可以覆盖它们
发出的 reasoning_effort此处 Chat 兼容适配器发送给接口的字段

v2.2.6 的Variant 实现会为普通自定义 Chat 兼容模型提供通用的 low / medium / high 备选;明确的 capabilities.reasoning 可以改变生成结果。这不是对任意接口的能力探测,其他提供方包也可能采用不同规则。

仅声明 "supported": true、不提供控制项,不会凭空得到一组准确档位。修改模型默认值,也不代表先前选中的 Variant 已经被清掉。若配置了额外的 variants,还要核对其中是否仍加入了不受支持的选项。

当前Chat 请求实现在解析后的选项有 reasoningEffort 时才加入 reasoning_effort。这解释了为什么要读实际请求,而不能只看选择器。若你的接口走 Responses 或原生 Messages,应查相应适配器;协议对照解释了这些路径的区别。

V2 示例:把支持列表与默认设置一起写清楚

先从服务商当前文档或对应请求的错误中确认允许值。下面假设你的具体接口已确认接受 low / high / max,并接受 high 作为请求值;它不是全部 GLM 模型或全部中转服务的能力清单。

这是一个 V2 自定义 Chat 兼容提供方示例,写入实际运行 OpenCode 的机器和项目的配置:

{
  "$schema": "https://opencode.ai/config.json",
  "model": "nbility/YOUR_MODEL_ID",
  "providers": {
    "nbility": {
      "name": "Nbility",
      "package": "@opencode/ai/providers/openai-compatible",
      "settings": {
        "baseURL": "https://api.nbility.ai/v1",
        "apiKey": "{env:NBILITY_API_KEY}"
      },
      "models": {
        "YOUR_MODEL_ID": {
          "settings": {
            "reasoningEffort": "high"
          },
          "capabilities": {
            "reasoning": {
              "supported": true,
              "effort": ["low", "high", "max"]
            }
          }
        }
      }
    }
  }
}

把 YOUR_MODEL_ID 换成模型列表中可用的真实 ID,再按该接口支持情况修改或去掉推理设置。示例仅展示推理配置,模型的工具、输入类型和上下文限制还应按已知能力单独核对。apiKey 引用进程环境变量;桌面程序不一定继承终端环境,请使用所运行版本支持的凭据入口。

两个位置的作用不同:effort 用来生成选项,settings.reasoningEffort 用来设置默认值。要明确验证 high,可在 V2 终端运行:

opencode run --model 'nbility/YOUR_MODEL_ID#high' 'Reply OK without using tools.'

这条命令只在接口支持上述档位且配置已加载时适用。一次成功只能验证这次模型请求;标题、其他 Agent 和工具往返仍要分别检查。本文没有执行这条带认证命令。

已经写了 high,为什么仍可能发送 medium

V1 的 provider、npm、options 与 V2 的 providers、package、settings 对应,不能混成一个定义

先看两种格式:

配置位置V1 常见写法本文 V2 写法
提供方根键providerproviders
适配器包npm: "@ai-sdk/openai-compatible"package: "@opencode/ai/providers/openai-compatible"
地址与请求选项optionssettings
推理能力细节不可直接套用右侧结构capabilities.reasoning

V2 保留了旧配置迁移逻辑,不意味着可以把新 settings、capabilities 塞进旧模型结构。v2.2.6 配置规范化源码会分别处理旧、新提供方;同一份输入里同一提供方 ID 同时出现时,新格式的整个条目优先,不是把两套条目的子字段逐一补齐。

因此,把 baseURL 留在旧块、只在新块补一个模型,可能丢掉原来期望保留的内容。先备份配置,再把同一个提供方整理成一份完整、版本匹配的定义,保留仍需使用的模型和地址。

还要查有没有更高优先级的配置、Agent 或命令选模,以及会话记住的 Variant。按官方 Config 文档检查运行目录、全局、项目和系统设置;远程服务要在服务所在机器上查。修改后用新会话复测,确认实际加载结果,别只看编辑器里那一份 JSON。

用本地脚本检查脱敏请求里的档位

如果客户端或你可控制的测试服务能导出请求 JSON,在本机检查原始记录,向外分享时只保留必要字段。不要导出 Authorization、完整对话、代码、图片或工具参数。Nbility 的用量日志可帮助关联模型、时间和状态,但不能假定它一定保存客户端的完整请求体。

保存为 inspect_effort.py:

import json
import sys
from pathlib import Path

request = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
if not isinstance(request, dict):
    raise SystemExit("Expected one request JSON object")

allowed = set(sys.argv[2:])
if not allowed:
    raise SystemExit("Pass the effort values accepted by this endpoint")

value = request.get("reasoning_effort")
if "reasoning_effort" not in request:
    print("reasoning_effort: absent; endpoint default not checked")
elif not isinstance(value, str):
    print("reasoning_effort: wrong type")
elif value in allowed:
    print("reasoning_effort: allowed by supplied list")
else:
    print("reasoning_effort: not in supplied list")

先用公开教学输入试运行:

{
  "model": "example-model",
  "messages": [{"role": "user", "content": "Reply OK"}],
  "reasoning_effort": "medium"
}

把它存为 request.json,执行:

python3 inspect_effort.py request.json low high max

该例应输出 reasoning_effort: not in supplied list。移除字段时输出 absent,表示这份请求没带此字段;不能由此判断服务端默认值是什么。脚本不打印请求内容,只比较你提供的列表,也不解析 Responses、嵌套请求或 SSE 日志。

本文从中英文最终代码块分别执行了字段缺失、high、medium、数值类型四组输入;检查的是 JSON 字段与脚本行为,未验证真实服务接受哪些档位。

修改后,按这条顺序验收

从确认版本和请求开始,经过短对话与 Variant 对照,再到只读工具和原失败路径

  1. 确认程序和配置。 记录实际版本、服务端位置、提供方与模型,备份配置后只改相关条目。
  2. 验证短对话。 新会话不选 Variant 试一次,再明确选择一个已确认支持的 Variant;分别关联对应请求,别把辅助调用的档位算到主请求上。
  3. 验证只读工具往返。 要求读取一个公开小文件并回答其内容,确认工具确实执行、结果被模型使用。
  4. 回到原来的失败路径。 若最初是图片、某个 Agent 或插件出错,再用公开材料测试那条路径,记录它实际使用的模型与设置。

若仍被拒绝,继续比较最终请求与接口允许值。若最终请求已没有 medium,却还看到旧报错,先按时间确认是否是旧运行、其他请求或另一套服务端,不要继续叠加配置。

使用 Nbility 时,先确认模型 ID、请求路径和当前接口能力,再逐步恢复长任务。保留一份能说明“哪个版本、哪条调用、带了什么档位”的短记录,比反复更换 Key 更有助于定位问题。

相关文章

用 Nbility 跑通你的 Agent 工作流

获取 API Key,统一接入 OpenAI 兼容模型和开发工具。

获取 API Key