第 25 节 · Router:把对的活派给对的模型¶
路由真翻过两次车:把活派给了错的供应商,以及整条停摆 9 天没人知道。这一节讲路由的三个真问题:选谁、错了退到哪、不干活时谁喊一声。
一、选谁:三档,不按内容,按名字¶
我这份路由脚本开在一个端口上(:8090)。它上面接一个协议转换代理(:8082),下面接本地推理(:11434)——一条串了三级的链路,一头进、一头出。
这三级的活不一样:最上面那级是命令行工具,说的是它自己那套协议;最下面那级是本地的推理引擎,说的是另一套。中间那个代理干的是一件事——把上游的话翻译成下游听得懂的格式。而 :8090 这层路由,干的才是真正的"派活":看名字,定档位。
它要做的第一件事,是把手里的请求塞进三档里的一档:
| 档位 | 模型 | 量化 | 占用 | 干什么 |
|---|---|---|---|---|
| fast | 9B | Q4_K_M | 5.4 GB | 纯文本——快的 agent、简单推理 |
| reason | 27B | Q3_K_XL | 12 GB | 纯文本——复杂推理、写代码 |
| vision | 27B 多模态 | nvfp4 | 18 GB | 带视觉——看图 + 复杂推理 |
5.4、12、18——这三个数不只是三档不同的精度。它们对应三种活法:便宜、聪明、看得见。fast 是干粗活的手,reason 是动脑子的脑,vision 是能看图的眼睛。你让那只手去解一道复杂的推理题,它会很快地给你一个错的答案。
三档不是拍脑袋分的。第 24 节的池子里注册了 10 项服务,可真正需要"按名字分流"的,就是这三档——因为它们争的是同一块机器内存:5.4、12、18,谁都想常驻,谁都不便宜。分档的本质不是分类,是分资源。
那么,它凭什么决定一个请求进哪一档?
按名字,不按内容。
这套路由不认识"难"和"易"。它只做一件事:看请求里那个模型名,拿它去一张别名表里查,查到哪一档就进哪一档——它把名字改写成对应档位的 tag。它还额外收了一堆旧名字,几个上一代小模型的老名,统统兜底映射到 fast。
一张别名表,把十几个可能被喊出来的名字收拢到三档上。路由不识货,只认名字。 它不回答"这活难不难",它只回答"这个名字归哪一档"。
图 A-25-1(待出):三级链路与三档分流——命令行工具 → 代理(:8082) → 路由(:8090) → 本地推理(:11434),路由按模型名把请求分进 fast / reason / vision
别把两张路由表搞混¶
公司里还有另一张路由表,27 条。这一张路由的不是模型,是人。
左边是你用大白话喊的活——"今天做什么饭""帮我学英语""做一个演示文稿";右边是"谁接 + 在哪干"。它把一句自然语言,翻译成"一个承接的角色 + 一个工作目录"。
两张表,差别在尺度:一张把机器派给机器(请求 → 模型档位),一张把活派给人(一句话 → 一个角色 + 一个目录)。名字都叫"路由",一个在系统内部,一个在组织里。
这层能力借不来¶
在造它之前,我试过一条近路:复用那个 Web IDE 网关来做模型路由。设计文档当场把这条路证伪,结论是:
那个网关只管网页和文件,零模型路由能力。
那个网关的本职,是页面、文件浏览和终端通道——它离模型隔着一整层。近路看着省事,其实是把两个不相干的零件硬焊在一起。
路由不是挂在边上的装饰,它得自己长出来。
第一次翻车:派给了错的供应商¶
2026 年 8 月 22 日,我记下的一条 bug。
界面里我选的是某云厂商,流量却发去了另一家。根因只有一句话,但很反直觉:
配置文件里那一节 env,优先级高于进程环境变量。
全局配置里那条指向另一家的地址,会盖掉宿主注入给子进程的供应商设置。也就是说:"我在界面上选了谁"和"活最后发给谁",是两条互不相干的链路——而且后者赢。
修法是在拉起子进程时加一句声明:"宿主托管 provider",让它忽略配置文件、以进程环境为准。
修对之后还有第二层:路由通了,供应商却返回"对该模型无访问权"——账号里有认证,却没有这个模型的权限。
"派对了"和"能干活",是两件事。 路由正确只是必要条件,不是充分条件。
图 A-25-2(待出):一次派错的链路——界面选 A → 全局配置 env 覆盖进程环境 → 实际发往 B;修法:宿主托管声明
二、错了,退到哪¶
翻开这份路由脚本,里面写着三处退路。
第一,兜底:如果喊出来的名字谁也不认识,就把它退到最快的 fast,并在日志里记一笔。兜底的意义是:调用方喊错一个名字,不该让整个请求就这么死掉——先让它落在最便宜的那一档上跑起来,同时把这件事记进日志。这是代码里的设计——到今天为止,这条兜底只活在设计里,没有一次真实调用触发过它,我如实标出来。
第二,一道限宽门:输入硬上限 64000 字符(约 3 万 token),超限就从最旧的历史开始丢。理由是预填充的时长——约 3 万 token 的输入,预填充不超过 45 秒。丢最旧历史是有代价的:如果某条被引用的工具结果正好落在丢掉的那一段里,调用链会断。这个取舍写在代码注释里。
第三,流式超时 600 秒——给 27B 那一档留冷启动的余量(那一档冷预填充能到 160 秒左右,给到 600 秒,是不让它刚开口就被掐断)。另有一道更粗的门:单次请求体硬上限 64MB,专门拦那些塞了超大图片的请求,别让一张图把内存吃满。
这几个数,正好是几句"退到哪儿为止":名字不认识,退到 fast;输入太长,退到最旧历史之外;生成太慢,退到 600 秒。
一个靠得住的路由,不是"永远不会出错",是"错了知道往哪退"。
那次账没算对¶
但有一道退路,我当时根本没设。这一处不是路由翻的车——路由把活派对了人,翻的是账:它漏设了一道退路。
2026 年 8 月 23 日,我在查"这台机器为什么一直发烫"。结论不是生成慢,是每一次请求都要把整个上下文重新读一遍。根因是一个数:上下文窗口被写成了 1,000,000——一个几乎永远触发不了压缩的天文数字。而本地推理这一层没有跨请求缓存:实测一次请求 16,012 个 token,预填充要 6.6 秒,缓存命中 0 次。
日常请求动辄 5 万到 20 万 token,换算下来每次 20–80 秒,GPU 就一直满载。
这不是"派错了模型",这是账没算对。路由选对了人,却不知道他手里那份活有多大。修法是把 1,000,000 改成 48,000,再在路由层补上那道 64000 字符的限宽门。
账算不清,退路就是假的——不知道该退到哪,是因为连自己站在哪都不知道。
(这块地的账,第 26 节单独算。)
图 A-25-3(待出):回退与限宽三件套——未知名 → fast;输入 > 64000 字符 → 丢最旧历史;流式超时 600 秒
三、它不干活的时候,谁喊一声¶
前两个问题,都能在代码里解决。第三个不能。
第二次翻车:整条停摆 9 天,没人知道¶
2026 年 9 月 10 日,一次例行盘点里记着"三处基础设施静默故障",其中一处就是这套本地路由——整条停摆 9 天。而停摆之前的那次例行会议,记录上还写着"已验证全绿"。两条主力业务线那段时间没有本地推理可用,没有任何人被告知。
同一次盘点里还记着另外两处静默故障:一个采集任务断了 5 天,一条数据源故障拖到了第 15 天。三件事是同一个形状——东西停了,系统没响。
整改项里那句话最难堪:
补最小告警规则(现在所有异常靠人工读日志发现)
那时候全公司没有一份独立的监控告警配置。路由器不转、任务断供、数据源静默失败——全靠人一条一条翻日志去发现。
这里没有英雄主义的余地。一套系统里最贵的,从来不是把一个零件造得多聪明,是让它在坏掉的时候被人看见。那 9 天不是"没能力修",是"根本不知道要修"——中间隔着的,就是那声没响的警报。
一个偶尔做错决定的路由不可怕。可怕的是它彻底躺平的时候,整套系统还按"一切正常"在跑。
没有告警的组件,像一栋没装烟感的大楼——真烧起来之前,没人会抬头看一眼天花板。故障不致命,静默才致命。
图 A-25-4(待出):路由三问——选谁 / 错了退到哪 / 不干活时谁喊一声
尾声:三个真问题¶
到这里,路由的三个真问题就齐了:
- 选谁——三档,按名字,不按内容。选错的形态不止一种:"派对了 ≠ 能干活"。
- 错了退到哪——未知名退到 fast,输入退到 64000 字符,生成太慢退到 600 秒。退路的前提,是你知道自己站在哪。那笔当时没算对的账(上下文窗口收成 48000,见第 26 节)不在退路里,它是漏设退路的代价。
- 不干活时谁喊一声——前两个问题都能在代码里解决,这一个不行。它需要一个会响的东西。
把它挂回整条链:
Model → Small Model → Model Pool → Router → Context → Knowledge/Memory
→ Agent → Harness/State → Evaluation/Gate → 闭环/反馈 → AI System
Model Pool 解决的是"池子里放谁",Router 解决的是"这一刻,这活派给池子里的哪一个"。池子是仓库,路由是调度——仓库再大,没有一台会派活的调度台,货也只是堆在那里。
而在这条线上,我踩过的三个坑——两个是真翻车(派错供应商、停摆 9 天没人知道),一个是漏设的退路(账没算对)——这三件事,对应它必须回答的三个问题:它得认识每一个候选,它得知道错了往哪退,它得在自己趴窝的时候喊出声来。
一个系统里最容易被忘掉的零件,往往就是那个不声不响、一直在干活的零件。
数据与出处¶
- 本地三档路由脚本(三档配置、别名表、输入硬上限、流式超时、未知模型兜底)
- 桌面端自动切换设计文档(网关零模型路由能力的自述、近路方案的证伪)
- 一次供应商派错的现场记录(2026-08-22,含根因、修复与"派对 ≠ 能干活")
- 一次上下文窗口没算对的现场记录(2026-08-23,含实测预填充耗时与缓存命中数)
- 一次例行盘点记录(2026-09-10,含三处静默故障与告警缺失)
- 一张 27 条的自然语言路由表(自然语言入口 → 承接角色 + 工作目录)