Skip to content

1. 工单的一生 ​

一个人在群里说:这个接口昨天开始偶尔返回 502,帮我看一下。

接下来发生的事按时间排开,每一步都由某个具体对象负责。

来源收事件。 群消息进入系统,被翻译成一个触发。来源先做两件事:丢掉机器人自己的消息,丢掉不在监听范围内的消息。

触发进线程。 这条触发归属到某个线程——如果它是对之前某条讨论的回复,就归到那个线程;如果是新话题,新开一个。

评估研判。 线程上积攒的触发按批进入评估。评估看三样:这批触发、线程快照(含该线程上所有工单,包括已结束的)、当前策略。它输出一个决定。

接受并建工单。 决定是接受时,创建一个工单,同时给出一份授权——记录这次工作凭什么可以做、能走多远。

派工给执行会话。 工单绑定一个执行器实例。系统为它准备一个工作目录,起一个会话,把工单内容与授权范围交给它。

执行器报进展。 它查日志、定位、准备改动,中间可能提问,可能报告就绪。

人跟进一句。 有人在群里补了一句"顺便看下是不是昨天上线的那个改动"。这条消息不需要重新评估——工单还活着,且它订阅了这个线程,所以消息直接扇出到执行器。

关闭工单。 修完、验证通过、报告关闭。

三天后有人再说一句。 同一个线程上有人问"那个修复上线了吗"。已结束的工单保留着订阅,所以这条消息到达了它。系统在恢复之前做一次重新校验:这个来源还在监听这个线程吗?当初的授权依据现在还成立吗?都通过,工单恢复,沿用同一份授权,不需要新的评估与决定。

这条链路里有两件事值得先记住。

执行会话不等于任务。 第三步到第五步之间可能换过执行器——如果某个会话崩了,工单不结束,重新派工继续。任务的生命周期长于任何一个会话。

授权不等于可见性。 第七步那条跟进消息之所以能到执行器,是因为工单订阅了线程,不是因为发送人有权限。授权在第四步已经判过,之后的讨论只是补充信息。


2. 它解决的问题 ​

多数多 Agent 框架处理的是「父 Agent 如何派生子 Agent 并收拢结果」。deepseek-ai/dsh-superagent-prototype 处理的是另一个问题:Agent 会话崩了、超时了、被压缩了,任务不能丢。

这个定位差异决定整套设计。如果任务是「派生并收拢」,核心是通信协议与结果合并;如果任务是「会话可替换」,核心是状态存在哪、谁能改它、改了之后如何被下一任执行者读到。

它的答案是:把状态放进一组领域对象,让 Agent 会话成为可替换的资源。长驻主会话只做路由,每个工单配一个执行会话,中间隔一层完整的领域模型。

接下来先看这层领域模型由哪些对象构成,再看它如何支撑上面那条链路,最后给批判。


3. 领域对象与各自保持的规则 ​

要让会话可替换,状态必须离开会话。这一步的直接产物是领域层:十个概念,每个都带一组不可违反的规则。

核心是五个:来源、线程、评估、授权、订阅。其余支撑。

来源(Source) 是一串事件流。种类是可复用的实现,实例是种类的一次配置(是否运行、监听什么)。它保持的规则:持有自己的读取位置与自己的一套机器人身份;定义什么算一个事件,包括只在时间上成立的条件;在事件进入领域之前就丢掉机器人自己的消息与监听范围外的触发;把每个触发翻译成它所属的线程与可回复的渠道地址。

「重启不得重放事件」这条规则是它存在的理由。读取位置存在领域层,不在内存里。

触发(Trigger) 是一个不可变事件,带来源自己的元数据(例如发起人)。规则:属于一个或多个线程;同一触发被接受两次只创建一个工单。

这条规则给出幂等性。没有它,网络重试会变成重复开工。

线程(Thread) 是按时间排列的触发序列,由来源定义哪些触发构成一个线程。规则:同时最多一个评估在它上面运行;可以关联多个工单;持有自己的订阅、正在运行的评估与决定历史。

这条规则给出「一致视图」。评估看到的线程快照不会被并发修改。

关联(Correlation) 是线程之间的关系:一个触发可以属于多个线程。规则:只增加上下文,永不参与路由。

