在 Chatbox 里添加自定义 OpenAI 兼容服务后,最容易混在一起的其实是三个不同问题:点击 Fetch 后列表为空、手动新增模型后出现 model_not_found、点击 Check 后又报图像或工具调用错误。它们不是同一层故障,也不应该靠反复换 Key 解决。
本文按 Chatbox 官方 v1.22.1 的当前源码说明真实调用链,并用一个只监听 127.0.0.1 的 Python 夹具复现成功和失败分支。先给结论:Fetch 成功不只要求 HTTP 200,响应还必须有顶层 data 数组;真正用于请求的值是 data[].id,不是你看到的友好名称。
先准备这组最小配置
进入 Chatbox 设置,添加自定义提供商并选择 OpenAI API Compatible。当前界面会显示 API Host、API Path、Model,模型区域有 New、Reset、Fetch,配置满足条件后还可以使用 Check。
如果服务使用标准 /v1 前缀,先把职责拆开:
| 字段 | 示例值 | 作用 |
|---|---|---|
| API Host | https://your-gateway.example/v1 |
模型列表和聊天请求的共同基址 |
| API Path | /chat/completions |
对话资源路径 |
| API Key | 当前服务要求的 Key | 只保存在客户端,不进入文章或截图 |
| Model ID | 从 /models 的 data[].id 复制 |
真正发送给服务端的模型值 |
先不要急着点 Check。用下面的命令确认模型列表契约:
bash
BASE_URL='https://your-gateway.example/v1'
curl -sS -i "$BASE_URL/models" \
-H "Authorization: Bearer $OPENAI_API_KEY"
合格的最小响应应同时满足三项:状态码是 200、正文是 JSON、顶层存在非空 data 数组。例如:
json
{
"object": "list",
"data": [
{"id": "provider-model-exact-id", "object": "model"}
]
}
这里真正要复制的是 provider-model-exact-id。不要把网站商品名、控制台展示名、自己起的中文别名或另一个客户端的模型名当成服务器 ID。
为什么 HTTP 200 仍然可能拉不到模型
Chatbox v1.22.1 的模型拉取实现会请求:
text
<normalized API Host>/models
拿到 JSON 后,它直接检查顶层 data。没有 data 就抛出错误;存在 data 时,每一项的 id 被映射为 Chatbox 的 modelId。因此下面这个响应虽然是 200,仍不满足当前契约:
json
{
"object": "list",
"models": [
{"name": "provider-model-exact-id"}
]
}
这类情况常见于"接口看起来像 OpenAI,但模型列表字段并不兼容"的服务。此时不要把问题归因成网络失败。你需要查服务方文档,确认它是否提供 OpenAI 风格的 /models;如果模型列表被禁用,只能使用 Chatbox 的 New 手动添加精确 ID。
Fetch、New、Check 分别做什么
Fetch:读取服务器模型目录
它适合确认当前 Base URL、认证和模型列表数据契约是否同时成立。列表拉取失败时,先保存完整状态码和脱敏响应体,再判断是 401、404、200 但结构不兼容,还是网络错误。
New:手动保存一个模型 ID
New 能绕过"服务没有开放 /models"这一限制,但它不证明模型存在。手动输入后,Chatbox 只是把这个值加入当前提供商的模型列表。下一次真实请求仍会把该 modelId 发给服务端,所以大小写、连字符、日期后缀和区域后缀都必须逐字一致。
Check:执行能力测试,不是一次 ping
当前 v1.22.1 的检查流程先发送基础文本请求;基础请求成功后,还会继续尝试一张 1x1 图片和一次工具调用。也就是说,一次 Check 最多可能产生三次模型请求。
如果你的服务按请求计费、模型不支持图像或工具调用,后两项失败不等于基础文本模型不可用。为了把问题收窄,建议先用一个最小非流式文本请求确认精确 ID,再决定是否运行完整 Check。
用最小聊天请求验证精确 ID
把模型 ID 从 /models 响应中复制出来:
bash
MODEL_ID='provider-model-exact-id'
curl -sS -i "$BASE_URL/chat/completions" \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d "{
\"model\": \"$MODEL_ID\",
\"messages\": [{\"role\": \"user\", \"content\": \"reply with OK\"}],
\"stream\": false
}"
成功信号是 HTTP 200,并且响应中能读到 choices[0].message.content。如果同一个请求把 model 换成友好名称后出现 model_not_found,就已经把故障定位到模型值,而不是 Base URL、Key 或 Chatbox UI。
本地实测:200、结构错误、别名错误和精确 ID
本次夹具只承认两个模型 ID,并故意准备四条分支:
text
GET /v1/models -> 200 + data[].id
GET /malformed/v1/models -> 200 + models[].name,无 data
POST /v1/chat/completions + 别名 -> 404 model_not_found
POST /v1/chat/completions + 精确ID -> 200 CHATBOX_MODEL_OK
执行命令:
bash
python3 06-evidence/probe_chatbox_model_fetch.py
脱敏结果:
text
CHATBOX_VERSION=1.22.1
GOOD_MODELS_HTTP=200
GOOD_MODEL_IDS=fixture-chat-v2,fixture-reasoning-v1
MALFORMED_MODELS_HTTP=200
MALFORMED_HAS_DATA=NO
WRONG_MODEL_HTTP=404
WRONG_MODEL_ERROR=model_not_found
EXACT_MODEL_HTTP=200
EXACT_MODEL_TEXT=CHATBOX_MODEL_OK
ONLINE_PROVIDER_REQUEST=NO
FULL_CHATBOX_RUNTIME=NO

