详情

首页手游攻略 DeepSeek Super-agent 双层调度实践指南之Router+Worker 架构实用指南

DeepSeek Super-agent 双层调度实践指南之Router+Worker 架构实用指南

佚名 2026-11-10 15:00:02

平时做技术实践时,很多问题不是概念不会,而是细节没串起来。拿“DeepSeek Super-agent 双层调度实践指南之Router……”来说,它看着像小点,放到项目里常会牵出环境、配置、兼容性和维护成本。下面按实际采用顺序,把思路、关键写法和容易踩坑的地方讲清楚,便于大家直接对照操作。

在这个场景下,上周把一个跑了三个月的单层 Agent 系统推倒重写了。原来所有任务都丢进一个队列,由一个总控循环挨个取出来执行,结果一到晚高峰就堵:长任务占着线程,短任务排半天;某个子任务失败了,整条链路卡死,谁也不知道卡在哪。重写后改成了 Router 加 Worker 的双层结构,事件驱动,失败的任务丢进后台静默复现同时修复。

这篇把这套骨架拆开讲,附一份能直接跑的参考实现,文末有自检结果。

为什么要从单层改成双层

实际处理时,单层调度核心的问题是「调度逻辑」和「执行逻辑」耦合在一起。调度器既要决定谁先跑,又要真正去跑,一旦执行路径出异常,调度状态就乱了。双层结构把这两件事拆开:Router 只负责「分发给谁」,Worker 只负责「怎么执行」。两者借助事件解耦,互不阻塞。

实际处理时,这里有个工程取舍要先说清楚。双层不是银弹。它把一次方法调用变成两次(投递事件、领取事件),还多了幂等和重投的账要管。对于一天只有几十个任务的小系统,单层更轻松,别为了架构而架构。只有当任务类型开始分化、失败需隔离、同时发开始吃紧时,双层才划算。

核心三件套:Router、Worker、Scheduler

下面这份代码就是随文脚本 super_agent_scheduler.py 的核心,删掉了自检部分(完整版带三态自检,可直接 python super_agent_scheduler.py --selftest 验证)。

import uuid
from collections import defaultdict, deque
class Worker:
    def __init__(self, wid, capabilities):
        self.wid = wid
        self.capabilities = set(capabilities)
        self.queue = deque()
        self.active = 0 # 当前并发执行数
        self.max_concurrency = 2
        self.done = set() # 已完成任务,用于幂等
    @property
    def load(self):
        return len(self.queue) + self.active
    def can_handle(self, task):
        return task["capability"] in self.capabilities
class Router:
    def __init__(self, workers):
        self.workers = workers
    def dispatch(self, task):
        # 只选能力匹配且负载最低的 Worker
        cands = [w for w in self.workers if w.can_handle(task)]
        return min(cands, key=lambda w: w.load) if cands else None
class Scheduler:
    def __init__(self, workers):
        self.router = Router(workers)
        self.workers = workers
        self.results = {}
    def submit(self, task):
        w = self.router.dispatch(task)
        if w is None:
            return False # 无人能处理,负控路径
        if task["task_id"] in w.done:
            return "dedup" # 幂等去重
        w.queue.append(task)
        return True

用法很轻松,三行就能起一个调度器:

w1 = Worker("w1", ["code", "browse"])
w2 = Worker("w2", ["data", "browser"])
sch = Scheduler([w1, w2])
sch.submit({"task_id": "T-01", "capability": "code", "payload": "..."})

工程取舍:事件驱动而不是轮询

结合项目来看,事件驱动的意思是,Worker 不是在死循环里反复问「有活吗」,而是被投递事件唤醒。好处是空转时零开销,高峰时按事件密度自然扩缩。

理解这一步时,比如我们线上一半的任务在 12.4 分钟内完成,约 95% 落在 15.7 分钟内,这个分布来自 DeepSeek Harness 团队公开的《Super-agent 设计探索》原型实测(来源:DeepSeek Harness 团队开源文档)。注意,这是公开原型的基准,不是我的生产数据,引入时要先在自己的负载上复测一遍。

在这个场景下,Router 的「选低负载」用的是最小负载策略,而不是随机或轮询。随机会把长任务堆到同一台 Worker 上,轮询不感知能力标签。最小负载配合能力标签,才能让「写代码」的任务和「抓数据」的任务分到不同机器,互不挤占。

踩坑清单:四个真正会咬人的地方

落到代码里,其一,幂等忘了做。任务因为网络抖动被重投一次,结果同一份活被执行两遍,下游直接翻倍。解决办法是执行前先查 done 集合,命中就得到 dedup,这是正控必须覆盖的路径。

实际处理时,第二,能力不匹配被静默吞掉。早期我们只在 Worker 里判断能不能处理,Router 也放行了不匹配的任务,最后任务卡在队列里既不报错也不执行。正确做法是 Router 分发时直接得到 None,上层立刻知道「没人能接」,而不是等超时。

