
1. 从单兵作战到蜂群协同为什么第19课要讲架构复用走到Agent开发的第19课基本上已经过了“能跑通一个Demo”的阶段了。前面十几课大概率你已经把单Agent的循环、工具调用、记忆管理、提示词编排这些东西摸了个遍能做出一个能查天气、能读文件、能调API的小助手。但真到了要处理复杂任务的时候你会发现单Agent的天花板来得特别快——一个Agent既要规划、又要执行、还要校验上下文越堆越长稍微复杂点的任务就开始胡言乱语或者干脆卡在某个步骤上反复横跳。这时候行业里给出的主流答案就是多Agent协同也就是大家常说的“Agent蜂群”。但蜂群这个词容易让人产生误解以为Agent越多越好、越热闹越强。实际做下来你会发现真正难的不是“怎么让多个Agent跑起来”而是怎么让它们不互相打架、怎么复用同一套代码骨架、怎么在并发下稳住不崩。这一课要解决的核心问题就是高级协同与架构复用——把Agent从“一个脚本”升级成“一套可复用的工程架构”。关键词里出现的Worktree、多工具协作、Codex、ThreadPoolExecutor其实正好对应了这条链路上的四个关键环节代码隔离、工具编排、模型接入、并发控制。我会按这个顺序把每个环节背后的“为什么”和“怎么做”拆开讲顺带把踩过的坑一并交代。适合已经写过至少一个能跑的Agent、现在想把它工程化的朋友如果你还在纠结Agent是什么建议先把前面的基础循环吃透再来看这篇。2. Agent蜂群不是堆数量协同架构的三种真实形态2.1 先搞清楚“蜂群”到底在解决什么问题很多人第一次听到Agent蜂群脑子里浮现的是一群Agent围着一个任务七嘴八舌。但真实的工程实践里蜂群架构解决的是任务分解与并行执行的问题而不是“人多力量大”。一个复杂任务比如“分析这份财报并生成一份带图表的摘要”单Agent要顺序做完读文件、抽取数据、算指标、画图、写文案。每一步都占用同一个上下文窗口到后面模型已经被前面的内容淹没了。蜂群的做法是把任务拆成子任务分给不同的Agent每个Agent只关心自己那一小块上下文干净输出质量自然高。但这里有个反直觉的结论Agent数量不是越多越好超过一定阈值后协调成本会指数级上升。我实测下来一个任务拆成3到5个Agent是比较舒服的区间再多就需要引入更严格的编排层否则消息传递的复杂度会吃掉并行带来的收益。2.2 三种协同形态流水线、主管制、去中心化落到具体架构上常见的协同形态有三种各有各的适用场景。流水线式Pipeline最简单Agent A的输出直接喂给Agent BB再喂给C。适合步骤明确、依赖线性的任务比如“抓取→清洗→分析→报告”。优点是可控、好调试缺点是并行度低一个环节卡住全链路都停。主管制Supervisor/Orchestrator是现在最主流的做法。有一个主管Agent负责规划和分派下面挂若干执行Agent。主管不干具体活只做任务拆解、结果汇总和冲突仲裁。这种结构的好处是职责清晰主管可以动态决定要不要加派Agent、要不要重试某个子任务。关键词里的“agent框架与编排”说的就是这个层面的东西。去中心化Peer-to-Peer最少见Agent之间平等通信没有中心节点。理论上最灵活实际上最难调试消息风暴和死锁是家常便饭。除非你有非常明确的理由否则不建议一上来就搞去中心化。协同形态适用场景并行度调试难度推荐指数流水线式线性依赖任务低低简单任务首选主管制复杂可分解任务高中生产环境主流去中心化探索性/对等任务高高谨慎使用2.3 主管制蜂群的落地骨架主管制的核心是消息协议和状态管理。我一般会定义一个统一的任务对象包含任务ID、描述、输入、期望输出格式、依赖关系这几个字段。主管Agent拿到总任务后产出一个任务列表每个任务带上依赖关系然后交给调度器去执行。这里有个容易忽略的点主管Agent本身也会成为瓶颈。如果所有子任务的结果都要回传给主管做汇总主管的上下文很快就会被撑爆。解决办法是让子任务输出结构化结果比如JSON主管只读关键字段原始大文本存到外部存储里需要时再按引用取。这个思路和数据库的“存指针不存大对象”是一个道理。3. Worktree让多个Agent各占一间房而不是挤一张床3.1 git worktree和git branch的本质区别关键词里有个热搜词是“git worktree与git branch区别”这个问题问得特别好因为很多人第一次接触worktree就是被Agent协同带进来的。简单说branch是同一个工作目录下的不同分支worktree是同一个仓库下的不同工作目录。你平时用branch切换分支的时候工作目录里的文件会被替换掉。这意味着同一时刻你只能在一个分支上干活。而worktree允许你把不同的分支同时检出到不同的目录里每个目录都是独立的工作区互不干扰。对Agent协同来说这一点至关重要——如果两个Agent同时改同一个工作目录一个在写文件另一个在切分支那画面简直不敢想。# 创建一个新的worktree检出到指定目录 git worktree add ../agent-task-001 feature/task-001 # 查看当前所有worktree git worktree list # 任务完成后清理 git worktree remove ../agent-task-0013.2 为什么Agent协同离不开Worktree假设你有一个主管Agent分派了三个子任务改前端、改后端、写测试。如果三个执行Agent都在同一个工作目录里干活它们会互相覆盖对方的改动git状态也会乱成一锅粥。用worktree给每个Agent分配一个独立目录每个Agent在自己的目录里自由折腾最后再通过合并分支来汇总结果。这套机制带来的好处不只是隔离还有可回滚。某个Agent把代码改崩了直接删掉对应的worktree就行完全不影响其他Agent的工作。我在实际项目里会把worktree的创建和销毁封装成工具函数Agent需要独立工作空间时自动申请任务结束自动回收避免目录越堆越多。注意worktree虽然好用但同一个分支不能被两个worktree同时检出。如果你的Agent需要并行修改同一个分支得先给它们各自开新分支最后再合并。3.3 Worktree在Agent沙盒里的实操细节把worktree和Agent沙盒结合起来是这一课比较硬核的部分。所谓沙盒就是给Agent一个受限的执行环境让它能跑命令、改文件但不会污染主仓库。worktree天然适合做这件事因为它本身就是仓库的一个“分身”。实操上我会这样做主管Agent接到任务后先为每个子任务创建一个worktree目录名带上任务ID。执行Agent的所有文件操作都限制在自己的worktree目录内。任务完成后主管Agent检查diff决定是合并还是丢弃。这套流程跑下来最大的感受是心理负担小了很多——以前总担心Agent手滑删错文件现在最坏情况也就是删掉一个worktree主分支纹丝不动。有个细节值得提醒worktree的路径最好放在主仓库外面比如../agent-workspaces/避免被主仓库的gitignore规则误伤也方便统一清理。另外如果Agent要跑依赖安装每个worktree都会有一份独立的node_modules或venv磁盘占用会上去记得定期清理废弃的worktree。4. 多工具协作Agent的手脚怎么接才不打架4.1 工具注册与发现机制Agent要干活离不开工具。单Agent时代工具就是几个函数硬编码在代码里。到了多Agent协同工具管理就变成了一个正经的工程问题谁有哪些工具、工具怎么注册、Agent怎么发现自己能用的工具。我的做法是建一个工具注册中心每个工具带上元数据名称、描述、参数schema、所属权限组。Agent启动时根据自己的角色从注册中心拉取可用工具列表。主管Agent通常只有“分派任务”“查询状态”这类管理工具执行Agent才有“读写文件”“执行命令”“调用API”这些实操工具。这样做的目的是最小权限原则——一个只负责写文案的Agent不应该有删除文件的权限。关键词里的“多工具协作”还有一层意思不同Agent可能用不同的工具集但它们需要协同完成一个任务。比如Agent A用爬虫工具抓数据Agent B用分析工具处理数据两者之间通过标准化的数据格式传递。这里的关键是约定好中间数据的schema否则A输出的格式B读不懂协作就断了。4.2 工具调用的冲突与串行化多Agent并发调用工具时冲突是必然会发生的。最典型的是文件写入冲突两个Agent同时往同一个文件里写内容后写的覆盖先写的。解决办法有两种一种是加锁一种是隔离。加锁适合共享资源比如一个全局的日志文件多个Agent都要写。可以用文件锁或者数据库的行锁来串行化写入。隔离适合可分割的资源比如前面说的worktree每个Agent写自己的文件最后合并。我一般优先用隔离因为锁会带来等待降低并发效率而且死锁风险高。还有一种冲突是外部API的速率限制。多个Agent同时调用同一个第三方接口很容易触发限流。这时候需要一个统一的速率协调器所有Agent的API调用都经过它由它来控制发送节奏。这个协调器可以用令牌桶算法实现简单有效。4.3 工具执行结果的标准化工具执行完返回什么这个看似小的问题在多Agent场景下会放大成大问题。如果每个工具返回的格式都不一样Agent处理起来就得写一堆适配代码。我的经验是强制所有工具返回统一结构{ success: True, data: {...}, # 实际结果 error: None, # 失败时的错误信息 meta: { # 元信息 tool: read_file, duration_ms: 120, agent_id: executor-01 } }这个结构看起来啰嗦但它让Agent的后续处理逻辑变得极其简单先看success失败就走错误处理成功就从data里取东西。meta字段在调试时特别有用能快速定位是哪个Agent、哪个工具、花了多久。5. Codex接入与并发控制ThreadPoolExecutor扛住蜂群的压力5.1 Codex作为模型接入层的定位关键词里Codex出现的频率很高还有“codex接入deepseek”“codex使用教程”这些热搜。这里需要先厘清一个概念Codex在这套架构里扮演的是模型接入层的角色它负责把Agent的请求转发给底层模型并处理流式响应、重试、超时这些脏活。为什么要在Agent和模型之间加一层因为多Agent场景下模型调用会变得非常频繁如果每个Agent都自己直连模型API重试逻辑、密钥管理、限流控制会散落在各处维护起来是灾难。有了统一的接入层这些逻辑集中在一处Agent只管发请求收结果。接入层要处理的核心问题包括请求排队、失败重试、超时熔断、响应缓存。其中重试策略尤其重要模型调用失败是常态指数退避重试能显著提升成功率。但重试也要有上限否则一个坏请求会拖垮整个队列。5.2 ThreadPoolExecutor内置线程池的正确打开方式“ai agent怎么扛并发”是个高频问题答案很大程度上落在ThreadPoolExecutor上。Python的concurrent.futures.ThreadPoolExecutor是内置的线程池实现用起来简单但坑也不少。from concurrent.futures import ThreadPoolExecutor, as_completed def run_agent_task(task): # 每个任务在一个独立线程里执行 return agent.execute(task) # 创建线程池max_workers根据任务类型调整 with ThreadPoolExecutor(max_workers8) as executor: futures {executor.submit(run_agent_task, t): t for t in tasks} for future in as_completed(futures): task futures[future] try: result future.result(timeout300) handle_result(task, result) except Exception as e: handle_error(task, e)max_workers的设置是个经验活。设太小并发上不去设太大线程切换开销和内存占用都会飙升。对于IO密集型的Agent任务大部分时间在等模型响应、等API返回可以设得大一些比如CPU核数的4到8倍。对于CPU密集型任务本地跑模型推理、大量计算设成CPU核数左右就够了。注意ThreadPoolExecutor的线程不会自动回收任务队列堆积时内存会持续上涨。生产环境一定要给队列设上限配合拒绝策略避免OOM。5.3 并发下的状态一致性多线程跑Agent最怕的是共享状态被改乱。比如多个线程同时更新一个任务状态字典不加锁的话很容易出现数据竞争。Python的GIL虽然保证了字节码级别的原子性但复合操作读-改-写依然不是原子的。我的做法是尽量让每个线程只操作自己的数据线程之间通过队列通信而不是共享内存。如果确实需要共享状态用threading.Lock保护临界区或者用queue.Queue做线程安全的消息传递。另外Agent的上下文对象最好是每个任务独立创建不要复用避免状态串味。还有一个隐蔽的坑日志。多线程同时写日志文件如果不加锁日志内容会交错在一起根本没法看。用logging模块的话它本身是线程安全的但如果你自己写文件就要小心了。6. 架构复用的边界哪些能复用哪些必须重写6.1 可复用的三层结构走到第19课架构复用是绕不开的话题。我的经验是把Agent系统分成三层来看基础设施层、协同逻辑层、业务逻辑层。基础设施层包括模型接入、工具注册、worktree管理、线程池这些这部分高度可复用换个业务场景基本不用改。协同逻辑层包括任务分解、消息协议、状态管理这部分大部分可复用但需要根据任务类型做调整。业务逻辑层就是具体的提示词、工具实现、输出格式这部分基本不可复用每个场景都得重写。很多团队踩的坑是试图把业务逻辑也抽象成通用组件结果抽象层越做越厚最后改一个业务需求要动五六个文件。我的建议是基础设施层做厚业务层做薄中间留一个清晰的接口。6.2 复用带来的隐性耦合复用不是免费的它会把不同模块耦合在一起。比如你把worktree管理封装成一个通用工具所有Agent都用它那么一旦worktree的路径规则变了所有Agent都受影响。这种耦合在早期是好事能提升开发效率到了后期就可能变成负担。判断标准是变化频率。如果两个模块的变化频率差不多耦合在一起问题不大如果一个频繁变一个很稳定那就要考虑解耦。worktree的路径规则通常很稳定耦合没问题但提示词模板变化频繁就不适合硬编码到基础设施层。6.3 从项目到产品的复用路径如果你想把一套Agent架构从单个项目沉淀成可复用的产品有几个动作是必须做的。第一是配置外置把所有环境相关的东西模型地址、密钥、路径抽到配置文件里。第二是接口稳定基础设施层的对外接口一旦定下来就尽量不改内部怎么重构都行。第三是文档和示例复用的人越多文档的价值越大一个能跑的最小示例胜过十页说明。关键词里的“agent项目”“agent开发学习路线”其实都指向这个方向从写一个能跑的Agent到写一套能复用的Agent架构中间隔着的就是这些工程化的功夫。这一课讲的东西本质上都是在补这块。7. 踩坑实录蜂群协同里那些让人头大的问题7.1 Agent执行突然终止错误传播链的排查“agent execution terminated due to error”这个报错我遇到过不下十次每次原因都不一样。有一次是某个执行Agent调用了不存在的工具异常没被捕获直接冒泡到主管Agent主管Agent又没做错误处理整个流程就挂了。排查这类问题的关键是保留完整的错误传播链。每个Agent捕获异常时要把自己的ID、当前任务、调用栈都记录下来再往上抛。主管Agent收到子任务的失败结果时不要直接终止而是根据策略决定是重试、跳过还是降级。我现在的做法是给每个任务设一个最大重试次数超过就标记为失败但不影响其他任务的执行。7.2 沙盒更新与权限问题“显示更新agent沙盒”这个提示通常出现在Agent试图执行超出沙盒权限的操作时。沙盒的权限配置是个精细活配太松等于没有隔离配太紧Agent啥也干不了。我的经验是按最小必要原则逐条加权限先给最基础的读写权限跑起来发现缺什么再加什么而不是一上来就给全权限。worktree沙盒还有个特殊问题Agent在worktree里创建的文件默认属于当前用户如果后续要用其他身份处理权限会不对。这个在容器化环境里尤其明显需要提前规划好用户和组的映射。7.3 并发下的资源耗尽蜂群跑起来最爽的是看着一堆任务并行推进最慌的是某天早上发现服务器磁盘满了或者内存爆了。并发场景下的资源耗尽往往是渐进的一开始看不出来任务量上去之后就崩了。我的应对策略是给每个资源设硬上限worktree数量上限、线程池队列上限、单个Agent的内存上限、API调用频率上限。超过上限就排队或者拒绝宁可慢一点也不要崩。监控也要跟上worktree数量、活跃线程数、队列长度这些指标要能实时看到出问题之前就能发现苗头。8. 我在这套架构上的一些个人体会把Agent从单兵作战升级到蜂群协同最大的感受是复杂度不是线性增长的而是阶梯式跳变的。单Agent的时候你只需要关心提示词和工具多Agent之后你要关心消息协议、状态管理、并发控制、资源隔离、错误恢复每一个都是独立的工程问题。Worktree和ThreadPoolExecutor这两个东西看起来一个是git技巧一个是Python内置库但它们解决的是同一类问题隔离与并发。隔离让Agent互不干扰并发让任务快速推进。把这两个基础打牢上层的协同逻辑才有发挥空间。Codex这类接入层的价值在Agent数量少的时候不明显一旦并发上来统一的重试、限流、缓存机制能省下大量重复代码。至于架构复用我的建议是先跑通再抽象不要一开始就追求通用等你有两三个场景跑下来自然就知道哪些该复用、哪些该重写了。最后分享一个小技巧调试多Agent系统时给每个Agent的日志加上不同的颜色前缀终端里一眼就能看出是哪个Agent在说话排查问题的效率能提升一大截。这个技巧不高级但真的管用。