mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-11-07 09:47:23 +00:00
## 真实缺陷(用户可见) 内核的上游 URL 是 `base_url + endpoint`(provider.go ChatURL),而适配器默认 `endpoint = "/chat/completions"`。多个渠道平台把**完整路径**写进了 `base_url` ⇒ 实际请求变成 https://copilot.tencent.com/v2/chat/completions/chat/completions ⇒ 上游回 `{"error_msg":"404 Route Not Found"}`(APISIX 的措辞,**不是**模型错误)。 受影响 8 个平台:buddy / cline / lobsterai / opencode / qoder / loomy / raccoon (zcode 当时已用 `endpoint` 覆盖,形态正确)。 另有 loomy 的**第二处**错误:host 用了 `loomy.xunfei.cn`(Web 前端/OAuth 回调域), 实测该 host 的 /chat/completions 返回 **HTML 网页**;正确推理域是参考的 `loomyad.xunfei.cn/api/v1`(返回正常 JSON)。 ## ★ 为什么这个缺陷活了很久(最重要的教训) **模型名被多个源服务**。`deepseek-v4.1-flash` 同时由 4 个源提供 (alittokenplan/vela/oin/channels-buddy)⇒ buddy 源失败后网关**继续尝试下一个源** 并成功 ⇒ 用户看到"能回话" ⇒ "buddy 通道是好的"这个结论**完全没有证据**。 本轮我一度正是这么误判的。而 `hy3` **只由 buddy 一个源**提供 ⇒ 一调就暴露真因。 ⇒ **判据必须断言源的结构不变量(URL 形态),不能靠"某个模型能调通"** —— 后者会被多源冗余掩盖。已按此写判据。 ## 排查手法(可复用) 怀疑"某模型不可用"时,先列**该模型由几个源服务**: 1 个 ⇒ 该模型是那条源的健康探针(用它测) >1 个 ⇒ 它调通**不能**证明任何单一源可用(换独有模型再测) 另:非流式请求会被 buddy 直接 400 拒绝 (`{"code":11101,"msg":"Non-stream chat request is currently not supported"}`), 我第一次用 stream=false 时四个模型全 400,**看不出**模型差异,差点把结论下成 "头组合无区别"就收工。 ## 修法 `base_url` 只留 host(或协议公共前缀),路径移入该平台的 `endpoint`。 逐平台按参考的 `apiBase` + `chatPath` 取值核对,并**实测**修正后的 URL: buddy/cline/lobsterai/qoder/raccoon → 401(缺凭据,正确) loomy → 200 `{"code":"100002","desc":"缺少 token"}`(正确) opencode → 500(路由存在;`https://opencode.ai/v1/...` 是 404+HTML,已证伪) 测试注入平台也一并对齐(免被当范例复制)。 ## 判据(2 条 + 变异 2/2) - `TestChannelSourceBaseURLHasNoChatPath`:静态扫描全部 `base_url`,不得含 `/chat/completions` `/v1/messages` `/generateContent` `/api/chat`。 - `TestChannelBaseURLJudgeBitesOnBadInput`:用**故意坏数据**验证判据的判别力 (防判据自己写歪成恒真)。 变异:把 buddy 恢复成旧形态 → 报红。 ## 顺带修正 `TestUpstreamGapsAreDocumented` 原先钉死 qoder 的**整串 URL 字面量** ⇒ 这次纯形态重构(语义未变)把它弄红了 —— **判据在咬自己**。改为断言 `base_url` 与 `endpoint` **两半**,语义不变但对形态重构免疫。