(十三)多Agent协同 --- 1+1>2
系列第13篇
作者:挖AI金矿
截至上一篇文章,我们一直在讨论单个Hermes Agent的能力。单个Agent已经很强了------它可以访问工具、调用Skill、管理记忆、切换模型。但在真实的大型项目中,单个Agent往往力不从心。
想想看:你要同时做三件事------调研数据库选型、写初版API代码、设计部署方案。如果只有一个Agent,它会顺序处理,要么你干等着,要么上下文被不同任务的内容撑爆。
多Agent协同就是解决方案。单个Agent是"一个人的团队",多Agent是你指挥的"一个团队"。
1.什么时候需要多Agent?大任务拆解
不是所有场景都需要多Agent。单Agent能搞定的事情,强行拆成多Agent反而增加复杂度。那什么时候该用?
1.1适合多Agent的场景
场景1:高度平行的子任务
比如你要同时评估Three.js、WebGL和Canvas2D三个前端图形方案。用单Agent,你得一个个问,效率很低。用多Agent,三个子Agent同时去调研,你在主会话中汇总结果。
场景2:角色分工不同的任务
比如开发一个API接口:A Agent负责写handler和业务逻辑,B Agent负责写单元测试,C Agent负责写集成测试。每个Agent只需要关注自己的角色。
场景3:上下文敏感的复杂流程
比如你修复一个bug:先让A Agent分析问题根因,B Agent基于分析结果写修复代码,C Agent验证修复是否正确。每个Agent收到的上下文是精简的------A只需看错误日志,B只需看A的输出和代码库,C只需看B的变更。
场景4:需要独立工具权限的任务
假设你需要同时查询生产环境日志和开发环境配置。单Agent切换上下文很麻烦。两个子Agent各持有不同的工具集------一个只读生产数据库,一个只读开发配置------互不干扰。
1.2不适合多Agent的场景
不需要拆的场景:简单问答、格式化输出、快速代码生成------Agent一轮对话就能搞定,拆成多Agent纯粹是过度设计。
不适合拆的场景:高度依赖前后文连续性的任务------比如逐步调试一个复杂bug,每一步都依赖上一步的精确结果。这种情况下,单Agent的完整上下文窗口比多Agent的碎片化上下文更有用。
1.3决策原则
如果两个任务之间需要频繁同步中间结果,用单Agent。
如果两个任务可以独立运行到底,只在最后合并,用多Agent。
在实际工作中,我通常这样判断:如果需要创建的中间文件超过3个,或者需要访问的工具权限差异很大,或者需要并行获取多个信息源,就考虑用多Agent。
2.delegate_task:把任务分配给子Agent
Hermes的多Agent协作核心命令是 delegate_task。你可以把它理解成"委托"------把一项子任务派发给另一个Agent实例去独立完成。
2.1基本用法
bash
delegate_task --name "调研-PostgreSQL-vs-TiDB" \
--instruction "对比PostgreSQL 16和TiDB 8.1在以下维度的差异:\n1. 一致性模型\n2. 水平扩展能力\n3. 事务支持\n4. 运维复杂度\n5. 适用场景\n\n输出一个markdown格式的对比表。" \
--tools database_search,web_search
参数说明:
- --name:子任务名称(用于后续引用)
- --instruction:给子Agent的完整指令,越详细越好
- --tools:子Agent可以使用的工具集(逗号分隔)
- --model:(可选)指定子Agent使用的模型,不指定则用默认模型
- --context:(可选)传给子Agent的附加上下文,比如相关代码片段
2.2子Agent的执行过程
当你执行 delegate_task 后:
- Hermes创建一个新的Agent实例(子Agent)
- 子Agent收到你的指令和工具集
- 子Agent独立运行,使用自己的上下文窗口
- 子Agent完成任务后,结果返回给主Agent
- 主Agent将结果展示给你,或继续处理
整个过程是异步的------你不需要等待。子Agent在后台运行,你可以继续和主Agent对话。
2.3查看子任务状态
bash
# 列出所有活跃的子任务
delegate_task list
# 输出
# 任务ID │ 名称 │ 状态 │ 进度 │ 耗时
# t-001 │ 调研-PostgreSQL │ 运行中 │ 65% │ 2m30s
# t-002 │ 写API代码 │ 已完成 │ 100% │ 1m15s
# t-003 │ 写测试 │ 待分配 │ 0% │ -
# 查看某个子任务的详细进展
delegate_task status t-001
# 获取已完成任务的结果
delegate_task result t-002
2.4在一个任务中引用另一个任务的结果
这是多Agent协同的关键能力------子任务之间可以传递结果:
bash
# 子任务A:分析日志
delegate_task --name "分析错误日志" \
--instruction "分析以下日志,找出错误根因" \
--context "$(cat /var/log/app/error.log)"
# 等待A完成后
delegate_task --name "写修复代码" \
--instruction "基于错误分析结果,写修复代码" \
--context "$(delegate_task result t-001)"
子任务B会收到子任务A的输出作为上下文,实现链式处理。
3.子Agent的上下文隔离 + 独立工具集
多Agent模式最容易被忽视但最重要的特性就是上下文隔离。
3.1为什么需要上下文隔离
假设你的主Agent已经聊了很多轮,上下文窗口里塞满了各种信息:项目背景、技术栈讨论、之前的代码修改记录。
如果在这个上下文中分配"写单元测试"这个子任务,子Agent会继承主Agent的全部上下文吗?
答案:不会。 这也是正确的设计。
子Agent的上下文是干净的 ------它只看到你通过 --instruction 和 --context 参数显式传给它的信息。这带来几个好处:
- 减少干扰:子Agent不会因为主会话中的无关讨论而"分心"
- 节省tokens:子Agent的上下文只包含与其任务相关的内容
- 信息隔离:敏感信息(如生产数据库密码)不会传递到不需要它的子Agent
3.2独立工具集
每个子Agent可以配置独立的工具集:
bash
# 子Agent A:只能读数据库
delegate_task --name "查询用户数据" \
--instruction "查询最近一周的活跃用户数" \
--tools database_read
# 子Agent B:只能写代码
delegate_task --name "生成数据报表" \
--instruction "基于查询结果生成HTML报表" \
--tools file_write
# 子Agent C:只能访问网络
delegate_task --name "搜索最佳实践" \
--instruction "搜索Go并发编程的最佳实践" \
--tools web_search
通过限制工具集,你可以精确控制每个子Agent的能力边界,避免意外操作(比如一个负责写代码的Agent不小心删了生产数据库)。
3.3工具集配置
可用的工具类型包括:
3.4上下文空间管理
子Agent也有自己的上下文限制。你可以给它分配更多或更少的上下文容量:
bash
# 给子Agent 32K的上下文(适用于需要处理大量日志的分析任务)
delegate_task --name "分析大量日志" \
--instruction "...
--context_limit 32000
# 给子Agent 8K的上下文(适用于简单的查询任务)
delegate_task --name "简单查询" \
--instruction "...
--context_limit 8000
合理分配上下文容量,让系统资源用在刀刃上。
4.实操:让子Agent写代码,自己继续需求分析
这是一个实际生产中我会反复使用的场景------让你真正体会"多Agent"的效率。
4.1场景说明
你在开发一个用户认证服务,需要同时做两件事:
- 继续完善需求分析(你亲自和主Agent讨论)
- 写注册接口的代码(交给子Agent)
两步可以完全并行。
4.2第一步:分配代码任务
bash
delegate_task --name "写注册接口" \
--instruction "实现用户注册API接口,包含以下功能:
1. POST /api/v1/register 接收 email, password, nickname
2. 密码使用bcrypt加密存储
3. 验证email格式和密码强度(最少8位,含大小写字母和数字)
4. 返回201状态码和用户ID
5. 如果email已存在,返回409冲突
技术要求:
- Go + Gin框架
- GORM操作PostgreSQL
- 使用testify写单元测试
- 错误使用中文返回
输出文件:
- handlers/register.go
- services/register_service.go
- models/user.go
- handlers/register_test.go" \
--tools code_write,code_read,git_operations \
--model anthropic/claude-3-5-sonnet-20241022
4.3第二步:继续和主Agent讨论
在子Agent写代码的同时,你可以继续和主Agent讨论需求:
bash
你: "我们还需要考虑密码重置流程,你觉得用邮箱验证码还是安全问题好?"
主Agent: "建议用邮箱验证码方案,原因如下:
1. 安全性更高(安全问题容易被暴力破解)
2. 用户体验更好(不用记住额外的答案)
3. 和注册流程的邮箱验证逻辑可以复用
流程设计:
1. 用户请求重置 -> 发送6位验证码到邮箱
2. 用户提交验证码 -> 验证通过后进入重置页面
3. 用户提交新密码 -> 更新数据库
你觉得这个方案怎么样?"
4.4第三步:获取子Agent结果
当子Agent完成后:
bash
delegate_task status t-001
# 状态: 已完成
delegate_task result t-001
子Agent返回的结果包括它生成的所有代码文件内容。你可以审查后合并到项目:
bash
# 或者让主Agent帮你审查子Agent的输出
"帮我看一下子Agent写的注册接口代码,重点检查安全性"
4.5第四步:继续分配更多任务
基于讨论结果和子Agent的代码,可以继续分配新任务:
bash
delegate_task --name "写密码重置接口" \
--instruction "基于以下需求实现密码重置API:
[粘贴刚才讨论的邮箱验证码方案]
数据库需要新增表:
- password_reset_tokens (email, token, expires_at)
输出文件:
- handlers/reset_password.go
- services/reset_service.go
- models/reset_token.go
" \
--context "$(delegate_task result t-001)" \
--tools code_write,code_read
注意 --context 参数传入了第一个子Agent的输出,这样第二个子Agent可以基于已有代码继续开发,保持代码风格和结构一致。
4.6效果对比
单Agent模式(串行):
bash
1. 讨论需求(10轮对话)→ 15分钟
2. 写注册接口(Agent写代码)→ 5分钟(这段时间你发呆)
3. 讨论密码重置方案(5轮对话)→ 8分钟
4. 写密码重置接口(Agent写代码)→ 5分钟
总计: 33分钟(你实际干活25分钟,等Agent 8分钟)
多Agent模式(并行):
bash
1. 子Agent写注册接口(后台运行)
同时:你讨论密码重置方案 → 10分钟
2. 密码重置方案确定后,分配第二个子Agent写接口
同时:你审查第一个子Agent的代码 → 5分钟
3. 所有子Agent完成 → 你汇总结果
总计: 15分钟(你全程在干活,没有等待时间)
效率提升50%以上,而且你的注意力没有被中断。
5.子Agent能力边界(什么不能做)
子Agent虽然强大,但也有明确的边界。了解这些边界可以帮助你合理分配任务。
5.1子Agent不能做的事
1. 不能创建新的子Agent
子Agent不能递归委派任务。这是一个安全设计------防止无限递归和资源耗尽。
bash
# ❌ 子Agent的内部不能执行 delegate_task
# 如果你需要多层级委派,只能从主Agent层面发起
2. 不能修改主Agent的配置
子Agent不能更改主Agent的记忆、Skills、配置等。它对主Agent工作区是只读的。
3. 不能访问主会话的未共享上下文
子Agent只能看到你通过参数显式传递的信息。主会话中之前讨论的内容,如果没有传给子Agent,它不知道。
4. 不能执行交互式操作
子Agent不能要求用户输入、不能打开交互式编辑器。它只能基于给定的指令和工具执行任务。
5.2子Agent的局限性
除了明确的"不能做",还有一些实际局限性:
上下文上限:子Agent默认使用较小的上下文窗口(通常是16K-32K)。如果任务需要分析100万行日志,你可能需要拆分或使用支持长上下文的模型。
工具范围受限:子Agent只能使用你明确授予的工具。如果某个任务中途需要额外的工具,你必须重新分配任务。
结果大小限制:子Agent的输出结果默认限制在几千行内。如果要求生成一个完整的Rails项目(上百个文件),结果可能被截断。这种情况下,应该让子Agent直接写入文件系统,而不是返回全部文本。
5.3安全边界
yaml
# 在 config.yaml 中配置子Agent的安全策略
subagent:
security:
# 允许子Agent访问的路径(白名单)
allowed_paths:
- /home/user/projects/
# 禁止子Agent执行的命令(黑名单)
forbidden_commands:
- "rm -rf"
- "DROP TABLE"
# 禁止子Agent访问的网络
forbidden_hosts:
- "10.0.0.*" # 内网
6.并行 vs 串行的选择
这是多Agent协同中最需要权衡的设计决策。
6.1并行模式
多个子Agent同时运行,互不依赖。
bash
# 三个调研任务并行执行
delegate_task --name "调研Three.js" --instruction "..." &
delegate_task --name "调研WebGL" --instruction "..." &
delegate_task --name "调研Canvas2D" --instruction "..." &
# 等待所有完成
delegate_task wait-all
优点:总耗时 = 最慢子任务的耗时,吞吐量最大化
缺点:如果子任务需要共享资源(比如都写同一个文件),可能产生冲突
适用场景:
- 独立调研任务
- 不相关的代码生成
- 数据采集
6.2串行模式
子任务顺序执行,后一个依赖前一个的输出。
bash
# 第一步:分析问题
delegate_task --name "分析Bug" --instruction "分析堆栈..."
# 等第一步完成
delegate_task --name "修复Bug" \
--context "$(delegate_task result t-001)" \
--instruction "基于分析结果写修复代码..."
# 等第二步完成
delegate_task --name "验证修复" \
--context "$(delegate_task result t-002)" \
--instruction "验证修复是否正确..."
优点:保证数据一致性,后续任务基于完整的前置结果
缺点:总耗时 = 所有子任务耗时之和
适用场景:
- CI/CD流水线
- 逐层代码审查
- 依赖链式输出
6.3混合模式
大多数实际场景采用的是混合模式------某些子任务并行,某些串行。
bash
# 阶段1:并行调研
delegate_task --name "调研数据库" --instruction "..." &
delegate_task --name "调研缓存方案" --instruction "..." &
delegate_task wait-all
# 阶段2:基于调研结果写设计方案
delegate_task --name "写设计文档" \
--context "$(delegate_task result t-001)\n$(delegate_task result t-002)" \
--instruction "基于数据库和缓存方案调研结果,写设计文档..."
# 阶段3:基于设计文档并行实现
delegate_task --name "实现数据库层" --context "$(delegate_task result t-003)" --instruction "..." &
delegate_task --name "实现缓存层" --context "$(delegate_task result t-003)" --instruction "..." &
delegate_task wait-all
这种模式既发挥了并行的效率优势,又保证了阶段间的依赖关系。
7.实战:用多Agent同时调研3个技术方案
纸上谈兵这么多,我们来一个完整的实战案例。
7.1任务描述
你要为一个新的电商后端做技术选型。需要在1小时内完成三个技术模块的初步调研和推荐方案:
- 消息队列: RabbitMQ vs Kafka vs Redis Streams
- 搜索引擎: Elasticsearch vs Meilisearch vs Typesense
- 监控方案: Prometheus+Grafana vs Datadog vs New Relic
7.2第一步:分配三个并行调研任务
bash
# 任务1:消息队列调研
delegate_task --name "调研-MQ" \
--instruction "对比 RabbitMQ、Kafka 8.0、Redis Streams 以下维度:
1. 消息可靠性(持久化、ACK机制)
2. 吞吐量(每秒可处理多少消息)
3. 运维复杂度(需要几个组件、维护成本)
4. Go SDK成熟度
5. 典型场景(什么场景选哪个)
最后给出推荐:对于中等规模的电商后端(日均100万订单),推荐哪个?为什么?
输出为markdown格式。" \
--tools web_search \
--model deepseek/deepseek-chat
# 任务2:搜索引擎调研
delegate_task --name "调研-搜索" \
--instruction "对比 Elasticsearch 8.x、Meilisearch 1.6、Typesense 0.26 以下维度:
1. 搜索速度(特别是中文分词效果)
2. 索引构建速度
3. 资源消耗(内存/CPU)
4. 高可用方案
5. Go客户端
场景需求:电商商品搜索,日均500万搜索请求,需要支持拼音搜索、拼写纠错、分类过滤。
输出为markdown格式。" \
--tools web_search \
--model deepseek/deepseek-chat
# 任务3:监控方案调研
delegate_task --name "调研-监控" \
--instruction "对比 Prometheus+Grafana、Datadog、New Relic 以下维度:
1. 部署方式(自建 vs SaaS)
2. 成本估算(日均10万指标点)
3. 告警能力
4. APM支持
5. 自定义Dashboard灵活性
6. 和Go应用的集成度
输出为markdown格式。" \
--tools web_search \
--model deepseek/deepseek-chat
7.3第二步:监控进展
bash
while true; do
clear
delegate_task list
sleep 10
done
输出:
bash
t-001 | 调研-MQ | 已完成 | 100% | 1m45s
t-002 | 调研-搜索 | 运行中 | 70% | 2m10s
t-003 | 调研-监控 | 运行中 | 50% | 1m30s
7.4第三步:最早完成的任务先审核
任务1最快完成。不等其他两个,先看结果:
bash
delegate_task result t-001
假设结果中推荐了 Kafka,但你的团队对Kafka不熟悉,你可以继续和主Agent讨论:
bash
你: "任务1推荐了Kafka,但团队对Kafka不熟悉,RabbitMQ的经验更丰富"
主Agent: "基于团队的技术栈考虑,RabbitMQ也是合理的选择。
不过可以折中:先用RabbitMQ,预留Kafka迁移路径。
建议在消息处理层做抽象,未来可以无缝切换。
具体建议:
1. 使用RabbitMQ Streams插件(支持消息回溯)
2. 消息处理接口抽象化(interface定义)
3. 预留Kafka的topic设计(分区键策略)
7.5第四步:汇总所有结果
三个子任务都完成后,让主Agent汇总:
bash
"把三个调研结果汇总成一个技术选型报告,包含:
1. 每个模块的推荐方案
2. 推荐理由
3. 备选方案
4. 实施建议(优先级、风险点)
5. 团队需要提前学习的技术
输出格式为markdown,保存到 docs/tech-stack-report.md"
最终产出的一份完整报告,包含了消息队列、搜索引擎和监控方案的选型建议,而且三个子任务的调研是并行完成的------总耗时约3分钟,而不是串行的9分钟。
8.多Agent的高级模式
除了基本的委派模式,还有一些高级用法值得了解。
8.1辩论模式
让两个子Agent从不同角度分析同一个问题,然后汇总:
bash
delegate_task --name "正方-用微服务" \
--instruction "从架构灵活性、团队协作、技术多样性角度,论证为什么应该采用微服务架构" \
--model gpt-4o
delegate_task --name "反方-用单体" \
--instruction "从开发效率、运维复杂度、性能角度,论证为什么应该采用单体架构" \
--model deepseek/deepseek-chat
delegate_task wait-all
"综合正反两方的分析,给出最终建议。考虑项目实际情况:5人团队,开发周期6个月,预期1年后用户量增长10倍。"
这种模式能避免单一视角的局限性,特别是对于复杂的技术决策。
8.2审查模式
一个Agent写代码,另一个Agent审查:
bash
# Agent A:写代码
delegate_task --name "写订单模块" \
--instruction "实现订单创建和查询功能..." \
--tools code_write
delegate_task wait t-001
# Agent B:审查代码
delegate_task --name "审查订单模块" \
--context "$(delegate_task result t-001)" \
--instruction "代码审查,重点关注:
1. SQL注入风险
2. 并发安全问题(订单状态更新)
3. 错误处理是否完备
4. 是否符合Go最佳实践
给出审查报告和修改建议。" \
--tools code_read
8.3看门狗模式
主Agent负责协调,子Agent负责监控:
bash
# 一个持续运行的任务
delegate_task --name "监控日志" \
--instruction "每分钟检查一次应用日志,
如果发现ERROR级别且包含'panic'或'fatal'的日志,
立即报告给主Agent" \
--tools file_read \
--persist true
--persist true 让子Agent长期运行,持续监控。
9.注意事项与陷阱
9.11. 不要过度拆分
三个独立调研任务并行跑是好的,但把一个"写注册接口"拆成"写handler"、"写service"、"写model"三个子任务就过头了------Agent写这三部分通常在一段上下文中完成,拆分反而增加了沟通开销。
经验法则:如果子任务的指令少于100字,说明拆得太细了。
9.22. 注意子Agent的数量限制
同时运行的子Agent数量有限制(默认3-5个,取决于配置)。超过限制的任务会排队等待。
yaml
# 在config.yaml中配置
subagent:
max_concurrent: 5 # 最大并行数
queue_size: 10 # 队列长度
timeout: 300 # 单任务超时(秒)
9.33. 避免循环依赖
bash
# ❌ 错误:循环引用
delegate_task --name A --context "$(delegate_task result B)"
delegate_task --name B --context "$(delegate_task result A)"
# ✅ 正确:线性依赖
delegate_task --name A
delegate_task --name B --context "$(delegate_task result A)"
9.44. 子Agent失败处理
任何子Agent都可能失败(超时、工具出错、模型异常)。要有容错机制:
bash
# 重试失败的任务
delegate_task retry t-001
# 查看失败原因
delegate_task log t-001
# 如果某个子任务不重要,可以忽略
delegate_task cancel t-001
10.总结
多Agent协同是Hermes Agent最接近"团队协作"的功能。它让你从一个AI助手的使用者,变成一个AI团队的指挥者。
今天的关键点:
- 何时用:大任务中有多个独立子任务时使用
- 怎么用 :
delegate_task分配任务,独立上下文+独立工具集 - 并行vs串行:独立任务并行,依赖任务串行
- 实战案例:并行调研技术方案、边写代码边讨论需求
- 高级模式:辩论、审查、看门狗
- 注意事项:不过度拆分、注意数量和依赖关系
下一篇文章,我们将讨论Hermes Agent的安全与权限控制------如何安全地让AI访问你的代码和系统。
作者:挖AI金矿
系列:Hermes Agent 从小白到高级 --- 第 13/18 篇
下一篇:安全与权限控制 --- 把Agent关进笼子里