首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >SuperAgent 实战:让 Router 看见全局,让 Worker 不再各说各话

SuperAgent 实战:让 Router 看见全局,让 Worker 不再各说各话

原创
作者头像
OneCode
发布于 2026-11-14 17:55:28
发布于 2026-11-14 17:55:28
360
举报
文章被收录于专栏:ooderAgentooderAgent

Agent Architecture · 工程复盘

SuperAgent 实战:让 Router 看见全局,让 Worker 不再各说各话

多智能体系统真正难的不是“把任务发出去”,而是让调度方和执行方共享同一份事实、同一套产物坐标、同一句人话。本文拆解 SuperAgent 的四层升级:上下文包、结果信封、提示词与资源治理,并给出可落地的分布式与防踩踏方案。

代码基线 ooder-pro / net.ooder.studio.chat.superagent 关键词 GlobalContextPacket · SaResultEnvelope · Router · Worker · 沙箱租约 · 人在环

一、当第二个 Agent 出现,问题就不再是提示词

只有一个 Agent 时,大部分问题都能靠“把提示词写长一点”缓解;当第二个、第三个执行体出现,系统面对的是一组协作问题:谁来决定、谁来执行、结果如何被一致理解、资源如何不打架。这些问题不会因为模型变强而消失,反而会因为参与者变多而放大。

落到工程上,是三个结构性缺陷:

  • 调度方看不见执行方:Router 只拿到一张候选清单,不知道谁在线、谁过载、项目做到哪一步。
  • 执行方看不见全局:Worker 只收到一条 content,不知道上下文、上游产物、下游依赖。
  • 结果没有唯一表示:同一次派发在助手正文、A2A 协作气泡、会话台账、运行台账里各有一套形状,前端按通道分别消费。

典型症状

“正文说已生成,产物横幅却是空的”“同一个失败提示出现两次”“用户追问后系统像没听见”“目标 Agent 已经离线,调用方还在空等”。这些都不是 UI 问题,而是事实没有归一。

二、结论先行:六条判断

  1. 上下文要“一次装配、按角色投影”,而不是每个环节各自查库、各自拼提示词。
  2. 结果要收敛成唯一信封,通道只负责搬运,不再各自组装话术。
  3. 大对象走坐标,消息只传引用:产物落 VFS,消息里只带 URI、类型与摘要。
  4. LLM 的全局感来自结构化事实,不来自更长的提示词;不可信的决策必须二次校验并回落规则。
  5. 防踩踏靠“单写者 + 租约 + 幂等键”,不靠调用方自觉。
  6. 可观测是架构的一部分:没有回执、诊断与回放,多 Agent 协作无法运维。

三、第一性拆解:SuperAgent 到底在管什么

把系统剥到最薄,SuperAgent 只做四件事:知道什么(上下文)、决定谁做(决策)、把事做成(执行)、别互相踩(资源)。这四件事分别对应四层能力,也对应本文的四个章节。

层

核心问题

关键构件

失败时的表现

上下文层

大家看到的是不是同一份事实?

GlobalContextPacket / 双投影

调度漂移、执行跑偏

决策层

该谁做、要不要追问、要不要改向?

RouterPolicy / ThreadRouter

误派、漏派、重复派

结果层

做完的结果怎么被所有人一致理解?

SaResultEnvelope / 产物坐标

正文有、产物没、失败说两遍

资源层

沙箱、端口、发布窗口怎么排队与隔离?

SerialExecutor / 租约与队列

并发写冲突、长时间等待

四、上下文层:GlobalContextPacket

4.1 为什么需要“事实包”

改造前,Router 与 Worker 各自拼上下文:Router 读候选清单,Worker 读会话消息。两边都只掌握局部事实,于是出现两类漂移——Router 把任务派给一个已经离线或排队很深的执行体;Worker 在不知道“项目已经做到第三步”的情况下从头再来。

GlobalContextPacket 的做法是:同一份事实只装配一次,然后按角色裁剪投影。它不携带聊天正文,只携带可核对的事实。

