Agent SLA 与生产级可用性工程 2026:从承诺、降级到熔断校验的闭环架构
把 SLA 当作一个三层闭环来展开,配套 SLO 一等公民、可观测性栈、退路熔断、对账工程、六个必走动作清单与 30/60/90 上线节奏。
约 18 分钟阅读5,242 字25 次阅读博主

把 SLA 当作一个三层闭环来展开,配套 SLO 一等公民、可观测性栈、退路熔断、对账工程、六个必走动作清单与 30/60/90 上线节奏。

一句话摘要:当 Agent 的输出从"演示"进入生产,SLA 就从工程议题变成商业契约。这篇文章把 SLA 当作一个三层闭环(承诺层、执行层、事后熔断层)来展开,配套 SLO 一等公民、可观测性栈、退路熔断、对账工程与六个必走动作清单。
把一个 Agent 从 demo 推到生产,最容易在"可用性、延迟、正确性"三个维度同时失约。失约不是模型抽风那么简单,它通常是三类黑洞叠加的结果:
这三类黑洞的本质都是契约未落地:契约写在文档里、API 描述里、合同里,但没有哪一行代码把契约当作运行时不可绕过的承诺。文章把 Agent SLA 当作一个**三层闭环:承诺层(语义化建模)→ 执行层(运行时约束)→ 事后熔断层(对账 + 灰度回滚)**来拆,并相应给出工程模式、可观测性栈、踩坑反模式与可操作的工程清单。
截至 2026-07-22,主流 Agent 平台(OpenAI Agents SDK、LangGraph、Claude Agent SDK、AutoGen、CrewAI)仍未把 SLA 作为一等公民——SLA 是写在 README 与计费条款里的话术,而不是写在 RPC header / 调用栈 / 看板上的契约。我们这篇文章要做的,就是把这种"嘴上承诺"翻译成"代码承诺"。
为什么 2026 是 SLA 工程的拐点——三个证据同时出现:(1) 大模型推理成本曲线终于在 2025 Q3 到 2026 Q1 走平,单位 token 价格从两位数年降率变成高个位数月降率,下游开始算"承诺率"而非"模型好坏";(2) Agent 调用模式从"人发起一次"演化成"系统持续调用数百次",复合 API 链路的失败概率超过 30%,SLA 不工程化就是散单;(3) 监管侧(欧盟 AI Act、美国 EO 14110、中国《生成式 AI 服务管理暂行办法》)开始把"可问责的自动化决策"提到合同级要求——而 SLA 是"可问责"的工程表达。这三条证据共同意味着,2026 年再不把 SLA 工程化,Agent 项目就要在合规与合同两端同时失约。
在进入工程实现前,先做一次严格形式化。Agent SLA 不是单一指标,而是三维契约——任何一维失约,SLA 三角就破了一个角,整体契约就失效。
SLA ≡ ⟨availability, latency_p99, correctness⟩
其中:
availability ∈ [0, 1] — 在单位时间内 (按月或按天) 正常服务的比例
latency_p99 ∈ ℝ⁺ ∪ {∞} — P99 延迟上界(秒);超过 = 契约破裂
correctness ∈ [0, 1] — 输出"对"的最低比例(在带标签评测集上)
三维契约工程化的关键,是把每个维度都拆成可观测量(SLI)+ 承诺上界(SLO)+ 违规触发器(burn-rate):
为什么是 burn rate(燃烧率) 而不是原始数值?SRE 工程实践反复证明,单纯的 SLO 阈值在事故窗口的第一秒几乎没用——一个 P99 延迟突然从 4 秒飙到 14 秒时,系统还需要几十秒才能"看清"是不是真的破约;而 burn rate 在第一秒就会告诉你"超阈值 3 倍,正在以每小时 X% 的速率消耗 error budget",触发自动熔断、降级、回滚预警。
承诺三角的不对称:三个维度不是平权的。correctness 是反射性的(错了必须改重,但要承担重做成本),latency 是即时的(超一秒就是超一秒,没有"延迟补救"),availability 是滞后累计的(月粒度的"服务可用率"在第 30 天才能算出一个数)。实际工程中,正确性差 1% 比延迟差 5% 危险得多——因为 correctness 失约会侵入到下游业务决策里("你帮我订机票,订到非洲去了")。
承诺层要做的事只有一件:把"99.9%"翻译成可执行的契约,让 SLO 既能落在文档里,也能落在 RPC header 与告警规则里。
# agent.runtime.sla v1.0
tenant: enterprise-tier
scope: agent.svc.fin_assistant
budget:
monthly_window: 43200 # minutes
availability_budget_minutes: 43.2 # 99.9% 对应
latency_budget_overruns:
p99: 8s
burn_rate_2x_5min: trigger_incident
burn_rate_14_30min: trigger_paging
error_policies:
on_correctness_fail: rollback_to_last_known_good
on_latency_exceedance: degrade_to_medium_model_with_prompt_compression
on_availability_drop: route_to_backup_region
compensations:
on_breach: 25% next-cycle-discount + engineering_postmortem_in_5bd
这份文档不是给人读的,它是 contract-as-code——任何运行时都按这份契约核对状态。值得强调的两点:
HTTP/1.1 200 OK
X-LLM-SLA: tenant=enterprise-tier; availability_target=0.999
X-LLM-SLA-SLO: latency_p99=8s; correctness_min=0.95
X-LLM-SLA-Region: us-east-1; failover=eu-west-1
X-LLM-SLA-Budget-Remaining: 37.4%
把 SLO 写在 response header 里,好处是任何调用方、网关、Audit 系统都可以零成本读取与核对。更进一步,可以在每条路径上贴上契约,例如:
# 服务端
def respond_with_sla_header(payload):
return 200, payload, headers={
"X-LLM-SLA-SLO": "latency_p99=8s; correctness_min=0.95; availability=0.999",
"X-LLM-SLA-Budget-Remaining": budget_remaining_str(),
}
# 客户端(网关)
assert response.headers["X-LLM-SLA-Budget-Remaining"] > "5%"
# 真要触发降级,而不是死磕
这一行断言看起来小,但它把"承诺"从口头变强类型:当 budget 低于 5% 时,网关自动降级(切小模型 / 切精简 prompt)而不是继续给最终用户开空头支票。
真实生产中不是单一 Agent——通常有 search agent、planner agent、tool agent、code agent,各自有 SLA:
| Agent | availability | latency_p99 | correctness | 备注 |
|---|---|---|---|---|
| router | 0.999 | 300ms | 0.99 | 一个请求分错路由比算错答案还糟 |
| planner | 0.998 | 2s | 0.95 | 慢一点可以接受,错了回不去 |
| tool | 0.997 | 5s | 0.99 | tool 失败要立即降级到 noop |
| code | 0.99 | 60s | 0.85 | 长任务,错误率稍高,但失败可重做 |
关键洞察:系统的整体 SLA 不是子 SLA 的平均值,而是子 SLA 的函数。SLA_system = f(SLA_router, SLA_planner, SLA_tool, SLA_code)——如果 router SLA 不及格,下游所有子 SLA 都白做;如果 code 子 SLA 不及格但可重做,整体 SLA 不一定不及格。承诺的工程化必须分层。
承诺层落地的关键是执行层——把 SLO 变成运行时不可绕过的约束。
超时不是单一阈值,而是一棵分层超时树:
end_to_end_timeout (8s)
├── planning_timeout (1.5s)
│ ├── llm_call_primary_timeout (1.2s)
│ └── llm_call_fallback_timeout (1.0s)
├── tool_dispatch_timeout (3s)
│ ├── sandbox_spawn_timeout (0.8s)
│ └── tool_exec_timeout (1.5s)
└── synthesis_timeout (2.5s)
└── llm_call_primary_timeout (2.0s)
为什么 timeout_sum < budget?因为每一跳超时不应触发全部预算。如果每一跳都用"8s 上限",整条调用栈累计可能吃掉 40s——burst 与排队失约的根因就是不收敛的超时树。推荐配置:总预算的 60-70% 给关键路径,30-40% 给异常路径。
长任务 Agent(planner 或 code agent)必须支持租约续约:
class AgentTask:
def __init__(self, ttl=60):
self.lease = Lease(ttl=ttl, owner=self.id)
self.heartbeat = every(5s)
def tick(self):
try:
self.renew_lease(extend=5) # 续约 5s
except LeaseExpiredError:
self.checkpoint_then_die() # 优雅死亡,残留状态已落盘
租约续约的工程价值:长任务不会"卡死"。当 Agent 的某一步陷入无限循环(典型 bug:function call 自调用、tool 返回值含循环引用),租约过期会强制它checkpoint 然后优雅死掉,而不是让用户等到天荒地老。
租约设计三条铁律:
降级不是单维度的——需要一张降级矩阵来对 (latency_burn_rate, correctness_burn_rate) 平面做工程响应:
| low_latency_burn | high_latency_burn | |
|---|---|---|
| low_correctness_burn | normal | degrade_to_fast_model + cache fallback |
| high_correctness_burn | degrade_to_better_model + rerank | emergency_breaker_full_route_failover |
降级矩阵的工程意义:在 P99 慢但答案对时,自动切到更快模型(甚至切回 7B 小模型);在 P99 快但答案错时,自动切到更强模型(甚至切到 RAG 重排序);当双维度都炸时,自动熔断切到 backup region。
在实现上,建议降级动作是一等公民枚举而不是函数库调用:
enum DegradeAction {
NOOP = "noop"
SWITCH_MODEL_FAST = "switch_to_fast_model"
SWITCH_MODEL_BIG = "switch_to_big_model"
COMPRESS_PROMPT = "compress_prompt_lz4"
RERANK = "apply_rerank"
DEGRADE_TO_RULE = "fallback_to_rule_engine"
ROUTE_FAILOVER = "route_to_backup_region"
EMERGENCY_BREAK = "emergency_break"
}
显式枚举的好处是任何动作都能被观测、可被审计、可被演练。
生产实践中降级经常被混淆为"瞬时动作"——切完就走。事实上,降级应该同时有两面:
为什么需要"持久"那一面?如果一个租户连续 24 小时触发 SWITCH_MODEL_FAST,但配置上每条请求仍可能在 v2 模型上决策——这是更隐蔽的失约:看起来还在用强模型,实际每次都切到 fast 模型。持久降级模式应该把整个 agent 服务的默认模型从 v2 切到 fast,并把这个降级写进租户级 SLA 文档;用配置而非每次请求的"动态切"来管理。这种"双视图"让降级既是单点决策(瞬时),又是状态合同(持久),互不干扰。
降级持久化的额外收益:复盘速度更快——不是去翻请求级 degradation_path,而是直接看"过去 24 小时这条租户是否处于 fast-only 模式"。这会让你的复盘从"翻请求 → 对账"变成"翻配置 → 决策",复盘时间可以从 30 分钟压到 5 分钟。
降级不是"出问题时少做一点"。降级是承诺被撕毁后的工程纪律:把"做错"转为"不做错",把"迟到的真答案"转为"按时的可信 fallback"。
工具调用失败时,常见三种曲线:
降级工程应该消除 B 与 C。具体来说:
class ToolCallPolicy:
def execute(self, tool, args):
try:
with deadline(deadline=tool.tool_timeout):
return tool.invoke(args)
except DeadlineExceededError:
# 不是 raw timeout 异常 → 在 SLA breaker 中标记"本 tool 已 timeout"
breaker.trip_on_timeout(tool)
return FallbackResult(
kind=tool.fallback_kind, # "noop" / "rule" / "cache_hit"
reason="timeout",
original_exception=DeadlineExceededError,
)
关键的工程动作是降级结果必须有 fallback_kind 字段,方便观测层区分"工具成功返回空"和"工具失败被降级"——这两类事件的应对完全不同。
降级不只是客户端——服务端要做前向降级:
X-LLM-SLA-Budget-Remaining: 2% 给下游前向降级是给下游的礼物——它让下游的 SLA 也跟着收紧,而不是"上游愉快地破约,下游毫无准备地破约两次"。
降级的红牌:
事后熔断是 SLA 的事后脸面——它决定你事故后是"半小时搞定"还是"睡 24 小时"。
事故后要回答三个问题:
事后校验的工程动作:
任何 prompt 改动、模型升级、工具接入必须经过灰度:5% → 25% → 100%。灰度判定的金标准是 burn rate × accuracy:
accuracy_old × (1 - burn_old) vs accuracy_new × (1 - burn_new)
如果新版本在该公式下 < 老版本,立即 100% 回滚;如果持平,留灰度再观察;如果更高,继续放量。
灰度反模式:用"用户投诉量"判灰度。用户投诉量比 burn rate 滞后 5-60 分钟,而 burn rate 是实时的——用户开始吐槽时,回滚的最佳时机已经过了。
对账是工程团队 vs 业务团队的共识:每天 04:30 CST,系统自动校准 SLO:
对账的工程价值:让 SLO 不是"客服收到抱怨"驱动的,而是机械可验证的。这种可验证性反过来稳定工程团队与业务团队的信任。
一份合格的对账报告,至少带四个证据:
证据四元组的工程意义:让对账不只是"昨天 SLA 多少",而是"昨天的失约都是什么、发生在谁身上、系统是如何处理的"。这四个证据加在一起,下一周的工程优化就有了目标。
把 SLO 当一等公民,意味着SLO 出现在 metric/log/trace/dashboard 每一层,而不是被压扁在一张"展示用 dashboard"里。
Prometheus 风格的 SLO metric 至少要四类:
slo_budget_remaining_ratio{tenant, agent} → 实时slo_burn_rate_5min{tenant, agent} → 滚动窗slo_attainment_daily{tenant, agent} → 每天 04:30 落slo_attainment_monthly{tenant, agent} → 每月 04:30 落SLO 不能只算率——还要算"自上次违约至今天数"——它是 stakeholder 沟通的语言。
每次输出都加 slo_context 字段:
{
"ts": "2026-07-22T08:14:23Z",
"request_id": "...",
"agent": "fin_assistant",
"slo_budget_remaining": 0.61,
"burn_rate_5min": 0.02,
"latency_ms": 1240,
"model": "gpt-5-mini",
"degradation_path": ["model_chosen", "prompt_compressed"]
}
degradation_path 字段是事后定位"为什么这次没满足 SLA"的金矿。没有它,事故复盘就变成"为什么会慢?"的猜谜。
Trace 是 SLO 的赛博骸骨:
不要把 SLO dashboard 与业务 dashboard 强行合并。SRE 工程经验:SLO dashboard 是"我守约了吗",业务 dashboard 是"用户来买了吗"。两者用不同的 refresh frequency、不同的看板主色调、不同的 owner。
四个 SLA 工程的高频反模式:
最后给落地清单——六条必走动作:
把以上六条实现一遍,Agent SLA 从"漂亮的承诺"变成"代码承诺"——这才是 2026 年 Agent 平台工程的真相。
光写清单不验收等于没写。给六条动作各配一个度量:
/etc/agent/sla/*.yaml 下应有 ≥ N 个租户文件 + CI fail 当缺字段"度量是 SLA 工程不被"滑过"的关键——上面的每一条都防止"清单写了但没做"的典型工程漂移。没有验收度量的清单是工程支票,没有行动清单的验收度量是空喊。二者缺一不可。
行动到度量都已就位后,按"30/60/90"上线节奏展开:
30/60/90 不是空架子——它是把"6 条动作"翻译成"日历里程碑"的尺度。每一阶段都有自己的可验证产出、有自己的失约代价、有自己的升级路径。没有时间维度的工程清单必然失约——这是我们在多个生产团队观察到的普遍规律。
Conversation
0 条