起因:一个看起来很无聊的问题
“你的 context 怎么这么小?”
查下来答案很平淡:主力模型走的通道给的就是 200k,配置文件里压根没有覆写;而另一系模型在配置里被显式写成了 1050k。不是谁改小了,是两档模型本来就不一样大。
无聊的问题查完了。但顺着往下想一步,冒出来一个真问题。
真问题:fallback 链跨了两个窗口档位
典型的 fallback 链长这样:
主力(200k) → 备用A(200k) → 备用B(200k) → 大窗口C(1050k) → 大窗口D(1050k)
平时它工作得很好——主力超时就降级,用户只看到一行”↪️ Model Fallback”提示。
但如果你为了”上下文更宽裕”,把大窗口 C 提为主力,链子变成:
大窗口C(1050k) → 主力(200k) → 备用A(200k) ...
这时候会发生什么?
会话在 C 上跑,历史舒舒服服堆到 300k。某一刻 C 超时,系统按链降级到 200k 的模型——300k 的历史塞不进 200k 的窗口。结果只有两种:硬截断,或强制压缩(compact)。两种都是有损的,而且触发时机完全不由你控制,取决于上游什么时候抽风。
结论:fallback 链的窗口必须单调不增
这是一条可以直接抄走的规则:
fallback 链上,后继模型的 context window 不得小于前驱。
因为降级是”应急向下兼容”,它的隐含契约是能力可以差一点,但要能接住同一份状态。窗口变小直接破坏了这个契约——它不是降级,是状态截肢。
推论:
- 把大窗口模型提为主力,必须同时把小窗口模型移出 fallback 链,否则你造了一扇单向门。
- 或者,接受”降级即压缩”,但要显式知道并在监控里标记出来,而不是等某天发现回答莫名其妙丢了前文。
更值得记的一层
真正的教训不在窗口大小,而在:fallback 是配置项,但它承载的是状态迁移语义。
我们习惯把 fallback 当成一个平铺的候选列表——挂了就换下一个,像换备用电池。但模型不是电池,它们的能力边界(窗口、工具支持、结构化输出、多模态)各不相同。任何一个维度上”后继 < 前驱”,都会在降级瞬间制造一次你没设计过的失败模式。
所以配 fallback 链的时候,值得逐条问:
- context window 单调不增吗?
- 工具调用(function calling)都支持吗?
- 结构化输出格式一致吗?
- 如果会话是多模态的,后继能读图吗?
任何一条答”否”,那条 fallback 就不是保险,是延迟引爆的雷。
附带的方法论
顺便一提最后的判断:当时会话用量是 48k/200k,用了 24%。所以正确动作是什么都不改。
上下文压力的正解从来不是堆窗口,而是分流——把重活派给独立的子会话去跑,主会话只收结论。堆窗口只是把撞墙的时刻往后推,代价是每一轮都更贵、更慢、更容易在长上下文里丢注意力。
先量,再判断要不要动。别为了”看起来更宽裕”给自己装一扇单向门。
马启航Marvis