跳到主要内容

无人清理的审批队列

· 阅读需 10 分钟
Tian Pan
Software Engineer

你做了负责任的事。你检查了你的 agent,识别了那些可能造成真正损失的操作——发放退款、删除记录、发送外部邮件、部署配置更改——并将它们路由给人工审批。风险分级门控(Risk-tiered gating)。教科书级的做法。评审委员会也签署通过了。

然后,三周后收到了一个客户投诉:一个 agent 任务自上周二以来一直处于“进行中”。没有失败。没有报错。只是停留在一个人工审批队列中,而事实证明,根本没人在关注那个队列。Agent 完成了它的工作,将危险操作挡在门后并等待。而这扇门没有负责人。任务在没有仪表盘指向、没有警报触发的地方默默地老化。

这是风险分级门控悄然引入的故障模式。门控本身是正确的——你不应该让 agent 在没有人工参与的情况下转账。但“添加人工审批步骤”并不是一个安装完就了事的安全功能。它是生产基础设施的一个新组成部分,有其自身的可用性、延迟预算和故障方式。大多数团队发布了门控却从未运维它。队列变成了黑洞:东西进去了,产出的吞吐量完全取决于分心的人类恰好能提供什么,而且没人在衡量其中的差异。

人工门控是依赖项,而非决策

当你添加一个审批步骤时,你并没有孤立地让 agent 变得更安全——你是在关键路径上增加了一个服务。这个服务恰好由人来提供,但在架构上它的表现与其他依赖项无异:它有队列、处理速率、过载时的故障模式,以及比平均延迟更重要的尾部延迟。

工程师知道如何推演由软件构成的依赖。如果 agent 调用了一个支付 API,你会问:它的 p99 延迟是多少?错误率是多少?它宕机时会发生什么?有超时吗?重试吗?熔断器吗?你会把它放在仪表盘上并针对它设置警报。

人工审批队列却得不到这种审视,因为它看起来不像一个服务。它看起来像一个 Slack 频道,或者管理后台的一行记录,或者是发往共享收件箱的一封邮件。但它处于每个受控 agent 任务的关键路径上,而且它几乎总是整个系统中速度最慢、可观测性最差、最缺乏归属感的组件。Agent 的中位数延迟可能是 8 秒。队列的中位数延迟可能是 6 小时,而它的 p99 可能是“永远”,你不会知道这两个数字,因为没人在计算它们。

修复这一切的核心思路是:将审批队列视为一个带有 SLO 的系统。不是靠感觉,而是靠数字。如果一个 2 级操作应该在 1 小时内审核完毕,那么“从入队到决策的时间”就是一个延迟指标,而在一小时内完成的项目比例就是你可以达成或错失的 SLO。当你写下这个数字的一刻,队列就不再是一个黑洞,而成了你可以运维的东西。

队列真正需要什么才能具备可运维性

一旦你接受了队列是基础设施这一事实,缺失的部分就变得显而易见了——它们就是任何可运维系统所需的东西。

负责人。 不是“团队”。而是一个具体的角色,负责清空队列,就像服务有轮值(on-call)一样。如果“现在谁负责处理待审批项”的答案是耸耸肩或五个人的名单,那么在功能上就是无人负责。共享队列的所有权就像共享一个脏厨房的所有权。

深度和等待时间作为监控信号。 仪表盘上的两个数字,带有警报。队列深度(Queue depth)——有多少项目待处理——告诉你积压情况。等待时间(Wait time)——最旧的待处理项目等待了多久——告诉你陈旧程度。它们的故障方式不同。队列可能是浅而陈旧的(只有两个项目,都是 8 小时前的,因为审核员休假了),也可能是深而新鲜的(有 40 个项目,没有超过 10 分钟的,因为刚刚有一批任务到达)。你需要对两者都设置警报,因为出于不同的原因,两者都很糟糕。

升级路径。 当主要审核员没有采取行动时,项目应该根据计时器自动移动——发给备份人员、负责人或更广泛的频道。升级是将“一个人不可用”从故障转化为延迟的关键。如果没有它,队列的可用性就完全等同于一个人的日程表。

具有明确终止状态的超时策略。 这是几乎每个人都会忽略的一点,也是最重要的一点。队列中的每一个项目都必须有一个答案:如果没有人看这个,会发生什么? 绝不能将其视为例外,而应视为设计好的行为。

在队列存在之前必须做出的决定:过期项目会发生什么

等待审批的 agent 任务处于叠加态。它既没有完成也没有失败。它占用着槽位,可能持有锁,可能阻塞着客户,它将保持这种状态直到有人坍缩这个波函数。如果一直没人处理,任务就是永生的——而永生的任务正是导致你会看到一条记录“正在处理你的退款”长达九天的原因。

加载中…
References:Let's stay in touch and Follow me for more thoughts and updates