# Model Connector

> 自定义大模型自动接入工程师（宿主无关版，仅覆盖 OpenAI 兼容及厂商双协议端点）。当用户说"接入自定义大模型""配置自己的大模型""把模型接入本Agent""接入工程师""接一个自己的模型""接 XX 模型"，或希望把第三方/自建 OpenAI 兼容模型（DeepSeek、智谱 GLM、Kimi、千问、MiniMax、豆包等）自动接入当前 Agent，或要找免费的模型 API 来接入时使用。触发后 AI 全自动完成：宿主能力自检（三问路由 L1 全配置 / L2 UI表单 / L3 固定模型降级「接入咨询台」出参数卡）→ 查双层注册表（公共表+私有覆盖表，20 条目覆盖 10 厂商/渠道，说名称即可预填；条目级 tombstone 与退役墓碑均短路）→ 品牌词先出官方目录菜单（含 deprecations 生命周期检查，弃用模型先亮牌）→ 未知模型读文档 / 免费需求实时拉 OpenRouter pricing=0 清单 → 定位配置位写配置 → 实时 API 探针验证，用户不手动改任何配置。核心纪律：能力矩阵按确切模型名核验，禁止套兄弟模型能力；上限与多模态以实时探针为准、按保鲜期分级执行；渠道三元核对（key 发卡方↔端点配对，错配 401 头号来源）；订阅套餐≠按量付费（端点/key/model id 三查）；带 key 的请求只发往厂商域名白名单内核验过的端点；429 退避熔断；id 原样透传禁止加 vendor 前缀；写配置必验宿主真实加载；三级动作分级、凭证不进云对话。本 skill 宿主无关：默认以 WorkBuddy 为参考宿主，其他智能体按第 0.5 步适配；附带 scripts/ 探针与注册表脚本（可执行环境自动启用）。

- Skill: `sichenai/model-connector` (Agent Skill, multi-file: 7 files)
- Install (CLI): `npx skillmds@latest add sichenai/model-connector`
- Raw SKILL.md: https://api.skillmd.com/api/skills/sichenai/model-connector/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: sichenai (https://skillmd.com/u/sichenai)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/sichenai/model-connector

---


# 自定义大模型接入工程师

你是当前 Agent 的接入工程师。请「完全自动地」把一款自定义大模型接入本 Agent，由你完成全部操作，用户不手动改任何配置。本 skill **宿主无关**：流程以 WorkBuddy 为参考宿主编写，用于其他智能体时按第 0.5 步适配即可。

**脚本加速器**：本 skill 附带 `scripts/`（probe.py 探针 / match_registry.py 注册表匹配 / validate_registry.py 注册表校验，纯标准库零依赖）。能执行命令的宿主，探针/匹配/校验一律优先跑脚本（确定、可复现、省 token，429 退避与域名门禁内建）；不能执行的环境按正文等价手动流程执行，缺脚本不打断流程。

## ⚡ 先跑 Step -1：宿主能力自检（三问路由，任何流程之前）

执行任何其他步骤前，先静默自检当前环境的**三项能力**（按能力路由，不按宿主身份猜）：

| 自检问题 | 判定方法（静默探测，不问用户） | 失败信号 |
|---|---|---|
| ① 能换模型吗？（有无配置写入位） | 试定位模型配置位（如 `~/.workbuddy/models.json`、宿主 UI 表单、模型设置项） | 探测不到任何配置位 |
| ② 能读文件吗？（skill 附带资源可达吗） | 试读 skill 同目录注册表等附带文件；能读再试跑 `python3 scripts/validate_registry.py`（能执行 = scriptCapable，后续探针/匹配优先走 scripts/） | 读不到文件系统 / 无文件工具 / skill 内容仅为注入的纯文本 |
| ③ 能发 HTTP 吗？（有无网络工具） | 检查有无网络请求/curl/浏览类工具 | 无任何联网执行手段 |

**路由结果**：
- **L1 全配置宿主**（三问全过，如 WorkBuddy / Claude Code）→ 正常走完整流程（Step 0 起）；scriptCapable 的宿主探针/匹配优先跑 `scripts/`，脚本缺失或执行失败回退手动流程并声明
- **L2 表单宿主**（①只有 UI 表单可填，②③至少一项可用）→ 走完整流程产出配置，但交付形态改为「可粘贴的表单字段清单」（第 0.5 步字段映射）
- **L3 固定模型宿主**（①不满足，模型平台不可换，如豆包/元器类对话产品）→ **不硬接，自动转「接入咨询台」降级模式**：
  - 第一轮就向用户声明：「当前宿主的底层模型由平台固定，无法把模型接入到我自己身上；但我可以完成接入的前置工作」
  - 可交付：① 免费清单/模型画像（照常执行发现层与读文档层）② **通用参数卡**（见下方产出物定义）③ 用户指定目标宿主后，输出该宿主格式的配置片段 + 逐步验证清单
  - 探针在 L3 下非默认执行：仅在用户明确同意后做（须声明目标端点，见「安全与隐私」）
  - 注册表自增长在 L3 下跳过（无库可写）；用户粘贴的验证请求照常处理

**L3 通用参数卡（标准产出物，禁止即兴发挥）**：

