DeepSeek Super-agent 设计探索中译中(上)的蓝发鲸鱼娘女仆封面

封面角色原型为上善无形的《溟月》,女仆版由 ZipZipPipe 二创。封面扩图和标题排版由 AI 辅助完成,按 CC BY-NC-SA 4.0 分享。

DeepSeek Harness 团队在 10 月 31 日深夜发布了一篇非常硬核的技术向文章,也就 6 个小时,还是大晚上的,已经接近 10 万了。这篇文章对于从事或将要从事 Harness 设计与开发的朋友可能会非常有启发。

但我相信,还是有很多朋友没太看懂这篇文章说的啥。我斗胆做一下中译中。毕竟我日常最爱用的 prompt 就是「用非程序员也能听懂的话重说」。

1. Super-agent

我们先说说这篇文章在聊个什么事。

我先假定你们都知道 Agent 是个什么东西,或者至少用过 Codex、Claude Code、Cursor、WorkBuddy、Manus 这类产品了,它和豆包这种聊天机器人(也叫 chatbot)不一样,它能调用工具运行程序,能直接给你干活。

你在用的时候是不是大部分情况下,有一个新的事就开一个新的对话(这叫 session),然后打字输入你的要求,比如:「帮我把邮箱里收到的发票都下载下来」。在这方面,AI 已经能干得很好了。但这需要你自己想起来,你亲自在 AI 对话框中输入这条指令。

这在单个任务中没有问题,但如果你要同时处理多个任务,那卡点就不是 AI 行不行了,而是你的脑子够不够用了。

你说我不处理多任务,我每次就干一个事。不是的,很多时候,你以为是一个事,实际上涉及很多个任务。

比方说你打算用 AI 做一个游戏,合成大西瓜这种吧。你想设计个不一样点的玩法,有两个 idea,但没想好用哪个,你开了一个新的对话跟 AI 聊,问问哪个玩法好。
AI 正思考呢,你想到休闲游戏啊,主题很重要,要亲民,于是你又开一个对话,跟 AI 讨论用什么主题比较好。
AI 正思考呢,你想到美术也很重要啊,现在流行什么风格呢?应该走像素风还是写实风呢?你又开一个对话跟 AI 讨论美术风格,让 AI 给你找找免费的素材,先感受感受。
AI 正思考呢,你想到碰撞合成时那种弹弹的质感比较重要,但你又不懂技术,于是你又开一个对话,问问 AI 怎么做吧,应该用什么技术方案,听说要用游戏引擎,游戏引擎又是什么?
AI 正思考呢,你突然冷静下来,是不是应该先做个市场调研啊,你赶紧又开一个对话,让 AI 调研一下休闲游戏近几年的情况。
AI 正思考呢,你又开了个对话让 AI 先帮你找一批音效素材。
这时候前面几个对话里的 AI 陆续思考完了。你开始看 AI 回的内容,然后再发一条消息追问。你开始在各个对话中不停地切换。像打地鼠似的,按下去葫芦起来瓢。
正当你大脑已经满负荷运转,温度急剧升高时,你突然想到,游戏的音频得跟主题和美术契合啊,你赶紧去主题和美术的对话里找,然后复制出来,发在音效对话里。
你脑仁都要炸了。

多任务会比很多人想象中来得更迅猛。因为人类对于生产力提升的追求是无止境的,当明知道 AI 有充分的能力能干更多的事,只是卡在人类的注意力不够时,就必然会诞生新的方法、工具、流程。

如果你已经明白了,现有的一个对话一个事的结构对于多任务和跨任务同步信息来说很费劲的话,那么 DSH 这篇文章试图解决的就是这个问题,他们设计了一套新的工程架构。(至于在现有产品里偏不一个对话一个事,所有事都发在一个对话里行不行,推荐去看我之前的文章。)

Personal Agent 的两方面不同

最近一窝蜂出来的 Personal Agent 的用法就和上面的不一样了。具体有两方面不同:

一是你不用一个事开一个对话了,它就只有一个对话框,你用它做游戏,什么玩法、主题、美术、调研、技术栈,全在一个对话里聊就完了。

二是有些事不用你主动输入了,比方说下载发票邮件的事,不用你亲自想起来,你只要关联一下邮箱账号。你一收到新的邮件,你的 PA 就会立刻读取你的邮件,然后分析需不需要干点啥。如果此时收到的是一个发票的邮件,并且你曾经让它下载过发票并发送给「财务李姐」,那这个 PA 就能自己分析、自己下载、自己发给李姐,最后告诉你一声,你啥也不用干。这就省了你大脑的算力了。

