找回密码
立即注册
搜索
发回帖 发新帖

5247

积分

0

好友

671

主题
发表于 半小时前 | 查看: 2| 回复: 0

上周,DeepSeek Harness 团队提出 Super-agent 架构,试图解决 Agent 在多个 Session 之间的任务协调、上下文隔离与持续执行问题。通过 Router / Worker 双层架构、事件驱动调度和独立的任务生命周期管理,Super-agent 让不同任务能够并行推进,并在后续消息到达或执行中断后继续工作。本文就来拆解这套机制的实现细节。

Router / Worker 双层架构

Router / Worker 的双层架构将全局任务协调与具体任务执行分离管理。Router 负责接收外部消息、判断任务归属、分派工作以及协调不同任务;Worker 则在独立的 Session 中运行,专注于修改代码、执行命令、运行测试等具体工作。

Router Layer 与多个 Worker Session 双层架构图

图注:Router / Worker 双层架构图,展示 Router 统一协调多个独立 Worker Session 的关系。图源 @DeepSeek

这样的分工意味着 Router 必须维护全局交互的上下文,掌握不同任务之间的关联、执行进展以及需要向用户反馈的信息。Worker 则各自保留代码、工具输出和中间结果,把注意力集中在单项任务的执行上。

以 GitHub 工作流为例:Agent 系统同时收到一个 Issue 修复任务和一个 PR 审查任务,Router 可以把前者交给 Worker A,后者交给 Worker B。Worker A 负责定位问题、修改代码并运行测试,Worker B 则去阅读代码变更并完成审查。两个 Worker 拥有独立的模型上下文和工具执行过程,可以并行推进,读取的文件、产生的测试日志和中间结果都保留在各自 Session 中,避免互相干扰。

任务分派出去之后,Router 仍然可以接收新消息、协调其他工作,不必等 Worker A 完成。Worker 的执行进展与结果会通过任务管理机制反馈给 Router。当然,实际并发规模仍受限于可用执行资源、Worker 调度限制以及模型服务的容量。

和主 Agent + Subagent 结构的不同

Router / Worker 与常见的主 Agent + Subagent 结构有一个关键区别。

在主 Agent + Subagent 结构中,主 Agent 可以自行完成任务,也可以根据模型判断将部分工作委派给 Subagent。如果主 Agent 选择自己执行耗时操作,就会长时间占用主 Session——即使通过提示词要求它优先委派,也很难保证模型每次采用相同的执行策略。

Super-agent 把任务委派提升为架构层面的职责约束:具体工作由独立 Worker 承担,减少了对模型自主委派决策的依赖。不过,Worker 内部仍然可以调用 Subagent。例如,面对需要分析多个模块的代码修改任务,Worker 可以进一步拆分工作,交给不同 Subagent 分别处理,再在自己的 Session 中汇总结果。

此外,独立执行的任务之间仍可能存在关联。一项任务可能涉及多个外部讨论,多个 Worker 也可能关注同一个 PR。如何让后续消息准确进入相关任务,同时避免不同 Session 上下文互相干扰?这就需要额外的事件关联机制。为此,Super-agent 引入了 Trigger、Thread 和订阅关系。

Trigger 与 Thread 的事件路由

Super-agent 通过事件驱动机制感知外部环境变化。GitHub 评论、CI 状态更新、群聊消息等外部事件进入 Agent 系统后,会根据当前任务状态判断是否需要由 Router 进一步处理。

外部事件的数量通常远大于实际任务数。一次 PR 更新可能只是普通状态通知,不需要新建任务;但如果是要求 Worker 继续处理的审查意见,系统就要把事件交给已有任务。在 Super-agent 中,外部事件被统一抽象为 Trigger,通过 Thread 组织连续事件。以 GitHub Issue 为例,初始描述和后续评论归属同一个 Thread,相关 Trigger 按时间顺序记录。

在 DeepSeek Harness 开源原型(下文简称"开源原型")中,Source 组件负责接入外部事件、维护事件读取位置,并将消息转换为 Trigger。每个 Trigger 是一条不可变记录,包含事件来源、发送者等元数据,为去重和审计提供依据。Thread 则维护相关事件的时间序列、订阅关系和评估历史。一个 Thread 可以关联多个任务,同一个 Trigger 也可以关联多个 Thread。这样,系统收到新事件时可以先检查是否有任务需要接收,再判断是否需要创建新工作。

