0%

稳定性:从传统工程到大模型时代

1. 缘起

在大模型时代,模型响应时间以及响应内容确定性未知的情况下,怎么做好稳定性?
过去做过的经验能不能复用呢?比如高并发系统(TPS 25w、QPS 100w、TP999 < 80ms)。
我理解是可以复用的,因为本质上都是软件工程的问题,但有几处会变,放在第 3 节展开
把事前、事中、事后这几步给做好,做扎实,怀着敬畏之心,肯定是 OK 的。

2. 全链路稳定性

全链路走六段:设计阶段留预案,开发阶段立规则,发布阶段卡流程,运行阶段看得见、扛得住,故障时先恢复再定位,复盘时把错误变成资产。
这些流程不是凭空定的,都是血泪教训总结出来的——不要因为省事而越过流程。

2.1 设计:让稳定性融入设计

把背景、约束理清楚。遇到高速增长的系统,我们要先考虑优化系统,再考虑增加硬件资源。平衡成本与收益。

  • 依赖分级:把系统的强弱依赖搞清楚,强依赖尽量保证能降级;
  • 失败处理:超时、重试、熔断、降级;写操作需要幂等( 网络默认是不可靠的 );
  • 隔离策略:按业务、租户、链路进行物理或逻辑隔离,避免互相影响。( eg:DB 的读写分离 )
  • 兼容策略:涉及不兼容问题、特别是 C 端需要考虑新老接口共存,逐步切流。

设计阶段需要产出一份稳定性预案,主要描述:这个系统什么情况下会挂、挂了怎么办、对应怎么处置。

2.2 开发:规则约束

  • 架构一致性:把架构边界写成测试( 比如”领域层不能依赖基础设施层” ),用 ArchUnit 做成用例,越界就红,不靠人记。
  • 流水线卡点:静态检查、规范检查、单测覆盖率、圈复杂度,全部设成”不通过不合并”。
    • 覆盖率看增量,不看整体。整体覆盖率会被历史代码稀释,新代码的覆盖率才反映当前质量;
    • 圈复杂度超阈值( 比如单函数 10-15 )就该拆。复杂的地方就是出 bug 的地方,也不好读。
  • 可观测埋点:新增的功能,默认的埋点是否足够?核心节点有相关日志吗?
  • CR 重点:超时、幂等、埋点、日志规范、可读性、复杂度 ( 当然,业务准确性也需要看的 )

形成良好的开发习惯、通过 CR 或者排查问题复盘等沉淀团队共识,不断完善机制。

2.3 发布:流程约束

过程中主要有:优雅启停、灰度发布、观察上下游日志、监控、告警,验证业务数据。
发布之前要有上线 checklist,发布的时候按顺序傻瓜操作。什么时候算通过、什么情况要回滚需要明确。

  • 灰度发布:分批次,按机房、按比例滚动发布;
  • 细心监控:发布过程心怀敬畏,如果发现不符合预期,暂停发布
  • 可靠回滚:允许一键回滚(一般是回滚程序,比较特殊的回滚:动态开关切回老链路),需要考虑数据兼容、版本兼容等问题。
  • 变更管控:明确发版节奏( 例如周二、周四发版 ),遇到大促等,严格管控变更。( 系统变更是万恶之源!)

2.4 运行:可观测、可容错

  • 可观测:尽早发现问题、排查问题
    • 业务层面:单量、转化率等等
    • 应用层面:延迟、错误日志
    • 硬件层面:CPU、内存、网络、load等等
    • 基础设施:依赖的 Redis、MySQL、发号器等
  • 监控告警:早于用户感知、有专人 oncall、有执行预案
  • 容错处理:限流、熔断、降级、隔离
  • 容量管理:定期压测、留有一定冗余、容量告警(默认 85%,增长快速的业务要时刻盯着)
  • SLO 与错误预算:上面几条讲的是”怎么发现问题”,这条讲”什么时候该停下来修”。
    • SLI 是实测量( 成功率、延迟 ),SLO 是目标( 比如 99.99% ),错误预算 = 100% - SLO;
    • 预算烧完就冻结发布,先修稳定性——否则稳定性永远排在业务需求后面;
    • 这一步的意义是把稳定性从”口号”变成”可量化的资源”:业务要快可以,但得花预算。