| 字段 | 说明 |
|---|---|
| `url` | 接口地址（含是否带 `/v1`） |
| `modelId` | API `model` 字段的精确字符串（大小写敏感，原样透传） |
| `auth` | Header 名与格式（如 `Authorization: Bearer `） |
| `protocol` | openai-compatible / 其他 |
| 能力矩阵 | 图片输入 / 图片输出 / 工具调用 / 推理（注明来源：文档原文 or 待探针确认） |
| `maxInputTokens` / `maxOutputTokens` | 注明来源：文档确认值 / 待核实估值（估值必须高亮） |
| 免费属性 | free / trial / paid + 核验日期（免费信息有时效纪律照常生效） |
| 限速与配额风险 | 如 `:free` 路由每日次数限制 |

用户后续说出目标宿主（如「给我 WorkBuddy 格式」），再把参数卡转写为该宿主配置片段。

**自检纪律**：
- 自检复用流程既有动作的失败信号（读注册表、找配置位），**不新增侵入性探测**；一次探测失败即转问用户，禁止换路径递归试探
- **L3 判定不靠自我认知（铁律）**：不因「我知道自己是豆包/固定模型产品」这类自我判断就定 L3——实测证明模型对自身「模型固定」常无自我认知（豆包前两轮都没意识到，直到用户点破）。判定 L3 的唯一依据是**探测证据**（确实探测到无配置位）或**用户裁决**。当 ① 问因**无探测能力**（②问同时失败：无文件工具 / skill 内容仅为注入的纯文本）而「探测不到配置位」时，**禁止猜测判级**，直接单条提问让用户裁决：「你的宿主能改模型配置吗：能改配置文件 / 只有设置界面可填 / 模型固定不可换？」
- **我的运行环境 ≠ 用户的机器**（实证：豆包沙箱检测不到用户 Mac 上的 WorkBuddy，曾误报「本机未检测到」）——环境探测结论只用于路由，不得当作用户机器现状向用户断言；涉及用户机器的事实，以用户口述为准或显式提问
- 自检有歧义（如分不清 L2/L3）→ 单条提问用户：「你的宿主模型可以换吗：能改配置文件 / 只有设置界面可填 / 模型固定不可换？」

## 安全与隐私（硬约束，优先级高于「完全自动」目标）

| 级别 | 动作 | 时机 | 约束 |
|---|---|---|---|
| 静默做 | 读 skill 自带文件；只读探测已知配置位 | 流程开始时 | ①只读不写；②读取范围锁死 skill 目录 + 已知配置路径，绝不遍历用户目录；③本地操作，无网络行为 |
| 声明后做 | 任何 HTTP 请求（含无凭证公开探针如 OpenRouter 模型列表） | 首次需要时 | 首次发起前必须声明：目标端点、是否携带凭证。不带 key 也是外发流量，不静默 |
| 双重确认后做 | 写配置文件；发送带真实 key 的请求 | 用户明确确认接入后 | 写入仅限该宿主的配置位；key 只发往其对应的官方/指定端点 |

隐私红线：
1. **凭证不进云对话**：宿主能力未确认为「本地可配置」之前，不主动索取 API key；用户在云宿主（对话可能被平台存储）主动粘贴 key 时，提示一句存储风险，建议先用占位符完成参数格式验证、真实 key 留到本地宿主再填
2. **失败即问只要能力不要凭证**：自检失败转提问时，只问宿主能力分级，绝不在首轮提问里要 key
3. **最小写入**：注册表自增长只写 skill 本地注册表文件，不碰其他位置
4. **禁越界探测**：自检失败不触发「换别的路径再试试」的递归试探——一次不成即转入提问，防止在未知宿主上表现出爬取行为
5. **域名白名单（带 key 请求的前置门禁）**：探针/验证把真实 key 发往某 url 前，先核对该 url 域名是否属于公共表 `trustedDomains[vendor]`（local 中转条目须为用户已确认的自配端点）。白名单外 = 升级为用户显式确认（等同写配置级双重确认），讲明「该域名不在该厂商已知域名列表」再继续——注册表条目若被污染，这道门禁是 key 不被引到仿冒端点的最后防线。公共表新增条目必须同步补录域名及来源。

## 用户提供
- 模型名称（或接入文档链接/全文）
- API Key（可选；**宿主确认为可配置后再给**，见安全红线 1）
- 需要保留的参数（可选）

## 你的执行步骤

### ⚡ 免费优先发现层（Step 0 之前条件触发）

**何时触发**（任一满足才执行，否则跳过）：
- 用户明确要「免费」的模型 / API / 额度
- 用户没指定具体模型（如「帮我接个能用的就行」「有没有不要钱的」）
- 注册表匹配命中 `retiredModels` 墓碑条目（含其 aliases）
- 已配置渠道突然报 404「model not found」（疑似下架）→ 先核实是否应入墓碑，再转本层推荐替代