代码语言:javascript
复制
public record GlobalContextPacket(
    int version, long asOf,
    Map<String,Object> identity,   // 租户 / 用户 / 入口画像
    Map<String,Object> project,    // 项目状态 / 进度 / 未完成任务
    Map<String,Object> thread,     // 线程绑定 / 主执行体 / 节点
    Map<String,Object> task,       // 本次任务(派发时补)
    List<Map<String,Object>> workers,   // 执行体运行态
    List<Map<String,Object>> artifacts, // 产物坐标
    Map<String,Object> clarify,    // 待用户回答
    Map<String,Object> constraints // 超时等硬约束
) { public static final int VERSION = 1; }

4.2 一次装配,两种投影同一份事实,按角色裁剪

两个投影的边界非常清楚:

  • Router 投影关心“谁在线、有多忙、项目到哪一步、有没有待回答的问题”;
  • Worker 投影关心“本次任务是什么、项目摘要、有哪些可用产物坐标”,不拿候选清单,因为执行体不做调度;
  • 两者都不携带聊天正文,避免上下文膨胀与事实混淆。

4.3 三个容易被忽略的工程细节

第一,只读。装配过程不触发任何写操作。依赖缺失时返回最小包,绝不阻断决策与派发。这保证了“上下文增强”不会变成新的故障点。

第二,可降级。整个能力由一个开关控制:ooder.superagent.context.packet-enabled。关闭后 Router / Worker 都拿不到事实包,行为逐字回落到改造前。灰度与回滚成本极低。

第三,有预算。Router 文本硬上限 2000 字符,超出直接截断并标注“全局上下文已截断”;候选执行体最多渲染 12 条。提示词不是越长越好,预算是硬约束。

4.4 Worker 侧的注入链

事实包如何真正进入执行体的提示词?链路只有四跳:

代码语言:javascript
复制
WorkerTask.contextPacket
  → A2A request.parameters["contextPacket"]
  → 执行端 ChatContext 变量 _globalContextPacket
  → PromptEngine 追加 systemPrompt(未注入则零行为变化)

关键设计是“不读也不影响旧行为”。执行端可以按需消费,不消费也不会报错。这让上下文升级可以按 Worker 灰度推进,而不是一次性全量切换。

实际界面(2026-11-14 实测):左侧会话列表承载“线程”语义——同一个会话就是一个项目线程。线程绑定决定了哪些请求可以直通、哪些必须回到 Router 重新决策。

五、结果层:SaResultEnvelope

5.1 四套形状的代价

改造前,同一次派发在四个通道里各有一套结果表示:助手正文、A2A 协作气泡、会话 A2A 台账、运行台账 sa_task。前端按通道分别消费,于是必然出现不一致:正文有结论、产物横幅为空;失败原因一个通道说“超时”、另一个说“执行体未完成”。

5.2 信封长什么样

代码语言:javascript
复制
public record SaResultEnvelope(
    String taskId, String conversationId,
    String agentId, String agentName,
    String state,          // SUCCEEDED / FAILED / TIMEOUT / UNDELIVERABLE / WAITING_HUMAN
    String humanText,      // 用户可读结论
    List<ArtifactRef> artifactRefs,
    ErrorInfo error,       // code + 用户原因 + 处理动作 + 是否系统错误
    ClarifyInfo clarify,   // 一次只问一个
    SteerInfo steer,       // 改成 / 换成 / 不是这个
    String source,         // superagent
    boolean suppressA2aBubble,
    long asOf
) { }

其中 ArtifactRef 只放坐标,不放内容:

代码语言:javascript
复制
record ArtifactRef(String id, String name, String url,
                   String path, String type, List<String> actions) { }

这是“桥接”的核心原则:大对象走坐标,消息只传引用。产物写入 VFS / 对象存储,信封里只带 vfsPath、类型与可执行动作(下载、编辑)。这样既避免了大字段在消息总线上传输,又让前端可以按同一个坐标去取权威内容。

5.3 失败也要说人话

错误码到用户话术的映射不是随手写的,而是三元组:userCause(发生了什么)、userAction(你该怎么做)、system(是不是系统问题)。例如:

错误码

用户原因

建议动作

归类

TIMEOUT

等待智能体回复超时

稍后重试;反复出现则转技术智能体

系统

UNDELIVERABLE / A2A_UNAVAILABLE

本次协作没有送达到目标智能体

稍后重试;反复出现则转技术智能体

系统

REQUIRES_INPUT

缺少必要信息

补充缺失信息后重试

业务

