多Agent协作项目的工程复盘:任务分配不均与通信风暴的解决方案

一、从单Agent到多Agent的"复杂度跳跃"

单Agent很好理解——一个LLM调工具,循环直到任务完成。但当把"写一份市场分析报告"拆成"搜索Agent + 分析Agent + 写作Agent"三个协作时,问题指数级增长:

  • 搜索Agent找到了300条相关信息,全部传给分析Agent——token爆炸+信息过载
  • 分析Agent的处理时间太长(3分钟),写作Agent一直在空转等待
  • 三个Agent之间的通信消息在5轮对话后累积到87条——通信开销超过了任务执行本身

单Agent到多Agent不是加法,是乘法——任务分配、负载均衡、通信协议、超时处理,每个环节都可能成为瓶颈。

二、三个核心问题的工程解法

问题一:任务分配不均导致的"木桶效应"

搜索Agent 30秒完成,分析Agent需要3分钟——整个流水线的速度由最慢的Agent决定。

解法:动态任务拆分。不是把"所有搜索结果→一个分析Agent",而是:

class TaskOrchestrator:
    async def execute(self, task: Task) -> Result:
        # 1. 拆分任务
        subtasks = await self.decompose(task)
        
        # 2. 估算每个子任务的复杂度
        estimated_times = [self.estimate_time(st) for st in subtasks]
        
        # 3. 动态分配——计算密集型任务分给多个Agent并行
        assignments = self.balanced_assign(subtasks, self.available_agents)
        
        # 4. 并行执行 + 超时控制
        results = await asyncio.gather(
            *[agent.execute(assign) for agent, assign in assignments.items()],
            return_exceptions=True
        )
        
        return self.merge_results(results, task)
    
    def balanced_assign(self, subtasks, agents):
        """将总工作量均分给所有可用Agent"""
        # 按估计时间排序,大任务优先分配
        sorted_tasks = sorted(subtasks, key=lambda t: self.estimate_time(t), reverse=True)
        
        assignments = {agent: [] for agent in agents}
        loads = {agent: 0 for agent in agents}
        
        for task in sorted_tasks:
            # 分配给当前负载最小的Agent
            best_agent = min(loads, key=loads.get)
            assignments[best_agent].append(task)
            loads[best_agent] += self.estimate_time(task)
        
        return assignments

问题二:Agent间的通信风暴

3个Agent在5轮对话中产生了87条消息。大多数消息是冗余的中间状态更新。Agent A说"我在搜索",Agent B问"找到了多少",Agent A回"120条,正在筛选"——这些对最终任务没有价值。

解法:分层通信——区分"控制消息"和"数据消息"。

# 通信协议精简
class AgentMessage:
    type: str  # "data" | "status" | "error" | "done"
    priority: int  # 0=低(调试), 1=正常, 2=高(阻塞)
    payload: dict

class CommunicationManager:
    def __init__(self):
        self.data_channel = asyncio.Queue()    # 数据通道——仅结果
        self.control_channel = asyncio.Queue() # 控制通道——状态/错误
    
    async def send_data(self, agent_id: str, data: dict):
        # 数据仅发给需要它的Agent,不全网广播
        target = self.get_downstream_agent(agent_id)
        await self.data_channel.put({
            "target": target,
            "payload": data,
        })
    
    async def send_control(self, message: AgentMessage):
        # 仅阻塞性状态变更才广播
        if message.priority >= 2:
            await self.control_channel.put(message)

效果:消息量从87条降到19条(78%减少),通信延迟从总时间的35%降到8%。

问题三:Agent失败时的级联恢复

如果分析Agent中途失败(API超时/LLM返回格式错误),下游的写作Agent会收到空数据。原来的方案是整个任务重来——浪费时间。

解法:检查点机制——每个Agent完成后保存中间结果。失败时从最近检查点恢复,而非从头开始。

class CheckpointManager:
    async def save(self, stage: str, data: dict):
        key = f"checkpoint:{self.task_id}:{stage}"
        await redis.set(key, json.dumps(data), ex=3600)
    
    async def load(self, stage: str) -> dict | None:
        key = f"checkpoint:{self.task_id}:{stage}"
        data = await redis.get(key)
        return json.loads(data) if data else None

class ResilientOrchestrator:
    async def execute_with_retry(self, task: Task):
        # 尝试从最近检查点恢复
        last_stage = await self.checkpoint.detect_last_stage(task.id)
        
        if last_stage:
            saved_data = await self.checkpoint.load(last_stage)
            # 从失败点继续,而非从头
            return await self.continue_from(last_stage, saved_data, task)
        
        return await self.execute_from_scratch(task)

三、架构的最优Agent数量

一个重要发现:Agent数量不是越多越好。

实验数据(处理同样一份市场分析报告):

Agent数量 总耗时 通信开销 LLM调用成本 结果质量
1(单体) 6m30s 0% $0.15 7.2/10
3 4m10s 8% $0.21 8.1/10
5 5m50s 18% $0.38 8.3/10
8 8m20s 32% $0.72 7.8/10

最优数量是3-5个。超过5个后,通信开销和调度复杂度抵消了并行带来的收益。8 Agent方案的通信开销占总时间的32%。

四、适用场景与边界

多Agent协作适合: 任务可以被明确拆分为独立子任务、子任务之间有清晰的数据依赖关系、单个Agent无法处理的任务规模(如"分析100份财报")。

不适合: 串行依赖的任务链(A的结果决定B,B的结果决定C——多个Agent不会带来并行收益)、简单任务(拆分的开销大于执行)。

当前系统的局限: Agent之间的"理解偏差"——搜索Agent认为"相关"的信息,分析Agent可能认为"噪音"。在Agent之间插入"过滤层"可以部分解决,但过滤规则本身也可能丢失关键信息。这是多Agent系统的根本权衡——信息传递效率 vs 信息保真度。

五、总结

多Agent协作的核心经验:

  • 动态负载均衡解决木桶效应——大任务拆分为小任务分给多个Agent并行
  • 分层通信(控制消息 vs 数据消息)减少78%的冗余消息
  • 检查点机制让失败恢复成本从"全部重来"降到"从失败点继续"
  • Agent最优数量是3-5个——超过5个后通信开销抵消并行收益
  • 不是所有任务都适合多Agent——串行依赖的任务链无需拆分

当前系统支持最多5个Agent同时协作,通过任务拆分、负载均衡和分层通信,将任务完成时间减少了约36%(对比单Agent),同时成本增加了约40%。这个ROI在"时间敏感"型的任务场景中是正面的。如果任务对"成本敏感"而非"时间敏感",单Agent仍然是更经济的选择。

更多推荐