已有任务的事件直通

Super-agent 使用订阅关系(Subscription)把 Thread 与任务关联起来。在开源原型中,获得授权的工作以 Ticket 形式记录和管理。Router 将工作交给 Worker 后,对应 Ticket 可以订阅相关 Thread。后续 Trigger 到达时,系统检查订阅关系,把事件交给需要处理它的任务。

Fast Path 与 Slow Path 事件路由流程图

图注:Fast Path / Slow Path 路由图,展示已有任务如何通过 Thread 订阅接收后续事件,以及新任务如何进入评估流程。图源 @DeepSeek

根据外部事件是否已有明确归属,系统有两条处理路径:

  • Fast Path:Trigger 命中已有订阅关系,直接投递给相关任务,减少 Router 重复判断的开销。
  • Slow Path:没有被现有任务接收、或需要单独判断的事件进入评估环节,由规则和模型决定是否创建新工作。

例如,Worker A 正在修复 GitHub Issue #128,对应 Ticket 订阅了该 Issue 的 Thread。用户补充新的复现条件时,系统根据订阅关系直接把评论交给原任务,Router 无需再判断归属。Worker A 后续提交 PR 后,同一 Ticket 还可以订阅 Review Thread,让新的审查意见继续进入原任务。

订阅关系支持多对多:一个 Ticket 可以订阅多个 Thread,一个 Thread 也可以向多个 Ticket 分发事件。但事件进入任务不代表自动获得执行权限。对于已结束的 Ticket,系统还要结合消息来源、任务状态和授权条件来判断是否重新打开。

开源原型还使用关联关系(Correlation)记录不同 Thread 之间的关联,帮助 Worker 获取相关讨论中的上下文。比如 Worker A 想了解某次代码修改的背景,可以参考其他 Issue 或 PR 的讨论。这些信息作为参考材料,但不会因此获得事件直通路由或重新打开已结束 Ticket 的权限。

通过区分事件的订阅、上下文的关联和任务授权,Super-agent 既能让后续消息进入正确任务,又避免了把关联讨论误当作执行指令。

Eval 与授权任务的创建

对于需要独立判断的外部事件,Super-agent 通过两级策略筛选:先由程序检查事件来源、发送者身份和准入规则等硬性条件;通过初筛后,再由模型结合自然语言规则、当前 Thread 上下文和已有任务来决定是否行动。

在开源原型中,这两级判断由评估流程(Eval)统一组织。评估时,系统会读取待处理的 Trigger、当前 Thread 信息、适用的策略规则,以及该 Thread 关联的 Ticket——包括已经结束的任务。

举个例子:研发团队设置规则,只允许指定成员提交缺陷修复请求。新 GitHub Issue 通过身份和准入检查后,模型会进一步分析任务描述并与现有工作比对。如果发现同一个问题已有 Ticket,就避免重复创建任务。

评估完成后,系统生成相应结果:接受、停止、忽略或暂时无法完成评估。如果接受请求,系统会创建 Ticket 并建立任务授权,记录授权依据,包括策略版本、允许执行的工作范围、匹配规则,以及发送者在事件来源中的身份信息。

任务授权创建后不可修改。后续事件提出超出原授权范围的要求时,系统需要重新建立授权,不能通过修改原授权来扩大执行权限。

为保障事件处理一致性,开源原型还设置了三项约束:

  • 事件去重:外部消息源重复投递同一 Trigger 时,系统根据已有记录识别重复输入,避免反复创建任务。
  • Thread 串行评估:同一个 Thread 在同一时间最多运行一次评估。评估期间到达的新事件进入后续批次,避免多个评估基于不同上下文快照创建冲突任务。
  • 策略版本管理:每次评估依据有效的策略版本。若评估过程中规则变化,相关评估需要重新处理,防止旧规则继续产生新授权决策。

Ticket 与 Assignment 的任务生命周期