DOMAIN_FORBIDDEN / AUTHZ

当前入口没有这项业务的办理权限

从对应业务入口发起,或联系管理员

业务

SUBJECT_MISSING

登录身份缺失或已过期

重新登录后再试

业务

还有一个细节:原始 message 只在“像人话”时才采用——长度不超过 120、包含中文、不含 Exception、不含 16 位以上裸 ID。否则统一给“本次协作未能完成”。技术细节不上屏,但技术面仍然保留原始原因。

5.4 不重复说两遍

suppressA2aBubble 解决的是渠道重叠:聊天链路已经有助手正文承载失败,A2A 侧就不要再冒一个气泡;而 Trigger 这类没有助手正文的链路传 false,必须保留气泡。一个布尔字段,消灭了“同一个失败说两遍”。

实际界面(2026-11-14 实测,已圈选):表单智能体返回的不是一段散文,而是可核对的结构化结果。图中标注:① Worker 标识与结论;② 结构化产物字段对齐表(实体 employee、模式 single_table_crud、4 个字段全部匹配)。这正是 humanText 与 artifactRefs 分离的价值——结论给人看,坐标给系统用。

六、消息与人在环:Router 如何组织反馈

6.1 消息分类

类型

方向

承载内容

落地形态

任务指令

Router → Worker

content / operation / 约束 / contextPacket

A2A request

状态回执

Worker → Router

ACCEPTED / RUNNING / 进度 / 失败码

TASK_STATUS / TASK_RESPONSE

产物

Worker → Router

坐标 + 摘要(不含大字段)

SaResultEnvelope.artifactRefs

追问

Worker/Router → Human

一次只问一个结构化问题

clarify + WAITING_HUMAN

改向

Human → Router → Worker

“改成 / 换成 / 不是这个”

steer directive

系统

双向

心跳、水位、告警、诊断

Watchdog / Ops

6.2 状态机一次派发的完整生命周期

6.3 三种“人话意图”必须分开处理

ThreadRouter 用三组词表把用户输入分成三类,优先级严格:

  • 取消类(取消 / 停止 / 中断 / 终止)优先级最高——命中即不当作改向,避免把“停止”派给 Worker;
  • 改向类(改成 / 换成 / 不是这个 / 重做)——沿用同一执行体,但重下任务方向;
  • 续作类(继续 / 接着 / 下一步 / 做到哪)——需要账本里有历史任务才生效,否则不派发。

此外还有一组“跨线程意图词”(另一个、上次那个、所有任务、改主意……),命中即不允许直通,必须回到 Router 重新决策。这些规则看起来朴素,但它们把“用户改主意”与“用户继续当前任务”区分开了——这是多轮协作最容易出错的地方。

实际界面(2026-11-14 实测):当意图存在歧义时,系统不猜,直接给结构化选项并进入“等待用户确认选择”。这就是 ClarifyInfo 的 UI 形态——一次只问一个,选项可点,超时可回退。

实际界面(2026-11-14 实测):用户消息发出后,界面出现“表单智能体”标识与“任务步骤:发送请求…”。这是 Router 完成决策、A2A 已发出的可见证据;Worker 返回后再由信封统一收口。

6.4 单写者:同一会话绝不并发两轮

重复决策、重复派发的根因是同一会话被并发处理。SerialExecutor 用最朴素的 actor 语义解决它:

代码语言:javascript
复制
public synchronized void execute(Runnable task) {
    tasks.offer(() -> {
        try { task.run(); } finally { scheduleNext(); }
    });
    if (active == null) scheduleNext();
}

同一会话的事件严格按提交顺序执行,会话之间通过共享线程池并行。这样既保证了单会话的一致性,又没有牺牲全局吞吐。

6.5 看门狗:只读诊断,不乱重派

SuperAgentWatchdog 扫描超过阈值未推进的非终态任务,结合协议状态与线程绑定给出“卡在哪、下一步查什么”的诊断。它只读,不重派、不改状态——自动重派在没有幂等保证时是灾难。

协议状态

诊断

建议

NONE

协议侧无记录:未发出,或仅本地占位

检查 A2A 连接、目录 nodeId、能力驻留

PUBLISHED / ACCEPTED

已投递/已受理但无 TASK_RESPONSE

目标 Worker 可能离线、排队或执行中