这个区分很精明:关联让评估能看到相关证据,但不改变「这条消息该归谁」。两件事分开,避免「因为看到了别的线程,所以把工作派错地方」。

渠道(Channel) 是线程的写侧,地址由来源定义。规则:一个线程可以有多个渠道;一个工单持有一组渠道。

确认、答复、拉取请求、已解决的评审,四者都是写操作,用同一个抽象。

策略(Policy) 是版本化的评估规则加上每个来源的配置:谁能通过该来源发起工作、在该来源自己的身份体系里、名单如何同步、哪些机器人可以产生候选。规则:版本覆盖规则、接受模式与来源配置;影响活工单的变更(例如它被接受时依据的规则变了)投递给它的派工,不撤销授权;不同来源上的身份永不互相映射。

最后一条规则来自一个具体教训:同一个真人在两个来源上有两个账号,系统不尝试合并它们,身份映射交给提示词层(团队可以写一个说明谁是谁的 Skill)。领域层不猜。

评估(Eval) 是对一批触发的判断,分两层:准入与研判。规则:输入是触发批次、线程快照、该线程上所有工单(含已结束)、当前策略;输出是一个决定;不持有状态。

「不持有状态」让它可重放。评估失败可以重新评估,不需要恢复中间态。

授权(Mandate) 是「接受」这个动作给出的东西:策略版本、范围、强度、匹配的规则、发送人在该来源上的身份、来源如何收到这个触发。规则:不可变;范围变宽意味着新工单;工单结束不结束授权,只有人的停止能撤回它,或者一个不再持有它的策略能撤销它;已结束的工单恢复前要按当前策略重新校验。

工单(Ticket) 是一个被授权的工作单元。规则:只有评估能创建;引用它的授权;持有渠道与输入;走 open → ended,当人在它的某个线程上发消息时可以回到 open,除非是被叫停的;定义自己的状态,不跟随执行器的阶段。

最后一句是整套设计的支点。

派工(Assignment) 是工单与一个执行器实例的绑定。规则:一个工单在时间上可以有多次派工;走 pending → running → ended,独立于工单;只有它的最终报告才能在执行器一侧结束工单。

「执行器在领域之外,但谁在执行必须在领域之内」——这句话解释了为什么需要派工这一层。

订阅(Subscription) 是工单与线程的多对多关系,由线程持有。规则里信息量最大:匹配的触发会扇出到每个活着的已订阅工单,不检查是谁发的;以这种方式到达工单的人的消息不进入评估;执行器为上下文跟踪的线程同样到达工单,但不授予任何权限,也不恢复它;已结束的工单保留订阅(包括已关闭或已合并的拉取请求的订阅),人的消息可以在同一份授权下恢复它,不需要评估或决定,机器人与 CI 的消息不恢复它;人叫停的工单什么都不接收;主会话只在消息明确指向某工单时才把线程附加到该工单。


4. 两条独立的状态线 ​

工单与派工各有一条状态线,这是「会话可替换」的技术前提。

对象状态迁移条件
工单open → ended人叫停、工作完成、执行器报告 dropped
工单ended → open人在它自己的线程上发消息,且未被叫停
派工pending → running → ended执行器启动、执行器报告、执行器崩溃

两条线独立带来的能力:执行器崩了,工单不结束,换一次派工继续;执行器完成了,工单可能还在等后续的评审意见。

这个分离解决了单 Agent 设计里一个常见问题:会话结束就等于任务结束。实际上任务的生命周期通常长于任何一个会话——一个拉取请求可能开三天,期间执行器换过两次。


5. 授权与上下文的分离 ​

这是整套设计里最值得学的一处。

授权决定能做什么。 它记录策略版本、范围、强度、匹配规则、发送人身份、来源接收方式。

订阅决定能看到什么。 每个活着的已订阅工单收到的线程消息,作为上下文扇出。

关键规则:扇出绕过准入检查,也不授予任何新权限。工单始终受限于它的授权范围。

这个分离带来的好处很直接:讨论可以自由流入执行器,不需要每次重新判断「这个人有没有资格说话」。资格在授权时已经判断过,后续消息只是补充信息。

它也带来一个需要正视的风险。规则明写扇出「不检查是谁发的」——包括机器人消息与 CI 消息。这意味着一条来自任何来源的消息都会进入执行器上下文,而执行器可能据此行动。