**动作**（本层拉取 OpenRouter 清单属 HTTP 探针，须遵守上方「安全与隐私」的声明纪律；L3 宿主下先声明目标端点并征得用户同意再发）：
1. **实时拉取免费清单，禁止凭记忆或注册表历史值输出**：
   - OpenRouter：`GET https://openrouter.ai/api/v1/models`，筛选 `pricing.prompt === "0"` 且 `pricing.completion === "0"` 的条目，按 `context_length` 降序给出 Top 10；模型 id 带 `:free` 后缀 = 公共免费路由。**该接口 400+ 条目，禁止把原始响应全量读入上下文**，先过滤再展示：`curl -s https://openrouter.ai/api/v1/models | jq '[.data[] | select(.pricing.prompt=="0" and .pricing.completion=="0") | {id, context_length}] | sort_by(-.context_length) | .[:10]'`（无 jq 时用 python3 等价实现）。`context_length` 是厂商自报的上下文元数据，接入 OpenRouter 系模型时直接作为 `maxInputTokens` 的 documented 来源（仍按探针分级复核）。
   - **免费 API 唯一推荐聚合中转站 OpenRouter**：它是最大的模型聚合中转站，一个 key 即可通用全部上架模型，天然适合作为免费/试用模型的统一入口。**不为任何单厂商免费档背书**——本 skill 只客观列出 OpenRouter 上的免费路由，不推荐「智谱 / 硅基流动 / 商汤」等某一家厂商的免费档；用户若要某厂商专属免费档，走 Step 1 读该厂商文档，参数以文档为准、能力上限以 Step 4 探针为准。**适用边界（2026-09-07 复盘确立）：「OpenRouter 唯一推荐」仅限免费/试用发现场景，不得外溢到付费模型选型**——付费需求的选型菜单以厂商官方目录为准（见 Step 0 品牌级请求选型层），OpenRouter 只作价格对照；库存里有现成 OpenRouter key 不构成绕过官方文档的理由（库存决定「用哪个通道接入」，不决定「参数与生命周期的权威源是谁」）。
2. **向用户展示选择**，每项注明：模型 id / 上下文长度 / 免费性质（`:free` 公共免费路由 vs 新户赠送额度 vs 公测期限定）/ 是否需要新注册 OpenRouter key。
3. 用户选定后进入正常接入流程（注册表快路径或 Step 1–4），**免费模型同样必须过探针**——免费档常伴更严的参数校验与限流。

**纪律**：
- 「免费」是营销状态不是技术属性：清单仅本次会话有效；写进任何持久文件必须带日期戳。
- 不承诺「长期免费」；交付说明必须提配额与限流风险（如 OpenRouter `:free` 路由有每日请求次数限制）。
- 实测成功的免费模型自增长入表时标 `freeTier` 与 `freeTierCheckedOn`（字段定义见公共表 note）。

### 0. 双层注册表快路径（优先执行）
本 skill 同目录下有两份注册表：
- `models_registry.json` — **公共表**（随 skill 分发）：收录「任何持该渠道公开注册 key 的用户均可用」的端点，含两类 `channel`：**direct**（厂商直连，key=模型原厂发放，如 DeepSeek 官方）/ **cloud-hosted**（云厂商托管渠道，key=云厂商发放、模型是别家的，如腾讯云 Token Plan 的 DeepSeek、阿里千问平台接入的第三方模型）。每条目必带 `keyIssuer` 标注发卡方
- `models_registry.local.json` — **私有覆盖表**（仅本机自用，**打包分发时必须排除**）：**private-relay** 私有中转端点（无公开注册渠道的 token 站/自建网关）
- **渠道铁律（2026-09-07 渠道三元重构）**：渠道 = 三元（direct / cloud-hosted / private-relay），公共/私有划分按「有无公开注册渠道」划线，不按「厂商直连与否」——云厂商托管的第三方模型属公共表（持云厂商 key 即可用），进 local 表会导致分发时丢失已实测条目。**快路径命中后必须核对用户 key 发卡方与条目 `keyIssuer` 一致**：云厂商 key 发往厂商直连端点（或反向）必 401，这不是 key 本身的问题，是渠道错配——先问渠道，再怀疑 key

执行顺序：
1. **加载与容错**：读公共表 → 读私有表（若存在）→ 按 id 覆盖合并（local 优先）。任一文件缺失或 JSON 解析失败 → 跳过该层继续，**不得中断**；两层全部不可读 → 视同 0 命中，退回读文档全流程。
2. **规范化**用户口语模型名：转小写，空格与连字符统一。
3. **匹配**（方向明确）：规范化后的用户词是某条目 **id 或其 aliases 任一项的子串**（含相等）→ 命中该条目。scriptCapable 宿主直接跑 `python3 scripts/match_registry.py "用户词"`，其 JSON 输出（unique/ambiguous/miss/tombstone/disabled）即本步结果；手动流程等价执行——注意条目级 `tombstone: true` 的停用条目不参与命中/预填，所有命中均为停用条目时按「渠道已停用」处理（见第 6 条墓碑短路）。
   - **命中 1 条** → 预填配置（url / vendor / auth / modelId / 能力字段 / token 上限全部采用注册表值），仅当用户未给 API Key 时才追问 key。跳过 Step 1–3，直接进入 **Step 4 验证**。
   - **命中 >1 条**（如只说「mimo」同时匹配 `mimo-v2.5` 与 `mimo-v2.5-pro`）→ **单条提问**让用户二选一，避免套错兄弟模型能力。选定后走预填 + Step 4。
   - **0 命中** → 退回 **Step 1–3** 读文档全流程。注册表是加速器不是替代品。
   - **品牌级请求选型层（0 命中且用户词是品牌/家族词时）**：用户说「接 gpt4 / 接 deepseek / 接 kimi」这类品牌词 ≠ 指定模型 id，真实待决问题是三件事：哪一代、哪个档位、免费还是付费。**数据源纪律（2026-09-07 复盘确立）：官方目录优先，聚合清单只作补充**——先拉厂商官方模型目录 + 官方 deprecations 页（如 OpenAI `developers.openai.com/api/docs/models/all` 与 `/api/docs/deprecations`），再可拉 OpenRouter `GET /api/v1/models`（无 key 公开接口）作价格对照。**禁止只用聚合清单出菜单**：它是转售商二手视图，价格是转售价、生命周期状态滞后（实证：官方已定 `gpt-4` 于 2026-10-23 停服、替代 `gpt-5.6-sol`，OpenRouter 清单同日仍在正常售卖，无任何停服标注——只看聚合清单会漏掉停服倒计时）。菜单每项标 model id / 上下文 / 单价 / 免费性 / **生命周期状态**，并主动点出代际差（实证：当前代 gpt-5.6-luna $0.20/$1.20 每百万、1M 上下文，比 2023 老款 gpt-4（8k 上下文、$30/$60）便宜一个数量级且更强——不亮菜单，用户不知道「gpt4」已是过时代词）。用户选定具体 id 后再回注册表/读文档流程。**禁止把品牌词默认映射到该品牌最老的同名模型**。
