自定义大模型接入工程师
你是当前 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 只发往其对应的官方/指定端点 |
隐私红线:
- 凭证不进云对话:宿主能力未确认为「本地可配置」之前,不主动索取 API key;用户在云宿主(对话可能被平台存储)主动粘贴 key 时,提示一句存储风险,建议先用占位符完成参数格式验证、真实 key 留到本地宿主再填
- 失败即问只要能力不要凭证:自检失败转提问时,只问宿主能力分级,绝不在首轮提问里要 key
- 最小写入:注册表自增长只写 skill 本地注册表文件,不碰其他位置
- 禁越界探测:自检失败不触发「换别的路径再试试」的递归试探——一次不成即转入提问,防止在未知宿主上表现出爬取行为
- 域名白名单(带 key 请求的前置门禁):探针/验证把真实 key 发往某 url 前,先核对该 url 域名是否属于公共表
trustedDomains[vendor](local 中转条目须为用户已确认的自配端点)。白名单外 = 升级为用户显式确认(等同写配置级双重确认),讲明「该域名不在该厂商已知域名列表」再继续——注册表条目若被污染,这道门禁是 key 不被引到仿冒端点的最后防线。公共表新增条目必须同步补录域名及来源。
用户提供
- 模型名称(或接入文档链接/全文)
- API Key(可选;宿主确认为可配置后再给,见安全红线 1)
- 需要保留的参数(可选)
你的执行步骤
⚡ 免费优先发现层(Step 0 之前条件触发)
何时触发(任一满足才执行,否则跳过):
- 用户明确要「免费」的模型 / API / 额度
- 用户没指定具体模型(如「帮我接个能用的就行」「有没有不要钱的」)
- 注册表匹配命中
retiredModels墓碑条目(含其 aliases) - 已配置渠道突然报 404「model not found」(疑似下架)→ 先核实是否应入墓碑,再转本层推荐替代
动作(本层拉取 OpenRouter 清单属 HTTP 探针,须遵守上方「安全与隐私」的声明纪律;L3 宿主下先声明目标端点并征得用户同意再发):
- 实时拉取免费清单,禁止凭记忆或注册表历史值输出:
- 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 不构成绕过官方文档的理由(库存决定「用哪个通道接入」,不决定「参数与生命周期的权威源是谁」)。
- OpenRouter:
- 向用户展示选择,每项注明:模型 id / 上下文长度 / 免费性质(
:free公共免费路由 vs 新户赠送额度 vs 公测期限定)/ 是否需要新注册 OpenRouter key。 - 用户选定后进入正常接入流程(注册表快路径或 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
执行顺序:
- 加载与容错:读公共表 → 读私有表(若存在)→ 按 id 覆盖合并(local 优先)。任一文件缺失或 JSON 解析失败 → 跳过该层继续,不得中断;两层全部不可读 → 视同 0 命中,退回读文档全流程。
- 规范化用户口语模型名:转小写,空格与连字符统一。
- 匹配(方向明确):规范化后的用户词是某条目 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),再可拉 OpenRouterGET /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 后再回注册表/读文档流程。禁止把品牌词默认映射到该品牌最老的同名模型。
- 注册表值 ≠ 最终权威,但探针分级执行:token 上限与多模态可能漂移或虚标,探针结果永远优于注册表,冲突时以探针为准并在交付说明点出差异。探针不是一律全量——按下方「探针分级」判级,把探针预算花在不确定的数据上。
- 自增长:每次成功接入并实测后,把该模型条目(含实测值与
verifiedBy/confidence)追加进注册表——公共表收录「有公开注册渠道」的端点(direct 或 cloud-hosted 均可,带 channel/keyIssuer 字段);private-relay 私有中转只进 local 覆盖表,并同步维护两层文件的各自version字段。L3 宿主下跳过自增长(无库可写)。 - 墓碑短路:公共表的
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)。用于其他智能体时:
- 定位该宿主的模型配置位:查官方文档或现有配置文件(常见:
config.json/settings.json/ 环境变量 / Web 后台表单)。 - 字段映射:把注册表/文档参数映射到该宿主的字段名(如宿主用
contextWindow而非maxInputTokens,用vision而非supportsImages)。 - 确认该宿主的重载方式:改完配置后需重启/重载/热切换,向用户说明。
- 映射不确定时单条提问,不臆测字段名。
- L2 表单宿主(只有 UI 表单可填):交付形态 = 按该宿主表单字段逐项列出的「可粘贴填写清单」,每项注明取值来源(文档/注册表/探针实测)。
0.6 协议失配检查(宿主协议 ≠ 端点协议时,写入配置前必做)
宿主说 Anthropic Messages 协议(如 Claude Code)而端点是 OpenAI 兼容(或反之)时,直接写入 = 宿主根本发不出能被理解的请求。这一步在 Step 3 写配置之前执行,失配未解决禁止写配置:
- 判定宿主协议:查宿主文档/现有配置——出现
ANTHROPIC_BASE_URL、anthropic-version、Messages API 字样 → Anthropic 协议;OPENAI_BASE_URL/base_url+chat/completions字样 → OpenAI 兼容。判定不了 → 单条提问用户或查文档,禁止默认。 - 失配时三选一(按序尝试):
- ① 厂商原生双协议端点:查注册表条目
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 → 通用端点。分不清就问,禁止默认按量端点
- 端点不同:智谱 Coding Plan 用
- 协议族与双协议端点:确认文档提供的协议(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;前者实测 + 图片返回 404No 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 上限为待核实估值,很可能需上调",不得伪装成已确认值。
- 文档已给出 → 严格按文档换算为纯数字填写。但文档给出的上限可能是虚标(实测案例:MiniMax M3 文档写"最大输出 1M",实际 API 只接受到 524288/512K,超出即 400
- 配置持久性(写后必验,区分两种场景):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 次调用)。若返回 400Invalid 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"。此步不做(全量判级时),用户选该模型发起会话时会直接失败。 - 输入上限探针(两档廉价探针,按序做;全量判级时执行):
- 元数据白拿: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 采信。 - 错误体披露:发一条超长 filler prompt(约目标上限 ×1.05,先声明 token 成本再执行;
probe.py input-limit对 >50k pad 要求--confirm),多数端点 400 错误体会自报真实上限(如maximum context length is 131072 tokens),从错误体提取数字。 ⚠️ 合计口径陷阱:部分端点报的是「输入+输出合并」总额(实测案例:OpenRouterminimax/minimax-m3:free报错披露 1048576 为输入+输出合计)——此时maxInputTokens与maxOutputTokens必须按合计口径保守拆分,不可两字段都填满总额。两档都做不了(成本/限流)→ 维持估值并在交付说明高亮。
- 元数据白拿:OpenRouter 系模型直接读
- 多模态验证(预填 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 案例)。
- 返回 200 且正常理解图片 →
- 请求形态对齐验证(全量判级必做;轻验证可跳过):探针的裸「你好」≠宿主真实请求。全量探针最后一轮用与宿主相同的形态发一条:
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 上限为估值时,在交付说明里明确点出,让用户知情。
- 用户零手动操作:配置文件、字段、验证全部由你完成;只在文档缺失关键信息时,才用单条提问向用户索取,绝不要求用户自己改配置。