能调通不等于能扛故障:AI服务生产部署的限流、降级、容灾与模型切换
目录
一、四个故障站在同一条账本上
(一)能调通掩盖了什么
(二)它们不是四个独立问题
1、共享同一份配额与时间预算
2、处置动作会互相改写彼此的输入
(三)贯穿场景:智能工单的智能层
(四)本文的判断、口径与不讨论的范围
二、失败语义:先翻译,再处置
(一)模型调用的失败至少要分成七类
(二)四类业务动作
1、可重试
2、换模型重试
3、降级
4、直接失败
(三)静默降级与版本漂移
三、限流:三种量纲、三处落点、一种拒绝语义
(一)并发、速率、令牌预算为什么不能互相替代
(二)网关、租户账本、供应商配额镜像
(三)四二九与重试建议头的正确读法
(四)本地闸门必须比对方更早动手
四、降级:换的是业务路径,不是错误文案
(一)四级阶梯
(二)每一级必须对外可感知
(三)回升比下降更危险
(四)必须演练,否则一定是坏的
五、容灾:提供商故障不是下游抖动
(一)四层故障面
(二)主备与多活
(三)合规与数据驻留把候选集合先砍掉一半
(四)容灾演练的最小矩阵
六、模型切换:可替换性是设计出来的
(一)三种粒度与推进次序
(二)路由层的四个输入与三条出路
(三)离线、影子、灰度的对照次序
(四)切回去的成本决定切不切得下去
七、把四件事装成一条控制回路
(一)装配次序
(二)时间与配额预算切分
(三)反馈路径与正反馈环
八、落地:代码形状与分层分工
(一)统一入口
(二)错误分类器、路由器、账本
(三)网关、应用、供应商三侧分工
九、观测、演练与发布门禁
(一)必须能回答的十个问题
(二)故障注入矩阵
(三)两条告警与一张看板
十、反模式清单与一页纸
十一、结语:从连通性到控制回路
参考资料
干货分享,感谢您的阅读!
把大模型接进一个 Java 服务,最快的路径是三行代码;让这条链路在模型不可用、响应过慢、配额耗尽、提供商整段故障时,仍然给出可解释、可回放、可降档的业务结果,是另一件事。前者验证的是连通性,后者交付的是控制回路。
本次我们沿着一条完整的工程路径走一遍:先把四类故障放到同一本账上切开,再把失败翻译成四类业务动作,然后分别讨论三种量纲的限流、四级阶梯的降级、四层故障面的容灾,以及三种粒度的模型切换;最后把四件事装回同一条由外向内的装配次序,配上时间与令牌两本预算、统一入口的代码形状、故障注入矩阵与值班十问。
限流保护配额,降级保护业务路径,容灾保护供应商,切换是执行动作——四者只有共享同一份预算、同一套失败语义、同一个装配次序,才构成能在故障那几分钟里自动收敛的控制系统。
一、四个故障站在同一条账本上
(一)能调通掩盖了什么
联调环境里的大模型调用几乎总是成功的。密钥有效、额度充足、网络干净、每次都在两秒内返回完整内容。于是三行代码被原样保留进生产:一个聊天客户端、一段提示、一次同步调用。上线当天之前,没有人觉得这有什么问题。
生产与联调的差别不在“会不会失败”,而在“四类失败会不会同时出现,并且共用同一份配额与同一段时延承诺”。模型不可用、响应过慢、配额耗尽、提供商整段故障,在联调日志里各是一个偶发异常,在生产里却是同一本账上的四笔透支。对其中一类做重试,会把另外三类的余量一起吃掉。
这四类故障还有一个共同的伪装:它们在适配层常常被映射成同一个异常。框架为了抹平各家接口的形状差异,会把状态码、响应头、错误体收成“调用失败”。业务代码于是写一个catch,里面要么重试,要么返回“系统繁忙”。这个catch在联调阶段永远进不去,在生产的第一次抖动里会把四类完全不同的处置揉成同一种动作。
把“能调通”和“能扛故障”并排放,中间隔着五步,而不是五个可以分别排期的独立需求。没有失败翻译,限流拒绝会被当成模型故障去换供应商;没有三处限流,降级会在额度打空之后才发生,那时已经没有余量可降;没有四级降级,容灾切过去之后只能给出一个更贵或更差的答案;没有可回放的路由,切过去之后说不清走了哪条路,切回来也没有对照数据。五步必须一起推进,这决定了本文的章节次序。
(二)它们不是四个独立问题
1、共享同一份配额与时间预算
一次智能工单请求在入口处承诺了两件事:分类要在八百毫秒内给出可落库的类目,知识问答要在两秒内吐出第一个字。这两段时间不是“模型调用的超时”,而是产品对坐席的承诺。同一笔请求还要记入三本配额账:供应商按每分钟请求数计的 RPM、按每分钟词元数计的 TPM、按租户日额度计的金额。四类故障消耗的是同一组数字。
模型不可用会触发重试,重试消耗 RPM 与线程;响应过慢会占住并发坑,流式输出按字计费,TPM 在用户已经离开之后仍在增长;配额耗尽会让备用账号被一并打穿,如果重试把四二九当成了普通失败;提供商整段故障会把流量压到另一家,另一家的额度与时延承诺立刻被改写。四类故障的处置如果各写各的,结果一定是某一类的“自救”变成其他三类的“加害”。
2、处置动作会互相改写彼此的输入
限流的拒绝如果被重试当成“可以再试一次”,到达率不降反升;熔断如果把四二九算进失败率,会在额度高峰期把健康依赖切断;降级如果返回一个空对象并记为成功,观测系统会认为回路已经收敛;模型切换如果不管能力矩阵,处置落点可能把“建议退款”执行成“真的退款”。这些反馈路径在稳态下都不显现,只在故障期间才全部激活——而故障期间恰恰是最不适合发现它们的时刻。
所以问题的正确形式不是“限流怎么配、降级怎么写、容灾怎么切、模型怎么换”四道填空,而是“四个机制在同一条链路上按什么次序装配、共享哪一份预算、彼此的输入输出怎么定义”。后文第七章会把这条回路画完整;在那之前,必须先有一套失败语义,否则回路的每一环都在用错信号。
(三)贯穿场景:智能工单的智能层
抽象讨论需要一个落点。本文沿用一套面向客服坐席与运营同学的企业智能工单系统作为贯穿场景。它同时具备时延承诺、副作用、成本归账与供应商可替换,是少数能把四类故障一次性长出来的题目。
智能层有四个落点,工程约束完全不同。工单分类的时延预算只有八百毫秒,失败代价是派错组、首响时钟冻结,可降级路径是规则引擎加人工。知识问答的时延预算是两秒首字,失败代价是对客户做了错误承诺,可降级路径是只检索不生成。回复草稿的时延预算是四秒完整输出,失败代价是坐席空等、采纳率下降,可降级路径是模板拼装。处置编排带着真实退款与短信,失败代价不可回滚,可降级路径只能是“只出建议,不自动执行”——这个落点几乎不允许跨供应商热切。
四个落点共用同一组供应商账号、同一份月度预算、同一套观测。后文所有例子都落在这四个落点上。分类用来讨论“必须在截止时间前给出可落库结果”,问答用来讨论“首字延迟与生成速率是两种慢”,草稿用来讨论“成本与效果的对照”,处置用来讨论“带副作用的路径为什么不能靠换模型蒙过去”。
(四)本文的判断、口径与不讨论的范围
文中出现的超时基线、额度水位、放大倍数、成本比例都是量级示意,用来说明设计意图与相对关系,不是任何一家供应商的真实参数。落地时应以自身的压测结论与所调用服务的文档为准。请求体字段名在不同供应商之间存在差异,文中展示的是结构与语义,不是具体命名。
三类东西本文不展开。第一类是提示词工程与模型效果调优——它们决定“说什么”,本文只讨论“失败时走哪条路、这条路怎么被证明还活着”。第二类是检索、切分、重排这些智能层内部的质量问题——它们会改变令牌消耗与时延,但处置原则与本文相同。第三类是把十次调用编排成一次 Agent 循环的工程——一次调用的控制回路,和把十次调用编排起来的控制回路,是两个话题;前者是后者的地基。
代码示例基于 Java 17 及以上,JDK 21 的虚拟线程会改变并发闸门的一个结论,文中会单独标明。异常处理遵循“捕获必须处理、禁止用异常控制流程、资源必须关闭”;金额与额度用BigDecimal计量;线程池禁止用工厂方法创建,必须显式配置有界队列。
二、失败语义:先翻译,再处置
(一)模型调用的失败至少要分成七类
“调用失败”不是一种失败。限流拒绝、瞬时抖动、模型过载、模型下线、内容拦截、上下文超长、余额与鉴权失败,这七类的处置动作完全不同:一个该减速,一个该有限次重试,一个该换同家小模型,一个该换已评测过的版本,一个重试一万次也没用,一个该裁剪后重发,一个该立刻告警并整档降级。把它们送进同一个catch,可用性一定会出问题,账单也一定会出问题。
分类器必须放在适配层之上、业务层之下。框架会把各家错误码映射成统一异常,但映射后的粒度往往只到“可重试 / 不可重试”。七类里至少有四类会被这层粒度抹平。抹平之后,限流拒绝会去打备用模型,内容拦截会去换一家再生成一遍,欠费会把备用账号的额度也吃光。
正确形状是一个错误分类器:输入是状态码、响应头、异常类型与供应商名,输出是下一节的四类业务动作中的一个,外加是否允许跨家。业务代码里不允许再出现对状态码的二次解释。分类器本身要可测:用真实线上见过的响应头做样本,不要靠想象编造——想象出来的四二九往往比真实的更规整,缺少重置时刻、限流维度、剩余配额这些决定动作的字段。
还有一类失败不在这七类的状态码里,却必须被看见:供应商静默升级。请求里指定的是某个版本,响应里带回的是另一个。它不报错,不限流,不超时,只是效果变了。如果观测不记录“请求模型名”与“响应模型名”两个字段,团队会把质量下跌归到自己上周的某次发布。这一类失败的处置不是重试,而是告警并冻结灰度,直到评测集给出对照。
(二)四类业务动作
分类器的输出必须是枚举,不能是一段注释。四个动作对应四本不同的账,选错的代价比失败本身更大,因为它会改写配额、时延与观测口径。
1、可重试
适用瞬时抖动、可恢复的网关错误、连接重置。预算计入本层重试预算,不另开一份配额账——每一次物理往返都要记入 RPM 与 TPM,但它们属于同一次逻辑请求。次数上限由剩余截止时间除以单次超时反推,再减一次留给降级。写成“固定重试三次”是错的:分类落点八百毫秒的预算里,三次各三百毫秒的重试在数学上不可能发生;草稿落点四秒的预算里,三次可能刚好把用户等走。
流式调用的重试还有一条额外约束:已经向坐席吐出内容之后,原则上禁止重来。半截答案作废重发,坐席看到的是一段突然换口吻的文字,比失败更难解释。首字之前可以重试,首字之后只能降级或失败。
2、换模型重试
适用模型过载、模型下线、同家模型级限流。预算走备用模型的独立配额账本,不能从主模型的水位里“借”。降级链长度通常不超过两跳:主模型到同家小模型,再不行就离开“换模型”这个动作,进入降级。内容拦截、租户合规拒绝、鉴权失败,不允许进入这一支——换哪一家都应当失败。
换模型时必须换提示词版本。拿为甲模型写的提示词去打乙模型,测出来的不是乙模型的能力,是乙模型对甲模型提示词的适应性。这一条在对照评测里会再写一次,因为它是线上最常见的“切过去效果变差、于是再切回来”的原因。
3、降级
适用配额耗尽、整家故障、预算告急、准确率掉档。降级不再消耗高档模型的令牌,换的是事先写好的业务路径。第四章会把路径写成四级阶梯。这里只需记住一条:降级的输出必须带原因码,且不能被记成技术成功。
4、直接失败
适用内容拦截、鉴权失败、契约不满足、以及“降级链也走不通且关能力尚未开启”的情况。预算零额外消耗。不允许把直接失败兜底成空对象、空列表或“暂无建议”——那是静默降级,第十章会把它列为最危险的反模式。直接失败要按失败类别单独计数,禁止并入一个笼统的错误率。
一条硬规则值得单独写:内容拦截换了模型也没用,应当直接失败。对称的一条:余额不足与密钥失效是调用方自己的问题,熔断被调方毫无意义,应当立刻告警并整档降级。把这两条写成分类器里的单元测试,比写进设计文档更不容易在发版时丢掉。
(三)静默降级与版本漂移
适配层还有一种“成功”需要被翻译成失败:返回了可解析的对象,但对象来自一条不该走的路径。框架在解析失败后返回空对象、降级链走到了规则引擎却仍记为模型成功、供应商把模型版本悄悄换掉——三种静默的共同点是技术成功率百分之百,业务指标在掉。
对策是三个必留字段:请求模型名与响应模型名,两者不一致就是静默升级;是否降级、降到哪一级、降级原因码,缺一不可回放;业务结果是否被人工改写,这是质量信号而不是技术信号。成功率必须拆成两张曲线,一张回答链路通不通,一张回答事办成没有。只看前一张,静默永远是绿的。
三、限流:三种量纲、三处落点、一种拒绝语义
(一)并发、速率、令牌预算为什么不能互相替代
大模型接口同时按三本账计费。并发保护的是调用方的线程与连接,量纲是在途请求数。请求速率保护的是供应商的 RPM 配额,量纲是每分钟请求数。令牌预算保护的是 TPM 与月度金额,量纲是输入加输出的词元数。只限其中一本,等于另外两本敞开。
“我们已经做了 QPS 限流”仍然会在月底爆账单,原因很具体。请求速率限制的是次数,不是每次的体积。一次带八万词元上下文的问答,和一次二十个词的分类,在 RPM 账上是相同的一笔。并发限制的是占坑,不是费用。流式输出可以占着一个并发坑慢慢吐,令牌按输出字数持续计费,用户已经离开,账单还在涨。令牌预算卡得住烧钱,卡不住线程占满:一堆短请求可以把线程池打满,而 TPM 几乎不动。
三本账必须同时存在,并且拒绝语义要能区分是哪一本打满。否则上游会用错动作:该换短提示的去换了供应商,该排队的去降了档,该给某个租户停智能层的去重启了应用。拒绝码要写成枚举,至少覆盖CONCURRENCY_EXHAUSTED、RPM_EXHAUSTED、TOKEN_BUDGET_EXHAUSTED三种,外加供应商侧的PROVIDER_429。
并发闸门在 JDK 21 虚拟线程下有一个容易被忽略的变化:虚拟线程让“线程用尽”不再是第一约束,连接池、文件句柄、供应商并发配额会先到顶。这时并发闸门的意义从“保护线程池”变成“保护下游许可”。许可数仍要显式配置,不能因为换成虚拟线程就把闸门拆掉。
(二)网关、租户账本、供应商配额镜像
三本账还要落在三处,每一处保护的对象不同。入口网关看见的是集群总到达率与恶意流量,口径是请求数每秒,按路由与租户切分;它看不见这次调用会消耗多少词元,所以不该在这一层做令牌估算。租户与任务账本看见的是单租户日额度、单任务月预算,口径是估算词元加实际词元对账;它看不见供应商此刻还剩多少配额。供应商配额镜像看见的是对方的 RPM、TPM 与并发,口径按对方文档镜像一份本地闸门;它看不见对方是否悄悄下调了阈值。
本地闸门必须比对方更早动手。供应商的四二九是已经发生的伤害:这次请求已经占用了对方的排队、已经计入你的错误率、已经可能触发上游重试。镜像闸门的目标不是精确等于对方阈值,而是在对方拒绝之前把到达率压到安全区。阈值取对方公开值的七到八成,留出突发与统计误差。对方没有公开值时,用阶梯压测找到拐点,再打七折。
租户账本必须以实际用量为准、以估算用量为拦截。估算与实际必然有偏差。水位要分三级:到八成告警,值班介入,检查是否异常租户或提示词膨胀;到十成对该租户降精度,改走小模型,其它租户不受影响;到十二成对该租户停止智能层,请求进人工队列。到顶才拒绝,等于把决策推到已经没有余量的时刻。全站一个硬顶的结果一定是大租户把小租户挤下去,而故障看起来像“模型挂了”。
网关这一层不要试图理解词元。它的任务是挡住明显的突发与扫描,把精细账留给应用层。两层职责写进同一份配置,是限流配乱的起点。网关拒绝要返回明确的配额类错误码,应用层不要把它再翻译成“模型调用失败”。
(三)四二九与重试建议头的正确读法
四二九不是下游故障,它是准入控制。读懂重试建议头,才能决定是等待、换账本,还是换供应商。信号至少有五样:状态码或等价的配额耗尽语义;等待建议秒数;剩余配额、重置时刻、限流维度;是账号级、模型级还是区域级;是否伴随余额不足或密钥失效。后两样决定动作完全分叉——账号级耗尽要换账本,模型级耗尽可换同家小模型,整家耗尽走容灾,余额不足则禁止再打任何一家。
错误动作的清单比正确动作更有用。当成五零二做抖动重试,会把到达率打得更高;忽略建议头按固定退避再打,会在对方明确要求等待的窗口里继续撞墙;熔断该依赖,会误伤其它健康模型;把拒绝计入下游失败率,会让值班去重启应用、回滚版本;静默换一个更贵的模型硬过,会在额度打满时把账单再推高一截。
拒绝语义一旦在适配层被抹平,外层只能放大。重试层看到的如果只是“调用失败”,它会再发一次——而四二九的正确响应是减速,不是加速。熔断层如果把四二九算进失败率,会在额度高峰期把健康依赖切断,高峰过后又在半开窗口里集中打回去。观测如果把四二九并入错误率,真正该看的配额水位就永远排不到值班的第一眼。因此适配层必须把“被限流拒绝”做成一种一等公民的结果类型,而不是一种异常。异常用来表达“不该发生的事”;四二九是配额系统的正常输出。
(四)本地闸门必须比对方更早动手
把前面三节收成一条可执行的纪律:本地永远先于对方拒绝。实现上是一面镜像闸门,一面租户账本,一面入口网关,三面的拒绝都带类型,都不抛成普通异常。镜像闸门按供应商加模型加区域三个维度建桶,桶的补充速率取对方文档值的八成;租户账本按租户加落点加自然日三个维度记账,金额用BigDecimal,禁止double;网关按路由加租户做请求速率限制,不碰词元。
下面是租户账本拦截的一份参考实现。它的核心不是“怎么计数”,而是“三级水位对应三种动作,且对其他租户无副作用”。
public final class TenantTokenLedger { public static final BigDecimal WARN_RATIO = new BigDecimal("0.80"); public static final BigDecimal DEGRADE_RATIO = new BigDecimal("1.00"); public static final BigDecimal STOP_RATIO = new BigDecimal("1.20"); private final TokenUsageStore store; private final AlarmService alarmService; public TenantTokenLedger(TokenUsageStore store, AlarmService alarmService) { this.store = store; this.alarmService = alarmService; } public LedgerDecision tryAcquire(TenantId tenantId, Endpoint endpoint, int estimatedTokens) { DailyQuota quota = store.quotaOf(tenantId, endpoint); BigDecimal used = store.usedTokens(tenantId, endpoint); BigDecimal projected = used.add(BigDecimal.valueOf(estimatedTokens)); BigDecimal ratio = projected.divide(quota.dailyLimit(), 4, RoundingMode.HALF_UP); if (ratio.compareTo(STOP_RATIO) >= 0) { return LedgerDecision.stop(ratio); } if (ratio.compareTo(DEGRADE_RATIO) >= 0) { return LedgerDecision.degradePrecision(ratio); } if (ratio.compareTo(WARN_RATIO) >= 0) { alarmService.warnQuota(tenantId, endpoint, ratio); return LedgerDecision.allow(ratio); } return LedgerDecision.allow(ratio); } public void commit(TenantId tenantId, Endpoint endpoint, int actualTokens) { store.addActual(tenantId, endpoint, actualTokens); } }估算用于拦截,实际用于对账。两者长期偏离超过一成,说明分词器口径或缓存折扣没有计入,要修的是估算器,不是把水位再放宽一档。影子调用与评测集回归必须走独立科目,禁止混进业务账——混在一起会让“业务成本上涨”这个信号失去意义。
四、降级:换的是业务路径,不是错误文案
(一)四级阶梯
降级被写得最多、被做得最假的,是把异常翻译成一句“系统繁忙,请稍后重试”。那不是降级,那是把失败抛给坐席。真正的降级是换一条事先写好、事先演练过的业务路径。路径要做成四级阶梯,按落点分别配置,禁止四个落点共用同一张梯子。
第一级是降精度:把改写、重排、评判换成小模型,召回略降,坐席几乎无感,成本下降明显,时延通常变好。触发条件是 P99 变差或 TPM 到八成。分类落点降精度之后,结构化输出的校验不能放松——字段仍要能落库,只是允许少做一轮校验重试。
第二级是降覆盖:只对高优先级或特定类目启用智能层,覆盖率下降,坐席感知明显,核心路径仍在,边缘路径回人工。触发条件是配额到十成或单家过载。降覆盖不是随机抽掉一半请求,而是按业务优先级切:催办、投诉、涉及金额的工单留在智能层,咨询类回人工。随机抽会让同一客户的连续工单体验分裂。
第三级是降档位:条件自动退回草稿待审,自动化率归零,人力成本回升,副作用工具从模型手里收回。触发条件是准确率掉档或整家故障。这一级是处置落点的默认容灾动作——不是换一家继续自动退款,而是把退款权收回。
第四级是关能力:智能层整体旁路,回到纯人工工单系统。它必须真实可用,且定期演练。这是熔断开关,不是理论上的退路。触发条件是跨家容灾也失败,或预算击穿。超过三十天没演练过的关能力路径,应当被视为不存在。
(二)每一级必须对外可感知
阶梯有三条纪律。第一条:每一级都必须是事先写好的业务路径。降精度之后分类仍要能落库,降覆盖之后未覆盖的工单要能进人工队列,关能力之后工单系统本身必须可独立运行。如果关能力之后工单列表都打不开,说明智能层被写进了主路径的构造函数里,旁路从来就不是旁路。
第二条:下降容易、回升困难。一次误操作即降档,升档需要连续达标窗口。故障期间的自动回升会造成半开共振——刚恢复的模型被放量打回去,再降,再升,形成方波。回升窗口建议覆盖一个完整的业务高峰,而不是连续五分钟错误率正常。高峰与低谷的错误率不可比。
第三条:跳级必须是显式决策。从降精度直接关能力,等于放弃了中间两级的缓冲。只有在“备用供应商同时不可用”这种已被演练过的场景下,才允许跳级,并且要在审计里留下跳级原因。值班同学按一个按钮跳到第四级是允许的,代码里写if (errorRate > 0.1) disableAll()是不允许的。
四级阶梯要按落点分别配置。分类可以降到规则引擎,问答可以降到只检索不生成,草稿可以降到模板,处置只能降到“只出建议”。处置落点没有“换个小模型继续自动退款”这一档。这一句在容灾章还会出现,因为它同时是降级问题与切换问题。
(三)回升比下降更危险
下降是保护,回升是冒险。刚恢复的供应商会在半开窗口里收到一波探测流量;如果回升逻辑按错误率自动拉满,探测会变成脉冲,脉冲会把刚恢复的实例再次打进过载。正确的回升是受并发闸门约束的探测:半开只许 N 个在途,成功后再按比例放,比例的步长写进配置,禁止一次回到百分之百。
回升还要看业务成功率,不只看技术成功率。模型恢复了、四二九消失了,但如果人工纠正率仍在高位,说明恢复的是连通性,不是质量。质量未恢复时回升,等于把用户重新置于一个已知更差的路径上,还关掉了已经在工作的降级路径。
(四)必须演练,否则一定是坏的
兜底路径的共同问题是平时不走、需要时才走,而不走的代码会腐化。三条可执行的做法:按比例影子调用,把千分之几的流量强制走低一档能力,用同一套校验比对结果;把降级次数与降级后的可用率做成两条独立曲线,任何一条异常都告警;每次模型或供应商变更后,跑一遍覆盖四个档位的回归集。演练成本很低,而没有演练过的降级路径几乎必然在需要时失效。
静默降级是最危险的一种成功。接口成功率百分之百,业务指标在掉——技术面全绿,业务面全红。三种静默前面已经列过。这里补一条观测纪律:降级必须打标并一路透传到坐席界面与审计表。坐席有权知道“这条建议来自规则引擎而不是模型”,审计有权知道“那天晚上分类走的是哪一档”。把降级藏起来,事后无法解释,事前无法回升。
五、容灾:提供商故障不是下游抖动
(一)四层故障面
提供商故障的爆炸半径按层扩大,处置也必须按层选择。区域不可达,爆炸半径是该区域所有模型,处置是切同家其它区域,前提是数据驻留政策允许跨区。账号级失效,爆炸半径是该密钥下全部额度,处置是切备用账号或备用账本,前提是密钥轮换与权限隔离已经做成。模型版本下线,爆炸半径是依赖该版本的落点,处置是切已评测过的新版本或同能力模型,前提是评测集与提示词按模型分版本。整家下线,爆炸半径是该供应商全部能力,处置是跨供应商容灾,走能力矩阵筛选,前提是至少一家能力对齐的备选。
同家换模型不是容灾,跨家才是。同一家的区域故障、账号故障、版本下线,常常会在短时间内变成整家不可用——因为它们共享网关、证书、计费与控制面。把备用模型放在同一家里,等于把鸡蛋放在同一只篮子的不同格子里。格子着火的时候,篮子也着火。
跨家容灾的代价是能力矩阵不齐:结构化输出、工具调用、上下文窗口、安全过滤级别都不一样。所以容灾切换不是改一个模型名,而是走一遍路由层的五步筛选。处置编排这种带副作用的落点,跨家热切要格外谨慎:两家对工具参数的理解不同,可能把“建议退款”执行成“真的退款”。这类落点的容灾默认是降档,而不是换一家继续自动执行。
密钥与账号的隔离经常被当成运维细节,实际上它是爆炸半径的开关。主备账号共用一个密钥,账号级失效会变成整家失效。主备账号的权限集合如果完全相同,主账号泄露时备账号没有减震。正确做法是备账号只开通容灾所需的模型与区域,额度单独封顶,轮换周期与主账号错开。凭据禁止写进配置文件与镜像,必须走凭据服务;轮换时正在飞行的请求用旧凭据完成,新请求用新凭据,中间状态要能同时验两种。
(二)主备与多活
主备与多活不是可用性口味问题,是能力与成本的结构选择。主备日常百分之百走主供应商,备选只在故障时拉起,成本低,平时只维护一套提示词与一套评测;风险是备选长期不走,故障当天一定是生的。多活日常按比例或按租户拆到两家及以上,切换时延接近于零;代价是每家一套提示、一套评测、一份对照,能力不齐会导致同一落点效果分裂。
推荐默认是“主备加影子”,而不是一上来多活。多数团队高估了整家下线的频率,低估了维护两套提示词的成本。每一次业务变更都要在两边同步并各跑一次评测,人手不够时两边都会变浅。影子调用让备选每天都见到真实流量分布,却不对外生效。它把主备的“生”变成“熟”,同时把多活的账单从双倍改成“主份加抽样”。
只有在单家配额长期不够、或合规强制数据驻留到不同提供商时,才值得把多活做成日常结构。即便如此,也按任务拆,不按请求拆。按请求拆会让同一张工单的分类走甲、草稿走乙,坐席看到的是口吻分裂的两段文字,排障时还要拼两家的日志。按任务拆的意思是:分类落点可以长期走甲,草稿落点可以长期走乙,一张工单内部保持一致。
多活还有一笔常常被漏掉的账:两家的安全过滤级别不同。甲拦下的提示注入,乙可能放行;甲误拦的正常工单,乙可能生成。过滤级别必须进入能力矩阵的一票否决项,不能等灰度时用线上工单去发现。
(三)合规与数据驻留把候选集合先砍掉一半
跨区域、跨供应商的第一道门不是效果,是合规。租户合同里写了数据不得出境,候选集合里所有境外端点都要在路由的第二步被滤掉,而不是在出了问题之后靠人工切回。模型厂商的训练用途条款、日志留存周期、子处理器清单,同样是一票否决。能力矩阵把这些项放在效果与成本前面,不是因为它们更“正确”,而是因为它们一旦被打破,后面的对照评测没有意义——评测集再漂亮,合同已经违约。
矩阵的用法不是“找一个全能模型”,而是“为每个落点划定候选集合”。分类落点的候选可以很宽,处置落点的候选通常只有一到两家。为了一个不存在的第三选项去做抽象,是浪费。安全过滤与数据驻留是一票否决项。矩阵要按季度复核:供应商会静默升级模型版本、调整过滤策略、变更区域可用性——准入条件是活的,不是一纸清单。
(四)容灾演练的最小矩阵
不演练的容灾等于没有。最小矩阵覆盖四层故障面各一次,外加一次“备选同时失败”。主区域流量打到备区,看时延与错误率是否回到承诺内;主动吊销主密钥,看切换时延是否在分钟级以内;把主模型版本在预发标记为下线,看路由是否切到已评测版本而不是让业务代码改模型名发版;把主供应商整段注入失败,看是跨家还是降档;最后把备选也打掉,看关能力之后工单系统是否可独立运行。不可接受的现象要写进演练手册:同家内部空转、接口成功但业务空转、处置落点跨家自动执行。
演练要在预发用真实供应商账号做,不要只用桩。桩只能验证分类器的分支,验证不了对方四二九的响应头、静默升级的版本号、以及跨家切换后的真实效果差。每季度至少做一次整家失败演练,并把“关能力后工单系统可独立运行”写成发布门禁。
六、模型切换:可替换性是设计出来的
(一)三种粒度与推进次序
统一抽象抹平的是调用形状,抹不平的是能力、语义与经济性。“改一行配置就能换模型”这句话在能力矩阵不齐、选项不可移植、提示词有模型依赖、计费口径不同、失败语义不同这五个地方不成立。可替换性的正确定义不是“任何模型都能无缝换上”,而是三条:换模型的改动集中在装配层的一个文件里;换完之后有一套自动化的对照评测能给出效果差值;切换可以按比例灰度,并能在几分钟内切回去。满足这三条就够了,追求“零改动”既不现实也不必要。
切换有三种粒度,代价与灵活度依次上升。进程级是配置文件里换模型名与凭据,滚动发布,适用于主力版本升级、供应商整体迁移,回滚是回滚配置再发一次,分钟级。请求级是在调用点通过选项覆盖模型名,适用于同一家内按输入特征选模型,跨家切换不要用这一档,因为选型逻辑会散落到业务代码。任务级是为每个候选模型各装配一个客户端实例,由路由组件按任务类型、租户、灰度分组、当前健康度与成本预算来选,回滚是改路由配置,秒级生效,是三者里唯一需要专门设计的。
多数团队一上来就想做任务级动态路由,实际上进程级已经能覆盖八成需求。推荐的推进次序是三步。第一步先做到按任务建客户端实例,业务代码里不写模型名——这一步不涉及多模型,却是后面两步的前提。第二步把模型名、凭据、供应商特有选项收敛到装配层的一个文件。第三步真正需要跨供应商灰度或故障转移时,才引入路由层。反过来做的团队通常会在第三步上花掉两个月,然后发现第一步没做,路由层选出来的实例仍然共用同一份提示词与选项。
为可移植性付的钱,未必换得回来。某团队为了“保持可移植”,坚持只用通用选项、只写一份提示词、不使用任何一家的原生结构化输出。结果是在主力模型上本可以省掉的重试全都留着,常年多付三成成本;而真正需要换模型那天,仍然要重调提示词、重跑评测。三种情况下应该主动放弃可移植:某项能力带来的收益远大于切换风险;业务已经与某项能力深度绑定;团队规模不足以维护多套提示词与多套评测。判断标准是这笔可移植性的钱多久能兑现一次,如果答案是三年内可能一次也不会,那就不该现在付。
(二)路由层的四个输入与三条出路
需要路由层时,把“选哪个模型”变成一次可解释、可回放的决策,而不是一段藏在工厂方法里的 if-else。四个输入分别是任务声明(这次调用属于哪个落点、需要哪些能力、可接受的延迟上限)、候选清单(每个候选模型的能力矩阵、单价、限流配额、近五分钟的健康度与分位耗时)、策略配置(灰度分组与比例、租户级偏好与合规约束、预算剩余情况)、运行时信号(当前熔断状态、最近的错误率与限流次数、本次输入的长度估算、是否是重试)。
决策过程是五步串行筛选:能力准入、合规过滤、健康度过滤、成本排序、灰度抽样。五步的顺序不能换——先筛掉不能用的,再在能用的里面挑便宜的,最后才让灰度比例起作用。把成本排序放到合规前面,会出现“便宜但不合法仍被选中”的事故。
三条出路是主选、降级链与拒绝。主选是筛选后成本最低的可用候选,要把选中的模型名、决策原因、被淘汰候选及淘汰原因一并写进共享上下文。降级链是主选失败时按顺序尝试的备选,要区分原因——限流可以换同家的小模型,内容拦截换了也没用、应当直接失败。拒绝是无可用候选时的出路:不要静默兜底,应当返回明确的降级结果,让上层决定是排队重试还是转人工。静默兜底会让效果劣化无声无息。
路由必须留痕的字段有六个:请求标识与工单号;任务类型与提示模板版本;路由选中的模型与被淘汰的候选及原因;是否处于灰度分组、属于哪一组;实际返回的模型版本;本次是否为降级重试以及前一次失败的分类。缺任何一项,事后都会遇到“说不清那天到底走了哪条路”的情况。工单号是高基数,只能进日志与链路追踪,绝不能作为指标标签;低基数的维度(任务名、模板版本、模型名、是否降级)进指标,两者通过请求标识关联。
路由做成顾问还是做成组件,取舍在于需不需要看到渲染后的提示。做成独立组件、在装配层选出客户端实例,结构简单,但决策时看不到最终提示、无法按实际长度选模型;做成一个序号极小的顾问,能看到完整提示、能按真实长度估算,但要在顾问内部切换下游模型实现。折中办法是组件先按任务与租户选出候选集合,顾问再按实际长度在集合内做最后一次挑选。
(三)离线、影子、灰度的对照次序
换模型之前有三种对照方式,它们不是备选项而是次序:跳过前两步直接灰度,等于拿线上工单做实验。离线评测成本最低、覆盖最窄,拿固定评测集在候选模型上跑一遍,能回答“新模型在我们见过的问题上是不是更好”,回答不了真实长尾。关键是评测集要定期从线上采样补充,尤其要补进人工纠正过的样本。影子调用成本中等、无用户影响,线上走主模型,异步复制一份给候选,能回答真实流量分布下的效果、延迟与成本;不能用于带副作用的链路——不能退两次款。灰度放量成本最高、结论最真,按租户或类目稳定分组,能回答业务指标有没有变好。分组要稳定,观察窗口要覆盖一个完整的业务周期,必须准备一键切回。
对照的前提:两边用的是各自调优过的提示词,不是同一份。拿为甲模型写的提示词去测乙模型,结论几乎总是错的,而且错得很有说服力。影子调用与评测都要单列预算科目。涉及退款、短信、改期的落点不能影子双写,只走离线评测加灰度,并且灰度期间的自动执行默认降一档。
(四)切回去的成本决定切不切得下去
切换方案如果只写“怎么切过去”,没有写“怎么在十分钟内切回来”,这个方案还不能上线。切回是改路由配置,不是发版,不是等流水线,不是等变更窗口。切回后要能识别出灰度期间产生的数据以便单独分析——这些数据不能直接进入下一轮评测集的“正确样本”,因为它们来自一条尚未被承认的路径。
切回还要处理“半成品”。草稿落点切回时,坐席可能正看着一段尚未完成的流式输出,客户端要把“路径已切换”提示给坐席,而不是把后半段用新模型续上——口吻跳跃比中断更糟。处置落点切回时,已经发出的工具调用按幂等键收敛,未发出的不再发。分类落点切回最简单,因为结果尚未被下游当成事实使用的窗口最短,但仍要把切回前后的类目差记进纠正队列。
七、把四件事装成一条控制回路
(一)装配次序
限流、降级、容灾、模型切换单独看都对,放在一起就会在接缝上长出正反馈。包装顺序决定的是语义,不是风格。推荐的由外向内次序是:关能力开关、四级降级、路由与模型切换、错误分类器、带预算的重试、按供应商加模型的熔断、三处限流闸门、超时与截止时间、供应商网关。每一层只能看见比它更内层的结果,看不见更外层发生了什么——所以语义必须向外传,时间与配额必须向内传。
两条不变量可以从这张次序里直接读出来。第一,任何一层都不应该消费掉它下层的语义。被限流拒绝、被熔断拒绝、被上游取消、被内容拦截,这四件事必须以可区分的形式一路向外传递,否则外层就会把它们当成“下游故障”处理。第二,时间信息必须一路向内传递。外层知道还剩多少毫秒,内层知道已经花了多少毫秒,两者只有拼在一起才是完整的预算视图。默认实现里这两半从不相遇,这正是超时倒挂的根源:上层已经给坐席返回了错误,下层还在为一个没有人接收的结果生成词元、消耗 TPM。
熔断与重试的内外之争,在模型调用这条链路上有一个明确答案:熔断在重试之内、限流之外。重试只对分类器标为“可重试”的动作生效;熔断看到的应当是净化后的失败,不含四二九、不含内容拦截、不含被取消的调用。把熔断放在重试之外,它会把同一次逻辑请求的多次物理失败当成多次独立失败,窗口里的失败率被放大,误断提前。把四二九算进熔断,额度高峰期会把健康模型切断。
正反馈环的形状值得单独看一眼。下游变慢或四二九,上游超时增多,各层重试同时启动,物理请求变成逻辑请求的乘积,配额更快耗尽,镜像闸门来不及收,对方持续四二九,备用账号也被打穿,熔断把四二九算进失败率,健康模型被切断,半开窗口集中放量,方波反复打回,兜底返回空成功,看板全绿,业务指标掉,回升无依据。拆环的四个动作必须同时在:限流拒绝不进入重试也不进入熔断失败率;重试次数由剩余截止时间反推;降级是换路径不是换空对象;关能力开关可一键拉下且工单系统可独立运行。
(二)时间与配额预算切分
时间预算自上而下切。入口承诺先扣本服务处理与检索,剩余才给模型。重试次数等于剩余时间除以单次超时,取整再减一,减掉的那一次留给降级或换模型。换模型重试必须在剩余预算内,否则直接降级。上层放弃后,下层必须能被取消,取消要传到 HTTP 客户端,不能只设一个已经不会被看到的超时。
令牌预算自外而内切。月预算切到日,日切到租户,租户切到落点。进入时按提示长度估算,超水位则拦;返回时按实际用量回写。八成告警、十成换便宜模型、十二成停止。影子与评测单列科目。
两本预算互相约束。时间不够时,即使额度充足也不该换一个更慢的大模型去“提高质量”——用户已经等不及了。额度不够时,即使时间充足也不该把超时调长去硬等——那是在用体验换一张已经透支的账单。分类落点的时间紧、令牌少,适合小模型;草稿落点的时间宽、令牌多,适合在额度水位健康时走主模型。把两本预算写进路由层的运行时信号,路由才能在“此刻”做出与“平时配置”不同的选择。这正是静态配置做不到、动态路由才值得做的原因。
截止时间要用绝对时刻透传,不要用“还剩多少毫秒”的相对值在每一层重新计算。相对值在排队与锁等待里会漂;绝对时刻在进入每一层时减去当前时间,才是这一层真正还能花的预算。透传的字段要进链路追踪,排障时才能回答“到底是哪一层把预算花光的”。
(三)反馈路径与正反馈环
回路要闭环,必须规定每一类信号回到哪一层。四二九回到镜像闸门与租户账本,不回到重试。首字超时回到路由的健康度过滤,可以换同家小模型,不把超时调长。模型版本不一致回到告警与灰度冻结,不回到自动切版本。人工纠正率回到降级档位的回升判据,不回到技术成功率。旁路开关的最近演练时间回到发布门禁,超过三十天视为开关不存在。
这些回边如果画成一张图,会发现它们都不经过“再发一次请求”这条边。这是刻意的。故障期间最贵的动作是增加系统总工作量,而重试是四个机制里唯一会增加工作量的。回路的目标是把乘法改成收敛,收敛的第一刀就是让不该重试的信号永远走不到重试层。
八、落地:代码形状与分层分工
(一)统一入口
业务代码只表达意图。失败翻译、配额扣减、模型选择都不出现在调用点。工单分类的调用看起来应当是“给出工单号、租户号、截止时间,拿回带原因码的分类结果”,而不是“new 一个某供应商的客户端再 catch 异常”。调用点越瘦,切换越快。
统一入口内部的顺序是固定的:若为重试则先让分类器翻译上一次失败;账本按估算词元做水位拦截;路由器按任务声明选出客户端实例;执行;按实际词元回写账本;把模型名、是否降级、原因码、用量一并带回。业务层根据结果决定落库、转人工或关能力。业务层看不到供应商名,也看不到状态码。
装配多实例时不要直接从零构造构建器,那样会丢掉自动装配配好的观测埋点、默认顾问与重试策略。用框架提供的配置器把自建构建器过一遍,自动装配的那部分才会被保留。每个落点一个客户端实例,提示词、顾问链、工具集合、超时都在装配层一次配齐。业务代码里出现零个模型名字符串,是第二步完成的验收标准。
(二)错误分类器、路由器、账本
三个组件的边界要写死。分类器输入状态码、响应头、供应商、异常类型,输出四类动作枚举加是否允许跨家,禁止在业务代码里二次解释状态码,内容拦截与欠费走直接失败。账本维度是租户乘落点乘自然日,水位八成、十成、十二成,估算拦截、实际回写,影子与评测走独立科目。路由器五步筛选顺序不可换,三条出路都要留痕,切回是改配置不是发版,处置落点默认不允许跨家热切。
分类器要用密封接口或枚举表达结果,不要返回布尔值或空对象。布尔值会把“可重试”和“换模型重试”压成同一个 true;空对象会把直接失败和降级成功压成同一种形状。账本的金额字段必须用BigDecimal,水位比较必须用compareTo,禁止转成double再比。路由器的候选清单是接口类型的列表,而不是某个供应商实现类的列表——否则“按任务建实例”会在类型上被击穿。
线程池禁止用Executors工厂方法。路由里的影子调用、异步回写账本、跨家探测,都要走显式配置的ThreadPoolExecutor,队列必须有界,拒绝策略要能把溢出记进观测而不是静默丢弃。业务代码里创建临时线程池是红线:它既没有边界,也没有关闭,故障期间会把自己变成新的故障源。
(三)网关、应用、供应商三侧分工
网关负责入口突发、恶意扫描、粗粒度的请求速率,不理解词元,不理解模型名。应用层负责三本账里的令牌预算与并发许可、错误分类、路由、降级阶梯、截止时间透传。供应商侧不受你控制,但你必须镜像它的配额、记录它的响应模型版本、把它的四二九翻译成一等公民的结果。三侧的拒绝码要能在日志里区分来源:网关拒绝、应用账本拒绝、供应商拒绝,混成一个 429 会让值班回到猜的状态。
服务网格可以分担一部分超时与重试,但不能分担失败翻译。网格看见的是 HTTP 状态码,看不见“这是内容拦截还是配额耗尽”,更看不见“这个四二九是模型级还是账号级”。把模型调用的重试交给网格,等于把七类失败重新压回两类。网格适合做连接级的超时与熔断,不适合做模型级的路由。职责写进同一份 YAML,是限流配乱在基础设施层的对应物。
九、观测、演练与发布门禁
(一)必须能回答的十个问题
值班要能在五分钟内回答十个问题。此刻主模型是否健康,按供应商加模型看错误率与 P99。四二九是哪一本账打满,RPM、TPM、租户日额度拆开。当前降到了第几级,按落点看降级次数与降级后可用率。路由选了谁、淘汰了谁,看决策原因与灰度分组。请求模型与响应模型是否一致,抓静默升级。单位工单成本有没有跳,按落点、按租户、按日。重试放大了多少倍,逻辑请求对比物理往返。人工纠正率有没有涨,这是业务成功率。备用供应商今天走过没有,看影子命中与备选错误率。关能力开关能不能立刻拉下,看旁路演练的最近一次时间。
低基数进指标,高基数进日志与追踪,两者用请求标识关联。工单号、坐席号、完整提示快照不能打成指标标签,时间序列会在几天内被撑爆。提示快照里可能仍含业务敏感信息,存储要加密并按最小权限开放;写失败必须降级放行,不能因为审计写不进去而拒绝服务。手机号、身份证号、银行卡号禁止明文进日志,脱敏后再落。
框架默认给的埋点回答的是“调用发生了什么”,业务归因需要的是“这次调用属于谁、为了什么”。要自己补的四个维度是业务对象标识、任务与模板版本、路由与档位、质量结果。缺了质量结果,静默降级在技术看板上会永远是绿的。
(二)故障注入矩阵
注入要写期望现象,更要写不可接受的现象——后者才是回路自我放大的指纹。主模型四二九,期望镜像闸门收紧、改走备用或降精度,不可接受的是重试把到达率打得更高。主模型首字超时,期望换同家小模型、P99 回落,不可接受的是把超时调长硬等。主模型版本下线,期望路由切到已评测版本,不可接受的是业务代码里改模型名发版。整家网关失败,期望跨家容灾或降档,不可接受的是同家内部空转。租户额度打满,期望该租户降档、其它租户不受影响,不可接受的是全站被一个租户拖死。内容安全拦截,期望直接失败并告警,不可接受的是换模型继续生成。备选同时失败,期望关能力且工单系统仍可用,不可接受的是接口成功、业务空转。半开窗口放量,期望探测流量受并发闸门约束,不可接受的是方波打回刚恢复的模型。
注入要在预发用真实供应商账号做。每季度至少做一次整家失败演练。演练记录进看板:最近一次时间、发现的不可接受现象、修复是否已进入门禁。没有记录的演练等于没有做过。
(三)两条告警与一张看板
一张值班看板要同时有技术面与业务面。供应商健康看错误率、P99、首字延迟、请求与响应模型一致性、四二九单独一条线。配额水位看集群 RPM 与 TPM、租户日额度分布、镜像闸门拒绝率、影子科目消耗。降级与路由看当前档位、降级次数与回升次数、路由淘汰原因、旁路开关状态。业务结果看人工纠正率、一次解决率、单位工单成本、转人工队列深度。只看错误率会错过静默降级;只看业务指标会错过配额击穿。
两条告警就够,多了没人看。告警一:任一落点的业务成功率与技术成功率之差超过阈值,持续五分钟。这是静默降级与静默升级的指纹。告警二:任一租户或任一供应商的令牌水位在十五分钟内穿过八成,且到达率未降。这是配额击穿前的最后窗口。其余信号进看板,不进电话。旁路开关的最近演练时间也要在看板上,超过三十天没演练过的关能力路径,应当被视为不存在。
租户账本的三级水位是告警二的落地形态。到八成只告警不改路径,给值班留出判断时间;到十成只动该租户;到十二成停止该租户的智能层。水位按租户隔离,全站一个硬顶一定误伤。
发布门禁有四条。评测集在四个档位上都跑过,且降级档的可用率不低于约定下限。影子科目的预算没有被业务流量挤占。旁路演练在三十天内有记录。业务代码的 diff 里不含模型名字符串、不含对状态码的二次解释。四条任何一条不满足,智能层的流量都不该放到百分之百。
十、反模式清单与一页纸
十二条反模式在联调阶段都看不出来,它们的共同特征是把一个机制的输出,当成了另一个机制本来的输入语义。把四二九当成五零二重试;只限 QPS,不限令牌与并发;降级返回空成功;四级阶梯平时不演练;备用模型放在同一家;跨家热切处置落点;一份提示词通吃所有模型;业务代码里写死模型名;技术成功率当业务成功率;影子流量混进业务账单;熔断把限流拒绝算进失败率;上层放弃后下层继续生成。
修法只有一种:把语义约定补回接缝。拒绝就是拒绝,降级就是降级,切换就是切换。不允许任何一层把这三件事翻译成“调用失败”再交给下一层自由发挥。反模式清单要进代码评审,而不是进事后复盘。评审问题可以写成四句:这次 diff 有没有新增对状态码的解释?有没有新增模型名字符串?降级路径有没有测试?影子与评测有没有走独立科目?
一页纸是最低交付标准,不满足其中任何一条,都不该把智能层的流量放到百分之百。失败语义要七类可区分、四类动作是枚举、内容拦截禁止换模型、欠费禁止打下游。限流要三本账、三处闸门、四二九外传且不计入失败率、本地比对方更早收手。降级要四级阶梯按落点配置、下降易回升难、关能力真实可跑、两张成功率曲线拆开。容灾要故障面分四层、主备加影子为默认、跨家走能力矩阵、处置落点默认降档。切换要按任务建实例、路由五步不可换、离线到影子到灰度、切回是改配置。观测与门禁要十问五分钟内可答、注入矩阵每季演练、旁路开关可一键拉下、评测与影子单列预算。
十一、结语:从连通性到控制回路
调通一次模型接口,证明的是网络、密钥、协议这三件事成立。生产要证明的是另一件事:当模型不可用、响应过慢、配额耗尽、提供商整段故障这四类事件同时出现时,系统仍然知道自己该限流、该降级、该容灾还是该切换,并且这些动作消耗的是同一本账、留下的是同一组可回放的证据。
限流保护配额。它不是一个 QPS 数字,而是并发、速率、令牌三本账,落在网关、租户、镜像三处,拒绝语义能一路传到重试与熔断之外。降级保护业务路径。它不是一句友好错误,而是四级事先写好、事先演练过的梯子,下降容易、回升困难,关能力是真实开关。容灾保护供应商。它不是同家里换一个模型名,而是按区域、账号、版本、整家四层选择处置,默认主备加影子,跨家走能力矩阵,处置落点默认降档。模型切换是执行动作。它不是“改一行配置”,而是按任务建实例、用路由五步筛选、用离线到影子到灰度的次序对照,并且切回去比切过去更重要。
四件事只有共享同一份时间与令牌预算、同一套失败语义、同一个由外向内的装配次序,才构成能自动收敛的控制系统。缺任何一项,它们就退化成四个互不知情的开关:平时看不出差别,而在故障的那几分钟里,正是这个差别决定了系统是自我收敛还是自我放大。连通性是起点,控制回路才是交付物。
参考资料
- Resilience4j 官方文档:限流、熔断与重试
- Sentinel 流量治理:规则、隔离与熔断
- Spring AI 参考文档:Chat Client 与模型抽象
- Spring AI 可观测性与观测约定
- OpenAI 平台:速率限制与最佳实践
- OpenAI 平台:生产最佳实践
- Azure OpenAI:配额与限制
- Google Cloud Vertex AI:配额与系统限制
- Anthropic:速率限制说明
- AWS Well-Architected:机器学习可靠性支柱
- Google SRE 手册:处理过载
- Google SRE 手册:应对级联失效
- Microsoft Azure 架构中心:熔断模式
- Microsoft Azure 架构中心:限流模式
- Microsoft Azure 架构中心:隔舱模式
- LangChain:回退与降级
- LangChain4j:模型与故障恢复
- Portkey:AI 网关的故障转移与可观测
- Kong AI Gateway 文档
- OpenTelemetry 语义约定:生成式 AI
- CNCF 云原生可靠性:熔断与超时
- NIST AI 风险管理框架
- OWASP 机器学习安全 Top 10
- Twelve-Factor App:配置、进程与丢弃
- JDK HttpClient 官方指南
- Micrometer 观测文档
- OpenAI Cookbook:生产环境的重试与幂等
- Google Cloud:生成式 AI 的负责任部署
- Azure 架构中心:生成式 AI 解决方案设计
- AWS Prescriptive Guidance:生成式 AI 的韧性