4. **注册表值 ≠ 最终权威，但探针分级执行**：token 上限与多模态可能漂移或虚标，探针结果永远优于注册表，冲突时以探针为准并在交付说明点出差异。探针不是一律全量——按下方「探针分级」判级，把探针预算花在不确定的数据上。
5. **自增长**：每次成功接入并实测后，把该模型条目（含实测值与 `verifiedBy`/`confidence`）追加进注册表——**公共表收录「有公开注册渠道」的端点（direct 或 cloud-hosted 均可，带 channel/keyIssuer 字段）；private-relay 私有中转只进 local 覆盖表**，并同步维护两层文件的各自 `version` 字段。**L3 宿主下跳过自增长**（无库可写）。
6. **墓碑短路**：公共表的 `retiredModels` 数组同样参与第 3 步别名匹配；命中墓碑 → 不预填、不追问 key，直接转「免费优先发现层」，告知该模型已失效并推荐替代。墓碑条目的 `recheckAfter` 之前免复核直接判死；过了该日期用户质疑时，可实时复核一次（复活则移出墓碑并更新注册表）。**local 表条目级墓碑（`tombstone: true`）同理**：该条目因用户停用某厂商/渠道（如 2026-09-07 移除商汤 SenseNova）而不再维护——命中即不预填、不追问 key，告知「该渠道已停用（removedOn/removedBy）」并转「免费优先发现层」或读文档全流程；`removedOn` 之前的历史探针数据仅供复活复核参考，不作为当前可用性依据。

### ⚡ 探针分级（进入 Step 4 前先判级，决定跑哪些探针）

判级依据 = 条目 `confidence` + `lastVerified`（距今天数）：

| 判级 | 条件 | 必做探针 | 说明 |
|---|---|---|---|
| **轻验证** | `confidence: tested` 且 `lastVerified` ≤ 14 天 | 仅 smoke（最简文本请求 200 即过） | 注册表值在保鲜期内，默认采信，不再重测上限/多模态 |
| **全量探针** | `confidence: documented/estimate`、`lastVerified` 缺失或 >14 天、local 覆盖改过 url 或 modelId、用户主动要求 | smoke + 输出上限 + 图片（预填 true 时）+ 输入上限（两档廉价探针）+ 请求形态对齐 | 完整纪律，一项不可省 |
| **用户显式跳过** | 仅限 `tested` 条目且用户明说「跳过探针直接接」 | 无 | 交付说明必须标注「本次未探针，数据取自注册表 lastVerified 实证」 |

- 判级歧义从严：分不清轻验证还是全量 → 全量。
- **探针预算声明**：开跑前声明预计请求次数（轻验证≈1 次，全量≈3-6 次，二分另计）——免费/公测档每日配额个位数时，探针本身可能耗光当日额度，此时主动建议轻验证或跳过，别让接入动作把接入结果变不可用。
- 同会话重复接入同一模型：直接复用本会话已得探针结果，不发重复请求。
- 自增长写表时同步更新 `lastVerified`（日期）与 `probe`（结构化实测值），`verifiedBy` 保留人类可读注记。

> 设计意图：注册表消除「读文档+追问参数」的摩擦，探针消除「静态表过时/虚标」的错误。已知模型零摩擦且写入值真实；未知模型有完整兜底；私有端点与公共分发隔离。

### 0.5 宿主适配（非 WorkBuddy 宿主必做）
本 skill 以 WorkBuddy 为参考宿主（`~/.workbuddy/models.json`，字段见 Step 2）。用于其他智能体时：
1. **定位该宿主的模型配置位**：查官方文档或现有配置文件（常见：`config.json` / `settings.json` / 环境变量 / Web 后台表单）。
2. **字段映射**：把注册表/文档参数映射到该宿主的字段名（如宿主用 `contextWindow` 而非 `maxInputTokens`，用 `vision` 而非 `supportsImages`）。
3. **确认该宿主的重载方式**：改完配置后需重启/重载/热切换，向用户说明。
4. 映射不确定时单条提问，不臆测字段名。
5. **L2 表单宿主**（只有 UI 表单可填）：交付形态 = 按该宿主表单字段逐项列出的「可粘贴填写清单」，每项注明取值来源（文档/注册表/探针实测）。

### 0.6 协议失配检查（宿主协议 ≠ 端点协议时，写入配置前必做）

宿主说 Anthropic Messages 协议（如 Claude Code）而端点是 OpenAI 兼容（或反之）时，直接写入 = 宿主根本发不出能被理解的请求。**这一步在 Step 3 写配置之前执行，失配未解决禁止写配置**：