那除了邮件、日程这种个人场景的活能让 AI 省事,其他的场景,比如工作中的活也能这样不?DSH 说,能的兄弟,能的。

其实这种「监听」本身并不困难,在群里加个机器人,不用你 @ 它,发了和它有关的消息,它自己跳出来回复是很容易实现的。难的是以下几点:

1. 不同场景下各条指令的连续

你给你的工程师发一个需求文档,然后线下跟他对了一下,发现有个地方要改,你俩口头说好了换成新方案。然后你跟老板开了个会,要求新版本要尽快上线,某些功能可以放到二期。你在飞书里私聊你的工程师:最后那个功能先不用做了,二期再实现。作为一个工程师,虽然很烦你,但是他是能处理好这些事的。

但如果是用 AI,你把需求文档的标题加上【定稿】,不用你发,AI 自己就开始按这个需求开发了,这时候飞书群里设计师说要改个地方,AI 立刻更新到文档上,并且开始按新的需求开发,并回滚之前的部分工作。

这时候老板在飞书上私聊你「这个版本比较急,优先保核心需求吧」,没等你说话呢,AI 直接回复一个「收到」,并开始重新规划需求优先级。

这时候的 AI 就很像那个烦你的工程师,它像是 1 个人,而不是 3 个对话。它能理清楚前后发生的事,知道「改」是在什么的基础上「改」,而不是你开 3 个 session 然后在不同对话里复制粘贴改需求了。

2. 一口气安排多个任务并行处理

前面做游戏那个例子,如果说不再开多个对话,咱们就在一个对话里把主题、玩法、美术、技术方案、调研、音效的事都干了呢。能,但是得排队。在第一条消息处理完返回结果前,你新的消息会排队。

Agent 产品提供了插队的功能,Codex 叫「引导」,Claude Code 叫「立即发送」,但这种插队是打断式的,会先中断前面的工作,再把新的指令合并过去重新处理。除非你在第一次的指令中就包含了所有要求,如果你在点了发送键之后才想起来的事,对不起,跟不上这趟车了,等 AI 处理完再说吧。

这就不够理想了,理想是我像个领导一样,想一出是一出,啪啪地派活儿,而 AI 还能每个都接得住,还能并行高效地开始干,这样的 AI 用起来才不烧心。

3. 主动但又有边界感

你希望你在工作群里说一句「好,那就按照 Zack 说的那么改」,AI 直接就出来回「好的收到」,并开始干活。但你又不希望你在奶茶群里说一句「今天的红茶拿铁半价诶」,AI 就跳出来说「好的已下单」。

4. 持久和稳定

别像 OpenClaw 一样动不动就挂了,成天出 bug,好的时候好用,坏了就得再找人修。

要满足这些需求,你现在用的 Codex、Claude Code、WorkBuddy 这类产品就得做些改造了。DSH 做了一些实践,效果还不错,具体抄成啥样可以自由发挥,但最好得有以下五个模块。

2. 双层架构

「一个事开一个对话」比「所有事都堆在一个对话里」要好,这是现有的产品架构决定的。但这不符合用户直觉,很多用户本能地就是想把所有事都发在一个对话里。应该改的不是用户直觉,而是产品架构。

DSH 的改法是,有一个对话 A,专门负责开启其他对话。这样用户所有事都可以发给对话 A,对话 A 永远不直接干活,它只负责开启新的或者使用之前开过的旧对话,来完成任务。

每次只帮你向前「推进一步」

我不讲分诊台和科室的比喻了,我用一个实际开发过的例子来讲。我之前设计过一个产品,我假定了需求的落地存在以下几个阶段:

  • 「还没有想清楚的」探讨阶段

  • 「明确了但描述不够清晰完整的」梳理阶段

  • 「清晰完备的」可实施阶段

  • 最后进入「开发」阶段

  • 实现之后的「Debug」阶段

这个产品的特色就是每次只帮你向前「推进一步」。尽管假定存在这样几个阶段,但我不能假定用户提供的内容每次都一定从第一个阶段开始,也不能保证每次一定能成功向前推进一步,3 次必进开发。

我的解决方案是设计两层 AI,第一层 AI 是路由层(Router),它只负责判断用户提交的内容当前处于哪个阶段,然后转发给负责这个阶段的第二层 AI。

