跳到主要内容

讨论拓扑:四种多智能体协作结构

本章讲什么: 解题框架(见 02 章)的第二阶段「讨论出方案」是整个框架最有料的地方。AgentVerse 把「一群专家怎么讨论」抽成可插拔的 decision_maker,提供了四种主要拓扑。这一章逐个拆开,你会看到多智能体协作的几种经典结构是怎么用几十行代码实现的。

1. 共同约定:solver 在头,critics 在尾

所有 decision_maker 收到的 agents 列表都遵循同一个约定(tasksolving_env/rules/base.py:104-113):agents[0] 是 solver(出方案的人),agents[1:] 是 critics(被招募的专家评论者)

基类 BaseDecisionMaker 给了两个广播工具(decision_maker/base.py:36-45):

# decision_maker/base.py:36-45
def broadcast_messages(self, agents, messages): # 发给所有人
for agent in agents:
agent.add_message_to_memory(messages)

def p2p_messages(self, agents, messages): # 点对点:只发给 solver 和原发送者
agents[0].add_message_to_memory(messages)
for message in messages:
for agent in agents[1:]:
if agent.name == message.sender:
agent.add_message_to_memory(messages)
break

这两个工具(广播 vs 点对点)是区分各拓扑「信息怎么流」的基本积木。

2. 四种拓扑一览

拓扑直觉critics 之间能否互相听见关键代码
vertical并发评审,solver 汇总否(并发,互不可见)vertical.py:35-55
horizontal轮流发言,边说边广播是(后说的能听见先说的)horizontal.py:39-54
central一个中心智能体协调由中心居中转述central.py:34-53
brainstorming像 horizontal,但讨论完清空记忆只留摘要brainstorming.py:29-67

3. 逐个拆

3.1 vertical:并发评审 → solver 吸收异议

直觉: 所有 critics 同时对当前方案发表意见(互相看不见彼此),solver 把「不同意」的意见收进记忆,然后产出新方案。像一次并行 code review。

# vertical.py:35-55(节选)
reviews = await asyncio.gather( # critics 并发评审
*[agent.astep(previous_plan, advice, task_description) for agent in agents[1:]]
)
nonempty_reviews = [r for r in reviews if not r.is_agree and r.content != ""] # 只留异议
agents[0].add_message_to_memory(nonempty_reviews) # solver 吸收异议
result = await agents[0].astep(previous_plan, advice, task_description) # solver 出新方案

重点看: 只有 is_agree=False 的评论才进 solver 记忆(vertical.py:48-52)——「同意」是噪声,过滤掉省 token。critics 之间因为是并发,谁也听不见谁。

3.2 horizontal:轮流发言,边说边广播

直觉: critics 依次发言,每个人说完立刻广播给全场,所以后说的人能听见前面所有人的话——像一场有序的圆桌。最后 solver 综合发言出方案。

# horizontal.py:39-54(节选)
for agent in agents[1:]: # 依次,不是并发
review = await agent.astep(previous_plan, advice, task_description)
if review.content != "":
self.broadcast_messages(agents, [review]) # 立刻广播,后来者听得见
result = await agents[0].astep(previous_plan, advice, task_description) # solver 收尾

vertical vs horizontal 的本质区别就一处: vertical 用 asyncio.gather(并发、互不可见),horizontal 用 for 循环 + broadcast(串行、层层叠加上下文)。一个图说清:

vertical(并发): horizontal(串行+广播):
C1 ─┐ C1 ──► 广播 ──► C2 听见 C1
C2 ─┼─► solver 汇总 C2 ──► 广播 ──► C3 听见 C1,C2
C3 ─┘ (C1/C2/C3 互不可见) C3 ──► 广播 ──► solver 听见全部

3.3 central:中心智能体居中协调

直觉: 不让专家直接讨论,而是有一个「中心」智能体(agents[1])代表所有角色发声,再把它的发言转交给 solver(agents[0])。

# central.py:38-53(节选)
result = await agents[1].astep( # 中心智能体,带上所有角色名
previous_plan, advice, task_description,
roles=", ".join([a.role_description[0].lower() + a.role_description[1:] for a in agents]),
)
agents[1].add_message_to_memory([result])
result = await agents[0].astep( # solver 拿中心的发言当聊天记录
previous_plan, advice, task_description, chat_record=result.content)