1. **判定宿主协议**：查宿主文档/现有配置——出现 `ANTHROPIC_BASE_URL`、`anthropic-version`、Messages API 字样 → Anthropic 协议；`OPENAI_BASE_URL`/`base_url` + `chat/completions` 字样 → OpenAI 兼容。判定不了 → 单条提问用户或查文档，禁止默认。
2. **失配时三选一（按序尝试）**：
   - ① **厂商原生双协议端点**：查注册表条目 `altProtocol` 字段（如 DeepSeek 的 anthropic-compatible 端点；`status: documented-未实测` 的必须先探针），或读厂商文档确认有无。有 → 按 0.5 字段映射走，⚠️ 该端点的 model id 可能与按量端点不同，必须按其文档另核。
   - ② **环境变量 shim**：宿主支持 `ANTHROPIC_BASE_URL`/`ANTHROPIC_AUTH_TOKEN` 类覆盖时，把 base_url 指向兼容端点。写环境变量属写配置，走双重确认。
   - ③ **明示不支持**：两者皆无 → 告知用户「此宿主 × 此端点组合需协议转换网关（如 CCR 类本地网关工具），本 skill 不覆盖网关搭建」，交付通用参数卡 + 网关选型建议，不硬写配置。

### 1. 读文档：提取接入必需项
- **⚠️ 本步是硬闸门不是可选项**：0 命中后必须真实读取**厂商官方接入文档**（模型目录 / API reference / deprecations 页），拿到一手参数后才准进 Step 2；聚合平台清单只能补充价格对照，不能替代官方文档成为参数来源（2026-09-07 复盘：该步曾被跳过——品牌选型用了 OpenRouter 清单后直接出了推荐，官方文档一次没读，导致漏掉官方弃用信息。反例教训：用户自己都能找到 `developers.openai.com/api/docs/models/all`，接入工程师没有理由找不到）。
- **生命周期检查（读文档必查项）**：确认目标模型在厂商官方 **deprecations 页**的状态——是否已弃用 / 停服日期 / 官方替代模型。命中已弃用或已定停服日期的模型 → **必须先向用户亮牌**（停服日期 + 官方替代），用户仍坚持才继续接入；禁止只看聚合平台还在售卖就当模型健康（实证 2026-09-07：`gpt-4` 详情页无 deprecated 字样，但官方 deprecations 页列明 2026-10-23 停服、替代 `gpt-5.6-sol`；弃用信息可能只在 deprecations 页而不在模型详情页，两页都要查）。
- 接口地址（含是否带 `/v1`）
- 鉴权方式（Header 名与格式，如 `Authorization: Bearer `）
- 模型标识（`model` 字段的真实取值，大小写敏感）
- 请求/响应格式、是否 OpenAI 兼容
- **上下文窗口 / Token 上限**：`maxInputTokens` 与 `maxOutputTokens` 的真实取值（注意单位换算：文档常写 `1M`→`1000000`、`1024K`→`1024000`、`384k`→`384000`）
- **订阅套餐 ≠ 按量付费（关键，接入前必查）**：厂商的「套餐制」（Coding Plan / Token Plan / 订阅）与「按量付费」是两套接入参数——**专属端点 + 专属 key + model id 三者都可能不同**：
  - **端点不同**：智谱 Coding Plan 用 `/api/coding/paas/v4`（不是通用 `/api/paas/v4`，用错报错/功能受限/不抵扣额度）；腾讯 Token Plan 用 `api.lkeap.cloud.tencent.com/plan/v3`；Kimi Code 订阅用 `api.kimi.com/coding/v1`（不是 `api.moonshot.cn`）
  - **key 不通用**：套餐 key 通常只对套餐端点生效（实测案例：Kimi Code 订阅 key 发到按量端点直接 401），且智谱 Coding Plan 要求用套餐概览页创建的专属 key，不能混平台普通 key
  - **model id 可能不同**：Kimi 订阅端点是 `kimi-for-coding`，而按量端点是 `kimi-k3`/`kimi-k2.7-code`——同一家族两个 id，套错按错计费方式扣费或 404
  - **计费形态不同**：订阅多为请求配额制（Kimi Code 300-1200 次/5h 滚动窗口、30 并发上限），非 token 计费；配额接近耗尽会 429
  - **接入前先问清用户持的是哪种 key**：套餐 key → 查套餐专属端点（注册表 local 表优先）；按量 key → 通用端点。分不清就问，禁止默认按量端点
- **协议族与双协议端点**：确认文档提供的协议（openai-compatible / anthropic-compatible / 其他），与宿主协议（0.6）对照；厂商同时提供双协议端点时记入注册表条目 `altProtocol`（标 `documented-未实测`），供协议失配时选用
- **能力矩阵（关键，逐项确认，以文档原文为准，不得假设）**：
  - 图片输入（vision / image input）
  - 图片/多模态**输出**（image generation / multimodal output）
  - 工具调用（function calling / tool_call）
  - 推理（reasoning / thinking）
  - ⚠️ **能力矩阵必须按「你要接入的【确切模型名】」核验，禁止从同系列兄弟模型推断**：文档的多模态/能力示例常使用另一个模型名（实测案例：用户要接 `mimo-v2.5-pro`，但多模态文档示例写的是 `mimo-v2.5`；前者实测 + 图片返回 404 `No endpoints found that support image input`，后者返回 200）。`mimo-v2.5-pro` 是纯文本 Agent 旗舰，多模态只在 `mimo-v2.5` 上。两模型 API Key/地址相同，仅模型名不同——套用兄弟模型能力会直接错配。

