事故响应与失控遏制

O6
运维 · 智能体运维:部署与运营

事故响应与失控遏制:那个停不下来的循环。

一个崩溃的服务会停止造成损害;一个失控的智能体会继续行动——在你手忙脚乱时仍在循环、花钱、向世界写入。智能体事故有一个普通故障没有的性质:系统就是攻击者的双手,而"什么都不做"并不安全,因为智能体还在跑。本文是值班的现实:你如何检测失控、按下什么把它停住、如何限定爆炸半径、以及轨迹里必须有什么才让复盘成为可能。

STEP 1

智能体事故是一个活跃的对手,不是一次被动的失败。

服务运维的反射在这里会误导你。一台卡死的 Web 服务器 fail-closed——它停止服务也停止伤害。一个卡死的智能体 fail-open:在你注意到并反应所需的整段时间里,它继续迭代、继续调用工具、继续花钱,可能继续向生产写入。成本与损害是在你的响应延迟上积分出来的。对智能体,平均遏制时间压倒平均诊断时间——先止血,再理解。

你实际会见到的失控形态:紧凑的工具调用循环(同一动作,速率上升)、反思螺旋(永远重规划,状态不变)、病态扇出(子智能体派生子智能体)、毫无进展的预算燃烧,以及危险的那种——一个连贯的智能体自信而飞快地做的事(一个坏提示或一次规模化的模型回归)。

STEP 2

从速率与进展检测,而非从错误。

失控往往零错误抛出——它在错误的事情上"成功"。错误率仪表盘保持绿色,而你在流血。真正会触发的信号是导数与比率:

  • 每运行步数与每分钟工具调用数越过硬上限——这是循环检测信号,也是 cost-control-in-the-loop 那个无进展指标兼任安全警报。
  • 每租户/全队的花费速率配一个绝对值告警,而不只是日总额——失控会在一小时内烧光当月预算,而日汇总明天才告诉你。
  • 写动作速率与首见目的地——有副作用的调用激增,或动作打到从未见过的目的地,无论"是否成功"都是遏制触发条件。
  • 进展对成本比塌缩——大量活动、零闭合回路——是最干净的单一失控特征。

对短窗口内的绝对速率告警,而非累计日总额。一个日花费仪表盘会在失控已经让你付出最多代价之后才报告它。有用的警报是"过去 5 分钟 X 美元",接到熔断路径上——不是一封邮件。

STEP 3

熔断开关,按爆炸半径与可逆性分层。

一个全局总开关太钝(为停一个租户而搞垮所有健康租户)也太慢够不着。建一架分级阶梯,让值班能用最小够用的锤子去匹配事故:

# containment/killswitch.py — checked before every tool call
def guard(run, tool, args):
    if flag("halt:global"):           # 4: stop the world
        raise Halted("global")
    if flag(f"halt:tenant:{run.tenant}"):  # 3: one tenant
        raise Halted("tenant")
    if flag(f"halt:run:{run.id}"):       # 2: one run
        raise Halted("run")
    if tool in frozen_tools():            # 1: disable a capability
        raise Halted(f"tool:{tool}")     # reads still flow
    return args

使熔断开关在压力下可信赖的性质:它在循环内、每个副作用之前被检查(不是一次部署,不是一次会丢状态的进程 kill)、它fail-closed(标志存储不可达 ⇒ 停机,而不是"放开继续行动")、并且能力冻结(第 1 级)存在,使你能让危险的写工具失效而只读诊断继续。

对一个持久智能体来说,"重新部署一下"或"杀掉 Pod"不是遏制——一个可恢复的运行会在下一个 worker 上回来,并精确地继续那个有害循环。遏制必须是一次循环内、感知状态、恢复路径也会遵守的停机,否则你造的是一个能熬过自己被杀的失控。

STEP 4

爆炸半径在事故之前就被限定,而非事故之中。

最有效的事故响应,是事故开始时就已经在位的那个限制。前几篇里的结构性界限就是遏制系统,预先安装好的:按任务的成本上限(cost-control-in-the-loop)封顶单个运行的损害;按租户的并发上限(concurrency-and-scaling)阻止一个租户变成全队事件;幂等的写(idempotency-and-retries)意味着一个循环的智能体重发同一个写只造成有界损害而非 N 次扣款;最小权限的工具范围限定任何单次调用最坏能做什么。一个没有预装界限的事故,按定义就是无界的——值班反应时间成了唯一的限制,而那永远不够快。

STEP 5

运行手册是一棵在压力下被执行的决策树。

凌晨三点,没人会从第一性原理推理。运行手册必须是一段预先决定好的序列,值班无需现场设计就能执行:

  • 先遏制。选最小够用的熔断级别(STEP 3)。止血不等于理解它;不要去调查一个仍在行动的智能体。
  • 评估范围。多少运行/租户,已经落地了哪些副作用(读副作用账本——真相之源,不是聊天日志),有风险的是价值还是只有 token。
  • 判定可逆性。识别需要补偿动作(退款、撤回)的写。幂等账本精确告诉你什么触发了、且只触发一次。
  • 沟通。受影响租户、状态、预计恢复时间——一个对客户数据做错事的智能体是一桩信任事故,不只是运维事故。
  • 审慎恢复。若是某个发布导致的,先回滚被固定的三元组(rollout-and-versioning)再解除停机;只在一个已知良好的契约上恢复运行。
STEP 6

只有先捕获了轨迹,复盘才可能。

你无法从指标重建一桩智能体事故;你需要决策级的轨迹——每个提示、模型输出、带参数的工具调用、以及发布戳,可按 run_id 与副作用账本关联。唯一的规则:不在轨迹里的东西,复盘就是猜测;而每桩事故都必须产出一个永久回归测试(确切场景,可重放,纳入评估闸门),使这类失控从此永远卡在 CI 上。一桩没有变成测试的事故,是一桩你已同意再来一次的事故。你无法在事故进行中临时加装可观测性——轨迹在事故之前就被捕获,否则复盘是虚构;在你需要熔断开关或轨迹之前就把它们建好,因为你需要它们的那一天,正是你无法再加上它们的那一天。