你把四个工作委托给四个 Agent,原以为自己可以专心做判断。
结果每几分钟就有一条新消息:“正在运行”、“已完成”、“需要确认”、“查询失败,是否重试”。最后你没在做决策,而在给 Agent 消通知。
这是多 Agent 系统很容易漏掉的一层:我们设计了执行队列,却没有设计人的注意力队列。
9 月 4 日的 GitHub Copilot 周报把 VS Code 新的会话管理放到了明显位置:相关 chat 组织成层级,并显示哪些需要用户关注。VS Code 1.136 的官方说明还提到,每个 chat 有状态和待批准项,通知可分别区分“需要你输入”和“已收到回复”。发布摘要 · 1.136 完整说明
这不是对它们内部实现的推测。但它提醒了一个很实际的工程问题:“需要注意”不能只是 UI 上一个红点,它应该是可检查、可去重的业务状态。

一个反直觉规则:失败也不一定要通知人
下面几种状态可以留在任务系统里,不必立刻打断人:
- 任务健康运行中;
- 遇到已知短暂故障,还在安全重试额度内;
- 非关键批任务的一个子项失败,可以进稍后异常汇总;
- 任务成功,但后续无需立即验收。
只有当一个人能提供不可替代的输入,而系统无法继续安全推进时,才进入 BLOCKED_HUMAN。
type TaskState =
| "QUEUED"
| "RUNNING"
| "RETRY_WAIT"
| "BLOCKED_EXTERNAL"
| "BLOCKED_HUMAN"
| "SUCCEEDED"
| "FAILED_FINAL";
function shouldOpenAttention(state: TaskState) {
return state === "BLOCKED_HUMAN";
}
这个判断很简单,但比“模型觉得好像该问用户”稳定。模型可以帮忙整理问题,不要让它绕过状态机任意打断人。
AttentionItem 交付的不是报错,是最小人工动作
一条“请检查任务”的通知,只是把排查工作甩给人。一个可用的队列项至少应包含:
type AttentionItem = {
taskId: string;
reason: "missing_input" | "approval" | "conflict" | "retry_exhausted";
question: string;
nextAction: string;
safeWhileWaiting: "pause" | "save_draft" | "continue_read_only";
risk: "low" | "medium" | "high";
blockedTaskIds: string[];
sourceUrl: string;
evidenceRevision: number;
dedupeKey: string;
};
比如:
报价中的税率与客户档案不一致。请选择使用档案中的 6%,或上传新计税依据。未选择前,报价只保留草稿,不发送。影响报价、合同和邮件三个下游任务。
这里同时给了冲突、选项、等待期的安全动作和影响面。用户不需要先翻日志,才知道按钮会改什么。
用去重键合并“四个 Agent 同时缺一份资料”
如果四个下游任务都缺同一份产品参数,应该让人补一次,而不是弹四次。
我会用 resourceId + reason + evidenceRevision 构造去重键:
const key = [resourceId, reason, evidenceRevision].join(":");
await db.transaction(async (tx) => {
const current = await tx.attention.findOpenByKey(key);
if (current) {
await tx.attention.addBlockedTask(current.id, taskId);
return;
}
await tx.attention.create({ ...item, dedupeKey: key });
});
不要只用 resourceId + reason。资料版本已变,旧问题的答案不一定还能解锁新任务。
去重后也要显示 blockedTaskIds。一份参数正在阻塞一篇文章,和它正在阻塞全部对外报价,优先级应该不同。
排序不需要假装精确
与其让模型打一个“紧急度 86.7”,不如按稳定维度做字典序:
- 高风险对象优先:资金、数据、合规、对外发送;
- 有明确有效期的优先;
- 阻塞下游更多的优先;
- 其他条件相同时,等待更久的优先。

这不是说永远不用分数,而是在数据还不够时,可解释的顺序比伪精确更容易维护。
队列满了,先减少新任务
注意力队列已有十几个未处理项时,系统还继续创建新的低优先级子任务,只会制造更多未来打断。
加一个简单的 WIP 上限:
attention:
maxOpen: 8
whenFull:
highRisk: allow
normal: queue
lowPriority: pause_new_tasks
数字 8 只是示例,应由实际队列数据调整。真正该观察的不是“同时运行了多少 Agent”,而是:
- 开放项的年龄分布;
- 处理一个有效决定需要几次打断;
- 重复项合并率;
- 人回答后因版本变化或上下文不足导致的返工率。
如果点击率很高,但每个决定要打开五个窗口,这个界面仍然是失败的。
处理后的恢复,比弹出通知更难
用户点了“批准”,并不意味着旧任务可以无条件继续。在等待期间,报价单、政策或源文件可能已经变了。
恢复前至少做两件事:
1. 记录 actor + action + timestamp + evidenceRevision
2. 比对当前依赖版本;不一致则让旧项过期,重新生成问题
超时也不能默认等于批准。执行 safeWhileWaiting:保留草稿、暂停,或只继续只读步骤。
不要把 Attention Queue 又做成一个通知中心
通知中心的默认是“尽可能记录发生过的事”。Attention Queue 的默认应该是“尽可能安静,只保留当前可操作的阻塞”。
实现时可以守住四条边界:
- 外部限流先自动处理,不立即找人;
- 通知已送达不等于事项已解决,服务端保留真实状态;
- 高风险操作不与普通偏好合并;
- 每个项都能回到发起它的任务、子步骤和证据版本。
岗位 Agent 的协作不该让老板一直值班
这也是我们做 Tipkay 时关注的一个取舍。Tipkay 面向小微企业、一人公司和小团队提供按需 AI 员工。不同岗位助手带着各自的经验、Skill、MCP 和流程接力,并把工作从生成推进到素材、排版、文件与发布准备。
本文的 Attention Queue 数据结构和调度方法是通用设计建议,不是对 Tipkay 已有完整实现的宣称。不过岗位化协作的目标应该很明确:不是把更多 Agent 窗口交给一个人巡逻,而是让大部分步骤安静接力,只在必要时交回一个说得清的问题。
一个人的生意,也能有一支专业团队。团队的价值不是同时有多少人在说话,而是你不必反复问:“还有谁卡住了?”