WorkBuddy LiteLLM Router Skill
用途
在 Windows 上跑一个 LiteLLM Proxy(http://127.0.0.1:4000/v1),把 10 个免费后端 provider(HF Router / Mistral / Cloudflare / Agnes / 微信 AI / LongCat / ModelScope / OpenRouter免费层 / OpenAI / NVIDIA NIM)聚合成一个 master key,对外只暴露 12 个模型给 WorkBuddy。同一大模型在多个后端间自动 fallback。OpenRouter 在此仅用其 :free 免费层(如 nemotron/gemma/gpt-oss 的免费 slug),不接入任何付费 slug。
> 📦 已发布到 SkillHub 市场(slug: workbuddy-litellm-router,状态 pending_review)。后续更新:skillhub publish C:/Users/31268/.workbuddy/skills/workbuddy-litellm-router --changelog "..."(token 已保存在 ~/.skillhub/credentials.json)。
核心架构(必须先理解)
fallback 实现
同一 model_name 在 litellm_config.yaml 的 model_list 下配多条 litellm_params,LiteLLM 调用失败时自动切到下一个后端。WorkBuddy 侧只看到 1 个模型名。
⚠️ LiteLLM fallback 的硬限制(最重要的坑)
_should_retry(litellm/utils.py:6412)只在以下状态码重试/切换:408 / 409 / 429 / ≥500。
| 状态码 | 含义 | 触发 fallback? |
|---|---|---|
| 429 | 限流 | ✅ 是 |
| 500/502/503 | provider 挂 | ✅ 是 |
| 408/409 | 超时/冲突 | ✅ 是 |
| 400 | 请求体非法 | ❌ 否,立即失败 |
| 402 | 额度耗尽 | ❌ 否,立即失败 |
| 403/404 | 无权限/模型名错 | ❌ 否 |
推论:fallback 链救不了 400 和 402。这两个必须靠下面的两条策略根除/稀释。
两条结构性短板的应对策略
策略 A:能力标志位对齐防 400(根除)
WorkBuddy 的 ~/.workbuddy/models.json 里每个模型有 supportsImages / supportsReasoning / supportsToolCall 三个布尔标志。标志位错了 → WorkBuddy 发错请求体 → 后端 400 → LiteLLM 不 fallback → 直接失败。
已踩过的两个 400 根因(务必记住):
1. duplicate field reasoning_content:模型标 supportsReasoning:true → WorkBuddy 把 reasoning_content 塞进 assistant 历史并走流式 → 后端反序列化报重复字段。
- 修复:deepseek-v4-pro、gpt-oss-120b 这类模型设 supportsReasoning:false。
2. Multimodal not supported for model: zai-org/GLM-5.2:模型标 supportsImages:true 但 HF 的 GLM-5.2 不支持多模态 → 400。
- 修复:glm-5.2 设 supportsImages:false。视觉需求走 mistral-medium-3.5 或 llama-4-scout-17b(这两个 supportsImages:true 实测通过)。
判断规则:
- supportsReasoning:true 仅当模型确实是 reasoning 模型 且 后端能正确接收 reasoning_content 历史字段(非流式 OK,流式可能 400)。不确定就先 false,实测通过再开。
- supportsImages:true 仅当模型确实多模态 且 当前 fallback 链里每个后端都支持多模态(链里只要有一个不支持,整条链都会 400)。
- drop_params: true(litellm_settings 里)会丢弃后端不支持的参数,但不能救"WorkBuddy 主动加的 reasoning_content 历史字段"。
如何把 reasoning 重新打开(例:DeepSeek V4 Pro 的 MAX 思考)—— 必须经付费后端,免费层做不到:免费后端 HF/MS/微信 在请求体里拒绝 reasoning_content → 400,所以默认关。要开,必须把主后端换成原生支持 reasoning_content 的 provider——DeepSeek 官方 api.deepseek.com(需 DEEPSEEK_API_KEY,原生接受 reasoning_effort:"max")或 OpenRouter 付费 slug(已有 token)。⚠️ 已实测验证:OpenRouter 的 deepseek/deepseek-v4-pro-0813:free 免费 slug 不存在(真实调用返回 HTTP 404,官网模型页虽有 "Free" 占位但 /api/v1/models 列表里只有付费版)——市面宣称"免费 V4 Pro API"基本是误导,不存在免费 MAX 思考。所以:免费形态只能是标准 V4 Pro(不开思考)走 ModelScope→HF;MAX 思考只能走付费(DeepSeek 官方或 OpenRouter 付费 slug),启用时建议单独开一个 *-max 模型别名(如 deepseek-v4-pro-max),后端只放付费推理原生 provider,并在 LiteLLM 层强制 reasoning_effort:"xhigh"。注意:OpenRouter 不接受字符串 "max",只接受 xhigh(OpenRouter 把 xhigh 映射为 max 思考),传 "max" 会 400 Invalid option;只有 DeepSeek 官方 API 才接受 "max"。
策略 B:多 provider 冗余稀释 402(缓解,非根治)
402 = HF 免费 Inference Providers 月度额度耗尽,HF 后端直接失败且 LiteLLM 不降级。唯一缓解方式:让 HF 不是唯一后端。
当前 V6 的冗余设计(全免费链路;HF 全部后置,避免 402 直接打挂;不接任何付费 slug):
- qwen3.5-397b:ModelScope → HF(MS 免费可靠作 PRIMARY;支持 reasoning)
- qwen3-coder-30b:ModelScope → HF(MS 免费可靠作 PRIMARY)
- deepseek-v4-flash:HF → ModelScope → 微信 AI(三后端,最稳)
- glm-5.2:Mistral → 微信 AI → NVIDIA NIM → HF(四后端;Mistral 为 Mistral 平台托管的 Z.ai GLM-5.2,文本模型非多模态)
- gpt-oss-120b:NVIDIA NIM → Cloudflare → HF(三后端;NIM 429 会 fallback,CF 免费层)
- deepseek-v4-pro:ModelScope → HF(两后端;不开启 reasoning,因免费后端拒绝 reasoning_content → 400;MAX 思考需另走付费别名,见上)
- OpenRouter 仅保留其 :free 免费层模型:nemotron-ultra-550b(nvidia/nemotron-3-ultra-550b-a55b:free)、or-gemma-4-31b、or-gpt-oss-20b——均为免费,无付费成本。
模型一致性铁律:fallback 链里只能放同一种模型。例如微信 AI 只提供 DeepSeek-V4-Flash,能进 deepseek-v4-flash 链,但不能进 deepseek-v4-pro 链(模型不同会返回不一致结果)。
402 仍可能发生的场景:HF 和 MS 同时额度耗尽(qwen 系列),或 HF 单后端模型(如 kimi-k2.7)。此时只能:客户端重试 / 等月初恢复 / 给该模型加更多 provider。
文件清单(部署包)
| 文件 | 作用 | 部署处理 |
|---|---|---|
litellm_config.yaml |
路由配置(模型列表 + provider 映射 + 超时/重试) | 原样复制 |
launcher_litellm.py |
无窗口启动器(加载 .env、强制 UTF-8、日志轮转、端口幂等) | 原样复制 |
register_autostart.ps1 |
注册/取消 Windows 计划任务(跨机器可移植) | 原样复制 |
models.json |
WorkBuddy 自定义模型清单(12 个) | 复制到目标机 ~/.workbuddy/models.json |
.env.litellm |
所有 API key(明文,勿提交 git) | 换成目标机自己的 key |
start_litellm_router.ps1/.bat |
手动前台启动(可选,路径已可移植) | 可选 |
test_custom_models.py |
端到端冒烟测试 | 可选 |
test_custom_models_comprehensive.py |
稳定性压测(3× 文本/视觉/推理历史) | 可选 |
跨机器部署 6 步
-
前置:目标机 Windows 10/11 64 位 + WorkBuddy(自带 managed Python venv
%USERPROFILE%\.workbuddy\binaries\python\envs\default)。装依赖:
powershell & "$env:USERPROFILE\.workbuddy\binaries\python\envs\default\Scripts\python.exe" -m pip install "litellm==1.97.0" "fastapi>=0.115.5,<0.117"
> 版本锁死 1.97.0;fastapi 必须>=0.115.5否则get_flat_dependantImportError。 -
拷贝项目文件夹(至少
litellm_config.yaml+launcher_litellm.py+register_autostart.ps1)到目标机,如D:\litellm-router\。 -
配置
.env.litellm:换成目标机自己的 key(HF / Mistral / Cloudflare / 微信 / ModelScope / NVIDIA / OpenRouter / Agnes / LongCat 各自注册)。变量名必须与litellm_config.yaml的os.environ/XXX对齐:
LITELLM_MASTER_KEY HF_TOKEN MISTRAL_TOKEN CF_TOKEN CF_ACCOUNT_ID AGNES_TOKEN WECHAT_AI_TOKEN LONGCAT_TOKEN MODELSCOPE_TOKEN OPENROUTER_TOKEN OPENAI_TOKEN NVIDIA_TOKEN -
安装 WorkBuddy 模型清单:把
models.json复制到C:\Users\<目标用户名>\.workbuddy\models.json(覆盖),重启 WorkBuddy。 -
注册开机自启(目标机 pwsh 7):
powershell pwsh -File register_autostart.ps1 # 专用 venv:pwsh -File register_autostart.ps1 -PythonW "D:\litellm-router\venv\Scripts\pythonw.exe"
任务名LiteLLM Router Autostart,触发=登录时,无窗口,幂等。 -
验证:
powershell schtasks /run /tn "LiteLLM Router Autostart" (Invoke-WebRequest -Uri 'http://127.0.0.1:4000/health/liveliness' -TimeoutSec 5).Content # 应返回 "I'm alive!"
取消自启:pwsh -File register_autostart.ps1 -Unregister
关键配置参数(litellm_settings)
litellm_settings:
num_retries: 1 # 超时/连接错误只重试 1 次,加快 fallback
request_timeout: 60 # 单后端最大等待 60s,防 hung 后端拖 2 分钟
drop_params: true # 丢弃后端不支持的参数,避免 400
telemetry: false
router_settings:
routing_strategy: usage-based-routing-v2
enable_pre_call_checks: false
cooldown_time: 60 # 后端失败后冷却 60s,期间不再打它
故障排查速查
| 现象 | 根因 | 修复 |
|---|---|---|
duplicate field reasoning_content |
supportsReasoning:true + 流式历史 |
模型 supportsReasoning 设 false |
Multimodal not supported |
supportsImages:true 但后端不支持 |
模型 supportsImages 设 false |
402 depleted monthly credits |
HF 免费额度耗尽,不 fallback | 多 provider 冗余 + 客户端重试 |
429 concurrent limit exceeded |
免费层限流 | LiteLLM 自动重试切后端,等几秒 |
FileExistsError (tiktoken) |
sandbox 写入失败 | launcher 已用 CUSTOM_TIKTOKEN_CACHE_DIR 重定向 |
ImportError: get_flat_dependant |
fastapi 版本不符 | pip install 'fastapi>=0.115.5,<0.117' |
| 端口 4000 被占 | 重复启动 | Get-NetTCPConnection -LocalPort 4000 查 PID 后 Stop-Process |
| 日志过大 | 长期运行 | launcher 已内置 10MB×3 轮转 |
升级路径
- 跨设备/外网:Cloudflare Tunnel(
cloudflared tunnel --url http://localhost:4000),暴露公网前务必改LITELLM_MASTER_KEY为强随机串。 - 成本追踪/虚拟 key:装 PostgreSQL,
general_settings.database_url,可给不同平台发独立 key + 月度预算。 - 加新模型:编辑
litellm_config.yaml的model_list+models.json,重启路由即可,无需改代码。
不要做的事
- 不要把
.env.litellm提交 git(明文密钥)。 - 不要在 fallback 链里放不同模型(模型一致性铁律)。
- 不要给所有模型无脑开
supportsReasoning:true/supportsImages:true(会触发 400 且不 fallback)。 - 不要升级 litellm 到非 1.97.0 除非已验证兼容(breaking change 常见)。