SENT

仅入发送队列

MQTT 未连接或 PUBACK 未达

RUNNING + Thread=ASSIGNED

任务已运行但绑定未推进

绑定状态机存在缺口

七、LLM 层:如何让模型“知道全局”

7.1 提示词分层

层

内容

稳定性

预算

系统层

角色、边界、禁止事项

高(可缓存)

固定

编排层(Router)

任务拆解、worker 选择、失败策略

中

≤2000 字符

执行层(Worker)

域知识、产出 schema、工具清单

中

按域分配

事实层

GlobalContextPacket 投影

低(每轮变)

硬上限 + 截断

约束层

输出 JSON Schema、必填字段、禁止跨域写

高

固定

7.2 Router 的系统提示词

Router 的提示词只做一件事:决定谁来做。它不执行业务,也不允许编造事实。

代码语言:javascript
复制
你是 Ooder SuperAgent 的 Router(调度层),只负责决定请求该由谁处理,不执行具体业务。
只输出 JSON,不要输出任何解释。字段:
{"decision":"DIRECT_ANSWER|ASSIGN_WORKER|STEER_THREAD|CLARIFY|IGNORE",
 "agentId":"候选清单中的 agentId,或 null",
 "reply":"给用户的简短回复,可 null",
 "steer":"改向时下发给执行体的新要求,可 null",
 "clarification":"需要用户补充的信息,可 null",
 "reason":"决策依据,一句话",
 "confidence":0.0-1.0}
约束:agentId 必须来自候选清单;不确定就选 CLARIFY。
STEER_THREAD = 用户要调整**同一任务**的方向(如“改成方案B”),沿用当前执行体重下任务;
若提供了【租户项目账本】等背景,只用于判断该派给谁,不要臆造未提供的事实。
若提供了【执行体运行态】,可参考其在线状态、所在节点与未完成任务数;
目标离线或明显过载时不要硬派,选择 CLARIFY 并说明原因(如“目标执行体当前不可用/繁忙”)。
不得编造未在清单中的执行体、负载或资源状态。

模型实际收到的用户提示词由 Router 拼接,结构固定为“背景 → 用户输入 → 会话坐标 → 候选清单”:

代码语言:javascript
复制
【当前项目】conv-20261114-xxxx | RUNNING | 35% | 未完成 2
【当前线程】PRIMARY / ASSIGNED | 主执行体 form.agent @ node-8124
【执行体运行态(在线/节点/未完成任务/能力)】
- form.agent | 在线 | node-8124 | 未完成 1 | form.modeling,form.convert,form.launch
- finance.agent | 在线 | node-8017 | 未完成 4 | finance.bankstatement,finance.voucher

用户输入:
把这张员工表建成表单

conversationId=conv-20261114-xxxx
sceneId=form-modeling
entryProfile=rt:form
候选执行体:
- form.agent | name=表单智能体 | node=node-8124 | caps=form.modeling,form.convert,form.launch
- finance.agent | name=财务智能体 | node=node-8017 | caps=finance.bankstatement,finance.voucher

注意最后一段:项目账本与执行体运行态都来自 GlobalContextPacket 的 Router 投影,而不是模型自己“想象”的。模型看到的负载、在线状态、节点,都是可核对的事实。

三个关键约束:

  • 枚举化输出:decision 只能取五个值,避免自由发挥;
  • 候选白名单:agentId 必须来自候选清单,服务端会二次校验;
  • 不确定就追问:宁可 CLARIFY,也不猜。

7.3 不可信的决策不生效

LLM 路径默认关闭(router.llm-enabled=false)。即使打开,也有三道保险:

  1. 输出解析失败 → 回落规则路径;
  2. agentId 不在候选清单 → 转 CLARIFY,绝不臆造目标;
  3. 模型不可用或超时 → 回落规则路径。

规则路径本身也不是摆设:显式 @ 命中直接派发;锁定场景或指定场景本地直答;命中续作意图且账本有历史任务时,确定性派回最近执行体。这条路径无网络开销、结果可复现,是 LLM 不可用时的保底。

实际界面(2026-11-14 实测,已圈选):用户点名一个不存在的执行体时,Router 不猜、不硬派,直接返回全局候选清单并说明“未找到”;下方任务步骤同步显示“请求已发送 / 正在理解需求”。图中标注:① Router 返回的候选清单;② 任务步骤状态。这正是“候选白名单 + 不确定就 CLARIFY”的可见形态。

