把"等人"做成一等公民状态:
Agent 的人机协同怎么设计才不卡死
一句话结论:Agent 出错很少是因为"它干了什么",更多是因为"它卡在等人类时整系统僵住了"。把"等人"当成异常去 try-catch,是最常见也最致命的设计错误。正确做法是把"等人"做成显式的、可恢复的状态——这一点上,DeepSeek 的 dsh-superagent 和 Lyncore 走到了同一个答案。

"等人"为什么会被误当成错误
设想一个 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 的终态与挂起态边界。
设计这种状态时,三件事不能省
- 结构化,不要靠猜。 用显式字段(dsh 的 Attention、Lyncore 的
waiting_approval状态位)标记"在等人",而不是解析 Agent 输出的自然语言。 - 可原位续走,不要重开。 人回复后,应在原任务/原 Ticket 上接着跑,保留它的上下文、目录、分支。dsh 用 resumed,Lyncore 用 resumed 阶段,都不是新建。
- 可独立恢复,不要连坐。 一个 Ticket 在等,不该冻结调度器或别的并发任务。Lyncore 把
waiting_approval纳入可观测但豁免回收,正是这个意思。
一个反例提醒
dsh 的文档里专门列了一条反向设计:不让 Executor 自己宣布"人被它叫停了"——只有 Eval 能决定"停"的强度。因为"停"是个有后果的判定,不能让执行器自说自话。这条也适用于任何 Agent 系统:把"是否终止/挂起"的裁决权,留在调度层,而不是下放给干活的那个会话。
本系列
- DeepSeek 开源 super-agent 架构精读:一条 @提到 怎么变成一次 PR
- 主会话 + 工作会话:Agent 编排里"大脑与手"为什么要分开
- 先决定、后动手:用事务外发队列让 Agent 重启不丢活
- 五层记忆 vs 查询式召回:Agent 到底该不该有长期记忆
- DeepSeek 给自主 Agent 定的三条铁律
本文以 DeepSeek 开源的 dsh-superagent(MIT)为例子。Lyncore 的
waiting_approval挂起态与任务生命周期见官网 lyncore.top 与技术文档。