### 2. 找配置位
定位**当前宿主**存放模型配置的地方（WorkBuddy 为 `~/.workbuddy/models.json`；其他智能体先走第 0.5 步适配），确认它支持的字段，典型字段：
`id` `name` `vendor` `url` `apiKey` `supportsToolCall` `supportsImages` `supportsReasoning` `maxInputTokens` `maxOutputTokens` `useCustomProtocol`

### 3. 填配置
严格按文档示例把字段写进去，特别注意：
- URL 末尾 `/v1`、Bearer 前缀、模型名大小写与文档**完全一致**
- **`id` 字段 = API 请求的 `model` 字段，宿主原样透传（中转站失败的头号根因）**：WorkBuddy 源码坐实——`stripCustomLocalModelPrefix` 只剥 `custom-local:` 前缀（`slice(13)`），**不剥 `vendor/` 前缀**，id 其余部分**原样**成为发给 API 的 `model`。所以 `id` 必须**精确等于**中转站要求的 model 字符串（大小写、含/不含厂商前缀一字不差）。⚠️ **中转站 model 命名规范不统一**：腾讯 Token Plan 要 `deepseek/deepseek-v4-pro-0813`（带 `deepseek/` 前缀），商汤 SenseNova 要 `deepseek-v4-flash`（平铺不带前缀）。**禁止自行给 id 加 `vendor/` 前缀**——写 `sensenova/deepseek-v4-flash` 会让宿主把整串当 model 发出，中转站返 400/404。id 取值以 Step 1 文档原文或 Step 4 探针为准；注册表的 `modelId` 字段才是要写进宿主 `id` 的值，别误用注册表的「id 键」（可能带 vendor 前缀）。
- **`vendor` 是元数据标签，不参与请求路由**（2026-08-20 源码实证，推翻此前"vendor 填错导致调用失败"的误诊）：WorkBuddy 对自定义模型用 `url` + `apiKey` + `id`（→model）三个字段路由，`normalizeCustomModel` 只做「加 custom 标签 + 解析环境变量」，请求构造中无任何 vendor 路由逻辑；此前在 app.asar 里用 strings 搜到的 `getVendorPrefix` 实为 css-tree 的 CSS 前缀代码，与模型无关（误读）。vendor 仅用于 UI 分组展示。故 vendor 填「提供 url 的一方」（中转站如 `sensenova`）只为分组合理，**填成原厂名不会导致调用失败**——把失败归因于 vendor 是误诊，真因是上方 id 前缀透传 + 下方「配置持久性」。建议 vendor 仍填「提供 url 的一方」保持分组正确，但别指望它决定成败。
- **能力字段如实填写**（本 skill 对原始版本的修复点）：
  - 文档**未声明**支持图片输出 → `supportsImages: false`，绝不为了"显得支持"而误填 `true`
  - 同理适用于工具调用、推理等能力字段
  - 原始版本未探测多模态能力，曾导致接入"不支持多模态输出"的模型时被错误启用图片能力
- **Token 上限（本 skill 第二次修复点，2026-08-17 确立）**：
  - 文档**已给出** → 严格按文档换算为纯数字填写。**但文档给出的上限可能是虚标**（实测案例：MiniMax M3 文档写"最大输出 1M"，实际 API 只接受到 524288/512K，超出即 400 `Invalid request parameters`）。所以填完后必须走下方"Step 4 上限实测"校验，被拒就下调到实测可用值并标注。
  - 文档**未给出** → **绝不允许静默臆测一个偏小的数字当事实写死**（实测曾默认填 `128000 / 8000`，而模型实际支持 `1024000 / 384000`，差 8 倍，直接把上下文窗口压短）。必须二选一：
    ① 用**单条提问**向用户索取正确上限；
    ② 若用户也未知，填入一个基于模型家族公开范围的**估值**，并在交付说明里**高亮标注"Token 上限为待核实估值，很可能需上调"**，不得伪装成已确认值。
- **配置持久性（写后必验，区分两种场景）**：WorkBuddy 对 models.json 有 file watcher（`watchFile`→`debounceSync`→`sync`），**运行中改文件会被立即重新加载**（2026-08-20 实测：改文件后 CLI `--model` 列表即刻出现 `custom-local:<id>` 新条目，且实际调用成功）。但**在 UI 模型选择器保存、或 WorkBuddy 重启时，宿主会用内存配置重写文件**，未被正确加载的条目会丢失（2026-08-20 case 观察：带 `sensenova/` 前缀的 id 条目被回滚消失——注意那次 id 本就写错，可能是被 SmartMerge 过滤而非单纯回滚）。写完配置必须：① 立即读回校验写入成功；② 用下方「宿主层验证」确认宿主真的加载（而非只落盘）；③ 若条目消失 → 改走**宿主 UI 注入**（设置→模型→添加自定义模型），观察 UI 生成的 `id`/`vendor` 格式照抄。**只写文件不验宿主加载 = 虚假的"接入完成"。** 注意 models.json 有**两级**：用户级 `~/.workbuddy/models.json` 与项目级 `<workspace>/.workbuddy/models.json`，先确认目标宿主读的是哪一级。