这个实测证明的是 Chatbox 当前源码所需的数据形状,以及精确 ID 与友好名称在本地夹具中的差异。它没有启动完整 Chatbox,也没有访问真实模型服务,因此不能据此推断线上价格、延迟、稳定性或模型能力。
按信号判断下一步
| 观察结果 | 优先检查 | 不要先做 |
|---|---|---|
/models 返回 401/403 |
Key、权限、请求头 | 反复改模型名 |
/models 返回 404 |
API Host、版本前缀、服务是否提供模型目录 | 直接点完整 Check |
/models 返回 200,但没有 data |
响应契约或服务方文档 | 把 200 写成"模型拉取成功" |
data[].id 可见,但 Chatbox 列表为空 |
当前版本、保存回显、客户端日志 | 自己猜一个友好名称 |
| 精确 ID 的文本请求 200 | 再决定是否做视觉和工具测试 | 把后续能力失败归因成 Key 失效 |
友好名称报 model_not_found |
逐字对比 data[].id |
删除版本/区域后缀 |
保存后的复核清单
- 重新打开自定义提供商,确认 API Host 和 API Path 回显正确。
- 在模型列表中确认保存的是服务器精确 ID;昵称只用于显示。
- 先跑一个最小文本请求,记录状态码和可读正文。
- 需要能力测试时再点
Check,并区分基础、图像、工具三项结果。 - 截图和日志只保留状态码、路径、模型 ID 和错误类型,不保留 Authorization 值。
- 如果服务没有
/models,在记录中明确写"手动添加",不要写成"自动拉取成功"。
适用边界
本文依据 Chatbox 官方 v1.22.1 和对应提交 7450ab2d。后续版本可能调整按钮、测试顺序或数据适配;不同 OpenAI 兼容服务也可能隐藏模型列表、使用其他路径或返回不同错误码。最终以你当前版本的设置回显、服务方公开文档和实际脱敏请求为准。
不要把真实 Key 写进命令历史、文章、截图或录屏。示例中的域名和模型名都是占位符,本地夹具使用固定的 fixture-only 请求头且只监听回环地址。
总结
Chatbox 获取不到模型时,先把问题拆成"模型目录""模型 ID""能力检查"三层。Fetch 要求 /models 返回顶层 data,真正请求值来自 data[].id;New 只是手动保存,不代表服务端支持;Check 在基础成功后还可能继续发送图像和工具调用请求。
最稳的顺序是:先验 /models 的状态码和结构,再复制精确 ID,最后只用一个最小文本请求确认 200。把这三步跑通后,再处理图像、工具调用或流式能力,定位会清楚得多。