在这个场景下,第三,重试风暴。失败的任务如果立刻无脑重投,会在一秒内把队列打满。我们的做法是失败先进入后台 replay 队列,由独立的修复进程去复现、定位、修复,修好再重投,不占用主链路。

从实现思路看,第四,负载均衡的「看起来均衡」。两台 Worker 负载差超过一时,说明分发策略有偏。自检里专门用正控验证「两 Worker 负载差不超过 1」,比肉眼看日志可靠。

一句大实话

记住,调度器可不可靠,看它怎么处理失败,而不看它架构图多漂亮。

辩证:双层调度也有代价

在这个场景下,不过要提醒一点,双层不是免费午餐。多出来的事件总线和幂等账,本身要维护;调试时一个任务从投递到执行跨了两个进程,栈不好追。另一个角度,如果你的团队还没踩过单层的坑,先别上双层,把单层的失败隔离和超时做好,性价比更高。需警惕的是,任何把「架构好看」排在「问题真实存在」前面的改造,最后都会变成负债。

一次真实的晚高峰雪崩

从实现思路看,光说骨架不够直观,把当初逼我们重写的那个故障摊开看。旧系统是单层:一个总控循环 while True 从队列取任务,取出来就同步执行。平时任务量小,看不出问题。到了晚高峰,三类任务混在一起:一类是几秒钟能跑完的「抓摘要」,一类是跑两三分钟的「生成长文」,还有一类是偶尔十几分钟的「批量评测」。

实际处理时,问题出在同步执行上。总控循环一次只处理一个任务,长任务占着线程时,后面几十个短任务全部排队。更糟的是,有一个「批量评测」任务执行到一半抛了异常,因为执行和调度在同一段代码里,异常把整个循环带崩,后面还没取出来的任务全卡住,坚控面板显示队列在涨,但 CPU 占用却掉到接近零,典型的「假死」。

我们当时是怎么查的:先看了队列长度,看着在涨,以为只是慢;后来比对了 Worker 进程日志,发现最后一条日志停在那个异常栈,才意识到是循环挂了,不是慢。这个排查经历后来直接写进了分层后的「负控」,让自检专门验证「异常任务不会拖垮调度循环」。

实际处理时,改成分层之后,同样的异常只会影响那一个 Worker,Router 照常把新任务投给别的 Worker,调度循环本身永不阻塞。

这个案例想说明的是:单层和双层的差别,平时几乎无感,真正的分水岭是「异常发生时系统怎么塌」。如果你的系统还没在真实负载下塌过一次,你很难说服自己值得为多出来的复杂度买单;但等真塌了再改,代价往往十倍于提前设计。

数据与事件来源

  • 在这个场景下,DeepSeek Harness 团队《Super-agent 设计探索》开源原型,Router 加 Worker 双层、事件驱动、后台静默复现同时修复 Bug,基准为一半任务 12.4 分钟内完成、95% 在 15.7 分钟内(来源:DeepSeek Harness 团队开源文档)。
  • 从实现思路看,Kimi Hosted Agents 把 Harness 当云服务,提供 7 乘以 24 小时托管、长程任务与多智能体协作(来源:Kimi 官方发布说明)。
  • 理解这一步时,文内参考实现与三态自检脚本随文附 super_agent_scheduler.py,已在本地 python super_agent_scheduler.py --selftest 跑通正控 4 项、负控 2 项、小样本守卫 1 项。

一个落地建议

结合项目来看,若你打算照搬这套骨架,我建议先从小步开始:先用单进程把 Router 和 Worker 跑通,确认分发和幂等对,再拆成事件驱动,最后才加后台 replay。每一层都加自检,正控验证正常路径、负控验证异常路径,不要等上线才发现问题。我们当时图快,直接上全套,结果前三天的故障有一半是自检没覆盖到的边界。

结合项目来看,另外提醒一句,坚控要看「负载差」而不是「队列长度」。队列长度为零不代表健康,可能只是任务全堆在某台 Worker 上没动。真正该盯的是各 Worker 负载差和失败重投率,这两个指标比任何仪表盘都更早暴露调度失衡。

互动思考题

你现在的 Agent 系统是单层还是双层?

若你的任务里混了长任务和短任务,你是怎么防止长任务拖垮短任务的?

你会把失败任务的「复现加修复」放进主链路,还是单独开一条后台进程?

欢迎在评论区聊聊你现在的 Agent 是怎么做失败隔离的。

实际处理时,总的来说,DeepSeek适合结合实际项目边做边理解。先抓住核心思路,再逐步补上细节和边界处理,最后效果会更稳定,也更容易复用。

点击查看更多
推荐专题
热门阅读