实际开发中,研发任务经常需要等待外部反馈:修复代码后等 CI、提交 PR 后等审核、收到修改意见后继续执行。这些过程可能持续数小时甚至跨越多天。如果任务状态完全依赖某个正在运行的 Session,系统就很难区分任务是否有效、当前由哪个执行实例负责、是否还要占用执行资源。

前文提到的 Ticket 持续保存任务授权、输入事件、Thread、执行报告和结果,并维护独立的任务状态。一个 Ticket 的状态可以从 open 转为 ended,也可以在符合条件时重新打开。为了让任务状态不依赖某个 Session,开源原型还使用 Assignment 记录 Ticket 与执行器之间的一次执行绑定,生命周期为 pending → running → ended。

Ticket 与 Assignment 任务生命周期流程图

图注:Ticket 与 Assignment 的任务生命周期,展示 Ticket 长期保存任务状态、Assignment 绑定执行实例、Worker Slot 按需分配与释放。图源 @DeepSeek

同一个 Ticket 可以先后拥有多个 Assignment。即使某次 Assignment 因执行中断而结束,Ticket 仍能保留原有任务身份、授权和历史记录,并在满足条件后安排新的执行。这样,Ticket 的生命周期就独立于 Assignment 的执行过程,同一项工作能够跨越多次执行继续推进。

从 Issue 到 PR 的执行过程

以 GitHub Issue 修复任务为例。假设用户在 Issue #128 中报告了 CSV 导出时的字段转义问题。经过事件评估流程后,系统接受请求,创建 Ticket T128 并建立任务授权。接下来进入具体执行阶段。

任务分派与执行绑定

系统为 T128 创建 Assignment A1,交给执行器。开源原型中,每个 Ticket 都有对应的执行器 Session 和工作目录。首次执行前,系统根据配置准备工作目录,例如通过 git worktree 创建独立的代码检出目录。准备完成后,执行器启动 Worker Session 并交付任务输入。Worker 随后读取代码、定位问题、修改实现并运行测试,执行过程中的代码上下文、工具结果和中间记录都保留在对应 Session 中。

执行报告与 PR 审核

Worker 完成修复后,创建 PR #135,并向执行器提交 ready 执行报告,表示工作已准备好交给人工 review。执行器将报告反馈给任务管理系统。由于当前任务关联着尚未关闭的 PR,Ticket 进入 Awaiting review 阶段。此时 Worker 可以停止推理并释放执行槽位,Ticket 则继续跟踪 PR 的后续变化。

这里有一个细节:开源原型不会因为收到一次 ready 报告就立即结束所有 Ticket。对没有关联 PR 的任务,ready 可以表示工作完成;对涉及 PR 的任务,系统还要结合 PR 状态判断是否满足结束条件。

审核反馈与继续执行

假设审核人员在 PR #135 中提出意见:CSV 字段中的换行符也需要正确转义。这条评论作为 Trigger 进入系统,通过已有的 Thread 订阅关系找到 T128。由于 Ticket 仍处于审核等待阶段,符合授权范围的人工后续消息可以解除等待条件,成为新的执行输入。

如果 Assignment A1 仍然有效,执行器将新输入交给原有 Worker Session;如果 A1 已经结束,系统可以在满足条件后创建新的 Assignment,继续处理 T128。Worker 收到 review 意见后,补充换行符的处理逻辑与相关测试,再次提交结果。整个过程中,Ticket 保留原有任务身份、授权和执行历史,而具体执行绑定可以根据需要变化。

PR 结束与任务完成

当 PR #135 被合并或关闭时,GitHub 事件接入组件将状态变化作为新事件送入系统。只有当 Ticket 负责的 PR 都满足关闭条件,且不存在尚未完成的后续工作、恢复操作或待处理输入时,任务才会结束。例如,PR #135 虽然已合并,但 Worker 仍在处理此前收到的一条修改意见,系统就不会仅凭 PR 关闭状态立即结束 Ticket,从而让任务结束条件与实际执行进度保持一致。

报告与任务阶段的判定

执行器会提交结构化报告,记录当前工作的执行结果。开源原型定义了六类报告:

报告类型 含义
progress 当前工作仍在推进
waiting 本轮工作结束,等待后续事件
ready 当前工作准备好交付或审核
blocked 遇到阻碍,需要人工处理
failed 执行失败,需要人工介入
dropped 当前任务没有需要继续处理的工作

