Agent A 向 Agent B 请求完成其任务所需的一项数据。Agent B 在回答之前,向 Agent A 请求生成该数据所需的一项上下文。这两项请求在发出途中都跨越了“需要人工审核”的边界。第一项请求落入了由 Priya 监看的 Slack 审批频道。第二项落入了由 Marcus 监看的 Jira 队列。Priya 正在吃午饭。Marcus 正在接听客户电话。两人都不知道对方的存在。工作流挂起了 19 个小时,直到一次客户投诉迫使有人去询问为什么汇总结果从未送达,才有人注意到。
这不是一种新颖的失败模式。它是分布式系统中最古老的失败模式,只是换了一身新装。Coffman 条件——互斥(mutual exclusion)、占有且等待(hold and wait)、不可剥夺(no preemption)、循环等待(circular wait)——早在 1971 年就被命名了,而一个带有“人机协同”(human-in-the-loop)审批队列的多 Agent 系统默认满足这所有四个条件。新的麻烦在于,死锁中的“资源”之一是人的注意力,这意味着你的活性保证(liveness guarantee)现在受限于两个互不相识的人独立进行上下文切换的速度。
如果你正在构建 Agent 工作流,且其中任何工具调用都可以路由到人工审批,那么你正在运行一个延迟是不确定的调度器,而你的死锁检测器是“有人在每日站会上提问”。这不是一种策略。这是一场赌博,赌你每天都能走运。
四个条件的“转世”
带着 Agent 系统的思维来审视 Coffman 条件。无论你是否针对它们进行了设计,每一个都会出现在你的技术栈中。
互斥。 一个 Agent 会话持有开启的状态——线程 ID、进行中的工具租约、部分写入的内存条目、持久层中的检查点(checkpointer)行。另一个 Agent 无法并行恢复该会话;框架的持久化模型假设每个线程只有一个写入者。会话就是资源。它被独占持有。
占有且等待。 Agent 在 interrupt() 处暂停以等待人工回复,但它仍然持有会话、检查点、它正在生成的任何内容的打开文件句柄、对话上下文,以及它在暂停前抓取的任何上游资源。LangGraph 的持久层对此非常明确:当线程中断时,检查点会无限期地持有冻结状态,没有内置的生存时间(TTL)。
不可剥夺。 审批语义不允许系统撤回请求。你不能告诉 Priya “实际上我们改变主意了,放弃审核吧”。即便你可以,Agent 也必须回滚它已经提交的部分副作用:写入数据库的一行、初步安排的日历事件、等待发送的草稿邮件。大多数 Agent 框架都没有可以干净利落中止的事务性子图(transactional sub-graph)概念。
循环等待。 这是最致命的一点。Agent A 的请求在 Priya 的队列中;Priya 的审核依赖于一个只有 Agent B 才能提供的事实;Agent B 的请求在 Marcus 的队列中;Marcus 的审核依赖于一个只有 Agent A 才能提供的事实。这个循环跨越了两个人类、两个 Agent 和两个队列。四方中没有任何一方拥有图的全貌。这个循环是不可见的。
经典文献通过防止四个条件之一或通过检测并打破循环来解决这个问题。大多数 Agent 框架在发布时都没有内置这些原语。图形成了;没有东西监视循环;工作流挂起;最终由人类注意到这种沉默。
为什么人类不仅仅是“另一个工具”
一种诱人的设想是将人工审核员视为一个高延迟的工具——像调用其他工具一样调用它,等待响应,然后继续。这种设想很快就会崩溃。
一个工具调用具有服务水平协议(SLA)。你知道大概的 p50 和 p99 延迟。你可以设置超时。你可以重试。工具要么返回,要么在有限的窗口内失败。
人工审批者没有这些。一个人的响应延迟取决于他们的日历、时区、手机电量、是否注意到了通知、是否理解了请求,以及他们是否认为你的请求比他们手头的其他 17 件事更重要。没有 SLA。没有重试语义。没有活性保证。
你拥有的是一个调度策略对你透明、队列深度无法读取、且其工作线程池是那些并不拿薪水来当你的运行环境的人类的调度器。将其视为“慢速工具”是你交付一个尾部延迟(tail latency)以天计的系统的方式。
正确的框架借用了分布式系统:人工审批者是一个不确定的外部服务,没有发布的 SLO,没有健康检查端点,也没有前向进展保证。你围绕这种服务构建的每个原语——熔断器、带升级的超时、死信队列、对队列深度的可观测性——都直接适用。大多数团队一个都没建,因为 Agent 框架的教程将 interrupt() 显示为一行代码的添加,使其看起来是免费的。
隐藏的失败模式 单智能体人类在环(human-in-the-loop)虽然烦人,但显而易见。智能体停滞,队列增长,某个仪表盘显示“待审批:47”,有人会问为什么。这种痛苦是剧烈的。
多智能体死锁则是无声的。从任何单一观察者的角度来看,系统只是变慢了。Priya 查看她的队列,发现有一个待审批。Marcus 查看他的队列,也发现有一个待审批。工作流仪表盘(如果存在的话)显示两个暂停的线程 —— 和往常的周二下午没什么两样。没有哪行日志会说“检测到由审批者 P 和 M 介导的线程 T1 和 T2 之间的循环等待”。没有任何东西损坏。没有任何告警。工作只是停滞不前。
这是最糟糕的一种生产故障:每个组件都处于文档记录的正常状态,但整个系统却卡住了。你的错误预算没有被消耗。你的延迟仪表盘没有变红。你的支持团队在接电话,但无法将其与任何特定信号联系起来。第一个信号通常来自系统外部 —— 客户询问他们的报告在哪里,内部用户放弃并直接给工程师发邮件,每周指标评审显示完成率在周四断崖式下跌,且没人能解释原因。
当你追溯原因时,死锁已经污染了队列的其余部分。被卡住的智能体所持有的会话正在消耗连接槽位、占用上游限流预算、占据 checkpointer 的索引,并悄无声息地阻止其他智能体取得进展。两个智能体的死锁循环变成了一个涉及数十个智能体的树状结构。
编排器本应做些什么 解决方法并不深奥。这正是操作系统五十年来一直在做的事情,只不过被应用到了一个尚未吸取教训的技术栈上。
维护跨智能体会话的等待图(wait-for graph)。 每当智能体向另一个智能体或人类审批者发起请求时,编排器都应该记录这条边:谁在等待谁。当线程因人类审批而中断时,边指向该人类,作为被等待的资源。当图中出现环时,就发生了死锁。图不需要很优雅;一个 (等待者, 资源, 开始时间) 的扁平表对于小规模的环检测就足够了,而当你拥有数百个正在运行的工作流时,一个真正的图数据库就体现出它的价值了。
检测并打破环。 当检测到环时,挑选一个“受害者”。代价最低的受害者通常是最近创建的边 —— 中止该分支,如果可以的话回滚其副作用,并将环的情况连同足够的上下文呈现给人类,以便其决定如何处理。中止一个分支会打破环,并让另一个分支继续进行。这正是数据库引擎在检测到事务死锁时所做的事情;智能体版本并不更难,只是没那么容易被下意识地构建出来。
默认设置带升级机制的超时,而不是无限期持有。 每个 interrupt() 都应该带有一个截止日期(deadline)。当截止日期到期时,系统会进行升级:升级给备选审批者、进入更高优先级的队列、执行工作流作者定义的默认操作(“如果 4 小时内未审核,则拒绝并通知”),或者仅仅是发出一个人类现在是瓶颈的告警。无限期持有是最简单的默认设置,也是最危险的。框架的 checkpointer 会很乐意永远持有状态;框架不会警告你正在积累僵尸线程。
使审批队列本身可观测。 队列 UI 应该显示每个待审批事项的下游影响 —— “此审批正在阻塞另外 3 个智能体会话和 1 份面向用户的报告。”这一单一的上下文信息会改变审核者的优先级。一个孤立地看优先级较低的审核,在审核者看到它是关键路径时,会得到正确的分类处理。大多数队列 UI 都不显示这些,因为编排器没有传播这些信息;而编排器不传播是因为没人要求这么做。
使用合成审批者进行压力测试。 在发布之前,在负载下运行智能体图,并使用合成的人类审批者,其响应延迟是从真实的分布中采样的 —— 一个带有周末和时区的长尾分布。观察图是否仍在取得进展。观察是否形成了死锁。这是大多数团队都会跳过的评估纪律,因为理想路径(happy-path)的教程将审批显示为即时点击按钮,而团队在心理上也以同样的方式建模了生产情况。
架构层面的认知 最深刻的教训是那些需要最长时间去内化的:在多智能体工作流中加入人类,并不是在原本自主的系统之上添加一个温和的修饰。它是对运行时正确性模型的改变。你插入了一个你无法控制的调度器,其决策你无法预测,其延迟没有上限,且其活跃性(liveness)取决于那些没有为你的工作流值班(pager rotation)的人员的好意和注意力。
保护操作系统或数据库免于死锁的每个分布式系统原语,都同样适用于这个技术栈,且无需修改。Coffman 四项条件并不是过时的理论;它们是一份操作清单。等待图(wait-for graphs)不是教科书上的练习;它们是检测你在阅读任何单一追踪(trace)时都无法看到的环的唯一方法。超时和升级不是悲观主义;它们是必须取得进展的工作流与从未同意处于关键路径上的人类之间的契约。
那些在构建智能体系统时不使用这些原语的团队,是在赌环不会形成。环最终总会形成。有趣的问题在于,你是从等待图中发现它,还是从客户那里得知。
构建图。观察环。为每个中断默认设置截止日期。向审核者展示谁被他们的决定阻塞了。在设计时请记住一件事:队列末端的人类不是工具。他们是一个调度器 —— 而且还是个无薪的调度器。
会员专享
余下内容仅对会员开放。 会员可读完整内容 —— 每一个公开发布的观点背后,那些框架、决策与推理的全貌。
— 完整文章,包含未公开存档的部分 — 可落地的工作框架,附带权衡与决策依据 — 新文章抢先看,先于公开发布 登录以继续阅读→ 随时取消 · 一次订阅,畅读全部