设计者的防护是:扇出只给上下文,不给权限,执行范围仍受授权约束。这个防护在「授权范围写得很窄」时成立;如果授权范围很宽(例如「可以修改本仓库任意文件」),扇出就成了一个软通道——外部内容能影响一次被授权的大范围修改。

我的判断是:这个风险没有被消解,只是被推迟到了授权范围的定义上。更稳妥的做法是让扇出也带来源标记,并在提示词层明确「来自非允许名单的内容仅作参考」。


6. 三档强度与准入表 ​

授权强度分三档。

  • 显式请求:允许名单上的人点名智能体,或在线程已有工单时跟进。
  • 推断候选:名单外的人跟进、准入表某格设为推断、来源机器人名单上的机器人消息。
  • 不准入:其余机器人消息、被准入表忽略的、以及关闭模式下的一切推断。

准入表是「接收范围 × 三格」。三格分别是:在名单上但未点名、不在名单上但点名、不在名单上且未点名。每格可选推断、忽略或显式。

把三档与被乘的三格展开,得到实际的准入矩阵:

接收范围名单上·未点名名单外·点名名单外·未点名
仅提及推断(可配)显式不准入
全部消息推断(可配)显式不准入(可配为推断)
机器人消息不准入不准入推断(仅限机器人名单)

接受模式独立于强度,是三态的:关闭、观察、自动。观察模式下,正向研判被记录为「本会接受」但不实际接受。从观察到自动的迁移路径很实用:先让系统跑一段时间,看它会接受什么,确认无误后再开自动。

6.1 准入表的问题 ​

准入表在表达力上没问题,在可理解性上有问题。它是接收范围乘三格的笛卡尔积,加上机器人名单、接受模式、策略版本,共同决定一条消息能不能变成工作。

一个非工程角色的团队负责人,要回答「我的团队里谁知道这个机器人能替他开工」,需要把五个概念一起读懂。这在实际部署里很难。

更可用的形式是「默认拒绝 + 显式列出允许的交集」:让团队直接列出「谁在哪个范围可以请求什么」,其余一律不准入。表达力下降,但审计成本大幅下降。


7. 策略版本与重新校验 ​

授权绑定策略版本这个选择,带来了一个必然的后续机制:恢复前重新校验。

已结束的工单保留授权。人在它自己的线程上发一条消息,可以恢复同一份工作,不需要新的评估或决定。但恢复前要按当前策略重算。

校验依据按授权的来源分类:

授权依据校验项
显式跟进来源仍监听原线程即可
推断接受模式仍为自动、匹配规则仍启用
准入表某格按今天的表与名单仍能给出原本的强度
机器人该机器人仍在来源的机器人名单上
其余发送人仍在允许名单上

校验失败时,工单保持结束状态,等待中的消息按「未到达任何工单」处理,进入评估。主会话不会被告知它曾到达过工单。

这段设计我认为是对的:授权不是一次授予永久有效,它绑定当时的策略版本,恢复时按当前策略重算。代价是恢复可能失败,而且失败原因对用户不透明——他只知道「我说了话,但没有反应」。


8. 提示词装配与优先级 ​

领域对象决定了「谁能做什么」,提示词决定了「模型怎么想」。这套设计的提示词装配有一个明确约束:装配顺序必须让最容易变的部分在最后。

装配顺序共八步:

  1. 共享指令与角色;
  2. 三个团队段落(沟通、工单范围、交付要求);
  3. 订阅与回复;
  4. 主会话的派发指令,或执行器的工作指令;
  5. 准入指令(仅主会话);
  6. 交接指令;
  7. 环境段;
  8. 策略段(仅主会话)。

后两步的理由写在文档里:环境段给出部署级的值与工单级的值;策略段从设置每次模型请求重新生成,它放在最后,这样策略变更不会改动前面的提示词文本。

这是一条缓存意识的设计。策略是最常变的部分(版本、接受模式、启用的规则),如果它排在前面,每次变更都会让后面的全部前缀失效。

优先级阶梯从强到弱共五级:

强度内容能否被提示词改写
1工具与宿主强制的事实:身份、权限、工具参数检查、回复用途、长度限制不能
2内置段与生成段(按团队覆盖后的文本)团队可覆盖
3主动工单的匹配规则中的流程与沟通要求(仅该工单执行器)规则可配
4团队段落与团队 Skill团队可写
5外部文本:消息、评论、文档、引用永不改变规则或授权