报告会影响 Ticket 的当前阶段,但系统不会仅凭某个报告值判断任务状态。开源原型还会结合 Assignment 状态、未交付的输入和人工等待条件,按照优先级推导 Ticket 所处的阶段。主要包括:

  • Queued:尚无 Assignment,或 Assignment 还未开始执行。
  • Working:执行仍在推进,且没有优先级更高的等待条件。
  • Waiting for a Trigger:最新报告为 waiting,或 Assignment 已结束,任务等待新事件。
  • Awaiting review:最新报告为 ready,且任务仍需等待审核。
  • Blocked / Failed:最新报告为 blocked 或 failed,此后尚未收到有效人工消息。

此外,如果任务输入未能成功交付到执行器 Session,Ticket 会优先显示为 Input undelivered,系统随后继续尝试交付。通过综合判断这些条件,调度器可以区分任务的执行进展、外部等待状态和输入交付情况,并决定何时安排后续执行。

举个例子:Ticket 进入 Blocked / Failed 阶段后,普通 CI 通知或自动化事件无法直接解除等待,需要收到符合规则的人工消息才能继续。而处于 Waiting for a Trigger 阶段的任务,可以在相关后续事件到达后进入下一轮执行。

执行中断与 Assignment 恢复

除了正常等待,Worker 也可能因为进程退出、插件重启或执行异常而中断。此时系统需要结束当前 Assignment,同时保留仍然有效的 Ticket,确保任务可以在新执行实例中继续。

开源原型中,Assignment 结束后,系统关闭旧的执行绑定,保留尚未交付到 Session 的输入,避免执行实例切换造成信息丢失。与旧 Assignment 关联的后台作业也会被清理。旧执行资源关闭后,系统可以根据 Ticket 当前状态建立新的 Assignment,并继续交付尚未处理的输入。如果执行在任务进行过程中中断,新执行实例还会收到恢复说明,帮助 Worker 重建必要的任务上下文。

这里要区分两种恢复情况:

  • 执行恢复:Ticket 仍处于 open,但原 Assignment 因中断而结束。系统可建立新的执行绑定,继续原有任务。
  • 任务重新打开:Ticket 已进入 ended,但后续有人在其订阅的 Thread 中提出新请求。系统需要检查任务是否允许重新打开,并根据当前策略重新验证原有授权。

恢复过程还受事件来源和授权条件限制。人工主动停止的 Ticket 不会因普通消息自动恢复,机器人和 CI 事件也不能独立重新打开已结束的任务。至于任务恢复后何时占用执行资源、后台作业完成后如何唤醒 Worker,则由后续的异步执行与 Worker Slot 调度机制负责。

异步执行与运行状态管理

Ticket 与 Assignment 把任务生命周期和具体执行实例分开管理,但任务持续存在并不意味着 Worker 需要始终占用计算资源。Worker 可能正在等待后台测试结果,也可能已提交 PR 进入人工审核。两者虽然都是等待状态,但对执行资源的管理方式截然不同。

Super-agent 通过后台任务和 Worker Slot 管理执行资源的占用与释放,利用持久化状态支持后续恢复。与此同时,任务管理器与 Outbox 协同处理内部状态更新和外部操作,让任务在不同执行阶段之间持续推进。

后台任务与 Worker Slot 调度

在常见的同步 ReAct Loop 中,模型发起工具调用后必须等待结果返回才能继续推理。如果工具执行时间很长,当前执行步骤就会被一直占用。Super-agent 引入异步工具执行机制,让耗时命令在后台运行,完成后将结果送回对应任务。

总体设计中,Agent 可以通过 start() 启动后台操作并立即获得启动结果;任务完成后,系统将执行结果作为事件送回 Session,必要时可用 stop() 终止操作。开源原型通过后台 Shell Job 和 Subagent 管理来实现这一能力:当 Shell 命令运行时间超过预设值,可以从前台转入后台,Worker 不必持续等待结果。

