「等人」不是错误,是一种状态——Agent 人机协同的常见误设

📌 原文由 Lyncore 团队撰写并发布于官网:https://lyncore.top/tech/human-in-the-loop-agent/
本文为 Lyncore 团队授权转载,仅作分发用途。

「等人」不是错误,是一种状态——Agent 人机协同的常见误设

大多数 Agent 系统把「需要人工介入」写进错误分支:status=blocked,然后 try-catch。可它明明没结束——人一回复,系统不知道往哪接。

把「等人」当成可恢复的一等公民状态,而不是错误,是人机协同设计里最容易做错、也最该纠正的一点。

「等人」不是异常:一条工单的状态表与源码依据

"等人"为什么会被误当成错误

设想一个 Agent 干到一半,需要你拍板:这个 PR 能不能合并?这个外部 API 密钥要不要填?它把问题发出来,然后……呢?

很多实现的本能反应是:把"等待"塞进一个 status=blocked 的终态,或者干脆让主循环阻塞在那一步。后果是:

  • 阻塞态意味着"这件事结束了",可它明明没结束——人一回复,系统不知道该往哪接;
  • 一个 Ticket 卡住,调度器可能把它当成失败去重试,结果重复发起、重复问人;
  • 更严重的是,所有挂在它身上的并发上下文一起冻结,因为"等"被当成了"死"。

根因是:工程师把"需要人"和"出错了"用了同一套语义。

dsh-superagent:把"等人"列进状态表

dsh-superagent 的做法很直白——在 Ticket 的阶段表里,把"等人"的几个情形显式列为一等公民(见 docs/003-execution.md):

阶段 含义
person/delivery 投递输入给执行会话失败,重试中
person/answer 有一个 Attention(在等某人回答)
person/review 最新报告是 ready,等复核
person/blocked / person/failed 卡住或失败,且此后没有人的消息到达

文档写得很清楚:person 阶段意味着 Ticket 在没有人工输入前无法继续。而且它给出了一条关键规则——人的一条新消息,会把 blocked / failed 的 Ticket 释放回执行器,让它进入下一个匹配的阶段(通常是 working)。

换句话说:等人类不是一个错误分支,而是一个会自己解开的挂起态。Attention 是结构化字段,不是从自由文本里"猜"出来的——文档特意强调,不能用报告的自由文本去推断"是否在等人",要用结构化事实。

Lyncore:waiting_approval 挂起态

Lyncore 在任务生命周期里给了同一个答案,名字叫 waiting_approval(T3 挂起态):

  • 它是一个非终态的挂起——代码里常量就是 ApprovalSuspendPhaseWaiting = "waiting_approval"(approval_guard.go),和终态(succeeded / failed / cancelled)平级但不同类;
  • 审批通过后,任务经 resumed 阶段原位续走——不是重开一个新任务,而是在原任务上接着跑;
  • 它不设超时:等的是人,不是定时器;
  • 重启时 waiting_approval 一并纳入扫描面,但一律豁免不回收——不会因为"挂太久"就被判定失败。

这条线和 dsh 的 person/* 阶段是同构的:都拒绝把人工介入算作失败,都把"等"做成可观测、可恢复、可原位接续的状态。差别只在触发语义——dsh 的"等人"更多来自执行中的 Attention 与 review,Lyncore 的 waiting_approval 更多来自需要人拍板的高危操作(T3 挂起域)。

对照阅读:《主会话 + 工作会话》 讲分层之后人怎么插进执行会话;《任务生命周期》 与 《三种终止》 讲 Lyncore 的终态与挂起态边界。

设计这种状态时,三件事不能省

  1. 结构化,不要靠猜。 用显式字段(dsh 的 Attention、Lyncore 的 waiting_approval 状态位)标记"在等人",而不是解析 Agent 输出的自然语言。
  2. 可原位续走,不要重开。 人回复后,应在原任务/原 Ticket 上接着跑,保留它的上下文、目录、分支。dsh 用 resumed,Lyncore 用 resumed 阶段,都不是新建。
  3. 可独立恢复,不要连坐。 一个 Ticket 在等,不该冻结调度器或别的并发任务。Lyncore 把 waiting_approval 纳入可观测但豁免回收,正是这个意思。

一个反例提醒

dsh 的文档里专门列了一条反向设计:不让 Executor 自己宣布"人被它叫停了"——只有 Eval 能决定"停"的强度。因为"停"是个有后果的判定,不能让执行器自说自话。这条也适用于任何 Agent 系统:把"是否终止/挂起"的裁决权,留在调度层,而不是下放给干活的那个会话。


本文由 Lyncore 撰写,源自官网:https://lyncore.top/tech/human-in-the-loop-agent/

posted @ 2026-11-05 10:48  我心飞翔1998  阅读(2)  评论(0)    收藏  举报