7.4 Worker 的提示词:模板 + 场景 + 事实包

Worker 侧不是一段提示词,而是按固定顺序装配出来的:

代码语言:javascript
复制
prompt = 场景模板.replace("{context}", context)
       + THINKING_LANGUAGE_RULE
       + "\n\n场景特定指引:\n" + scene.buildSystemPrompt(context)
       + DECISION_ENTRY_GUIDE          // 仅 rt:decision 入口
       + 面板上下文                     // sceneContext
       + 全局事实包块                   // _globalContextPacket
       + 沙箱上下文                     // 项目元信息 / 运行时状态

其中“思考语言规则”和“事实包块”由统一出口注入,不依赖各执行体自觉:

代码语言:javascript
复制
【思考与输出语言】
- 推理/思考过程(reasoning)必须使用简体中文,不要用英文思考。
- 最终回答也使用简体中文;仅代码、命令、路径、报错原文、专有名词保留原语言。
代码语言:javascript
复制
【全局上下文(SuperAgent 事实包)】
- 租户/用户: default / user-001 | 入口: rt:form
- 项目: conv-20261114-xxxx | RUNNING | 35% | 未完成 2 | 员工表转表单
- 本次任务: task-7f3a | 执行体 form.agent | 节点 node-8124 | 状态 RUNNING
- 约束: timeoutMs=120000, deliveryTimeoutMs=15000
(以上只用于保持与项目一致;不得修改项目状态,缺信息就问。)

最后一句是防越权的关键:模型可以读项目状态,但不能改;缺信息就问,而不是猜。这也是 Worker 投影里 workers=[]、clarify.pending=false 的原因——执行体不需要看到调度信息,也不应该替 Router 发起追问。

领域 Worker 的实际系统提示词

以银行流水转换执行体为例,其默认系统提示词不是泛泛的“你是助手”,而是把工具顺序、风险口径、交付格式写死:

代码语言:javascript
复制
你是OODER平台的专业资金流水转换助手。你的职责是帮助用户将银行流水自动转换为资金日报格式。

工具调用顺序(必须严格遵循):
1. 当用户上传了银行流水文件时,先调用 bank_statement_parse 工具解析流水
2. 拿到解析结果后,调用 bank_statement_convert 工具执行字段映射与项目分类
   (贷方→收款金额、借方→付款金额)
3. 根据转换结果,向用户输出转换报告、统计汇总和下载链接

★ 列健壮性与风险检查策略:
1. 解析结果附带 warnings 时,向用户提示缺失列并列出识别到的列名/表头,
   请用户确认或补充列映射;冗余列自动忽略,不中断转换。
2. 转换结果附带 risks(双向金额并存、单笔≥100万疑似单位错误、
   常规科目金额明显异常、有对方户名无金额疑似列错位)时,
   必须在报告中显著列出风险条目与行号,建议核实后再入账。
3. 转换完成后输出“📌 下一步提示:您可以…”横幅,给出 3-4 条后续建议。

★ 强制下载链接要求:
只要生成了新的转换/筛选/重查结果,最终回复**必须**附带最新 Excel 下载链接;
地址必须使用工具返回的真实 downloadUrl,禁止输出“下载地址”等占位符;
若本轮没有可下载产物,明确写“(本次无新生成的可下载文件)”。

★ NLP 兜底机制:
条件不明确或存在歧义时,主动向用户确认关键条件,而不是臆测执行。

这段提示词体现了三个原则:工具顺序确定、风险口径确定、交付格式确定。模型可以决定“怎么表达”,但不能决定“跳过哪一步”。

实际界面(已脱敏):资金日报场景中,执行体返回“共 5 家公司”的结构化查询结果,包含流水笔数与明细行数;第一列与银行账号列已用 ×××× 覆盖,保留统计口径与结果形态,便于核对“结论 + 数字”的交付方式。

执行体的能力契约

Router 看到的“能力”不是模型的自我描述,而是执行体注册的 op 清单。表单执行体注册了 10 个 op,这张表同时是路由依据与验收依据:

op

用途

典型调用方

ping / health

连通性与健康探针

Directory / Watchdog

