DeepSeek super-agent:把「别打扰人」写成一张准入表
它不靠提示词说"别打扰人",靠三个格子:谁在哪个范围里说了什么话,才够格开一个新的活。默认模式只记录"它本会做什么",一条都不执行。
一个 agent 在群里多回一句,一个团队就少干一件事。 所以我们平时解决"agent 太吵"的办法,几乎都是提示词:别插话、别重复、只在必要时说。而这些办法的共同问题是——提示词不是权限,模型可以不听。
DeepSeek 的 Harness 团队 2026-11-01 开源了一个研究原型 dsh-superagent(MIT,v0.1.0),配套一篇设计文《Exploring Super-agent Design》。媒体全在写它的 Router + Worker 双层的性能(p95 15.7 分钟 vs 29.5 分钟),但它真正稀缺的东西在仓库的 docs/ 里:一整套把"尽量少占人注意力"写成代码的授权协议——两条进入路径、强度三档、一张按接收范围分行的准入表、三档执行模式、不可变的凭证与恢复前重校验、策略版本化,以及一节"我们刻意否掉了哪些概念"。
英文世界目前还没有文章拆这一层(二手报道停在"三种模式")。这篇按文档顺序把它拆开,并在最后给出与 Claude Code / Codex / Copilot cloud agent 的对照,以及能直接抄进自建平台的五条。
先说边界:这是研究原型不是产品。README 自己写了两句话必须记住——"文章里有些设计是未来方向,本原型并没有实现"、"代码是 agent 写的,风格与工程实践可能不一致"。性能数字全部自报,且只测合成消息与模拟工具。我们读的是文档,没有部署、没有实测(数据截至 2026-11-02)。
一、一句原则,四条推论
文档开篇只有一句:"take as little human attention as possible, and give as much value as possible"。接下来所有设计都被这句话判过一遍——一条消息、一个通知、一个提问、一个设置项、面板上的一个格子、文档的一段话,都在花别人的注意力。
落到 agent 行为上,是四条可检查的规则:
| 规则 | 原文口径 |
|---|---|
| 只在被要求、或被写好的规则允许时才行动 | "请求"只算一种:直接点名 agent、且来自团队许可的人;没人要求的工作必须有团队写下的规则,而且先跑在"只记录它本来会做什么"的模式里 |
| 只在有人必须行动、或结果能派上用场时开口 | 确认一次并说明要做什么 → 干活时保持安静 → 有结果、有需要决策、或做不完时才再说 |
| 一次只问一个具体问题,并且用结构化表单 | 让它落到人眼前,而不是埋在长报告里 |
| 按影响停,不按投入停 | 局部改动做到可评审为止;需要设计决策的改动就停下并问 |
还有一个容易漏掉的:它把"责任"写在设计文档里,而不是写在条款里——agent 用你团队天天看得见的账号发言、开 PR,所以你欠同事的注意力,也由你负责。具体到操作上有一句非常反直觉的话:
当 agent 太吵时,把它的提示词改短改严,或者直接暂停;不要为了解释噪音而再发一条消息。
二、先把概念砍到五个
这套设计最"工程师"的地方是它拒绝造词。001-domain 只保留五个核心概念,其余全部是附属:
| 概念 | 一句话 | 它守的规则(关键几条) |
|---|---|---|
| Source | 一个事件流(GitHub / 飞书各是一个实例) | 自己记读位、自己有 bot 身份;重启不能重放事件;在进入系统前就丢掉自己的消息与不在监听范围内的事件 |
| Trigger | 一个不可变事件(带 actor 等元数据) | 同一个 Trigger 被接受两次,只产生一个 Ticket——它是审计单位和幂等单位 |
| Thread | 按时间排好的 Trigger 序列 | 同一 Thread 同时只跑一个 Eval;一个 Thread 可以有多个 Ticket;持有 Subscription 与 Decision 历史 |
| Eval | 对一批 Trigger 的判断,分闸门与评估两层 | 自己不持有状态;输入是这批 Trigger + Thread 快照 + 该 Thread 上所有 Ticket(含已结束)+ 当前 Policy;输出是一个 Decision |
| Ticket | 一个"被授权的工作" | 只有 Eval 能创建;有自己的状态机(open → ended → 被人的消息唤回 open);状态不跟随执行会话 |
| Mandate | accept 授予的东西:策略版本、范围、强度、命中的规则、发送者身份、消息是怎么被收进来的 | 不可变;要扩大范围只能开新 Ticket;Ticket 结束不等于 Mandate 结束 |
外加两个支撑概念:Assignment(Ticket 与某个 Executor 实例的绑定,一个 Ticket 先后可以有多个)与 Subscription(Thread 与 Ticket 的多对多关系,由 Thread 持有)。
文档还专门有一段"人不是域概念":人的身份只是 Trigger 上由 Source 给出的标识;"许可名单"是某个 Source 的 Policy 的一部分;同一个人在不同 Source 上有不同账号,这件事只是提示词里的文本,不进数据模型。
三、两条进入路径:新工作 vs 续作
这是整套授权的地基。一条消息要变成"活",只有两条路:
| 路径 | 谁决定 | 授予什么 |
|---|---|---|
| Eval(闸门 + 评估) | 主会话判断"这是不是一个新请求" | 通过后创建 Ticket 与 Mandate |
| Subscription(订阅扇出) | Thread 把消息投给已订阅的 Ticket | 什么都不授予——只作为上下文到达;仍受该 Ticket 的 Mandate 约束 |
关键在第二行的后半句:"投递绕过闸门,但不授予任何新权限"。也就是说,一旦某个 PR 上的 Ticket 还活着,别人在这个 PR 里说十句话,都会送到那个 Worker 面前,但它并不能因此扩大自己可以做的事;越界就要"交还"(hand back),由主会话重新判断。
文档还写了交还的三种情形,很细:超出 Ticket 范围、属于另一个 Ticket、或者"我跟着的这个 Thread 其实是无关的"。同一条消息只会被交还一次;交还之后如果没有任何 Ticket 还持有它,它才进入 Eval,且候选里会带上各 Executor 给出的理由。
四、强度三档,和那张三格子的准入表
一条消息"有多强",被固定成三档:
| 强度 | 什么时候成立 |
|---|---|
| explicit(显式请求) | 名单内的人点名 agent;或名单内的人在已经有 Ticket 的 Thread 里追问;或准入表里把某一格设成"显式请求" |
| inferred(推断候选) | 名单外的人在已有 Ticket 的 Thread 里追问(除非那一格是显式);准入表里设成"推断候选"的格子;bot 名单里的 bot 点名或出现在"收全部消息"的范围 |
| not admitted | 其他 bot 的一切;准入表里"忽略"的一切;以及 off 模式下所有 inferred 的情形 |
然后是那张表。它按 Source 的"接收范围"分行,每行最多三个格子:
- 飞书:每个配置过的群、"其他 @ 它的群"、私聊;
- GitHub:新 Issue、新 PR、评论、主干 CI。
| 格子 | 可选项 | 默认 |
|---|---|---|
| 名单内的人、没点名 | inferred / ignore | inferred |
| 名单外的人、点名了 | explicit / inferred / ignore | ignore |
| 名单外的人、没点名 | inferred / ignore | ignore |
这张表有三处设计值得单独记:
- 默认值全是"最保守"的那一档。 陌生人点名 = 忽略;陌生人随口说 = 忽略;名单内的人不点名 = 只能是推断候选(还要看执行模式)。
- "没点名"的两个格子只在"能收到全部消息"的范围里才存在(飞书设成"全部消息"的群、GitHub 开了全收开关的范围)。
- CI 结果没有发送者,所以它那一行只有第三个格子;bot 走另一套"bot 名单",而且文档写死了:bot 不能产生显式请求,也不能停掉 Ticket。
有一个容易被忽略的反向开关:把"名单外的人、点名了"这一格设成 explicit,就等于"这个群里任何人 @ 它都能派活"——文档举的用例是专门的客服群。这是唯一一个"故意放宽"的旋钮,而且它在任何执行模式下都生效。
五、三档执行模式:默认只记录,不执行
| 模式 | 效果 |
|---|---|
off |
推断类一律不进候选(没点名的消息、bot 的消息、名单外的人的追问) |
observe(默认) |
推断类照常评估,评估为"该接"的记成 ignore,并标注 "would accept";一条都不接 |
auto |
推断类评估为"该接"就接 |
显式请求在任何模式下都生效,所以 observe 不会让正常协作卡住——它只是把 agent 的"主动性"先关进观察室。文档给的迁移路径也很朴素:先跑 observe,翻一翻记录下来的 Decision,再切 auto。
切到 auto 之后还有一道闸:主动接手必须至少命中一条团队写下的规则。每条规则描述一类"主动工作",命中哪些规则会被写进该 Ticket 的 Mandate,Worker 也会收到这些规则的名字与指示。规则里的流程与沟通要求会覆盖默认值(比如"沉默规则"可以压掉确认消息,"评审规则"可以指定必须由谁批准才能合)。
六、Mandate 的一生:只有三件事能改它
| 事件 | 对 Mandate 的影响 |
|---|---|
| Eval accept | 创建它 |
| 人按下 stop | 撤回它(Ticket 保持 ended,之后什么都不会再收到) |
| 恢复前重校验失败 | 吊销它 |
除此之外,Ticket 结束不结束 Mandate——这正是"一句话就能让旧活复活"的原因:已结束的 Ticket 保留 Mandate 与 Subscription,人在它自己的 Thread 上说一句,就能就地恢复,不走 Eval、不产生新 Decision;而 bot 与 CI 唤不醒已结束的 Ticket。
但复活不是无条件。恢复前要过一张矩阵(这可能是全文最值得抄的一张表):
| Mandate 的来路 | 恢复前还要求什么 |
|---|---|
| 显式追问(explicit) | 直接放行 |
| 推断而来(inferred) | 当前必须是 auto,且命中的规则仍启用 |
| 由准入表某格授权 | 今天的表与发送者名单仍能给到至少原强度——所以把人从名单里删掉,能吊销它当初授权的 Ticket |
| bot 的 | 那个 bot 仍在 bot 名单里;且需 auto + 规则启用 |
| 其他 | 发送者仍在许可名单里 |
还有一个细节:恢复还要求 Source 仍在监听原来那个 Thread,但不要求它仍在"收全部消息"——因为"重新开始听"和"继续把每条消息都推过来"是两件事。
重校验失败之后会发生什么,文档写得很不客气:Ticket 保持 ended,一条等着它的消息都不接;每条消息被当成"没到过任何 Ticket",如果没有别的 Ticket 持有它,就回到 Eval;而且主会话不会被通知"它到过某个 worker"。之后的活要重新走一次 accept。
七、策略版本化:改规则,不砍在跑的活
团队改规则(规则文本、执行模式、各 Source 的 Policy)会产生一个新的策略版本,每张 Mandate 都记着自己被接受时是哪个版本。版本一变:
- 在跑的 Eval 被丢弃,它的 Trigger 重新排队;
- 受影响的活 Ticket 会收到一条
policy-changed输入,说明是命中规则改/不再适用、是请求人不再有资格、还是主动工作不再被接受(比如从auto退回observe); - 不受影响的 Ticket 不收到任何通知(少一条消息就少一次打扰);
- 不立即吊销 Mandate。
文档还专门把"改策略就取消在跑的 Ticket"列为被否掉的方案,理由是"被取消的变更会留下半成品"。这句是一整套设计的气质:它宁可多一次通知,也不愿意制造一个半成品。
还有一根手术级别的分界线,值得单独引用:
在对话里说 "stop" 是普通 Trigger,由 Eval 判断这是不是一个停止;在面板里按 stop 是命令,直接作用于 Ticket,并记为人工停止。而且——Executor 不能宣告"是人让我停的"。
同一逻辑还处理了"等一会儿"这件事:像"没人认领 N 小时"这种等待条件,属于 Source,由它自己等、自己再查;Eval 永不等待。这也是为什么文档把"等待中 / 已撤回"的 Eval 状态一并否掉——状态一旦多出这两种,重试与审计就说不清了。
八、执行侧的常数
003-execution 里全是可直接抄的数字:
| 机制 | 值 | 意图 |
|---|---|---|
| 前台 shell 转后台 | 5 秒(worker 与 router 同一套) | 让 worker 能在下一步收到新消息,而不是等长命令跑完 |
| 一轮结束后后台还能跑多久 | 最多 30 分钟,到点杀 shell 并上报(列出被杀的任务与仍在跑的子 agent) | 不让"以为在干活"变成永久占用 |
| 交还给人时 | 立刻释放 worker slot;shell(比如留给评审的本地服务)可以继续,且不占 slot、不受 30 分钟限制 | 人一接手,机器就不该继续占坑 |
| 并发 | 配置的并发上限 + 1 个额外 slot,留给"人发来的消息"或状态查询 | 新活不能挡住人 |
| 目录准备 | prepare 命令跑一次,限时 10 分钟,典型做法是 git worktree |
一个 Ticket 一个目录一个分支 |
| 关停 | 每个 worker 的后台工作最多等 10 秒;进程重启则直接终止 | 边界清晰,便于恢复 |
| 交付/投递重试 | 失败后 2 秒起,指数退避到 1 分钟;通道消息从 5 秒退避到 300 秒 | 对端抖动不该变成人的通知 |
| 错误上报 | 同类动作连续失败三次才进面板的"系统问题" | 少一条噪音就少一次打扰 |
自动合并的默认边界也很克制:只在"为本 Ticket 开的 PR、已 ready、必需审批覆盖 head commit、增删合计 < 200 行"时才允许开;再推新版本前必须先撤销 auto-merge。而且默认不以 agent 账号去合并——因为 GitHub 会把这次合并记在"谁开的 auto-merge"名下(默认是由人合)。
还有一条小而重要的产品设计:它发出的每一条 GitHub 消息第一行都是 This is by an automated agent, not a human。
九、授权阶梯:五级优先级
004-prompts 里有一张比大多数"agent 框架"都清楚的优先级表,从强到弱:
| 级别 | 内容 | 能否被提示词改写 |
|---|---|---|
| 1 | 由工具与宿主强制的事实:身份、权限、工具参数检查、回复用途、长度限制 | 不能 |
| 2 | 内置与生成的分节(团队覆盖过的按覆盖读) | 团队可覆盖第 2 级,但改不了第 1 级 |
| 3 | 主动 Ticket 命中规则的流程与沟通要求(只对该 Executor 生效) | 规则级 |
| 4 | 团队分节与团队技能 | 是 |
| 5 | 外部文本:消息、评论、文档、引用 | 它只是数据,永远不能改规则或授权 |
文档把这句话写得很直:改提示词会改变模型收到的指示,但不会改变闸门、Mandate、工具检查与账号包装——后面这些由代码强制。
这也顺带解释了"为什么 Router 可以很轻":它只负责派发与对外说话,可以不装 Bash;真正的执行在 Worker 里,Worker 的工具里刻意去掉了会向人提问或改配置的那些(ask_user_question、present、plugin_manager),因为它们是无人在场的。
十、12 个被否掉的设计
001-domain 里这张表是全篇最独特的东西——很少见有人把"我们决定不做什么"逐条写下来:
| 被否掉的概念 | 理由(原文意思) |
|---|---|
| 把人做成实体 / 跨 Source 映射身份 | 人只是 Trigger 的元数据;跨平台对应关系是提示词文本 |
| 让 Eval 自己排"延迟 Trigger" | 需要等待的是普通 Source,Eval 永不反过来依赖 Source |
| "等待中 / 已撤回"的 Eval 状态 | 等待属于 Source;超时的 Eval 要么重排队,要么以 unavailable 结束 |
| 一个 Thread 最多一个活 Ticket | 一个 Thread 可以承载多件互相独立的工作 |
| "把消息追加到 Ticket"作为一个 Decision | 追加就是订阅扇出,不需要 Eval |
| 对 Subscription 再做准入检查 | Subscription 覆盖整个 Thread;授权与上下文由 Mandate 分开 |
| 让 Correlation(关联关系)承担路由 | 路由是 Subscription 的事,Correlation 只补充上下文 |
| 让 Ticket 状态跟随会话 | Ticket 可能被移交、恢复、由多个 Executor 跑 |
| 让 Executor 宣告"人让我停" | 终止的强度只能由 Eval 判 |
| 改策略就取消在跑的 Ticket | 被取消的变更会留半成品;改为记录并投递 |
| Executor 失败/阻塞时锁死它全部工具 | 那会挡住人直接在会话里接手,也会挡掉恢复后的报告;暂停自动输入就够 |
| 从报告的自由文本里推断"在等人" | 分类需要结构化事实,所以要求用结构化字段提问 |
最后两条尤其值得抄:"锁死工具"和"从自由文本推断状态"都是很自然的第一反应,而它们分别破坏了人的接手与机器的可判定性。
十一、我们的整理:它和现有产品差在哪
| 维度 | DeepSeek super-agent(原型) | Claude Code(本地会话) | Codex(云端任务) | Copilot cloud agent |
|---|---|---|---|---|
| 工作单位 | Ticket(带不可变 Mandate) | 会话 | 云任务 | Issue / 分配 / 自动化 |
| 授权来源 | 显式 / 推断 / 准入表三档 + 执行模式 | 权限规则(allow/deny/ask)+ 审批 | 账号与仓库范围 | 计划与组织级设置 |
| 未点名时会不会干活 | 默认不干,只记录"本会干" | 不会(除非 hook/自动化) | 取决于任务创建 | 可以(automations) |
| 权限能否被提示词改写 | 不能(闸门与 Mandate 由代码强制) | 部分(规则在配置里) | 账号侧 | 组织侧 |
| 改规则对在跑的活 | 通知 + 不吊销 | 下次会话生效 | — | 取决于平台 |
| 人的消息优先级 | 独立额外 slot | 交互式 | — | — |
我们不认为这构成"谁更强"的结论——它测的是合成消息,真实代码质量没测。但它给出了四个可以直接借的结构:
- 默认 observe:任何"主动"能力上线第一步,先只记录"它本来会做什么",让人翻记录再开闸。这个开关比任何"我们很安全"的声明都便宜。
- 权限靠不可变凭证,不靠提示词:Mandate 记下"为什么可以做、能走多远",改提示词改不动它;范围要扩大就开新的。
- 授权要能级联吊销:把人从名单移除,应当让"当初靠这个人授权的活"在下次恢复时失效——否则一次权限回收只是心理安慰。
- 改规则不要砍在跑的活:通知 + 记版本 + 让下一次恢复时重校验,比"立刻取消"更少半成品。
- 人的消息要单独一条通道:并发满了也要留一个额外 slot 给人。这条和"少打扰人"是同一枚硬币的两面——不打扰 ≠ 让人插不进来。
如果你想接着读"多 agent 怎么组织",可以看 循环在节点之内,图在节点之间;想看"权限被绕过"的真实案例,看 Claude Code 的 shell 变量绕过 与 九起 agent 事故与六项控制。
十二、边界
- 原型,不是产品:README 明写"仅供研究参考、非正式产品",并说明设计文里部分方向在原型中未实现;代码由 agent 参与编写,风格与实践可能不一致。
- 数字是自报且口径有限:p95 15.7 分钟 vs 29.5 分钟来自约 300 条消息(其中约 20 条真需求)、每方案 5 次、合成消息 + 模拟工具;基线在 5 次里调用子 agent 0 次;token 约 23.2M(cache-read)vs 6.1–10.8M。真实代码质量未测。
- 安全姿势要自己加:Worker 以操作者的 OS 用户身份免审批执行命令,文档也要求"先隔离再上";默认
observe与准入表是减噪与减权限,不是沙箱。 - 本文为文档整理(主源为仓库
docs/000–005英文设计文档,参考团队设计文与二手报道),我们没有部署、没有实测;术语尽量保留原文英文,便于对照。
Sources
- 仓库与文档:
deepseek-ai/dsh-superagent-prototype(MIT,v0.1.0)https://github.com/deepseek-ai/dsh-superagent-prototype docs/000-philosophy.md、001-domain.md(含 Rejected concepts)、002-authorization.md、003-execution.md、004-prompts.md、005-panel.mdREADME.md("研究原型"声明与"部分设计未实现")- 设计文:DeepSeek Harness 团队《Exploring Super-agent Design: Towards a More Autonomous Agent System》,2026-11-01(英文为中文原文的机器翻译)
- 性能数字的二手来源:MarkTechPost《DeepSeek Open-Sources dsh-superagent》(2026-11-01)、CloudNavi《Why DeepSeek's AI dropped the session》(2026-11-01)——两者均标注为"自报/转述"
同主题阅读
本文由 Software 2.0 整理编译,转载请注明出处。文中所有机制描述以仓库文档原文为准;性能数字为发布方自报,我们未复测。
这是「Agent / MCP」专题的一部分。完整地图见:Agent / MCP / 自动化完全指南