第二层 AI 平行地分成 4 个执行器(Executor,等同于 Worker),每个执行器有不同的工具权限,对输入输出有不同的要求,使用的也是不同的系统提示词。这样,无论用户提交什么样的内容,产品都可以给出最匹配当前内容的回复和调整建议。

DSH 设计的架构也是一样的思路,但更灵活。第二层不约束固定的执行器,而是由第一层的路由层现开现设计一个执行器(应该也可以有提前定义好的执行器)。在这样的架构下,用户永远只和第一层的路由层对话,执行层开了几个会话这些都不需要用户了解。

回到游戏的那个例子,你在一个对话里把玩法、主题、美术这些想到一个就发一个,还插播让它设个闹钟。这一切都能处理,因为路由层在后台偷偷开了好几个不同的 Worker,一个个给你干呢,每个 Worker 之间也不会有上下文污染的问题。这就基本实现了「一口气安排多个任务并行处理」的要求,也符合用户交互的直觉:啥事都发在一个窗口里。(我还是更喜欢 Grok Bot 的设计)

体验双层架构

事实上,我们现在几乎已经可以直接体验这种双层架构了。你完全可以在 DSH 中直接调用 Codex CLI 并执行命令,并且新建的会话会出现在 Codex app 列表中。我在 DSH 中配置了 Codex 遥控和 Claude Code 遥控,这样我所有的任务都直接发在 DSH 中,由它路由到 Codex 和 Claude Code 中,由它决定使用哪个旧 session 或者创建新 session,并给出具体的指令来执行任务。(现在改用 Grok Bot 遥控他们仨了,顺便解决 Remote 的问题)

如果你也尝试这样做了,你很快就会意识到,Worker 们的上下文是隔离了,Router 层的上下文污染怎么办?且往下看。

3. 事件驱动

在聊 Router 层的上下文污染前,我们还要先进一步增加 Router 层的上下文输入。

触发器与两层过滤

为了实现前面像 Personal Agent 的那种「有些事不用你主动输入了」的体验,我们得提前设定好,什么事件可以自动触发,比如说群聊消息、邮件、文档出现评论等等。每类事件都需要定义触发条件,编写对应的程序。这些机制统称为「触发器」(Trigger)。之前的「人主动在输入框中输入内容并发送」当然也是触发器的一种。

这样就从之前「人类说一次话才干一次活儿」变成了有可能自动开始干活儿了。

这带来的第一个问题是,如何保证触发时机的精准?因为触发场景多了,就会带来很多没用的不该触发的事件(噪音)。

在前 LLM 时代,只能用一堆 if else 进行判断的时候,要想做这个事,必须每个场景去抠规则,既要保证该触发的都触发,又得让不该触发的别触发(噪音),这类工作养活了一些策略产品经理。

但是大人,时代变了,我们可以让能提前判断好的,确定的场景用固定程序来触发,不能的,直接交给 LLM 大人就好了,我们不妨就相信模型的判断。

全交给 LLM 大人判断行不行?理论上也不是不行,但你有那么多钱烧 token 吗?说话!

原文中的例子:

「监听群聊」就是新增的触发器,但不是所有群聊内容都是应该被 super agent 处理的。比方说闲聊群,就可以在程序上设置成「永不触发」,你在闲聊群里说破大天,报一个 P0 级线上事故,AI 也不会理你。

用户群就不能永不触发,万一用户发现个严重 bug,管理员却迷失在表情包梗图里,漏看了这条怎么办?所以在程序过滤这步得放过去。但用户发的是夸夸消息,是梗图怎么办?这时候就交给 LLM 大人来判断了。如果用户发的是「新版界面看着挺清爽」,那 LLM 就会判断为「没用,不触发」,如果发的是「切换会话时偶发白屏」,那 LLM 就会判断为「有用,触发」,并交给路由层开始工作。

原文配图:三类群聊触发事件

注意,这里「负责第二层过滤判断的 LLM 大人」和「开始分配工作的路由层」在逻辑上不是同一个东西,尽管两者都是 LLM,甚至实际操作中,你就让路由层把过滤判断的活也干了,不该触发的就丢弃不发给任何 worker,我觉得也不是不行(甚至 DSH 原型就是这么做的)。但在逻辑上,这是两个环节,两件事。

跨场景指令的连续

这种触发器的设计结合全局唯一的路由层,就实现了「不同场景下各条指令的连续」。你在项目群里对需求的讨论,你对文档的修改,工程师对需求单的操作记录,这些都经由专门的触发器,发送到唯一的路由层,这就实现了 AI 能在全局上进行理解:只要是关于同一个事的,就转发给那个负责这个事的 Worker。

