做Agent工程的朋友应该都有这种体会:单轮问答、单步工具调用,怎么做都还算可控;任务一旦拉长到几十步,或者要把活分给几个Agent一起干,各种问题就接踵而至。这篇笔记是我在某个多Agent项目里的实战记录,专门聊长任务编排和多Agent协作。里面没有多少图纸级别的理论,全是配置过的参数、跑过的流程、踩过的坑和修过的Bug。如果你正在写自动化报告、批量数据处理、多源信息采集这类长时间运行的任务,又或者打算把一个大任务拆给多个Agent,这篇文章应该能帮你省下一两周的摸索时间。
1. 长任务为什么总在中间段崩掉
先说结论:长任务失败,绝大多数不是模型"变笨了",而是工程结构没跟上。拆开来看,我归纳出三个最主要的元凶。
1.1 上下文漂移:模型"忘事"比想象中更早
上下文窗口的增长,不等于模型能有效利用所有上下文。做长任务的时候,早期指令、早期工具结果会逐渐被后续内容淹没。模型对前文的"注意力"会随着长度和中间干扰量(大量工具返回、中间推理)明显下降。打个比方:人开会开久了,总会忽略前面讲过的细节,模型其实也一样。
我维护的某数据采集任务,第一版把所有步骤写进一个超长prompt里。前期跑得不错,到第40多步,模型开始忽略"去重"指令——拿到重复数据还照单全收。把完整日志翻出来看,那条去重指令在prompt里离触发点已经隔了上万token,模型不是不执行,是真的"没记住"。
怎么判断是上下文漂移,而不是prompt本身写错?有个很笨但很有效的办法:把任务切短,分成5段单独跑。如果每段都表现正常,那基本可以确定是长上下文导致的漂移,而不是指令本身有问题。这时候再优化prompt已经没太大意义,得从工程层面解决。
1.2 工具链路断裂:一个"小意外"拖垮整条线
真实世界里的工具返回,永远不会像文档里写那么规整。有时返回内容包了一层Markdown代码块,有时字段名大小写不一致,有时干脆响应超时。长任务里每一步都依赖上一步的精确输出,任何一个格式化异常,都会让后面的推理跟着出错。
我印象最深的一次:数据清洗步骤要求工具返回JSON数组,结果上游Agent给了一个带注释的JSON,解析器直接挂了。表面上看是解析代码不够健壮,实际上是因为任务太长,上游Agent的"输出格式规约"在上下文里被冲淡,导致它的输出偏离了协议。
对策也很直接:关键步骤增加格式校验,不合格的结果让上游重跑,而不是盲目往下传。这里的"校验"不一定要用复杂框架,一个Schema校验函数就够用。宁可让上游多跑一遍,也不能让坏数据进入下游。
1.3 编排失控:没有依赖管理的协作就是一场灾难
多Agent和长任务经常叠在一起,因为一旦任务超过一定规模,人第一反应就是拆分给多个角色。但拆完之后,执行顺序、依赖关系、中间产物归属如果不管理,很快就乱套。
我第一版多Agent写得很天真:两个Agent,一个负责采集,一个负责分析,让它们"互相配合"。结果发现两个Agent经常同时读取同一个未写完的文件,分析Agent拿到半截数据就开始推理,产出完全不可用。
根源不在模型能力,在于工程上没有任何依赖图、没有中间产物的读写权限约定。这不怪模型,怪我自己没把工程做扎实。
2. 长任务编排的具体落地做法
踩过前面的坑之后,我把整个长任务执行框架重新改了一遍。核心思路就一句话:把"超长自由发挥"变成"短步快跑+结构化状态"。
2.1 任务分解:把超长任务切成"小步快跑"
分解粒度怎么选?我的经验是控制在"单个子任务需要3-5次工具调用"的量级。太粗会让单步执行时间过长,失败重跑代价大;太细又会让调度开销和上下文切换成本高得离谱。
实际操作中,我给任务分解定了一个固定模板:
- 顶层规划Agent只做任务拆解,输出一份JSON计划,字段包括task_id、dependencies、objective、acceptance_criteria。
- 执行Agent拿到子任务目标后,自己再写两步内部规划:先确认用什么工具,再确认产出什么格式。
- 每完成一个子任务,更新一次计划状态,把结果反馈给调度层。
注意:计划环节一定要让模型输出JSON,并且加一道Schema校验。不合格直接把计划单打回重做。否则后面所有依赖关系都是建立在沙地上的。
我之前试过让模型用自然语言输出计划,比如"先采集数据,然后清洗,然后分析",看起来没问题,但程序没法稳定解析。改成JSON加校验之后,整个流程的可靠性上了一个台阶。
2.2 上下文管理:记忆压缩不是"简单截断"
第一反应是把旧对话直接截断,但这样做会丢失关键信息,比如之前设定的规则、已产出的中间文件路径、某个关键变量的取值。单纯的截断等同于"失忆"。
我的做法是三层记忆:
- 短期记忆:当前子任务的即时上下文,几轮跑完就清理。
- 工作记忆:保存"当前进度、关键变量、已产出文件清单",以结构化键值对形式存在上下文里,每次刷新。
- 长期记忆:定期让Agent把已完成部分浓缩成摘要,替换原始内容。
关键参数是压缩触发时机。我设在上下文使用率到70%时触发一次压缩;每次压缩保留当前子任务的最近两轮完整链路,其余内容转成摘要。
上下文预算我会做个硬分配(仅供参考):工具结果40%、历史摘要30%、当前指令20%、进度快照10%。每个部分设上限,超过就压缩。
为什么要保留最近两轮完整链路?因为Agent刚做完的几步是离"现在"最近的信息,细节最有用。再往前的内容,模型往往只需要知道"发生过什么",而不需要"原话怎么说"。
2.3 状态机与检查点:让任务可暂停、可恢复
长任务跑一半,下游API限流了,或者模型报错,不能每次都从头重跑。于是我把任务执行改成一个"检查点驱动"的状态机。
具体做法:
- 每个子任务完成后,把状态写入检查点文件(JSON),字段包括cursor(下一个任务ID)、产物列表、关键变量、最近快照。
- 启动时先读检查点,有记录就从记录位置续跑,没有就从计划第一条开始。
- 每个子任务写产物时带一个区分度高的文件命名规则,比如task_001_output.json,避免不同任务互相覆盖。
检查点写入要保证原子性:先写临时文件,再改名替换。否则写到一半进程崩了,留下的检查点文件是损坏的,整个恢复机制就白搭了。
实测收益非常明显:以前一个40步任务失败率大约两三成,重跑整个任务很痛苦。改成检查点之后,重跑时一般只需要续跑最后几步,整体稳定性和使用感受提升巨大。
3. 多Agent协作模式与通信机制设计
长任务解决了"稳定性",多Agent解决的是"分工"和"并行"。但这部分坑更深。如果把长任务比作一个人的马拉松,那么多Agent就是一个团队的接力赛——更考验组织方式。
3.1 三种协作模式怎么选
管道式(Pipeline):按固定步骤依次执行,前一个Agent的输出是后一个Agent的输入。适合处理流程固定的业务,比如采集-清洗-分析-生成报告。优点是依赖关系简单、调试容易,缺点是不够灵活,任务结构一变就要改代码。
主从式(Orchestrator-Workers):一个调度Agent负责任务拆分、分发、回收结果,多个Worker Agent专注执行。适合任务结构动态变化、需要并行处理的场景,比如多源信息采集。缺点很明显:调度Agent是单点,任务多的时候调度本身可能成为瓶颈。
联邦式(Peer-to-Peer):Agent之间通过消息总线自由协作,没有中心节点。灵活,但调试是地狱,消息风暴极其难控制。我建议除非团队排障能力很强,否则第一版别碰。
我的建议是第一版用主从式最稳。等业务逻辑很成熟、团队对排障也有经验了,再考虑往联邦式演进。这就像项目开发,先让代码可维护,再谈架构先进。
3.2 Agent之间怎么"说话"才不吵架
犯过的错:让Agent之间用自然语言传消息。比如上游Agent说"数据已经处理好了,你自己去看吧"。下游Agent面对这种模糊消息,经常不知道看哪个目录、哪个文件,最后只能靠猜。
改法:所有Agent间通信走结构化消息。一个消息体就是一份固定格式的JSON,示例如下:
{ "sender": "planner", "receiver": "worker_01", "task_id": "T-003", "message_type": "task_assign", "payload_ref": "/workspace/T-003/spec.json", "status": "ok", "ttl": 600 }每个Agent还要能读到"共享黑板"——本质就是文件系统上的一个工作区目录。每个Agent负责的部分写入自己的子目录,其他Agent按task_id读取,不允许跨目录写。这个约束写在系统指令里,运行时有权限校验兜底。
TTL字段特别重要:消息设置了过期时间,超时没被确认就当失败处理,调度端可以重派给别的Agent,不会一直傻等。
3.3 角色边界与资源竞争怎么处理
角色冲突最大来源是"写权限"。我规定:每个中间产物只能有一个属主Agent,其他Agent只能读。需要改动必须先提申请,由属主Agent来改。这样从设计上避免两个Agent同时改一个文件。
经验:在提示词里给每个Agent定义一句"职责边界",比如"你只负责检查采集结果的完整性,不负责修复数据"。这句话看似简单,实际能少踩很多坑——模型确实会认真对待角色职责,只要你说得足够明确。
并行调用同一API时的限流,我用全局信号量。比如同时最多3个工具调用,超出的排队等待。这样下游API不会被打爆,代价是局部延迟增加,但换来的整体稳定性是值得的。
4. 踩坑实录与排查技巧速查
多Agent系统跑久了,总会遇到一些"模型行为奇特"的现象。这里整理一份速查表,都是实际项目中高频出现的坑。
4.1 高频坑位与对应修复办法
| 坑位 | 现象 | 排查方式 | 修复办法 |
|---|---|---|---|
| 死循环 | Agent反复调用同一个工具不退出 | 看日志里同一action连续出现多次 | 限制单Agent最大工具调用次数,我设20次,达到上限强制终止并上报 |
| 重复产出 | 多个Agent对同一产物反复生成 | 检查产物文件的创建时间和Agent ID | 任务队列维护产物ID,写入前检查是否已有未完成版本 |
| 上下文污染 | 早期错误信息扩散到后续步骤,后面Agent拿着错误前提继续推理 | 对比每个Agent的输入上下文 | 每轮开始前用规则过滤与当前任务无关的报错字段 |
| 消息丢失 | 某个Agent挂掉或超时,消息没人认领 | trace链路中找不到确认记录 | 加TTL和重派机制,超时自动改派 |
死循环这个坑特别值得展开。有一次一个Agent在"调用搜索工具-发现结果不够-再次调用搜索工具"之间循环了14次,token消耗翻了三倍。加了20次调用上限之后,这种问题彻底根治——就算模型想钻牛角尖,工程上有硬性止损。
4.2 可观测性:日志和链路追踪一定要提前做
多Agent系统没有日志,等于闭眼开船。我的做法是:每个Agent的每个动作都输出一条结构化日志,字段包括agent_id、action、input_ref、output_ref、status、latency_ms、parent_trace_id。
每个任务分配一个trace_id,所有子任务和工具调用都挂上去。事故发生后,按trace_id拉全链路日志,能快速定位是哪一步出的问题。这个习惯救了我太多次。
可视化不一定需要多高端。我把每个Agent的实时状态和等待队列渲染到一个简单的Dashboard,一眼就能看到"谁卡住了、谁在等谁"。这种直观反馈对调试的帮助,比任何理论框架都管用。
4.3 兜底策略:超时、限流和人工介入
- 超时重试:单个子任务设超时时间,超时自动重试;连续三次失败进入人工队列。
- 全局限流:控制同时运行的Agent数量,防止下游API、数据库被打爆。初期并发数设3-5个就够,不要贪多。
- 暂停-人工-恢复通道:长任务系统一定会有需要人工决策的场景。设计一条"暂停整个流水线、让人处理、再恢复"的通道,比事后救火便宜得多。有些问题模型就是搞不定,硬逼它只会浪费更多token。
5. 不同场景下的配置速查与调优心得
最后把这套东西落到实际业务里,给一份可以直接参考的选型和参数速查。
5.1 什么时候值得上多Agent
- 任务步骤8步以内、链路固定:单Agent串联工具最合适,上多Agent只会增加延迟和故障点。
- 任务步骤30步以上、需要不同领域知识、需要并行采集:主从式多Agent加检查点加黑板,收益非常明显。
- 介于两者之间:先单Agent跑通,再根据瓶颈决定要不要拆分。不要为了"架构先进"而过度设计。
5.2 关键参数参考表
| 参数 | 推荐范围 | 说明 |
|---|---|---|
| 子任务粒度 | 3-5次工具调用 | 太小调度开销大,太大失败代价高 |
| 单Agent最大工具调用次数 | 20次 | 防止死循环烧token |
| 上下文压缩阈值 | 70% | 达到后触发记忆压缩 |
| 消息TTL | 5-10分钟 | 超时未确认自动重派 |
| 全局并发Agent数 | 3-5(初期) | 后续按下游API承受能力调整 |
| 检查点写入频率 | 每子任务一次 | 保证断点续跑能力 |
这些参数不是万能公式。不同业务、不同模型、不同下游服务的承受能力都不一样。但初始值放到这个区间,大概率是稳健的,后面再根据实际表现微调。
5.3 模型选型与成本平衡
简单子任务,比如JSON提取、字段清洗,用轻量模型完全够用;复杂规划、意图理解、跨领域推理,才需要上强模型。
这么搭配,成本能降下来,延迟也低。但有一个前提:强模型和弱模型之间必须保证完备的通信协议。否则弱模型产出格式不稳,下游解析频繁失败,重试成本反而把省下的钱全赔回去。
我个人实际用下来最强烈的感受是:长任务能不能稳定跑完,往往不取决于模型聪明不聪明,而取决于工程细节。上下文怎么管理、检查点怎么设计、消息怎么传、日志怎么留,这些事没有多少炫技空间,但每一步都在决定系统到底可不可用。
最后再分享一个小技巧:不管你的目标多宏伟,先让系统在一条单一任务上稳定跑通50次,再考虑加并行、加多Agent。基础稳定性没解决之前,铺得越大多翻车越狠。