多Agent协作实战:Java AgentScope新手教程
第八章 多 Agent 协作:orchestrator + workers 模式,主 Agent 主持 Subagent 群聊
继续深入。在 1.x 版本中,实现多 Agent 对话通常依赖 MsgHub,它把“群聊”抽象为一个共享消息总线——任何 Agent 发出的消息自动广播给 Hub 内所有成员。设计直观,但在 2.0 中,这个类已被移除。
替代方案?等价语义完全可由一个“主 Agent”接管。就像会议不需要自动广播喇叭,只需一位主持人,明确谁该发言、谁该倾听。下表对比了 1.x 与 2.0 的对应关系:
1.x MsgHub 语义 | 2.0 替代方案 |
|---|---|
| 多 agent 共享消息总线 | 主 agent 持有 Map 状态 |
| 任一 agent 发言自动广播 | 主 agent 逐一向每个 subagent 传递“上一人发言” |
hub.add(a, b, c) | HarnessAgent.builder().subagent(a, b, c) |
hub.broadcast(msg) | 主 agent 自行构造 prompt,循环中 spawn |
| 异步 / 多轮 | 主 agent 每轮执行 agent.call(...) 一次 |
8.2 首个群聊实例:四方圆桌会议
理论落地,直接上手。模拟一种典型场景:产品、技术、运营、法务就一个需求各抒己见。这是团队协作中最易出状况、也最能凸显多 Agent 价值的环节。
import io.agentscope.core.agent.RuntimeContext;
import io.agentscope.core.formatter.openai.OpenAIChatFormatter;
import io.agentscope.core.message.UserMessage;
import io.agentscope.core.model.OpenAIChatModel;
import io.agentscope.harness.HarnessAgent;
import io.agentscope.harness.agent.subagent.SubagentDeclaration;
import ja va.nio.file.Path;
import ja va.util.List;
public class Chapter08_Roundtable {
public static void main(String[] args) {
OpenAIChatModel model = OpenAIChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4o-mini")
.baseUrl("https://api.openai.com")
.stream(true)
.formatter(new OpenAIChatFormatter())
.build();
SubagentDeclaration product = speaker("product", "产品经理", "关注用户价值与优先级");
SubagentDeclaration tech= speaker("tech","技术负责人", "关注实现成本与稳定性");
SubagentDeclaration ops = speaker("ops", "运营负责人", "关注上线节奏与风险");
SubagentDeclaration legal = speaker("legal", "法务顾问", "关注合规与用户协议");
HarnessAgent host = HarnessAgent.builder()
.name("roundtable_host")
.sysPrompt("""
你是一个会议主持人。
1. 你管辖 4 位参与者:product / tech / ops / legal。
2. 拿到议题后,调用 agent_spawn 让 product 先发言,
然后把 product 的发言喂给 tech,再把 tech 的发言喂给 ops,
最后喂给 legal,legal 收尾。
3. 最后用一句话总结四方共识。
""")
.model(model)
.workspace(Path.of("./workspace"))
.subagent(product)
.subagent(tech)
.subagent(ops)
.subagent(legal)
.build();
host.call(List.of(new UserMessage("user", "议题:我们打算把免费用户的 API 限速从 60/min 降到 30/min,请四方评估。")),
RuntimeContext.empty()).block();
}
static SubagentDeclaration speaker(String id, String role, String concern) {
return SubagentDeclaration.builder()
.name(id)
.description(role + ",参与四方会议;每次发言聚焦自己的关切:" + concern)
.inlineAgentsBody("你是" + role + "。发言简洁(不超过 60 字),聚焦:" + concern)
.build();
}
}
执行这段代码,四个角色的输出自然串联成一段逼真的“圆桌会议纪要”。实现成本极低,效果生动。
8.3 多轮群聊:将“上一轮”传递给“下一轮”
1.x 中 MsgHub 的真正精髓在于多轮对话——上一轮 A 的发言自动成为下一轮 B 的上下文。2.0 未丢失此能力,只是换了一种实现:借助主 Agent 自身的 AgentState。
具体而言,每执行完一个 subagent,主 Agent 在其 call() 上下文中自动获得该 subagent 的回复。当需要 spawn 下一轮 subagent 时,主 Agent 可在 system prompt 中显式拼接“上一个发言者说了什么”。
// 在主 agent 的 system prompt 里加一条规则:
"""
第 N 轮发言前,先在 user 输入里写:
'以下是上一位 speaker 的发言:<上一段完整文本>'
然后再让下一个 subagent 接着说。
"""
关键点在于:主 Agent 每轮自行拼接这段前缀,业务代码无需编写循环。这正是 HarnessAgent + agent_spawn 相比硬编码 Pipeline 更灵活之处——LLM 自行组织“上下文传递”。
8.4 规避“消息风暴”
1.x 的 MsgHub 长期被诟病:4 个 Agent 在 Hub 内互发消息,跑 5 轮后 token 用量暴增。2.0 通过两种机制解决。
8.4.1 限制发言轮次
最直接有效的办法:在主 Agent 的 system prompt 中加入规则 "每位参与者最多发言 1 轮"。LLM 受到约束后,自然会避免重复 spawn 对话。
8.4.2 使用 CompactionMiddleware 自动摘要
更优雅的方式是挂载 CompactionMiddleware:
HarnessAgent host = HarnessAgent.builder()
...
.middleware(new CompactionMiddleware(CompactionConfig.builder()
.triggerTokens(8000) // 上下文超过 8k token 时压缩
.keepRecentMessages(6) // 保留最近 6 条
.summaryTarget("MEMORY.md")
.build()))
.build();
当上下文超过 8k token 时,自动将“早期发言”压缩为摘要,写入 workspace/MEMORY.md。下一章将详细拆解此机制,此处只须了解其存在。
8.5 最小迁移清单(1.x MsgHub → 2.0 orchestrator)
若你正从 1.x 迁移,下面速查表可快速定位。核心原则:1.x 要求手动编排 Agent 顺序,2.0 则将此顺序交由 system prompt 控制。
| 1.x 用法 | 2.0 等价实现 |
|---|---|
MsgHub hub = new MsgHub() | 隐式:主 agent 持有 subagent 列表 |
hub.add(a, b, c) | HarnessAgent.builder().subagent(a, b, c) |
hub.broadcast(Msg) | 主 agent 自行组织 prompt,循环 spawn |
hub.enter().broadcastAll(...) | 主 agent 一次性发送多 spawn(async) |
Pipelines.conversation([a,b,c]) | 主 agent 编写“轮次规则” + subagent 列表 |
迁移技巧:先明确你的“四方会议”发言顺序,然后将该顺序用自然语言描述到 system prompt 中。1.x 时代编写 Pipeline 时本就需要设计此顺序,现在只是换了位置。
8.6 完整可运行示例:3 人辩论赛
最后看一个更生动的场景——辩论赛。正方、反方陈述观点,裁判判定胜负。此例可作为理解 orchestrator + workers 模式的补充素材。
public class Chapter08_Debate {
public static void main(String[] args) {
DashScopeChatModel model = DashScopeChatModel.builder()
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.modelName("qwen-plus")
.build();
SubagentDeclaration pro= debater("pro","正方","AI 会大幅提升生产力");
SubagentDeclaration con= debater("con","反方","AI 不会减少劳动时间");
SubagentDeclaration judge = SubagentDeclaration.builder()
.name("judge")
.description("辩论赛裁判;正反方发言结束后给出一句胜负判定")
.inlineAgentsBody("你是裁判,只返回'正方胜'或'反方胜',不要解释。")
.build();
HarnessAgent host = HarnessAgent.builder()
.name("debate_host")
.sysPrompt("""
你是辩论赛主持人。流程:
1. 拿议题,先 agent_spawn pro,让正方开场
2. 把 pro 发言喂给 con,反方反驳
3. 把 con 反驳喂给 pro,正方再反驳(最多 1 轮)
4. 最后让 judge 收尾
5. 总结一句:哪个论据最有说服力
""")
.model(model)
.workspace(Path.of("./workspace"))
.subagent(pro)
.subagent(con)
.subagent(judge)
.build();
host.call(List.of(new UserMessage("user", "辩题:未来 5 年,AI 是否会替代大部分白领工作?")),
RuntimeContext.empty()).block();
}
static SubagentDeclaration debater(String id, String side, String stance) {
return SubagentDeclaration.builder()
.name(id)
.description(side + "辩手;坚持立场:" + stance)
.inlineAgentsBody("你是" + side + "辩手,立场:" + stance + "。发言不超过 80 字。")
.build();
}
}
8.7 本章小结
- 1.x 的
MsgHub与Pipelines在 2.0 中整体移除,取而代之的是SubagentDeclaration+agent_spawn组合。 - 所谓“群聊”,在 2.0 中本质上是 orchestrator + workers 模式:一个主 Agent 担任主持人,用 system prompt 描述发言顺序。
- 多轮群聊的上下文保留依赖主 Agent 自身的
AgentState,无需共享消息总线。 - 防止 token 爆炸的两道防线:一是限制发言轮次,二是挂载
CompactionMiddleware实现自动摘要。
