接口成功了,体验却失败了
想象一下:你已经和同一个角色聊了好几个星期。你熟悉 TA 的语气,知道 TA 会怎样接住你的话,也已经习惯了这段对话的节奏。
但某一天,在你没有改动角色、也没有切换模型的情况下,一切突然变得不太对劲。
回复等了十几秒才开始出现;原本依赖结构化输出的功能突然失效;角色开始胡言乱语,句子里夹进另一种语言,或者说话方式突然像换成了一个能力弱得多的模型。
可是在服务器眼里,这次请求没有任何问题:接口返回了 200 OK,token 正常传回,账单也正常产生。
只有用户知道:这个模型突然“变差了”。
这正是我们为 Reverie 增加供应商金丝雀探针与路由隔离机制的原因。
一个模型名称,背后可能是许多套推理环境
OpenRouter 是 Reverie 目前主要使用的模型网关。它的优势之一,是可以让许多独立的算力供应商共同承载同一个模型。一家供应商不可用时,请求可以交给另一家。这给我们带来了更充足的算力、更有竞争力的价格,也避免了整个平台依赖单一节点。
但它也引入了一个用户看不见的变量。
模型名称没有变,真正执行推理的环境却可能每次都不同。不同供应商可能使用不同的推理引擎、硬件配置、量化精度、工具解析器、聊天模板、排队策略,甚至额外的内容策略层。
OpenRouter 的供应商路由文档说明,默认路由会考虑近期可用性和价格,并把其他供应商作为后备。这些指标很重要,但一个节点完全可能“在线、便宜、能返回内容”,同时又不适合某个具体产品的需求。
OpenRouter 自己也公开讨论过同一模型在不同供应商之间存在可测量的表现差异。理论上,同一套权重、同一种精度应该有相似表现;但把一个大模型稳定地部署到生产环境并不简单,差异会在实际推理中出现。
我们所说的“模型降级”是什么
“模型降级”不是一个只有单一成因的标准诊断,它也不等于“供应商一定在故意偷换更小的模型”。
在 Reverie,我们采用的是一个可操作的定义:如果某个推理节点在具有代表性的请求下,无法继续提供目标模型应有的行为、能力或交互性能,那么这个节点就发生了降级——即使请求在技术上依然成功。
我们最关注以下几种表现:
- 延迟降级:连接已经成功,但首 token 迟迟不出现,实时对话变成了盯着加载动画等待。
- 能力降级:原本应该支持结构化输出的节点开始返回格式错误的文本,无法满足既定 schema。
- 行为降级:回复出现乱码、无关片段、严重的语无伦次、异常重复,或者持续夹杂不该出现的语言。
- 策略不匹配:上游节点自行叠加了更严格的过滤,把 Reverie 支持的场景变成拒答、空回复或半截内容。
这些问题可能有许多原因。低精度量化在某些困难任务上确实可能损害表现,OpenRouter 的量化说明和公开的量化研究都提到过这种风险。但量化并不是万能解释:推理引擎的 bug、工具调用解析器、tokenizer 或聊天模板配置错误、队列过载,以及供应商额外加入的中间层,同样可能造成降级。OpenRouter 也曾观察到在工具调用场景中,解析器实现往往比精度本身更容易造成供应商差异。
所以真正重要的问题不是“哪个原因听起来最可疑”,而是:我们能不能固定到某一个节点,并稳定复现问题?
两个让问题变得具体的案例
这不是一次纸面上的架构讨论。
在一组固定供应商的对照测试中,某个节点在 20 次回复中有 19 次污染了回复开头:它会凭空插入标点,或者加入一小段毫不相关的 token。与此同时,另外 14 个承载同一模型的节点一共测试了 49 次,出现次数为 0。在角色扮演场景里,回复的第一个字符经常是 Markdown 的斜体标记,一个多余字符还会连带破坏整段内容的格式。
另一次测试中,某个节点在 3 次生成中有 3 次出现严重的葡萄牙语和西班牙语混杂。同样的提示词在模型官方节点以及其他相同精度类别的节点上都能保持葡萄牙语。这时,用“模型本来就有随机性”来解释已经不太合理——问题明显跟着推理节点走。
这类故障之所以难发现,是因为它们不一定抛出异常。传统可用性监控看到的是一个健康的 API;用户看到的却是一个突然不会好好说话的角色。
我们的答案:逐个检测路线,而不只检测模型
每 30 分钟,我们的金丝雀系统会获取当前正在承载 Reverie 默认聊天模型的全部供应商节点,然后向每个节点发送一组小型的合成请求。每次探测都会固定到唯一供应商,并关闭自动后备。
“固定供应商”非常重要。如果探针允许自动切换,那么第二个健康节点会掩盖第一个节点的故障。只有确保请求不会换路,我们才能知道结果究竟来自哪套推理环境。
目前系统检测四类范围明确、可以由代码判断的契约。
1. 首 token 延迟
对于流式对话来说,每秒生成多少 token 只决定了体验的一半。第一个 token 什么时候出现,决定了用户要盯着加载动画多久。
我们直接从流中计时。如果一个节点超过 10 秒仍未输出第一个 token,它会在这一项探测中失败。这个阈值故意留得比较宽松,而且一次变慢并不会让节点被隔离——短暂的排队压力确实可能发生。
2. 额外过滤与静默拒答
我们会发送一条固定的角色扮演续写请求,它代表 Reverie 明确支持的一类真实使用场景。探针会检查结束原因、高置信度拒答模式,以及没有任何文字的空输出。
我们还会把已知存在额外过滤的节点当作“阳性对照”。如果这些对照节点反而通过了检测,系统不会因此宣布其他节点全部健康,而会标记探针本身已经失效。一个连已知问题都检测不出来的测试,不能作为健康证明。
3. 结构化输出
系统会要求模型返回一个符合固定 schema 的小对象,并对结果进行验证。传输错误和格式错误会被区别对待:前者说明这次没有成功连接供应商,后者则说明请求完成了,但节点已经不能可靠履行结构化输出能力。
“接口宣称支持这个参数”和“接口能稳定正确地执行这个参数”,并不是同一个承诺。
4. 语言完整性
Reverie 支持 17 种界面语言,因此只用英语做冒烟测试,会漏掉一部分用户真正遇到的问题。
金丝雀会轮换所有已支持语言,使用合成的角色扮演提示词生成回复。一个完全在本地运行、结果确定的语言检测器,会寻找高置信度的整段语言切换、持续的多语言混杂,以及明显的编码损坏。它不会拿用户的真实对话做探测,也不会再调用另一个模型进行主观打分。
含义不明确或字数太短的结果只会被记为“无法判断”,不会算作失败。一旦发现语言异常,系统会立刻追加两次确认,并要求三次结果中的多数都失败。下一轮还会继续测试同一种语言,避免某个节点因为语言轮换刚好跳走,就绕过“连续两轮失败”的规则。
我们首先防范的,是系统自己的误判
自动移除推理容量很有用,但一次误判也可能制造比原问题更大的故障。因此,我们为金丝雀加了多重刹车:
- 传输错误不算质量失败。 超时、限流和 5xx 只会冻结计分,不会推进隔离所需的连续失败次数。
- 一轮失败还不够。 同一个节点必须连续两个每半小时执行的轮次都失败。
- 观察和执行是分开的。 我们可以先在观察模式运行完整探针,只记录“如果开启实弹就会隔离谁”、通知管理员并测量误报率,再决定是否启用自动操作。
- 可用容量设有底线。 如果隔离后会让生产节点少于两个,系统不会继续自动移除,而是通知管理员人工处理。
- 恢复也必须经过检测。 已隔离节点仍然会接受探测;连续两轮全部通过后,可以提前恢复。
- 重复出问题会逐级增加隔离时间。 首次为 1 天,再次为 3 天,之后为 7 天。如果 14 天内没有再犯,历史记录会淡出,隔离阶梯重新从 1 天开始。
我们的目的不是惩罚任何供应商,而是在问题能够稳定复现时,先把用户流量移出异常路线;当供应商修复问题后,也保留一条清晰的回归路径。
三层路由保护
最终参与路由的屏蔽列表由三部分组成:
- 经过评审的内置屏蔽项:用于我们已经测量确认、不希望被意外放回来的质量缺陷或策略不匹配。
- 运行时事故屏蔽项:管理员发现新事故后可以立即添加,不需要等待重新部署。
- 金丝雀限时隔离项:节点达到自动判定阈值后,由系统写入,并在满足条件后自动到期或提前解除。
这三层结果会合并到 OpenRouter 的 provider.ignore 路由参数中。用户不需要反复重试,直到随机抽中一家正常供应商;异常路线会直接退出候选范围。
所有自动决定都有审计记录。管理员会在状态发生关键变化时收到通知,也可以在必要时手动解除隔离。
这套系统能保证什么,又不能保证什么
我们的金丝雀刻意保持了很窄的判断范围。延迟、schema 合法性、高置信度过滤、语言完整性和编码健康,都可以交给代码判断;但它不会假装自己能回答“这个角色够不够有趣”“情绪理解是否细腻”“是否完美遵守了复杂人设”这样更主观的问题。
更广泛的模型质量仍然需要具有代表性的评测提示词、人工审查、生产数据,以及最重要的——用户反馈。
同样,并不是每一次奇怪回复都能证明模型降级。生成式模型本身具有随机性;很长的对话可能积累互相冲突的上下文;角色设定里也可能存在相互竞争的指令。有时候,一条奇怪的回复就只是一条奇怪的回复。
真正的变化是:“模型名称相同”不再是调查的终点。现在我们可以把请求固定到具体路线,复现客观故障,并让后续用户流量避开它。
稳定,不只是“接口有返回”
多供应商路由依然非常有价值。它让 Reverie 在个别节点下线时仍有足够容量继续提供对话,也让整个平台更有韧性。
但韧性不能只用 HTTP 是否成功来定义。一条迟迟不开始、丢失所需结构、拒绝平台支持内容,或者突然切换语言的回复,不会因为账单上显示“调用成功”就变成健康结果。
对于 AI 角色产品来说,稳定性最终是一件更接近人的事情:无论这一次由哪台机器替 TA 开口,角色都应该还是那个角色。
这就是 Reverie 新路由保护机制想要守住的标准。
如果你发现回复质量或语言突然发生变化,欢迎在反馈中附上模型名称和大致时间。这些信息能帮助我们把你看到的体验,准确对应到当时实际提供服务的路线。