命令转入后台后,系统还要判断是否继续占用执行资源。Worker Slot 是开源原型控制并发执行的资源单位,调度器结合当前执行阶段和后台作业状态,按三种情况处理:

  • 后台作业仍在运行时:若当前推理步骤已结束,但还有 Shell Job 或 Subagent 在执行,Ticket 继续占用 Worker Slot。后台作业完成后,结果唤醒对应执行器,Worker 在原有上下文中继续执行。
  • 进入普通等待时:若当前推理结束,且没有需要继续处理的后台工作,执行器提交 waiting Report,任务释放执行槽位,等待新 Trigger 到达。
  • 进入人工交接时:当 Worker 提交 ready、blocked、failed 等需要外部处理的报告,或主动向用户提问时,系统释放 Worker Slot,任务进入相应等待阶段。

人工交接时还有一套特殊规则。例如,Worker 启动了一个供用户检查的预览服务,随后提交审核请求。此时预览服务可以在 Worker Slot 释放后继续运行,但服务结束时不会自动唤醒执行器;等用户反馈到达、任务恢复执行后,Worker 再读取结果。而普通后台测试仍属于当前执行过程,系统需要保留 Worker Slot,并在测试完成后唤醒 Worker。

开源原型还为后台作业设置了超时和清理规则,防止长时间运行的任务持续占用资源。Assignment 结束时,执行器会清理关联的 Shell Job 和 Subagent,阻止过期执行结果继续唤醒旧 Session,避免影响后续新建的 Assignment。一项任务即使持续数小时或数天,也只需根据当前阶段占用相应资源,避免等待外部反馈时长期占用 Worker Slot。

任务管理器与 Outbox 投递

长期运行的任务还需要处理内部状态更新与外部操作之间的一致性问题。比如 Worker 完成代码修复后,既要提交最终报告、更新 Ticket 和 Assignment 等内部状态,又要通过 GitHub API 发送外部回复。两类操作可能出现不同结果:内部状态更新成功但消息发送失败,系统需要保留未完成的投递;消息已发送但本地没有记录投递结果,后续重试可能产生重复评论。

针对这些情况,Super-agent 把内部状态更新与外部操作分开处理,通过事务和 Outbox 管理各自的执行结果。

领域状态的事务处理

在开源原型中,任务管理器(Engine)是核心领域对象的唯一写入方。执行器提交报告时,任务管理器通过同步命令处理状态变化,在存储事务中更新 Ticket、Assignment 等领域记录。需要调用外部服务的操作,则通过相应接口记录待执行操作,在领域事务提交后再执行外部调用。整个处理流程如下:

执行器提交执行报告
      │
      ▼
任务管理器处理命令
      │
      ▼
数据库事务更新任务状态
      │
      ▼
记录待执行的外部操作
      │
      ▼
事务提交
      │
      ▼
调用外部服务

Outbox 的消息投递

对于需要发送到外部 Channel 的回复,Super-agent 用 Outbox 保存待投递消息及其处理状态。消息进入 Outbox 后,系统安排对应的 reply 外部操作,通过 SourceRegistry 组件找到目标,再调用外部接口完成发送。发送成功后,投递回执被保存下来,供后续状态检查和重复处理控制使用。

如果外部 Source 暂时不可用,消息继续保留在 Outbox 中等待后续投递;如果目标会话已失效或不再处于监听范围内,系统按相应规则终止投递。Outbox 还使用稳定的消息标识,减少重复处理产生多份内部消息记录的情况。

这套机制仍然需要面对内部事务与外部 API 之间的一致性边界。假设 GitHub 已成功发布一条评论,但网络中断导致系统未能保存投递回执,本地无法确认外部操作是否成功。此时若重新发送,就可能产生重复评论。因此,Outbox 能持久化投递意图、管理失败重试并记录投递结果,但外部操作的重复执行仍需结合目标 API 的幂等能力和已有投递记录进行控制。

持久化状态与运行时恢复

Ticket / Assignment 的生命周期设计说明:即使某次执行绑定结束,任务仍能保留原有状态,并在满足条件时建立新 Assignment。如果整个运行时进程退出,系统还需要进一步恢复任务记录、执行状态和尚未处理的输入,确保任务能够从中断位置继续。