2.5 故障:优先恢复而不是定位问题

实行故障分级制度,可以按时间、业务指标(GMV)等进行定级。
尽量做到 1-5-10:1 分钟发现、5分钟响应、10分钟恢复。
先恢复服务再查根因:降级、回滚、切流等进行止损,别召集排查代码。

出现重大故障,只能单一指挥,否则会出现 N 个人一直给 oncall 的人施加压力。

大部分故障来源都是:应用变更、配置修改等。所以变更管控是稳定性里性价比最高的一环。

2.6 复盘:让错误成为资产

尽量避免追责到人,否则当事人会倾向于隐藏事故,导致故障影响扩大。

复盘主要看:根因、改进(有 owner、有期限)、下次再出现怎么快速发现?

改进部分可以贯穿整个迭代过程:设计、编码、发布、监控等环节。

3. 大模型场景:这套框架哪里要变

框架是通用的,但大模型有几处和传统后端根本不一样,得单独说。

3.1 差异:三处和传统后端根本不同

  • 输出非确定:同样的输入,两次输出可能不同。
  • 计费在运行时:传统系统的容量成本是阶梯的( 加机器 ),大模型是每请求计费,成本随流量线性涨。
  • 依赖不可控:供应商的配额、限流、故障你修不了。

3.2 指标:四个维度都要换口径

延迟上,TTFT 只是体验的一半。

  • TTFT( 首字延迟 ):决定用户”等多久”;
  • TBT / TPOT( token 间隔 ):决定用户”读得顺不顺”。
  • 端到端总时长;
  • 流式中断率:首字出来了、后面断了。传统接口没有”部分成功”这种状态,流式有。

成本上,token 用量要拆开看。

  • 输入 / 输出 token 分开统计( 输出单价通常是输入的 3-5 倍,混在一起看不出问题 );
  • 缓存命中率(KV-Cache / prompt cache):直接决定成本,也直接反映 prompt 布局好不好;
  • 单会话 token 成本;
  • 成本突增要当故障告警。prompt 膨胀、重试风暴、Agent 死循环,表现都是 token 飙升——它是前兆,不是账单。

质量维度,传统稳定性里完全没有这一块。

  • 格式合法率:结构化输出( JSON / 卡片 )能不能解析;
  • 幻觉率 / 忠实度;
  • 工具调用成功率;
  • 拒答率:该拒的拒了吗( 这类场景,漏拒比误拒危险 );
  • 轮次衰减:聊到第 15 轮,效果掉没掉。

容量上,吞吐的单位变了。

  • 看 token/s,不是 QPS。同样的 QPS,输出长度不同,资源占用能差 10 倍;
  • 上下文长度分布:长上下文是资源黑洞,一条超长会话能拖垮一个实例。

3.3 机制:要新增的四件事

  • 评测门禁:输出非确定、没有断言可写,”系统是否正常”只能靠评测集判断。评测门禁就是大模型系统的回归测试,而且是唯一可行的那个。
  • token 预算 + 超限降级:单请求、单会话、单租户都要有预算,超了降级( 缩上下文、切小模型、直接拒 ),不能放任。
  • 多模型路由 + Failover + 可插拔:供应商挂了、限流了、涨价了,都要能切。模型是可替换的商品,不是基础设施。
  • 格式校验:结构化输出在渲染前先校验(schema / AST),非法的往下走不了。

3.4 小结:大模型带来的三条增量

上面 2.1 到 2.6 该做的还是要做。
大模型带来的增量就三条:

  • 不能靠断言判断对错,所以要有评测;
  • 成本随流量线性涨,所以要有预算;
  • 模型不是你能修的东西,所以要有替换方案。