这样的设计会不会让路由层的负担太重,以及上下文污染呢?

上下文污染

我们先来说说什么是上下文污染。我们在跟 AI 来来回回发消息时,例如:你先发给模型的内容 A,收到模型的回复内容 B,然后给模型发送内容 C。

真实发生的是第一次调用模型时,注入的是(大量的系统提示词工具定义等)+ 内容 A,然后模型返回的是内容 B。

第二次调用模型时,注入的是(大量的系统提示词工具定义等)+ 内容 A + 回复 B + 内容 C。

由于模型是没有记忆的,所以每次新的调用其实是客户端主动把历史记录也拼在一起发过去了。这显然会随着历史记录的增加越积越多。

实际上在使用 Agent 的时候情况会更严重,因为一个指令 A 过去,Agent 通常会执行很多步(循环),这些执行结果都会写到回复 B 里,并且在下次发送指令 C 的时候把这些全都带上。

如果 AB 和 C 是完全不相关的两件事,就会造成上下文的污染。

当然,不是说 AB 和 C 关系不大就一定是一种「污染」,「污染」主要是无关细节混入,过期信息和当期信息并存以及噪音,究其根本,是因为这些会影响到模型的「智能」。

浪费上下文窗口,就是浪费模型的智能。

路由层的输入来自各个触发器,这种上下文过度积累和污染会更加严重。那要怎么办呢?

从源头给路由层减负

第一步就是能不进路由层的尽量不进,从源头控制。

前面讲了对触发器的过滤,不是什么都进路由层,但通过的触发器也不是都能进路由层。一个事情在路由层安排给一个具体的 Worker 之后,这个 Worker 就会订阅这个 Thread,这个 Thread 一有更新(会产生一次触发)就不用进路由层进行判断了,直接发给那个订阅了的 Worker。这就实现了给路由层减负的效果。

但是这里面要注意一下,一个 Thread 可以理解成是一系列 Trigger 的序列,但不是所有差不多的 Trigger 都放到同一个 Thread 里。什么内容会进同一个 Thread 里完全由 Source 定义。它通常是一个具体的功能,比如说飞书里的话题功能,或者 GitHub 上的 PR。

也就是说,有人创建了一个飞书话题,讨论问题 A:如何调整字号。这产生了一个 Thread,并被一个 worker B 订阅了。然后一个反馈群里有用户 @superagent,提及了字号调整问题。这是个能通过过滤的 trigger,但它不会走订阅直通,因为这条消息不是发在那个飞书话题里的。虽然聊的是同一件事,这条消息还是会进入路由层,由路由层判断该交给哪个 Worker。

在理论上,路由层是有可能把这个相近主题的 Trigger 也分配给 Worker B 的。但我看了 DSH 的原型,在具体的实现上,他们用提示词给这种分配设了很高的门槛。这是故意为之的,因为「错误分配的代价(Worker B 从此收到反馈群那条 Thread 下所有消息)比重复建 Worker 高。」

前文提到的「不同场景下各条指令的连续性」并不是由同一个 Thread 来保证的,而是由 Ticket 和路由层来保证的。我在初读本文时,产生过这样的误会。

Ticket(一个 Worker 的工作记录,一个 Ticket 对应恰好一个 Worker Session)。

Source 是外部世界和 super-agent 之间的翻译层,飞书、GitHub 的 API 和数据格式会被翻译成 super-agent 系统认识的 trigger、Thread 之类的。简而言之,你在群里发的消息,你在 GitHub 上提交的 issue 能被 Agent 处理,是因为 source 层有过定义。Agent 处理完,在群里回你消息,在 GitHub 上评论,也是 Agent 通过 source 层实现的。

如果说在 source 层面就没有将群聊消息定义成一个 thread,群聊消息的每个 trigger 都不会跟别的 trigger 放到同一个 thread 里,都会单独拥有一个 thread ID。

写在最后

DSH 那篇文章发布当晚,我就起笔这篇内容,说是中译中,其实更像读后感。但我很快就发现工作量远超想象,越读越有新思考,许多问题我都需要借助 DSH 查看原型的具体实现,但这个过程也属实获益良多。考虑到还有其他的工作,有点担心以全文为翻译目标的话,最终本文会流产,那不妨就先把这些发出来吧,也五千多字了。全文手打,不含 AI 生成内容。