### 4. 做验证
先按 Step 0「探针分级」判级决定探针范围；scriptCapable 宿主用 `scripts/probe.py`（`smoke`/`output-limit`/`image`/`tool`/`input-limit`/`context-metadata` 子命令，结果单行 JSON，429 退避与域名门禁内建），手动流程按下述等价步骤。

**错误码决策表（探针失败先查表再行动，禁止自由发挥）**：

| 状态码 | 含义 | 动作 |
|---|---|---|
| 401/403 | key 无效或 key↔端点不配对 | 先查「订阅套餐≠按量付费」配对（最便宜假设），再查 key 本身 |
| 404 model not found | 模型下架或 id 写错 | 先核 id 精确串；id 无误 → 查墓碑 → 转免费发现层推荐替代 |
| 400 | 参数被拒 | 上限/参数问题：走上限二分或核对请求体字段 |
| 429 | 限流（非拒绝） | 退避重试，见下方硬规则；**禁止计入上限边界** |
| 5xx | 端点故障 | 声明后重试一次，仍 5xx → 交付「端点侧故障，接入暂停」 |

- **宿主层验证（API 能通 ≠ 宿主能通，必做）**：「基础验证」是你自己用 curl/脚本直打 API，只证明 url/model/key 本身对。但中转站失败常发生在**宿主实际发出请求**这一层——id 前缀透传（`sensenova/deepseek-v4-flash` 被整串当 model 发出）、配置被回滚、vendor 误配。因此必须在宿主里**实际选该模型发一次请求**，从宿主日志/实际请求反推「实际发出的 `model` 字符串」与 HTTP 状态码，确认它等于 API 要求的精确值。**WorkBuddy 有两条快速验证手段**：① `codebuddy --help` 看 `--model` 选项列表——已加载的自定义模型会以 `custom-local:<id>` 形态列出（新条目出现 = 宿主已加载）；② 直接跑 `codebuddy -p -y --model custom-local:<id> "测试提示词"` 做真实请求，返回正常即链路打通。日志位置（WorkBuddy/macOS）：`~/Library/Logs/WorkBuddy/main.log`、`renderer.log`，或 `~/.workbuddy/traces/*/trace_*.json`（搜模型名/端点/返回的 error body）；其他宿主/其他 OS 的日志路径以该宿主文档为准，禁止臆测路径。**拿真错，别从字段名猜。**
- **基础验证**：用该模型发一条最简文本请求（如「你好」），确认能正常返回。
- **上限实测（全量判级必做；轻验证判级默认采信注册表值，防文档虚标导致发起会话 400）**：先验注册表/文档值——发一条 `max_tokens` = 所填 `maxOutputTokens` 的请求，200 即收工（1 次调用）。若返回 400 `Invalid request parameters`，说明上限虚标——二分探测真实可用边界（524288/512K 这类 2 的幂常为硬上限）。**二分纪律**：轮次上限 8 轮，未收敛则取已验证值并在交付说明标注区间；**429 退避硬规则：2s→8s→30s 三次仍 429 即熔断**，该模型上限标注「限流阻断未定界（日期）」，禁止继续打、禁止记假边界。⚠️ **区分 429 与 400**：中转站（如 SenseNova Free 公测）配额极紧，探测会间歇性返 `429 Workspace allocated quota exceeded`（限流非拒绝），只有 400 才计为真上限拒绝——曾因把 429 误判为 max_tokens 拒绝得到假边界（304625 而非真实 384000）。实测出更小边界就把 `maxOutputTokens` 改为实测值，并在交付里说明"文档标称 X 但 API 实际仅接受 Y"。**此步不做（全量判级时），用户选该模型发起会话时会直接失败。**
- **输入上限探针（两档廉价探针，按序做；全量判级时执行）**：
  1. **元数据白拿**：OpenRouter 系模型直接读 `/api/v1/models` 的 `context_length`（免费无 key，可跑 `probe.py context-metadata`）；**Moonshot 系模型同走此路**——`GET https://api.moonshot.cn/v1/models` 返回每模型的 `context_length`/`supports_image_in`/`supports_video_in`/`supports_reasoning` 能力位（2026-09-07 官方文档核实），一次请求同时核上限与多模态，能力字段预填值可直接对表纠偏；其他厂商的文档明示值按 documented 采信。
  2. **错误体披露**：发一条超长 filler prompt（约目标上限 ×1.05，先声明 token 成本再执行；`probe.py input-limit` 对 >50k pad 要求 `--confirm`），多数端点 400 错误体会自报真实上限（如 `maximum context length is 131072 tokens`），从错误体提取数字。
  ⚠️ **合计口径陷阱**：部分端点报的是「输入+输出合并」总额（实测案例：OpenRouter `minimax/minimax-m3:free` 报错披露 1048576 为输入+输出合计）——此时 `maxInputTokens` 与 `maxOutputTokens` 必须按合计口径保守拆分，不可两字段都填满总额。两档都做不了（成本/限流）→ 维持估值并在交付说明高亮。
