
1. 任务分派隐身为什么总有一个代理在浑水摸鱼先说一个我实际跑项目时候遇到的现象。你以为把任务拆好、分给不同的代理它们就会各司其职太天真了。我第一次搭建一个五个代理协作的小系统时A负责抓取数据B负责清洗C负责分析D负责写报告E负责审查。听起来分工明确实际跑起来一片混乱——C经常把B的活干了D偶尔帮C写分析结论E对D的报告挑完毛病之后直接自己重写了一份最后系统里出现了两份报告没人知道哪份才是权威版本。这个问题在 Multi-Agent 实践里特别典型我给它起了个名字叫任务隐形边界。原因其实不难理解大模型本身对职责边界的感知是模糊的。你虽然在 system prompt 里写清楚了每个代理负责什么但在一次长对话里后续任务的上下文可能会让某个代理觉得自己应该多管一点。尤其是当某个环节输出质量不高时下一个代理很容易产生我来补救一下的冲动于是边界就崩了。我后来做了一套比较有效的约束方式核心思路是输出结构化 状态机校验。# 每个代理的最终输出必须是结构化对象 class AgentOutput(BaseModel): agent_role: str # 当前代理身份 task_phase: str # 当前阶段标识 payload: dict # 实际内容 confidence: float # 置信度 # 调度层校验当前阶段是否匹配 def validate_phase(output: AgentOutput, expected_phase: str): if output.task_phase ! expected_phase: raise PhaseMismatchError( fAgent {output.agent_role} 输出了不属于自己阶段的内容: fexpected {expected_phase}, got {output.task_phase} )这样做的逻辑很直白不让模型自己觉得自己该干什么而是让程序强制规定它只能干什么。模型可以想干别的但输出一旦不是它这个阶段该有的东西调度层直接拦截报错。实测下来这个方案能减少大概八成的边界混乱问题。另一个值得注意的点是任务粒度的控制。我踩过的坑是任务拆得太细。你以为代理越多效率越高恰恰相反。代理越多通信开销越大上下文越长出错率也越高。我试过把一个简单的文本处理任务拆给六个代理做流水线结果光上下文传递就占掉了大量 token而且每个代理因为只看到局部信息做出的决策质量反而不如一个全能代理直接做。比较合理的粒度参考是这样任务复杂度推荐代理数量说明简单问答、单一转换1~2一个执行一个审查就够中等流水线抓取→清洗→分析→输出3~4每个环节独立但不要细分到子步骤复杂研究、多源信息整合4~6需要专门的协调者/调度代理超过6个代理慎重建议重写架构而不是继续加代理多代理不是数量游戏而是分工效率游戏。这一点是我在这类项目里最大的认知修正。2. 上下文窗口膨胀与选择性失忆共享记忆并不是越长越好Multi-Agent 系统里另一个大坑是上下文管理。很多初次实践的人以为既然模型支持很长的上下文窗口那把完整的历史记录全部传给每个代理不就行了我试过效果非常糟糕。首先是成本问题。一个五个代理协作的任务每个代理都要接收全部历史Token 消耗直接爆炸。其次是效果问题——上下文越长模型越容易丢失早期信息尤其是埋在大量对话中间的某个关键结论。模型不是人没有翻回去看看的习惯它只是从全部 Token 里做概率预测早期信息在当前回复时已经被稀释得差不多了。我自己碰到过一次特别崩溃的现场一个数据清洗代理在处理到第 20 条记录的时候突然忘记了自己之前定义过的清洗规则把一条本该删除的无效数据当作有效数据保留了下来而且后续代理完全没有察觉一路把这个错误带到了最终报告里。事后排查花了一整个下午。后来我用的方案是分层记忆 摘要压缩。具体来说是三层短期记忆只传递当前任务相关的最近几轮对话一般控制在 2000~4000 token 以内。工作记忆每个代理保存自己的任务执行状态和结果包括关键中间产物。长期记忆项目级的关键结论、规则、约束以压缩摘要的形式存在只有在相关时才注入上下文。摘要是工具链里我自己觉得最有价值的一环。当代理 A 完成某个阶段后不是把原始输出全部传给代理 B而是由调度层生成一份结构化的阶段摘要只保留关键信息、结论、置信度和遗留问题。这样代理 B 拿到的是经过提炼的接力棒而不是一大堆历史包袱。给你看一个我常用的摘要模板阶段摘要 - {agent_role} - {timestamp} **完成事项** 1. [事项描述 结果摘要] 2. [事项描述 结果摘要] **关键输出** - 主要结论: {结论内容} - 产出物位置: {文件路径 / 结构化数据引用} **遗留问题与风险** - [问题1 及说明] - [问题2 及说明] **建议下一阶段关注** - [重点关注内容]这个格式的核心价值不只是节省 token更重要的是迫使系统做信息提炼。模型在读完整上下文后生成这个摘要的过程本质上是一次信息压缩而压缩会过滤掉大量冗余。实测下来用摘要传递的系统比用原始记录传递的系统完成任务准确率更高消耗 token 也更少。还有一个细节上下文注入时机。不要把所有记忆一股脑全塞给每个代理而是按需注入。比如审查代理需要看到的是产出物的完整内容不太需要看到前几个代理的原始推理过程而调度代理则需要全局视图但不需要过于精细的底层数据。每个代理的上下文视角应当不同这正是多代理系统相对单代理的核心优势可惜很多人没意识到这点简单粗暴地做全局广播最后所有代理变成了同一个模型的多个分身失去了差异化的价值。3. 调用链的俄罗斯套娃子代理嵌套失控时到底怎么救另一个高频坑是代理之间的嵌套调用。为了完成一个复杂任务调度代理会委派子任务给子代理子代理又可能再委派孙任务给孙代理。层级一深问题就来了。我的一次经历是这样的一个研究任务调度代理判断需要搜集三类资料于是启动三个子代理。其中一个子代理发现资料涉及多个数据源又启动了两个孙代理分头去拉数据。孙代理拉数据时发现某些数据源需要额外的字段映射其中一个孙代理又调用了另一个工具代理去查询数据字典。等到整个链路跑完我数了一下调用深度达到了六层。这个流程本身没有崩溃但问题出在状态管理上。第六层的代理返回结果时链路中的每一层都需要逐级透传中间任何一层处理超时或者解析出错最终调度代理拿到的结果就是残缺的。那次真正让我崩溃的是某个孙代理的返回结果缺少了一个关键字段但上层代理没有校验就直接传递了最终导致整个研究报告的数据缺失而报错信息却指向了最顶层的代理——一个和数据字典毫无关系的调度层。这种问题本质上是一个分布式系统中的调用链追踪问题。Multi-Agent 的调度过程和微服务架构里的服务调用链几乎没有区别。我的应对方案是引入三个机制。3.1 全局唯一的 Trace-ID 贯穿链路import uuid class AgentContext: def __init__(self): self.trace_id str(uuid.uuid4()) self.parent_span_id None self.span_id str(uuid.uuid4()) def create_child_context(self): child AgentContext() child.parent_span_id self.span_id child.trace_id self.trace_id return child每个代理在启动时都携带 trace_id所有日志和中间输出都自动带上这个标识。这样一旦出问题我可以从日志系统里按 trace_id 捞出一条完整的调用链直接定位是哪个环节断了。这套做法我在早期没有用每次排查要手动去翻不同代理的输出目录痛苦不堪用上之后定位问题的时间缩短了一个数量级。3.2 限制嵌套深度我给系统设定了一个硬性的嵌套深度上限默认是 3 层超过就拒绝创建子代理迫使上层的调度代理自己处理而不是无限往下甩锅。这个限制一开始会让人觉得不够灵活但实际上它倒逼你提前规划好任务分解方式。如果嵌套超过三层说明你最初的任务拆解就有问题应该在架构层面优化而不是在运行时无限繁殖子代理。MAX_DEPTH 3 def create_sub_agent(parent_agent, task): if parent_agent.depth 1 MAX_DEPTH: raise MaxDepthExceededError( f无法创建子代理: 深度 {parent_agent.depth 1} 超过上限 {MAX_DEPTH} ) # 正常创建逻辑3.3 每一层的返回校验父代理在拿到子代理返回结果后必须先做一次结构校验再决定是否向上传递。这个校验要包含三个维度必要字段是否存在数据结构完整性内容与任务是否匹配语义近似度判断置信度是否达标低于阈值则触发重跑或人工介入校验不通过时父代理可以有两种选择直接要求子代理重跑或者自己基于已有信息做补全。前者更安全后者更高效具体用哪种取决于任务类型——数据收集类的我倾向于重跑因为准确度优先文本润色类的我可以接受上层代理直接补全因为效率优先。4. 并行协调中的互掐与死等调度算法选型实战多代理系统一旦涉及并行执行协调问题立刻变成头号麻烦。最常见的两种事故是互掐和死等。互掐指的是两个代理同时修改同一个共享资源导致数据不一致。我在一个项目里让两个代理分别负责获取最新价格数据和生成价格趋势图表它们的任务原本没有交集但因为底层共享了一个缓存文件同时写入时把缓存弄坏了导致两个任务都失败。排查了很久才发现是文件锁的问题和代理本身的逻辑毫无关系。死等更麻烦。当一个代理需要等待另一个代理的结果才能继续时如果被等待的代理因为某种原因卡住了整个任务链就冻结了。我见过最夸张的例子是一个代理因为网络请求超时设置成了 60 秒而调度器给它分配的是 30 秒的等待预算结果系统每轮任务都要无谓地多等 30 秒整体效率下降了一半以上。后来我梳理了一套协调机制分成几个层面4.1 共享资源读写锁所有共享资源的访问必须走统一接口接口内部实现读写锁。这个接口很轻量但它能堵住绝大多数互掐问题。import threading import functools class SharedResourceManager: def __init__(self): self._locks {} self._lock threading.Lock() def _get_key_lock(self, key): with self._lock: if key not in self._locks: self._locks[key] threading.RLock() return self._locks[key] def read(self, key): lock self._get_key_lock(key) with lock: return self._read_impl(key) def write(self, key, value): lock self._get_key_lock(key) with lock: self._write_impl(key, value)4.2 引入超时预算每一个代理任务必须有明确的超时预算。不只是执行超时还包括等待依赖结果的超时。执行超时和等待超时要分开设置因为前者通常是计算问题后者通常是协调问题分开设置才能更精确地定位瓶颈。超时类型建议设置失败处理执行超时根据任务复杂度预估 30% 余量终止任务并告警等待超时依赖方预算的 50%使用缓存结果或降级策略整体链路超时所有环节上限之和触发全局补偿逻辑4.3 避免共享资源的竞态条件这是最治本的方法。很多时候两个代理会访问同一个资源并不是因为它们必须访问而是因为架构设计时图省事让它们共享了同一个数据源。如果能把数据源按照代理职责拆分开每个代理只访问自己的那份数据从根源上就不存在竞态问题了。这种做法叫数据分片在多代理系统里同样适用。我之前设计过一个系统用一个代理写数据另一个代理读数据。如果让它们直接共享同一个变量就必须加锁。但如果我把写入的数据落盘让读取代理去读文件而不是读内存两者之间就通过文件系统隔离不再有内存层面的竞争。代价是多了一点磁盘 IO但换来的是逻辑上的彻底解耦。5. 角色错位与越权发言System Prompt 并不是护身符角色错位是 Multi-Agent 系统里一个反直觉的坑。你以为给每个代理设置了明确的 system prompt它们就会安分守己地扮演好自己的角色事实远非如此。最典型的场景是审查代理。审查者的职责是找出报告中的错误并给出修改建议但实际运行时它经常越权——直接开始帮忙改内容而不是指出问题。我见过一个审查代理把一段完全正确的技术描述改成了事实性错误原因仅仅是它在某个训练样本里见过类似的改写模式。这正是角色模糊带来的风险模型在找错和改写之间选择了后者因为它觉得输出对用户更友好。为什么会这样因为 system prompt 的角色设定本质上只是一种弱约束。模型的核心训练目标是生成合理的、连贯的文本而不是严格执行开发者的指令。当严格执行指令和生成更完善的内容产生冲突时模型往往会偏向后者因为它觉得后者才是用户真正想要的——即使你明确说了不要这样。那怎么办我的思路是把约束从提示词层下沉到代码层。5.1 输出格式强制审查代理的输出必须是一个错误清单每一项包含错误位置、错误类型、严重级别、修改建议。它不输出修改后的完整文本——那是编辑代理的职责。{ review_items: [ { location: 第 3 段第 2 句, error_type: 事实性错误, severity: high, suggestion: 确认数据来源后修正 } ], overall_assessment: ..., recommendation: pass / revise / reject }如果审查代理试图输出完整文本解析层就会直接失败。模型当然可以在 JSON 的某个字段里夹带完整文本但至少格式约束能拦住大部分情况。5.2 语义层面的角色校验格式约束能防住形式上越权但挡不住内容上越权。比如审查代理在 suggestion 字段里写了整段替换文本——格式合法但行为越权。所以我在实践中加了语义校验层用一个小型分类器判断审查结果里是否包含完整的修正内容。如果包含就把它拆出来单独放入待编辑队列而不是直接接受为审查结果。这个过程听起来很繁琐但它确实能显著提高系统的可预测性。多代理系统的最大风险不是某一个代理性能差而是代理之间的行为边界不可预测。每一条你亲手建立的约束都是在把不可预测性往下压。5.3 系统设计层面的期望管理最后一点比较抽象但很重要在设计多代理系统前应当明确哪些任务适合多代理哪些任务不适合。我吃过一次教训。当时为了给一个简单的信息整理任务套多代理架构我拆分了三个代理一个负责整理一个负责扩充一个负责校验。结果这三个代理在一个 500 字的输入上协作了五轮最终输出了一篇 3000 字的内容——信息密度反而变低了。有些任务用单代理就够了强行上多代理只会引入更多的通信开销和出错概率。多代理架构的核心价值是并行处理不同的视角和职责如果任务本身是线性且确定性的单代理反而是最佳方案。这个判断需要回归项目本身的需求而不是为了技术炫技而使用 Multi-Agent。6. 可观测性缺失项目一复杂调试全靠推理最后聊一个所有 Multi-Agent 项目做到后期都会撞上的问题系统复杂到一定程度后你根本不知道它内部在做什么。如果没有好的可观测性体系排查问题的过程就会变成写代码以来最痛苦的经历。我见过最简陋的调试方式是靠 print 输出每个代理打印自己的输入输出。这在两个代理的时候还行三四个代理就有点吃力六个代理以上就彻底没法看了——大量日志交织在一起顺序错乱你分不清哪条日志是哪个代理、哪一轮任务产生的。后来我借鉴了后端开发里的可观测性三件套日志Logging、指标Metrics、链路追踪Tracing。这套方法论用在多代理系统里一样成立。6.1 日志必须带上结构化的上下文每个代理的日志必须包含 trace_id、agent_name、task_phase、timestamp 这些基础字段。不要用纯文本日志要用结构化日志比如 JSON 格式方便后续过滤和检索。{ timestamp: 2025-06-15T14:23:01.123Z, level: INFO, trace_id: a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d, agent: research_agent, phase: knowledge_retrieval, message: 查询到 5 条相关文档, context: { query: quantum error correction, top_k: 5 } }这套日志格式看着简单但排查问题时价值巨大。比如当最终报告出现错误时我按 trace_id 过滤出整条调用链的日志可以清楚地看到错误是从哪个代理、哪个阶段第一次出现的而不是在顶层代理的输出里猜来猜去。6.2 指标监控每个代理的执行效率除了日志还要记录指标。最核心的指标包括每个代理的执行耗时每个代理的重试次数每个代理的成功率上下文 token 消耗量子代理创建数量这些指标能帮你评估整个系统的健康状态。我记得有一次性能优化通过指标发现某个代理的平均耗时是其他代理的三倍。进一步看日志才发现它在每次任务时都把全部历史记录塞进上下文token 消耗巨大推理速度自然慢。定位到根因后我把这个代理的上下文切成最近 5 轮对话 当前任务摘要执行耗时直接降了 60%。6.3 可视化一张图看清整个协作过程进阶手段是做一个简单的可视化面板把每个代理的执行过程以节点图的方式展示出来。这不复杂用现成的 tracing 工具就能做到。你只需要把每个代理的开始时间、结束时间、父子关系上报上去就能生成一个时间线视图。有了这张图系统里哪个代理在哪个时间段做了什么、和其他代理之间的依赖关系是什么一目了然。我确信很多 Multi-Agent 项目后期的问题排查效率瓶颈都卡在看不清整体上。可视化不是锦上添花它是必要的生产力工具。6.4 快照回放失败后的最后一根稻草最后再分享一个我目前在用的进阶玩法——状态快照回放。每个代理在执行前系统将其输入状态做一份不可变的快照存档。任务失败后不是只看错误日志而是直接回到那个快照点用调试模式重放一次观察模型在那个确切状态下为什么会走错分支。这个方法在做一次失败型 bug难以稳定复现的问题时特别有用。你会看到某一轮代理因为某个上下文细节产生了误解进而做出错误决策但下次重跑时同一个模型给出的结果又可能是对的——这种非确定性 bug 如果没有快照几乎无从排查。用快照把当时的输入固定下来后你就可以反复实验不同的 prompt 和参数找到稳定可靠的修正方案。7. 写在最后把 Multi-Agent 当分布式系统养而不是当魔法使我踩过很多坑之后的一个核心体会是Multi-Agent 系统的复杂度本质上是分布式的复杂度而不是 AI 的复杂度。大多数问题的根源都不是模型不够聪明而是架构设计不合理、通信机制不健全、可观测性缺失。模型的随机性只是让这些架构问题变得更加隐蔽和难以定位而已。如果你正打算从零开始搭建一个 Multi-Agent 项目我的建议是先用最简单的单代理方案跑通主链路再逐步拆分成多代理。每拆分一个环节都要想清楚三个问题这个环节拆出去之后原来由谁承担的责任是否完整转移了拆分后的两个代理之间通信协议长什么样如果有一个代理挂了整个任务链怎么降级这三个问题都能回答清楚你的多代理架构大概率能站住。回答不清楚的话加再多代理也只是给未来的自己埋雷。少就是多简单就是稳定。这句话放在 Multi-Agent 的开发里是我近年最认同的结论。