10 月 31 日,DeepSeek Harness 团队在 GitHub 开源了 dsh-superagent-prototype,MIT 协议,TypeScript。一天内破 4,000 星,我写这篇时仓库是 11,427 星、1,173 个 fork。它的定位是一句话就能说完的:一个常驻在单个 GitHub 仓库里的编程 Agent,被授权的人 @ 它,它就开工单、在自己的会话和目录里干活、回到原对话交付一个 PR。
这个形态本身不算新,今年讲「常驻 Agent」的文章已经不少。让我愿意单独写一篇的原因是另一件事:它把「谁能给它派活」从提示词里搬了出来,写成 host 侧一条真的权限。 而且这条权限有版本号、有回滚语义、有审计记录。
先把官方自己贴的标签说清楚,这决定了你该怎么用它。README 第一段就写着:研究原型,只供参考,不是 DeepSeek 官方产品;代码大部分由编程 agent 写的,可能还有 bug,风格和工程实践可能不一致;发布前做过改写,因此和内部版本、以及设计长文里描述的实现都不完全一样,长文里有些设计只是未来方向、原型里没实现。所以要拿它上生产之前,先接受它是一个可读、可改、可抄的图纸。
它长什么样
先说架构,因为这决定了后面权限往哪里挂。它把「跟人打交道」和「干活」拆成两层:一个常驻的 Router 负责接所有人类入口、维护共享上下文、把活派下去;每个任务一个隔离的 Worker,拥有完整工具集,从不直接跟人说话,只向 Router 汇报,由 Router 代它回复。官方的说法是「会话不该是 agent 工作的单位」,落到实现上就是你不会看到几个 agent 同时回答你。
围绕着两个角色还有几个部件:一个 Watchdog 盯失败和卡死的循环;长任务自动转后台;状态落在单个 SQLite 里;一个本地 Web 面板只列需要人决定的项。工作流是:有人派活 → 开一张工单(Ticket)→ 在它自己的会话和目录里做 → 回原对话交付,通常是一个 PR。Source 支持 GitHub,可选飞书群。
代码规模可以量一下:src 下 92 个 TypeScript 文件、12,390 行。文档比代码厚,docs/ 里 007-architecture.md 一个文件就 70KB。而真正管权限的 src/policy.ts 只有 242 行。这个比例挺说明问题:它的复杂度不在代码里,在设计约定里。
权限模型:把「谁能派活」写成一条真的权限
这是我认为值得抄的部分,展开讲。
整套设计的一句话原则写在 docs/002-authorization.md 开头:host 强制允许名单、规则和 Mandate;消息、评论和引用的文档只是决策的输入,不能改变这些权限。 这句话是整个文件的地基,也是它和绝大多数 Agent 实现的分水岭。多数 Agent 的「你只能做 X」是写在系统提示词里的一段话,模型可以不听;这里的「谁能派活」是 host 进程里的判断,模型说什么都不影响。
两条路
一个触发信号进来只有两条路可走。第一条叫 Eval,决定要不要开工;第二条叫 Subscriptions,用于延续已有的活。第二条这条路的定义值得单独说:订阅线程上的每条消息都会作为上下文送到它的工单,这条投递绕过闸门,但不授予任何新权限,每个工单仍然被自己的 Mandate 限制。也就是说「让已有工作持续获得信息」和「让人获得新的派活能力」被明确分开了。
三级强度
闸门把每个触发判成一个强度,只有三级:
| 强度 | 什么情况 |
|---|---|
| explicit | 名单上的人直接 @ 它,或在已经有工单的线程上跟进 |
| inferred | 名单外的人在已有工单的线程上跟进;admission 表某一格设成 inferred;bot 名单里的机器人的消息 |
| not admitted | 其他 bot 的一切消息、被 admission 表忽略的一切,以及 accept mode 为 off 时的全部 inferred |
admission 表:行是接收范围,三格是三种人
每个 Source 有一张 admission 表。行是接收范围(飞书是每个配置的群、其他 @ 到它的群和私聊;GitHub 是新 issue、新 PR、评论和默认分支的 CI)。每行最多三格,决定了这种消息是变成显式请求、推断候选,还是什么都不做:
| 格子 | 可选值 | 默认 |
|---|---|---|
| 名单内,但没 @ 它 | inferred / ignore | inferred |
| 名单外,但 @ 了它 | explicit / inferred / ignore | ignore |
| 名单外,也没 @ 它 | inferred / ignore | ignore |
后两格只在「这个 Source 接收全部消息」的范围里才可用。把「名单外但 @」这一格设成 explicit,效果就是那个范围里任何人都能派活,官方点明这适合专门的客服群。
几个细节能看出设计者的克制。给未 @ 消息用的格子只在接收全部消息时才生效,也就是说默认情况下,一条不提它的消息连候选都进不去。另有一条顺序上的讲究写在 policy.ts 注释里:先判 admission 表的显式格子,再判线程跟进,这样「一个线程里已有的工单」永远不会把一个显式请求降级成跟进。
accept mode:默认只观察
模式有三档,这是「主动性」的油门:off 什么都不接;observe 是默认值,推断候选会被评估,正评估记成一条带「本来会接」标记的忽略,但不接;auto 下正评估就直接接。显式请求在任何模式下都有效。
我特意去看了默认规则,只有两条:一条管错别字、措辞、标点和既有翻译,一条管局部的间距、对齐、溢出和样式。第二条的指令写得很长,其中一句是「定位到某个元素、或者说它不一致、不好看、难理解,都不构成一个已确立的目标」。这句话把「什么算可以自动接的活」写成了克制的散文,而不是一个宽泛的开关。
Mandate:只有三种操作能改它
工单被接受时会产生一个 Mandate,记录它是被谁、凭什么、在哪个权限版本下接受的。整个系统里只有三个操作能改动 Mandate:accept 创建它,人 stop 撤回它,重新校验失败撤销它。
配套的是权限版本。版本号是 rules、accept mode、各 Source 策略三者拼起来的哈希,当前叫 mandate-v3。policyVersion() 上面有一条注释把边界划得很清楚:改动模型、并发数、暂停状态或轮询间隔,不会让已有决策失效。也就是说设计者明确区分了「授权相关的参数」和「运行参数」,前者变动要通知已开工的活,后者不用。权限变更会丢弃运行中的 Eval、重新入队,并给受影响的工单发一条说明,讲清是匹配的规则改了、派活的人不再有权限,还是主动工作不再被接受。
权限收紧时它做的取舍也值得看:不会立即撤销已开工工单的 Mandate,但当这些活恢复时会重新校验。校验按 Mandate 的来源分类:显式跟进直接通过;推断来源需要 auto 模式且匹配的规则仍然启用;靠表格格子进来的,要求今天这张表对这个人仍然给出不低于原来的强度;靠 bot 进来的,要求那个 bot 还在名单上。校验失败的话工单保持结束状态、不接收等待中的消息,要重新派活得重新走一次 accept。
源码里的几条佐证
把文档和 242 行 policy.ts 对着看,有几处是文档没强调、但代码里很硬的:
跨 Source 不做身份映射,文件开头注释写的是「谁能发起工作由每个 Source 用它自己的身份配置,策略从不跨 Source 映射一个人」。飞书群里的张三是谁,跟 GitHub 上的 zhangsan 是不是同一个人,这里的答案是「不管」。
评估必须穷尽:每条启用的规则都要恰好被判一次,多一条少一条都直接抛错。验收还要同时满足三个条件,实现意图成立、目标已定且没有未决的设计决策,至少匹配到一条启用规则,以及必须提供意图证据。缺任何一条都落回忽略。
机器人那条路被收得很窄:bot 名单里的机器人最多只能产生推断候选,不能发显式请求,也不能停工单。但这里有个容易踩的坑,文档专门提了一句:一个用普通账号跑的自动审查工具,在 host 眼里就是个人。它得像人一样被处理,团队提示词里必须显式标明它是自动审查工具。
还有个细节是拒绝也有分类。闸门给出的理由是 not_a_member、admission_ignored、no_mention 三种,不是笼统一的「不行」。对一个要长期跑在自己仓库里的东西来说,能区分「你不在名单上」和「你没 @ 我」,直接决定了排障时你要去改哪一处配置。
我对这套设计的三点判断
第一,它的价值不在于管得严,而在于把权限变成了可审计、可回滚、有版本的东西。显式请求在任何模式下都有效这一点很关键,说明它要拦的是「它自己觉得该做的事」,对人的显式请求则一路放行。
第二,提示词里的权限是意图,host 里的权限是事实,这两者必须分开放。写在同一处的时候,任何进入上下文的东西、包括一段被引用的文档、一次网页抓取回来的内容,都有机会跟着权限一起被改写。分开之后,注入能影响的只是「它决定做什么」,影响不了「谁有资格让它做」。
第三,默认值选得对。默认 observe、默认不自动合并、默认名单里只放自己,这三个默认值加起来的效果是:装上之后你先看它想干什么,再决定放不放。多数 agent 产品在这里选的是相反的默认值。
怎么把它装起来
装法有两条路,官方推荐的是第一条。
推荐路:让一个 agent 带你装
README 的建议是把 SETUP.md 交给你的编码 agent,然后说一句「读这个仓库里的 SETUP.md,和我一起把 dsh-superagent 装好」。
这个设计本身挺特别,值得说一下:SETUP.md 其实是写给 agent 的执行规程,人读它只是顺带。它的开头几条是对安装者(agent)的约束:第一条问题之前只读这个文件和理念文档;一次只问一个问题、等回答再问下一个;每条消息控制在 300 字以内;提问期间不要执行任何操作,先把答案收齐、发一份完整计划、等确认了再动手;永远不要在聊天里索要密钥,需要人手操作的步骤单独发一条消息给准确命令;把理念拆进问题里教,而不是做一次讲座。
它会问九个问题,其中两个需要你先想清楚:谁可以派活(推荐答案只有你自己),以及要不要给它的 PR 开自动合并(推荐不要)。其余六项有好的默认值,它不问、直接写进计划:独立的 superagent profile(DSH Web profile 的副本,单独放是为了重启它时不关掉你自己在用的会话)、端口 3082、状态目录用绝对路径、从你自己的 profile 里每小时检查一次、分支前缀 agent/、accept mode 用 observe。
还有一个问题容易被忽略但很关键:它要用哪个 GitHub 账号。推荐答案是一个专门为它创建的账号,绝不用你自己的,因为它在 issue 里的每条评论、每个 PR 都挂在这个名字下。这里还有个硬约束,它需要的是一个普通用户账号,不能用 GitHub App,原因是它要读这个账号收到的通知。
手动路:命令序列
要自己来也可以。前置清单是 Node.js 22.19+ 或 24+、pnpm、git 2.32+、tmux、Python 3.8+、GitHub CLI 2.48+,以及一个配好模型的 DSH。然后:
npm install -g @deepseek-ai/dsh@0.2.1-rc.3
git clone https://github.com/deepseek-ai/dsh-superagent-prototype
cd dsh-superagent-prototype
pnpm install && pnpm build
dsh superagent --from-default-profile web --dump-config > /dev/null
dsh plugin --profile superagent add "link:$PWD"
之后把 examples/profile.cordis.patch.yml 复制到 ~/.dsh/profiles/superagent/cordis.patch.yml,把里面的 stateDir 改成绝对路径。面板默认在 3082 端口,而且只监听本机回环地址,想从外网看要自己加隧道或反向代理。
如果这个 profile 已经存在过(比如你之前装过一版),不要再用 --from-default-profile 那条命令,它会覆盖 profile 并删掉已有的 cordis.patch.yml,正确做法是改那个文件而不是替换它。
三个最容易卡住的点
第一个是账号。前面说过,需要一个独立的 GitHub 用户账号,且不能用 App 形式。额外注意一点:它读的是这个账号的通知,所以要接受仓库邀请、要能收到提到它的消息。
第二个是隔离,这条最需要你在动手前想清楚。官方文档写得很直接:两个预设都以 danger-full-access 跑在操作者的系统用户下,账号包装器只决定用哪份凭据,不隔离进程;一个 worker 可以按路径执行程序,也能读操作者的 home 目录。官方的建议是用一个专用的系统账号,里面只放这个 agent 的凭据。如果这台机器同时是你的主力开发环境,我个人会直接跳过这条路,换成独立机器或独立系统账号。
第三个是「常驻」这件事本身要靠机器撑着。机器休眠、进程退出,持续观察就断了。仓库里的启动脚本用 tmux 跑服务,再配一个每小时检查兜底重启,所以你得有一台常开的机器,这一点靠软件绕不过去。
第一次试用
装完之后,从允许名单里的账号,在仓库的一个 issue 里评论一句 @<agent 的登录名> <你的需求>。大约一分钟,面板概览里会出现一张工单。想验证权限确实生效,可以换个不在名单上的账号 @ 它一次,看它是否安静。
最后三条,来自仓库自己的守则
docs/000-philosophy.md 给操作者列了四条责任,我挑三条最重要的说,因为它们比安装步骤更决定这东西用得好不好。
Start narrow(从小开始)。一开始把允许名单放短、主动工作只记录不执行,等它「本来会做的事」看起来对了,再放宽。
Tune for less noise(为降噪调,不为解释调)。当它太吵,就把提示词改短改严,或者直接暂停它,不要靠加更多消息去解释刚才那条消息。
Own what it says(它说的话算你的)。它在你的团队天天看得见的账号下评论、开 PR、给人发消息,每条都在消耗同事的注意力,责任在你。要去读它用你的名义发了什么,错了就改提示词。
这三条合起来其实是同一件事:这套东西的设计目标写在理念文档第一行,尽量少占人的注意力,同时尽量多给价值。剩下的每一处设计选择,包括前面那整套权限模型,都是在为这句话服务。
我的结论
它是个研究原型,别当成品看。但它的图纸价值高于很多成品:多 agent 协作这一年里,大家都在拼编排和并行,很少有人认真把「授权」当成一层单独设计、还给它配上版本和重新校验。这 242 行政策和那几百行文档,是我见过的公开材料里,把这件事讲得最完整的一份。
要提醒的是它的 MIT 协议和「图纸」属性是一体两面:你可以整段抄走这套权限模型,别人也可以,包括闭源竞品。所以值得盯的是这套「谁能派活」的写法会不会变成下一年的默认做法,这比它眼下多能干活重要得多。
站内 9 月拆过 DSH 本身(那篇讲的是插件架构与形式化理论),这篇拆的是跑在它上面的 agent 以及它的权限模型,两篇分工不重叠。想先理解它脚下的那层,见《DeepSeek Harness 深度源码解析》;想把它和别的编排方式对照着看,见《多 Agent 编排:三种 Orchestrator 模式深度对比》;这条「操作层」为什么会被大厂收编,见《94 位顶级工程师,只有 8 位还在逐行读 Agent 的代码》。