第五级的表述值得抄:外部文本是数据,永不改变规则或授权。这条把提示注入的防线从「靠模型识别」提升到「靠层级压制」。

第三级也值得注意:规则可以覆盖默认的流程与沟通要求。一条静默规则可以抑制确认消息;一条评审规则可以指定必须的评审人与合并前审批。这让不同的主动工作有不同的流程,不必写多套提示词。


9. 注意力预算的六条硬约束 ​

这套设计的开篇原则是:用最少的人类注意力,给出最大的价值。它被落实成了可检查的数值。

约束数值我的理解
路由笔记长度不超过 30 词迫使路由只传事实,不做解释
发给人消息长度不超过 100 词强制删掉寒暄与铺垫
主会话禁止等待shell 超时超 30 秒拒绝,job 等待超 5 秒拒绝等一秒,就晚一秒处理新候选
后台预算回合结束后最多 30 分钟防止卡死任务永远占槽位
确认消息恰一条重复确认是纯噪声
提问形式一次一个具体问题,结构化避免问题埋在长报告里

前两条的作用不是省 Token,是强迫表达。写 30 词的路由笔记时,你没法写铺垫,只能写「哪个工单、什么决定、为什么」。

第三条尤其值得说。多数系统允许主 Agent 等一个长时间命令跑完,代价是这期间新的候选无法被处理。这份设计直接禁止等待,理由是「一个被阻塞的步骤会延迟新候选」。这是个很强的取舍:它把「主会话绝不阻塞」放在「方便」之上。

这条规则还带出一个实现细节:shell 命令执行 5 秒后自动转到后台作业,让执行器能在下一步收到新消息,不必等长命令结束。这个转换是透明发生的,模型看到的是「命令在后台运行」。


10. 执行器与槽位 ​

每个工单获得一个名为 worker-<工单 id> 的执行会话,以及一个按工单短名命名的目录。

准备工作。 第一次派工前,如果工单目录不存在,用 shell 在执行根跑一次准备命令,限时十分钟。典型用法是创建工作树。失败则工单失败并记录输出。

准备命令的存在说明一个取舍:环境准备放在领域之外。系统不假设环境怎么来的,只提供一个钩子。

后台工作与槽位。 一回合结束时若还有 shell 任务或子代理在跑,工单保留槽位并保持「工作中」,不报告为等待。任务完成会唤醒执行器。

交接给人即释放槽位。 报告为就绪或提出问题后,除非之后又来了新输入,槽位立刻释放。

交接时子代理停止,shell 任务可继续。 这条区分很细:子代理继续跑会产生新的回合,与「等人在处理」冲突;shell 任务(例如起一个供人评审的服务)继续跑是有用的。

并发上限加一个额外槽位。 配置的并发上限之外留一个槽位,专供人的消息与状态查询。人的唤醒可以在正常槽位满时使用额外槽位,并且优先于新任务启动。

最后这条是整个配额设计里最漂亮的一处。它把「自动化任务的紧急程度永远低于人的直接请求」这个隐含假设写成了显式规则。多数系统的并发池先到先得,结果是自动任务占满槽位时,人打字没反应。


11. 我的批判 ​

八条,每条挂在上文某个具体设计决策上。

11.1 挂在「主会话只路由」上 ​

路由不做技术决定,但要做范围判断,两者矛盾。

文档说主会话「只转发原始触发与路由事实,不做技术决定」。但同时它要判断「这条消息该接受、该附加到已有工单、还是该忽略」,这本身就是一个技术判断——判断它是新工作还是已有工作的延续。

文档给出的办法是「只在消息明确指向某工单的拉取请求、议题、来源消息或致谢时才附加,主题相似不够」。这条规则可靠,但它把「明确指向」的识别责任交给了路由模型,而路由模型被要求不读代码。

替代做法:把指向性做成可计算的特征(消息里是否含工单的标识符、是否是对该工单消息的直接回复),不必让模型判断。领域层已经有这些数据。

11.2 挂在「领域事件齐全」上 ​

事件齐全,但不记录判断理由。

系统记录了每次决定、授权与派工,但「为什么这样判断」没有结构化留存。面板显示状态,不显示推理。

