给 Super-agent 的 Router 做一次体检
10 月底,DeepSeek Harness 团队发了一篇《Super-agent 设计探索:迈向更自主的 Agent 系统》,同时开源了一个原型。
我尝试搭了一个回放评测台,在上面跑了几十组实验。这篇是阶段性的记录。
先说结论:Super-agent 的持久、并行、事件驱动,业内都有对应的做法;和它们差别最大的,是一个读整条团队消息流、自己决定“这是不是活、归谁”的 Router,所以这篇主要测它。Router 的判断力是够用的,但它会带来一类显式触发根本不会有的错误:安静、会连锁放大。值不值得,取决于能不能把这类错误的代价压下来。
Super-agent 想解决什么
去掉“连续、高效、主动、持久”这几个形容词,原文的主张其实就两条:
- 瓶颈正在从模型能力转移到人的注意力。 单个任务的上限看模型,多个任务的上限看人。一个人通常只能同时盯住三五个活跃的 Session。
- Session 是从 LLM API 延续下来的设计惯性。 它应该退成实现细节,不该让人去管。
所以它把工作单元从“对话”换成了“事项”:一个有生命周期、有授权、能订阅多个消息来源的任务对象。原型里叫 Ticket。架构分两层:
- Router(主 Session) 读所有进来的消息(群聊、GitHub 评论、CI 结果),决定接单、挂到已有事项、忽略还是叫停。它自己从不动手干活。
- Worker 每个事项一个,只收自己订阅的 Thread 上的消息,干完了把结果报回来。
原文的消融实验里,最有说服力的不是“快了多少”,而是这一组对比:只有一个 Session、给它一个 subagent 工具时,“分不分工”完全取决于提示词和模型的临场判断。中性提示下,5 次运行里一次都没有委派;改成“优先委派”,也只有 4/5。双层架构直接让 Router 没有执行工具,这个问题在结构上就不存在了。能用结构保证的,不要交给提示词。 这条原则我很认同,后面还会反复看到它。
先看业内:大部分已经有人做了
评估一个设计,得先知道它和现有做法差在哪。我把 Super-agent 的模块逐个和业内已经发布的东西对了一遍,只看官方文档和官方博客。
| Super-agent 的主张 | 业内已有的做法 |
|---|---|
| 持久:事项不随本机进程结束 | Claude Code 云端会话:每个任务一台新 VM,合上笔记本也接着跑 |
| 计算、状态、环境分离 | 同一篇:GitHub token 不进 VM,由代理持有;会话只拿到只能推送到自己分支的短期凭据 |
| 事项是工作单元,Session 只是实现细节 | Linear 的 Agent Session:issue 交给 agent 代办,人仍是负责人。Copilot coding agent:把 issue 指派给它,它交回一个 PR |
| 主动、事件驱动 | Cursor Automations、Claude Code routines:由定时、Slack、GitHub 等事件触发 |
| 运行中的消息怎么处理 | OpenClaw 的队列模式:steer(插进当前轮)、followup(排到下一轮)、collect(合并成一轮) |
还有两个值得单独说。
一个是 LangChain 最近那篇《Why aren’t you building proactive agents yet?》。它说 LangSmith 上的托管 agent,超过 70% 的运行是定时触发的,不是人发消息触发的。最常见的“主动”其实就是 cron:每 10 分钟醒来扫一遍 Slack 或邮件,有要紧的就通知人。
另一个是 8 月上线的 Grok Bot。它走了一条和 Super-agent 正好相反的路:工作单元不是“事项”,而是“Bot”,一个有名字、有职责的常驻同事。人按角色把活分给不同的 Bot,再开一个“chief of staff” Bot 去协调它们。VentureBeat 的说法是,这“减少了用户充当 router 的角色”。换句话说,默认还是人在分派,协调靠的是另一个普通 Bot,不是结构上的保证。
这么一对,差别就清楚了:
业内的主流是显式触发(@、指派、规则)、定时分拣,或者人按角色分派。只有 Super-agent 让一个 Router 持续读整条团队消息流,并把判断直接落成对事项的操作:这句是不是活,那句补充属于哪个事项,这句话是不是要停下。
所以真正要问的不是“持久、并行能不能做到”,那些业内已经做到了。要问的是:这一层判断带来的好处,能不能抵过它引入的误判和复杂度?
怎么测:一个回放台
我没有真实团队的长期运行数据,所以先做了一件更便宜的事:搭一个回放评测台。
它把一段按时间排好的消息流(请求、补充、闲聊、CI 结果、各种难例)灌进原型的真实代码:真实的 Engine、真实的准入策略、真实的提示词、真实的工具、真实的候选卡片。被替换掉的只有三样:
- 时钟是虚拟的:一小时的群聊几秒钟就能放完;
- Worker 是模拟的:只占时间、按设定读消息和报告,不真的写代码;
- Router 可以换:要么是“标准答案”模拟器,按标注做出正确决定,用来测调度机制;要么是真实模型,用来测判断。
语料是一个中文团队群聊,我按原文消融实验的形态生成:一小时约 20 个请求、几百条消息。我在里面专门放了难例,每条都按原型的提示词标好“哪些结果算对”:
- 兄弟工单:“登录页按钮错位”和“登录页验证码看不清”同时开着,补充消息得读出说的是哪一张;
- 叫停、问进度、换种说法的重复请求、话题相近的新需求;
- 指代不明的“刚才那个也顺便处理一下”、“先别动手等我确认”、“你不用管”。
后来又加了固定剧本:比如同一个人刚问完某件事的进度,45 秒后说“算了,这个我自己来改吧”。这时叫停那张工单是对的,先问一句也是对的。评分也从“对几条”改成了按代价加权:错误叫停的代价是 5,挂错工单是 3,误建工单是 2,丢一条补充是 2。不同错误给人造成的麻烦,差得很远。
模型主要用 DeepSeek 的 deepseek-flash,对照用了 deepseek-v4-pro。
发现一:判断力本身够用
先说好消息。
难例上,两个模型都是 77/78。 叫停、指代不明、“先别动手”、委婉的请求、话题相近的新需求,基本都判对了;兄弟工单之间一次都没有挂错。
批处理不伤判断。 原文认为 Router 在高峰时把积压的消息攒成一批处理,能提高吞吐。我强制让 Router 攒够 N 张卡片再处理:批大小从 1 加到 20,准确率没有可见的下降;模型调用从 334 次降到 50 次,输入 token 从 8.6M 降到 1.9M,大约是四分之一。更有意思的是,批大了以后,模型能看到前后消息的关系。有一条补充,它的判断理由是“后面的消息已经叫停了这张工单,这条补充不再适用”。批处理带来的不只是省钱,还有上下文。
选 Router 的模型,尾部延迟比准确率更重要。 v4-pro 和 flash 准确率一样,但单次调用的 p95 是 19 秒,最慢的超过 180 秒。Router 是一个串行的单点,一次慢调用会让后面排着的确认都等上 3 分钟。flash 的确认 p95 是 7 秒。小而快的模型当 Router,大模型留给 Worker。
Router 远没到瓶颈,瓶颈在 Worker 槽位和轮询上。 在一小时 20 个请求的负载下,Router 的繁忙率约 1%。首次确认的延迟几乎全部来自消息来源的轮询间隔:60 秒轮询时,确认回复的 p50 约 33 秒;改成推送约 2.5 秒。
发现二:判断层会带来一类新的错误
坏消息在这里。显式触发的做法,要么接到了,要么没接到;有了一个“自己判断”的 Router,就多出一类错误:它判断了,而且判断错了,还没人知道。
猜错叫停,并且连锁放大。 “你不用管”、“不用你弄了”这类没说是哪件事的话,Router 常常按发送人去推断:“他手上唯一在办的就是 X,那就是叫停 X”。叫停是破坏性操作:进行中的工作丢了,被人叫停的工单之后也挂不上新消息了。
在专门的剧本上,原版提示词错误叫停了 9 次。更糟的是后面的连锁反应:
- 6 条明确要叫停那张工单的消息,被当成“已经停了”而忽略;
- 12 条发给它的补充,因为“工单已被叫停”而丢失。
9 次误停,一共连带了 18 条消息。
修法是在提示词里加一句:叫停和挂单一样,必须明确指向某张工单;没说是哪件事的,先问一句,不按发送人去猜。加了之后,错误叫停从 9 次降到 0 次。代价是回复多了,从 42 条变成 56 条,因为它学会了先问。后来在一个更大的综合剧本上复测,按代价加权的损失也从 52 降到了 16。
改一句提示词,会影响到别处。 这是我觉得最有方法论价值的一条。当时还有两个看起来很明显的提示词问题:
- 一条规则被模型用错了地方,导致它以“新功能需要先定设计”为由忽略了明确的请求;
- 另一条规则“话题相近的消息不能挂到已有工单”,看起来会制造重复工单。
我分别改了一版,又合起来改了一版,结果三个版本都比原版差:原版两次都是 127/130,修改版是 119 到 122,误建的工单也更多了。只是在一段里加了一句话,跨 Thread 的补充被当成新请求、“先别动手”被接单的次数都涨了,而这两类消息和新加的那句话没有直接关系。每条新规则都会改变模型在“接单、挂单、忽略”之间的整体倾向。 所以这两处我最后都没改。叫停那条之所以敢改,是因为它在专门的剧本上跑了两次,又在原来的难例上跑了两次回归,四次结果都站得住。
有些丢失根本不经过模型。 原型默认的准入模式下,人在别的 Thread 补充一句“导出 CSV 那个,记得兼容 WPS”,如果没有 @ 智能体,会被准入策略直接静默丢弃:完全不 @ 时丢 100%,一半 @ 时丢 45%。所谓“跨渠道连续”,目前还得靠人主动 @。
发现三:交接环节的漏洞
双层架构最大的好处是 Router 永远不被具体工作阻塞,代价是多了很多交接:Router 交给 Worker,Worker 报回给 Router,Router 再转给人。回放台在这些交接点上测出了几处真实的漏洞。
工单结束时,没读到的补充会丢。 Worker 发出“完成”报告的那一刻,如果正好有人发来新的补充,工单结束时会把这条还没读的消息“放回去”重新判断。但 Router 之前见过这条消息被挂到这张工单上,就认定它是“重复投递”并忽略了。26 条被放回的补充里,Router 只挽回了 20 条。
我在原型里给候选卡片加了一个 released 标记,并在提示词里说明“这不是重复投递”。修复后挽回了 26/26。这个修复已经合进了我 fork 的原型。
工单自己 Thread 上的叫停,要靠通知。 人直接在工单的 Thread 上说“这个不用做了”,消息会直接送到 Worker;Router 只有收到一条 follow_up 通知,才知道有这件事。我一开始的回放台没有模拟这条通知,所以上面那条叫停规则在这里有没有误伤,最初是测不到的。补上之后再测:在工单自己的 Thread 上,叫停和拒绝都是 10/10 判对。
卡死的 Worker 发现不了。 原型里,正在运行的一轮没有任何时限。我在回放台里让 15% 的 Worker 随机卡死:不报告、不读新消息,但一直占着槽位。默认只有 3 个槽位,3 个一起卡住,整个系统就停摆了:
| 完成 | 卡死被发现 | 一直没开始的请求 | |
|---|---|---|---|
| 没有 Watchdog(现状) | 270/400 | 0/45 | 85 个;20 次运行里 11 次停摆 |
| 加 Watchdog:20 分钟没有动作就停掉这一轮 | 338/400 | 62/62 | 0 |
卡住期间,也没有任何人被告知。Watchdog 能兜住卡死,但发现不了循环:循环中的 Worker 一直在动,需要另外的步数或成本预算,原型里还没有。
和业内的开放问题对一下
最近 LangChain 的 Harrison Chase 发了一篇《Context engineering challenges, late 2026》,列了常驻、主动 agent 带来的六个新难题,并且明说业内“不一定有通用解法”。其中三个,正好是 Super-agent 的 Router 要回答的问题:
- 主动但不烦人。 推给人的东西“不多不少”很难校准。上面那条叫停规则就是一次可以量化的取舍:多 14 条回复(大多是先问一句),换来错误叫停 9 次变 0 次。
- 异步工作。 任务交给 Worker 之后,主 agent 要随时响应新消息,还要处理“做完了怎么通知”“停下来等人批准”这些状态。Super-agent 用结构回答了“主 agent 不被阻塞”,但交接本身的漏洞,就是上面发现三里的那几处。
- 记忆。 难的不是怎么存,是“该记什么”。Router 现在靠对话历史,加上按需读取工单和消息记录。它记错或记漏时,会“表现得像记得、其实不记得”,这比明确说不知道更伤信任。这一块我还没测。
另一个绕不开的问题是验收。Anthropic 在《How the Claude Code team does code review》里披露,公司代码库每天落地的改动从 5 月约 2500 个涨到 9 月 3 万多个;Faros 的报告也显示,AI 用得多的团队合并的 PR 多了 98%,review 时间多了 91%。Super-agent 省下了派活和搬上下文的注意力,但人并没有被解放:人“上移到决策与验收”之后,验收本身就成了新的瓶颈。原文没有讨论这一点,原型里也没有验收这一层。Anthropic 的做法可以直接参考:交付前先拿证据,每个 PR 都过一遍自动 review,再按风险分级决定要几个人批。
没测到什么
这篇只回答了一部分问题,局限要说清楚:
- Worker 是模拟的。 我测的是调度和路由,不是代码质量。
- 语料是我写的。 是合成的中文群聊,不是真实团队的记录;难例再刁钻,也是我能想到的刁钻。
- 样本小。 每组 3 到 5 个种子,主要用 flash 一个模型。
- 最根本的问题没测: 人的注意力是不是真的省下来了,验收负担是变重了还是变轻了。这需要真实团队、真实任务。
一点思考
回头看,Router 这一层的问题,不在“判断得准不准”。77/78 已经足够好。问题在于,它的错误和显式触发的错误不是一类东西。显式触发漏了,人会再 @ 一次,错误是看得见的。Router 判错了,往往是安静的:一条补充被当成重复投递,一次叫停被当成已经停过,一个卡死的 Worker 占着槽位,没有任何人被告知。而且一次错误会顺着后面的判断传下去。
所以要让这一层值得,我觉得关键不是继续提高准确率,而是三件事:
- 让错误变便宜。 拿不准就问一句,而不是猜。破坏性的操作(叫停)要求比挂单更明确的指向。
- 能用结构保证的,就别交给提示词。 原型里很多地方已经是这么做的:“LLM 负责判断,代码负责约束”。
released标记是同一个思路:不让模型从记忆里猜“这条消息我是不是见过”,而是在卡片上直接写明它为什么回来了。 - 把提示词当代码来测。 改一句话会影响别处,所以每次修改都要在回放台上做 A/B 测试,同一版本至少跑两组,看清运行之间的波动。
下一步,我想做一次更像样的端到端对比:同一批真实的编码任务,一组用 Super-agent,一组用业内现状(显式触发、每个任务一个云端会话、steer 和排队),再加一组 cron 分拣。比完成时间,比找了人几次,比 review 负担。到那时,Router 这一层到底带来了多少净价值,才算有答案。
评估文档、回放台代码和实验记录都在我 fork 的仓库里:jiaoqsh/dsh-superagent-prototype。完整的评估见 research/superagent-evaluation.md,相关工作见 research/related-work.md,跟踪事项见仓库的 Issues。