- **多模态验证（预填 true 或用户声称支持时必做；预填 false 时按需复验）**：用该【确切模型名】发一条带图片的请求验证（图片 URL 或 base64 均可）。判定规则：
  - 返回 **200** 且正常理解图片 → `supportsImages: true`。
  - 返回 **404** `No endpoints found that support image input`（或类似）→ 该确切模型**不支持**图片输入，`supportsImages: false`，**即使同系列兄弟模型（如 `mimo-v2.5`）支持也不能套用**。
  - ⚠️ 注册表快路径预填 `supportsImages: true` 的，图片探针**从"按需"升级为"必做"**——注册表值可能随厂商漂移，不实测即写入 = 把能力错配风险原样传给宿主（mimo 踩坑的镜像场景）。文档未声明支持图片输出时，跳过图片输出验证。
  - ⚠️ 不要因为「用户说支持多模态」或「兄弟模型文档说支持」就跳过实测或反填 `true`；这两类来源都可能与你接入的确切模型名不符（见 Step 1 的 mimo 案例）。
- **请求形态对齐验证（全量判级必做；轻验证可跳过）**：探针的裸「你好」≠宿主真实请求。全量探针最后一轮用**与宿主相同的形态**发一条：`stream: true` + 一个哑工具定义（如 get_weather，可跑 `probe.py smoke --stream` 与 `probe.py tool`）。它暴露纯文本探针测不出的问题：① 端点要求流式才有正常响应；② tool_calls 响应格式方言；③ reasoning 参数方言（`reasoning_content` / `reasoning` / `thinking` 各家不一）。方言例外记入注册表条目 `quirks` 数组，写入宿主配置时检查宿主能否表达该参数，不能则在交付说明声明行为差异（如「思考模式强制开启，宿主的关闭开关会无效」）。
- **交付说明**：明确告知用户——
  - 实际接入的模型名、vendor、接口地址
  - 能力矩阵（图片输入 / 图片输出 / 工具调用 / 推理 的支持情况）
  - **Token 上限（`maxInputTokens` / `maxOutputTokens`）的取值来源**：是文档确认值、用户给定值、注册表历史实证值、探针实测值，还是待核实估值？同时注明本次探针判级（轻验证/全量/用户跳过）与输入上限探针来源（元数据/错误体披露/未探）。**输入上限若未过两档探针，必须在交付说明里单独声明其来源与局限**（如"输入上限来自注册表历史实证，未经本次实时校验，厂商若缩窗会在长会话时报 400"）。若为估值必须高亮提示"很可能需上调"。
  - 任何与用户预期不符的点，例如：*"你提供的模型不支持多模态输出，已按文档如实将 `supportsImages` 置为 false，不会误启用图片能力。"* 若为 L3 降级交付，还须注明：探针是否已执行（未执行的项，参数卡对应字段标注「待探针」），以及该参数卡适用于哪个目标宿主。

## 关键约束
- **注册表是假设，探针是真相，分级是纪律**：从注册表预填的值仅作起点；输出上限与多模态（预填 true 时）的最终取值以 Step 4 实时探针为准，冲突时修正并在交付说明告知用户。探针按「探针分级」执行——不确定的数据必须全量探，保鲜期内的 tested 数据轻验证即可，探针预算不为已确定性买单。输入上限走两档廉价探针（元数据/错误体披露），做不到才声明来源。
- **带 key 只发白名单域名**：发真实 key 前核对 url 域名 ∈ `trustedDomains[vendor]`（local 中转条目须用户已确认）；白名单外升级双重确认。公共表加条目必须同步补域名+来源。
- **429 退避熔断**：2s→8s→30s 三次仍 429 即熔断，标注「限流阻断未定界」；429 永不计入上限边界，也不因限流得出能力否定结论。
- **协议失配不硬写**：宿主协议 ≠ 端点协议时按 0.6 三选一（双协议端点 / env shim / 明示不支持转参数卡），禁止把 OpenAI 兼容端点硬写进 Anthropic 协议宿主。
- **双层注册表隔离**：公共表只收官方公开端点；私有端点（中转/订阅专属）只进 local 覆盖表；**分发打包时必须排除 models_registry.local.json**。
- **免费信息有时效**：任何「免费」结论必须来自本次会话的实时查询（OpenRouter pricing 字段 / 厂商官网现状），禁止把注册表历史值当「当前仍免费」引用；条目的 `freeTier` 只是最近一次核验记录，距 `freeTierCheckedOn` 较久即视为存疑。模型下架后移入公共表 `retiredModels` 墓碑而非直接删除，保留曾经验证的参数供同类渠道复用与溯源。
- **id 原样透传**：宿主只剥 `custom-local:` 前缀，`vendor/` 前缀与其余字符原样成为 API 的 `model` 字段。id 必须精确等于 API 要求的 model 字符串（含大小写、含/不含厂商前缀），禁止自行加 vendor 前缀——这是中转站接入失败的头号根因。
- **写配置必验宿主加载**：只写文件 ≠ 接入成功——宿主在 UI 保存/重启时可能重写 models.json，未正确加载的条目会丢。必须读回校验 + 宿主层验证（CLI `--model` 列表 / 实际请求）；条目失效则走宿主 UI 注入，绝不把「写文件」当成交付终点。
- **不臆测能力**：任何能力字段必须以文档原文或实测为唯一依据。
- **不臆测 Token 上限**：`maxInputTokens` / `maxOutputTokens` 必须来自文档或用户明确给定；缺失时按上文"Token 上限"规则处理，**绝不把猜测的小值当事实静默写入**（这会悄悄把模型的真实上下文窗口压短）。
- **不静默降级也不静默升级**：能力与实际不符、或 Token 上限为估值时，在交付说明里明确点出，让用户知情。
- **用户零手动操作**：配置文件、字段、验证全部由你完成；只在文档缺失关键信息时，才用单条提问向用户索取，绝不要求用户自己改配置。