开源原型中,Runtime 启动后先打开 Store、初始化组件,再处理此前中断的 Assignment,并重启周期性任务和 Effects。整个恢复过程依赖 Store 中持久化的领域状态,使系统能识别哪些任务有效、哪些执行绑定需要结束、哪些输入尚未交付。

比如说,某个 Worker 正在处理 PR 的 Review 评论,运行时进程意外退出。恢复时系统需要处理两件事:一是原有 Assignment 已中断,需要关闭旧绑定并按 Ticket 状态安排后续执行;二是新评论可能已被系统接收,却尚未成功送入 Worker Session。对于尚未交付的输入,开源原型保留相应记录并在恢复后继续尝试交付,避免因执行实例退出而丢失外部消息。这也解释了任务输入为什么需要独立于 Session 历史进行持久化——即使 Session 中断或重新建立,系统仍能从 Store 中恢复任务状态和已接收的输入。

在 Super-agent 的总体架构设计中,这种分离延伸到计算、状态和执行环境三个层面:Agent Loop Compute 负责模型推理与执行循环,Persistent State 保存长期状态,执行环境提供代码、文件和工具运行环境。三者可以部署在不同位置,让 Agent 访问本地设备或云端执行环境,并在计算实例更换后恢复工作。总体设计还考虑了上下文增长后的按需恢复,以及对执行停滞、基础设施异常和任务偏离的独立监测。当前开源原型更明确的实现集中在任务状态持久化、执行绑定恢复、输入交付和外部操作管理。

双层调度的性能表现

为验证 Router / Worker 双层架构的效果,DeepSeek Harness 设计了一组合成负载实验。每轮实验包含约 300 条消息,其中约 20 条为实际工作请求,其余包括普通聊天、任务补充、取消请求和 CI 通知。具体任务由模拟耗时操作完成,因此实验主要考察不同架构的调度效率,并未验证真实代码修改的正确性。

实验共设置四种配置:完整的 Super-agent、关闭 Pass-through 的 Super-agent,以及分别采用中性提示词和鼓励委派提示词的单 Session + Subagent。每种配置运行五次,任务完成时间如下:

四种配置的任务完成时间与 Cache-read Tokens 对比

从结果来看,Super-agent 的任务完成时间更加稳定,尤其是 p95 明显低于两种单 Session + Subagent 配置。原因在于:单 Session 架构中的主模型可能选择自行执行耗时任务,导致其他消息等待处理,即使通过提示词鼓励委派也无法完全避免。而 Super-agent 将耗时工作交给独立 Worker,减少了单项任务对全局协调的影响。

Pass-through 的收益主要体现在路由开销上。关闭这一机制后,任务完成时间变化不大,但 Router 的决策次数增加约 30%,Cache-read Tokens 从 23.2M 增至 31.8M,增幅约 37%。这表明事件直通能减少重复的模型路由判断,降低持续处理外部事件时的 Token 消耗。

总体来看,Router / Worker 分层有助于降低多任务调度的尾部延迟,Pass-through 则能减少路由开销。不过实验采用简化的合成负载,每种配置仅运行五次,尚未覆盖真实研发环境中的复杂任务依赖、共享资源冲突和执行故障等情况。

结语

Super-agent 将长期运行的 Agent 工作拆分为多个相互关联、具有独立生命周期的对象。Router / Worker 负责全局协调与具体执行,Trigger / Thread 把持续到达的外部事件关联到已有任务,Ticket / Assignment 则管理任务状态及其与执行实例之间的关系。

其中,Ticket、Assignment 和 Worker Slot 的分离尤其值得关注。Ticket 可以在等待人工反馈、接收新事件、后台工具执行和任务恢复等阶段持续存在,Assignment 与 Worker Slot 则根据任务实际状态进行创建、释放和重新分配。配合任务管理器的事务管理与 Outbox 投递,系统还能分别跟踪内部状态更新和外部操作的执行结果,减少长期运行过程中因中断或投递失败造成的状态不一致。

参考资料




上一篇:Oracle 19c ADG 运维常用 SQL 100 条:主备切换与同步巡检速查手册
您需要登录后才可以回帖 登录 | 立即注册

关闭

站长推荐上一条 /1 下一条

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-11-11 17:48 , Processed in 0.066743 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表