重点看: central 把多专家「压缩」成一个中心智能体的单次发言(它在 prompt 里列出所有 roles),适合想省调用次数、又要兼顾多视角的场景。

3.4 brainstorming:讨论完清空记忆,只留摘要

直觉: 流程几乎和 horizontal 一样(依次发言 + 广播),但多了一个关键收尾:讨论结束后把所有人的记忆清空,只塞回一条 solver 写的摘要

# brainstorming.py:56-66(节选)
result = await agents[0].astep(previous_plan, advice, task_description) # solver 当摘要器
for agent in agents:
agent.memory.reset() # 清空所有人记忆!
self.broadcast_messages(agents, [Message(
content=result.content, sender="Summary From Previous Discussion")]) # 只留摘要

为什么这么做(类的 docstring 自己解释了,brainstorming.py:20-25): 多轮头脑风暴会让上下文飞快膨胀、撑爆模型窗口。把每轮压缩成一条摘要,既保留要点又控制 token——这是多智能体长程讨论的实用压缩技巧。

注意:这里 solver 扮演的是摘要器角色(对照 brainstorming 配置里 solver 的 prompt:「You are a summarizer」,tasks/tasksolving/brainstorming/config.yaml:28-33)。同一个 SolverAgent 类,靠 prompt 切换成了完全不同的职责。

4. advice 怎么进讨论

四种拓扑开头几乎都有同一段:如果评委给了 advice,就先广播进场(以 horizontal 为例,horizontal.py:35-37):

# horizontal.py:35-37
if advice != "No advice yet.":
self.broadcast_messages(agents, [Message(content=advice, sender="Evaluator")])

这就是 02 章那条反馈线在讨论阶段的落点:上一轮评委的批评,作为这一轮讨论的起点广播给全体专家。

5. 巧妙之处

  • 同一目标的四种拓扑只差「信息流」:vertical/horizontal 的全部差异就是「并发 gather」还是「串行 for + broadcast」。把协作结构归约到「谁在什么时候听见谁」,是非常干净的抽象。
  • brainstorming 的记忆重置:用「清空记忆 + 注入摘要」解决多智能体长讨论的上下文爆炸,是可直接借鉴的工程手法(brainstorming.py:57-66)。
  • prompt 决定角色,类决定结构:solver 在 vertical 里是「方案作者」,在 brainstorming 里是「摘要器」——同一个 SolverAgent 类,职责完全由 YAML 里的 prompt 决定。结构(decision_maker)和内容(prompt)彻底解耦。

6. 边界与局限

  • dynamic(带 manager 的动态讨论)是未完成品:文件里写着 ## To Do: implement dynamic,astep 里还有一段死循环式的可疑逻辑和被注释的备份代码(dynamic.py:24-81)。生产勿用。
  • 各拓扑对 agents[0]=solver 的硬约定意味着换拓扑不能换 agents 的排列假设。
  • critics 的「同意/不同意」判定依赖 CriticMessage 的 is_agree 字段,而该字段来自 output parser 对 LLM 文本的解析(见 04 章),解析失败会影响过滤。

7. 横向对比

这四种拓扑覆盖了多智能体协作的几种经典模式:vertical ≈ 并行评审(map-reduce 式),horizontal ≈ 顺序圆桌(链式上下文累积),central ≈ 协调者模式,brainstorming ≈ 带压缩的迭代发散。换 decision_maker 而其余三阶段不变,体现了 02 章「流水线工序可插拔」的设计——讨论这道工序内部还能再换拓扑。

8. 代码地图

主题文件符号
讨论基类 + 广播/点对点工具agentverse/environments/tasksolving_env/rules/decision_maker/base.pyBaseDecisionMaker.broadcast_messages
并发评审拓扑agentverse/environments/tasksolving_env/rules/decision_maker/vertical.pyVerticalDecisionMaker.astep
串行圆桌拓扑agentverse/environments/tasksolving_env/rules/decision_maker/horizontal.pyHorizontalDecisionMaker.astep
中心协调拓扑agentverse/environments/tasksolving_env/rules/decision_maker/central.pyCentralDecisionMaker.astep
头脑风暴(记忆重置)agentverse/environments/tasksolving_env/rules/decision_maker/brainstorming.pyBrainstormingDecisionMaker.astep
未完成的动态拓扑agentverse/environments/tasksolving_env/rules/decision_maker/dynamic.pyDynamicDecisionMaker.astep