对一个需要人审的系统,这是实际缺口:人在复核一次「本会接受」的观察记录时,看不到判断依据,只能看到结论。

替代做法:把判断理由写进事件,但在面板上折叠,不占人的注意力,需要时可查。

11.3 挂在「30 分钟后台预算」上 ​

预算截断长任务连续性。

这个约束的目的是防止卡死任务占用槽位,但代价是把长任务的连续性交还给人:跑测试超过 30 分钟,任务被杀,报告等待,人要么重新触发,要么手工继续。

替代做法:按任务类型区分预算。测试类给更长预算,交互类给更短预算。预算的依据应当是「这类任务通常多长」,不是一个统一数字。

11.4 挂在准入表上 ​

表达力换可审计性。

已在第 6 节说明。替代做法是默认拒绝加显式交集。

11.5 挂在「策略变更不撤销授权」上 ​

安全类策略变更被推迟。

规则明写:策略变更会丢弃正在运行的评估、重新排队触发、给受影响工单发变更通知,但「不会立即撤销活工单的授权」。撤销只发生在下次恢复前的重新校验。

理由是「被取消的变更会留下半完成的工作」,所以选择记录并通知。

我认为这个权衡在多数情况下成立,但有一个它没覆盖的场景:策略变更的原因是安全问题(例如发现某条规则会导致越权)。此时「推迟到下次重新校验」意味着风险继续存在。

替代做法:给策略变更一个紧急标记,紧急变更走撤销路径,并接受半完成工作的代价。

11.6 挂在「扇出不检查发送人」上 ​

扇出是软通道。

已在第 5 节说明。

11.7 挂在「单工单单派工」上 ​

并行只能靠子代理,而子代理不再收窄权限。

一个工单同时只有一个派工,所以工单内部的并行只能靠执行器起子代理。文档说执行器的子代理使用宿主的「标准」预设,并保留执行器的允许工具,工单结束时失去这些工具。

缺口在这里:子代理继承了执行器的权限,没有进一步收窄。如果执行器有写仓库的权限,它的子代理也有。

对一个把「授权与上下文分离」做到极致的系统,这一处的粗糙显得不协调。替代做法:让子代理的权限由派工时声明的子任务类型决定,不复用执行器的全部工具。

11.8 挂在「与宿主原生机制并存」上 ​

概念重叠。

宿主的委派家族已有进程内、分叉、跨运行时等后端,另有一套团队机制提供身份、邮箱、任务与共享检出。superagent 又实现了自己的执行器会话、邮箱式消息传递、来源上的身份。

三套东西并存,分工靠文档说明维持。这带来一个实际问题:部署者要判断什么场景用哪套,而这个判断没有明确依据。

替代做法:在领域层显式声明哪些能力委托给宿主机制、哪些自己实现,并给出判据。


12. 三条成立的设计 ​

批判之后要说清哪些成立。

第一,授权与上下文分离。 这是整份设计里最有价值的一条。它把一个常见混乱——「谁看到了什么」与「谁被允许做什么」——拆成两个独立的领域对象,各有明确的创建与失效规则。第 5 节提到的风险不否定这条设计本身,只说明它在「扇出不检查来源」这一点上留了口子。

第二,执行器可替换。 因为状态在领域对象里,执行器只是一个可替换的端口。这带来的不只是容错,还有可测试性:你可以给同一个工单换不同的执行器,观察结果差异。

第三,把注意力当一等预算。 30 词的笔记、100 词的消息、禁止等待、额外槽位,这些看似零散,实际是同一条原则的多次落实。它迫使每个交互都回答「这次交互值得占用多少人的注意力」。


13. 可迁移的三条经验 ​

如果把这份设计抽象成三条可迁移的经验:

第一,把「任务」与「执行它的会话」分开。任务有自己的状态与生命周期,会话是可替换的资源。这条能直接用在任何长流程 Agent 系统上。

第二,把「能不能做」与「能看到什么」分成两个独立的授权对象。混在一起时,每次消息到达都要重新判断资格,而这个判断本身会成为瓶颈。

第三,给人类的请求留一条不排队的通道。看起来是个小优化,实际上它决定了系统在高压下是否可用。

三条都不依赖这份原型的领域模型,可以直接用在别的架构里。