README 从 642 行降到 131 行,只保留简介、核心能力、最小快速开始、文档索引、 改名说明、目录结构与测试入口。其余内容迁到 docs/,中英文索引一一对应: docs/features.md 实现与完整特性清单(原 README 的"实现/特性") docs/configuration.md 配置文件、自动探测模型、多密钥与配额 docs/routing.md 模型路由、AUTO 链、冷却与半冷却、源/适配器、源模板 docs/performance.md 实测内存占用与调优、thinking 预算 docs/webui.md WebUI 使用 docs/adapters.md Lua 适配器协议与内置适配器清单 docs/gui.md 桌面端 Headless/Desktop 两种产物与构建 docs/development.md 目录职责、开发协作、测试 docs/deployment.md 部署、发行包、目录权限、llmsproxy 迁移与验证清单 中性化:README / README_EN / GUI 注释里的 `Clash Verge` 类比全部改为 「桌面壳 / desktop shell」,仓库内(源码与文档)已无该表述。
6.7 KiB
路由与调度
本文档由 README 拆分而来:模型路由、AUTO 链、冷却与半冷却、源与适配器、源模板。 返回 README。
模型路由
/v1/chat/completions 的 model 解析顺序:
source/model或source:model前缀 → 指定源;- 精确匹配某个源的
model; - 配置
default_model: AUTO时 → 走优先级页保存的 AUTO 链(见下); - 否则回落(返回错误)。
任何 OpenAI 客户端,只要 model 设为某个源的 name/任意名,即可锁定走该源;
设为 AUTO(或网关配了 default_model: AUTO)即自动按优先级链选源。
AUTO 链(优先级页)
AUTO 调度只由优先级页保存的规则决定,源配置里的 models[].priority 数字不再
参与调度、也不再显示。聊天与生图是两套独立的链,在优先级页面通过「聊天 / 生图」
按钮切换编辑:
- 聊天链(
runtime_file的auto字段):服务POST /v1/chat/completions的model: AUTO请求;只接受kind: chat模型,配置在链里的生图槽位会被忽略。 - 生图链(
runtime_file的auto_image字段):服务POST /v1/images/generations的model: AUTO请求;只接受kind: image模型。未配置生图链时,AUTO 生图回落 到「所有含生图模型的源、按注册顺序」的旧行为,兼容旧配置。
每条规则 = 一个「槽位」:{ model, source, tier, token_quota, period, hours }。
tier表示优先级档位:同一档的模型并排、共享该优先级;档位从上到下递减。- 同一模型可配置多个槽位(如 A 源低配 → B 源低配 → A 源高配 → B 源高配),按档位顺延。
token_quota> 0 时该槽位在重置周期内用满即顺延到下一槽位;period支持hour/week/month/nhour(配合hours),空 = 不限。- 同 tier 内按「偏好分」排序,失败槽位冷却后自动跳过;冷却 / 配额耗尽 / hard 错误会顺延到下一 tier,全部失败时返回 503 并附上每档失败摘要。
冷却与半冷却探测(自愈调度)
槽位失败后进入指数退避冷却(5s → 10s → 20s …,上限 5 分钟)。冷却不是全时段 硬闸:
- 前半段完全静默:刚失败的槽位不接任何流量,避免对故障上游持续施压。
- 后半段放行 1 个探测:窗口过半后,同一
(源, 模型)最多允许 一个 请求作为 探测通过。探测槽数 =clamp(max_concurrent / 10, 1, 2)——max_concurrent: 10的源恰好放行 1 个探测请求。 - 探测排在最后:同 tier 内探测候选永远排在健康候选之后,只有没有正常槽可用时 才承接真实流量,因此探测不会抢走可用容量。
- 探测即真实请求:成功则立即清零失败计数与冷却,槽位当场回到正常轮询—— 上游恢复/额度恢复后不必等满整个冷却窗口。失败则重开一个新窗口,下次探测顺延到 新窗口的中点,不会连续重试。
分类冷却策略:
| 情况 | 冷却 | 说明 |
|---|---|---|
| 传输错误 / 5xx | 5s→10s→20s…,上限 5min | 指数退避 |
| 401 / 403 凭据错误 | 10min | 换 key 后由下一次探测自动接回 |
| 429 限流 | 30s 固定 | 「太快」不等于「坏了」,不进指数阶梯 |
配额耗尽(insufficient_quota / 余额不足 等) |
对齐配额窗口,上限 30min | 不进指数阶梯、不涨失败计数,额度恢复即可用 |
WebUI「优先级」页的槽位健康标签会区分 冷却 / 待探测 / 探测中,悬停可看窗口
起点、探测放行时刻与完全恢复时刻。
源(Source)与适配器(Adapter)的关系
- 源 是到某个上游的连接描述:
name、base_url、api_key、模型列表与优先级。 - 适配器 是协议转换逻辑(Lua 脚本):把统一 OpenAI 格式请求转成上游原生格式, 再把上游响应/流式分块转回统一格式。
- 一个适配器可被多个源复用(如
openai.lua同时服务多个 OpenAI 兼容站点); 同一个源也可以切换不同适配器(改adapter字段即可)。 - 源决定“连谁、暴露哪些模型”,适配器决定“怎么对话”——二者在
sources[]条目中 通过adapter字段关联。
加载流程(internal/core 的 Core 负责装配):
- 启动时
lua.NewVM(adapter_dir)加载全部适配器:内置适配器(编译期 embed)+adapter_dir下的同名覆盖文件;内置文件写在代码内,覆盖文件要求更高优先级。 config.Load读取config.yaml,config.NewStore(runtime_file)读 WebUI 改动的 运行时源,二者按名称合并成完整源列表。rebuildRegistry为每个源创建provider.Provider(持有目标适配器),并按源的 并发上限配置适配器 worker 池上限(不预分配,见下)。- 请求进来时
Registry按model路由到 Provider,Provider 调适配器transform_request→ HTTP 发送 →transform_response/transform_stream_chunk。
WebUI 上的"新增/编辑源"、"上传 Lua 适配器"、"改 AUTO 优先级链"与"密钥模型范围"
都即时生效(写入运行时文件或 adapter_dir 后重新装配,无需重启);直接编辑
config.yaml / adapter_dir 下的文件则需要重启进程才会重新加载。
在 WebUI 删除源/适配器时:运行时(WebUI 创建)的源与 .lua 文件会真正移除;
config.yaml 中定义的基源无法改写配置文件,采用删除标记隐藏(重启后仍隐藏),
在 UI 里重新添加同名源即可恢复。
源模板(多 key 负载均衡)
多个上游 key 共享同一套配置(URL / 适配器 / 模型列表 / 并发 / rpm)时,逐一复制
整段 source 太冗余。源模板保存除 name 和 api_key 以外的全部源字段,
从模板一键创建多个仅 key 不同的源,天然支持多 key 负载均衡(每个展开源独立调度、
独立冷却、独立健康状态)。
- WebUI「源」页右上角「模板管理」按钮:列出全部模板,可编辑 / 删除 / 新建。
- 添加源弹窗右上角两个按钮:
- 从模板创建 → 选模板 → 表单自动填充(name + key 仍需你填);
- 存为模板 → 把当前表单非 key/name 字段存为新模板。
- 模板持久化在
runtime_file的source_templates字段,与运行时源一样热管理, 改完即时生效。模板本身不含密钥,可以安全共享 / 纳管。
模板不会自己变成源——它只是「配方」。真正承担流量的是从模板创建出来的、带 key 的具体源。