capabilities

返回能力目录(与注册清单同源)

Router 候选汇总

modeling

按模式建模,返回产物摘要

主能力:自然语言建表单

convert

外部表单 → 统一 formSchema → 字段对齐报告

导入/迁移场景

launch

启动或复用表单流程实例

@智能体 / 流程型执行体

query / status

查询会话绑定与暂停实例状态

续作 / 人工接管

list / download

产物查询与下载

产物横幅 / 审计

注意:能力契约里没有“自由对话”。执行体只接受有限的 op,这既缩小了攻击面,也让结果可验证。

7.5 防幻觉四原则

  1. 字段带来源:事实字段标注来自账本、流程还是用户输入;
  2. 未知即未知:没提供的事实不允许推断;
  3. 结论可核对:用户面结论必须能映射到结构化字段或产物坐标;
  4. 技术细节不上屏:异常栈、裸 ID 只在技术面保留。

八、资源层:沙箱、线上服务与排期

8.1 当前已经存在的“硬约束”

在讨论资源调度之前,先看代码里已经生效的几组参数——它们其实就是最朴素的资源治理:

参数

默认值

作用

superagent.executor-size

4

会话执行线程数,决定全局并行度上限

task.max-per-user

3

单用户并发任务上限,防单点刷爆

task.timeout-ms

120000

任务总超时

task.delivery-timeout-ms

15000

跨节点投递确认窗口,超时立即 UNDELIVERABLE

session.max-active

500

活跃会话上限

session.idle-ms

86400000

会话空闲回收

其中“投递确认窗口”是一个很值得借鉴的设计:远端节点进程不在时,MQTT 发布本身会成功,旧行为只能空等到 120 秒。加入 15 秒确认窗口后,收不到受理回执就立即失败——把“空等”变成“快速失败”。

8.2 资源模型:池、槽、租约

沙箱与线上服务排期的本质是有限资源的分配问题。建议统一建模为:

代码语言:javascript
复制
Pool(type) → Slot[]        // 每类资源一个池,池内是槽位
Slot { id, state, lease, owner, labels }
Lease { leaseId, taskId, ttl, renewAt, preemptible }

三类典型资源:

  • 沙箱:容器位、CPU/内存配额、临时文件系统;
  • 线上服务:部署位、端口、域名、发布窗口;
  • 外部配额:LLM token、第三方 API QPS。

8.3 三阶段协议:申请 → 授予 → 释放资源调度:先排队,再租约,最后回收

8.4 排序与公平

  • 交互优先于批处理:用户在等的那一轮,永远排在离线任务前面;
  • 同域串行、跨域并行:同一表单 / 财务对象的写入串行化,避免覆盖;不同域互不阻塞;
  • 老化防饥饿:等待时间越长,权重越高;长任务与短任务分队列;
  • 预留与抢占:高优先级可抢占低优先级的租约,但被抢占任务必须保留检查点、重新入队,并有最大抢占次数。

8.5 防踩踏:五道闸

闸

机制

解决什么

命名空间

tenant / flow / task / worker 四级前缀

不同租户、不同任务的产物互相覆盖

锁分级

对象锁(单产物)、域锁(表单/财务)、流程锁(实例级)

跨对象并发写冲突

乐观锁

写前校验 version,冲突则重读重试

长事务下的丢失更新

幂等键

同一逻辑操作唯一键,重复提交合并

用户重复点击、消息重投

写集预检

执行前比较读集/写集,冲突则排队或拆分

两个 Worker 同时改同一实体

工程边界

当前版本以会话级单写者(SerialExecutor)、线程绑定、任务超时与投递确认、看门狗诊断为主;资源池、租约与抢占按上述协议在后续版本逐步接入。把边界写清楚,是为了让每次扩展都有明确的责任面。

九、分布式部署

9.1 角色拆分与一致性

组件

扩展方式

一致性要求

Router Cluster

无状态水平扩展

共享 TaskBoard,单写者 CAS

Worker Pool

按域独立扩缩容

任务幂等 + 租约

Resource Scheduler

单主 / 选主

强一致,防双主分配

Message Bus

分区 + 持久化

至少一次 + 去重窗口

上下文快照

对象存储 / VFS

最终一致,版本号收敛

9.2 容错

  • 心跳 + 租约续期:Worker 失联后租约到期,任务自动重派;
  • 幂等 + 重试上限 + 死信队列:重投不会产生重复副作用;
  • 断点续跑:从 TaskBoard 快照恢复上下文,而不是从头再来;
  • 灰度与降级:Worker 版本灰度;Router 可降级为规则路径;上下文包可整体关闭。

十、落地清单

阶段

目标

关键动作

验收

P0

事实与结果归一

GlobalContextPacket + SaResultEnvelope 全链路透传

正文、产物、台账口径一致

P1

资源与隔离

租约调度器、命名空间、锁分级、幂等键

并发写零覆盖,长任务不饿死

P2

分布式与容错

Router 无状态化、心跳、重派、断点续跑

单节点故障不影响整体

P3

智能编排

能力卡入提示词、全局任务图、失败自动改道

派发准确率与一次成功率提升

P4

体验与治理

HITL 卡、关键路径埋点、审计回放、成本核算

阶段可见、可诊断、可回放

十一、三条经验

第一,先把“事实”统一,再谈“智能”。上下文包和结果信封看起来不性感,但它们决定了系统是否可解释、可回滚、可灰度。没有这两样,模型越强,漂移越大。

第二,让 LLM 做它擅长的事,把不确定性关进笼子。Router 只输出枚举化 JSON,目标必须在候选白名单内,解析失败一律回落规则路径。模型可以建议,但不能拍板。

第三,资源治理的本质是“承认有限”。线程、沙箱、端口、发布窗口都是有限的。租约、队列、幂等、单写者,这些老掉牙的招数,在多智能体系统里一个都不能少。

一句话总结

SuperAgent 的升级不是“让 Agent 更聪明”,而是让一群 Agent 共享同一份事实、同一套坐标、同一句人话。做到这三点,协作才从“看起来在配合”变成“真的能交付”。


代码基线(ooder-pro / net.ooder.studio.chat.superagent)

GlobalContextPacket.java · GlobalContextAssembler.java · SaResultEnvelope.java · SaResultAssembler.java RouterPolicy.java · ThreadRouter.java · WorkerTask.java · A2aWorkerDispatchAdapter.java SerialExecutor.java · SuperAgentWatchdog.java · SseReplyAdapter.java · SuperAgentProperties.java 前端消费:static/shared/chatnext-base/ui/ArtifactBar.js · PromptEngine.java

界面截图来自测试环境实录;架构图为原创 SVG。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • SuperAgent 实战:让 Router 看见全局,让 Worker 不再各说各话
    • 一、当第二个 Agent 出现,问题就不再是提示词
    • 二、结论先行:六条判断
    • 三、第一性拆解:SuperAgent 到底在管什么
    • 四、上下文层:GlobalContextPacket
      • 4.1 为什么需要“事实包”
      • 4.2 一次装配,两种投影同一份事实,按角色裁剪
      • 4.3 三个容易被忽略的工程细节
      • 4.4 Worker 侧的注入链
    • 五、结果层:SaResultEnvelope
      • 5.1 四套形状的代价
      • 5.2 信封长什么样
      • 5.3 失败也要说人话
      • 5.4 不重复说两遍
    • 六、消息与人在环:Router 如何组织反馈
      • 6.1 消息分类
      • 6.2 状态机一次派发的完整生命周期
      • 6.3 三种“人话意图”必须分开处理
      • 6.4 单写者:同一会话绝不并发两轮
      • 6.5 看门狗:只读诊断,不乱重派
    • 七、LLM 层:如何让模型“知道全局”
      • 7.1 提示词分层
      • 7.2 Router 的系统提示词
      • 7.3 不可信的决策不生效
      • 7.4 Worker 的提示词:模板 + 场景 + 事实包
      • 7.5 防幻觉四原则
    • 八、资源层:沙箱、线上服务与排期
      • 8.1 当前已经存在的“硬约束”
      • 8.2 资源模型:池、槽、租约
      • 8.3 三阶段协议:申请 → 授予 → 释放资源调度:先排队,再租约,最后回收
      • 8.4 排序与公平
      • 8.5 防踩踏:五道闸
    • 九、分布式部署
      • 9.1 角色拆分与一致性
      • 9.2 容错
    • 十、落地清单
    • 十一、三条经验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档