Hermes Agent 源码探秘 (8):子代理系统 — Agent 生 Agent
系列:Hermes Agent 源码探秘 作者:元思未来 字数:约3000字
前面七篇我们聊的都是"单个 Agent"怎么工作。但现实中,复杂任务光靠一个人是不够的。
你想一下:
- 如果一个任务需要同时做三件事(查资料、写代码、测试),一个 Agent 只能串行做
- 如果一个子任务需要完全隔离的环境(不同的工作目录、不同的工具集)
- 如果你想让 Agent 长时间在后台运行,同时你继续做别的事
这些场景,就需要子代理(Sub-Agent) 机制。
一、问题分析:为什么需要子代理?
场景一:并行工作
用户需求:帮我对比一下 Go 和 Rust,写一份报告
没有子代理(串行):
1. 搜索 Go 的资料
2. 搜索 Rust 的资料(等上面完了才开始)
3. 对比分析
4. 写报告
总耗时:1 号+2 号+3 号+4 号
有子代理(并行):
子代理A → 搜索 Go 资料 ──┐
├── 同时进行
子代理B → 搜索 Rust 资料 ──┘
主Agent等待两个都完成后 → 对比分析 → 写报告
总耗时:max(1号, 2号) + 3号 + 4号
场景二:环境隔离
场景:Agent 需要读取生产环境的日志分析问题
主Agent:运行在项目开发目录
子AgentA:SSH 到生产服务器分析日志
子AgentB:在另一个目录写修复代码
三个 Agent 互不干扰,各自有独立的文件系统和工具集
场景三:长时间任务
场景:Agent 有一个需要跑 30 分钟的任务
主Agent:继续跟用户聊天、处理其他请求
后台子Agent:跑那个耗时任务,完成后汇报结果
二、Hermes 的子代理机制
Hermes 通过 delegate_task 工具实现子代理。
使用方式
在对话中,Agent 可以调用 delegate_task 工具:
# tools/delegate_tool.py (简化)
def delegate_task(goal: str, context: str = "",
toolsets: list = None, tasks: list = None,
task_id: str = None) -> str:
"""将任务委派给子代理"""
# 1. 创建子 Agent
child = AIAgent(
model=parent_model, # 可以用和父级相同的模型
toolsets=toolsets, # 只给子代理需要的工具
max_iterations=50, # 子代理有自己的迭代限制
platform="delegation", # 标记来源
...
)
# 2. 子代理独立运行
result = child.run_conversation(
context + "\n\n任务:" + goal
)
# 3. 返回摘要
return json.dumps({
"summary": result["final_response"],
"status": "completed"
})
并发模式
Hermes 支持并发派发多个子代理:
# 并发派发 3 个子代理
tasks = [
{"goal": "搜索 Go 语言的优势和劣势", "toolsets": ["web"]},
{"goal": "搜索 Rust 语言的优势和劣势", "toolsets": ["web"]},
{"goal": "查找 Go 和 Rust 的性能对比数据", "toolsets": ["web"]},
]
result = delegate_task(tasks=tasks)
# 注意:tasks 是数组,3 个子代理会并发执行
delegate_task 接收到 tasks 数组时,会创建多个子代理并行执行:
if tasks:
# 并行模式:每个 task 一个子代理
with ThreadPoolExecutor(max_workers=len(tasks)) as executor:
futures = []
for task in tasks:
future = executor.submit(
_run_child_agent,
goal=task["goal"],
toolsets=task.get("toolsets", default_toolsets)
)
futures.append(future)
results = [f.result() for f in futures]
return json.dumps({"results": results})
三、子代理的隔离机制
子代理不是"瘦客户端",而是完全独立的 Agent 实例:
主 Agent
├── 上下文:当前对话
├── 工作目录:/project/main
├── 工具集:所有
└── 迭代限制:90
│
├── 子代理 A
│ ├── 上下文:仅有委派时传递的内容
│ ├── 工作目录:独立临时目录
│ ├── 工具集:web(只有搜索)
│ └── 迭代限制:50
│
├── 子代理 B
│ ├── 上下文:仅有委派时传递的内容
│ ├── 工作目录:独立临时目录
│ ├── 工具集:terminal, file
│ └── 迭代限制:50
│
└── 子代理 C
├── 上下文:仅有委派时传递的内容
├── 工作目录:独立临时目录
├── 工具集:web
└── 迭代限制:50
隔离的内容包括:
| 维度 | 是否隔离 | 说明 |
|---|---|---|
| 对话上下文 | ✅ 完全隔离 | 子代理看不到主 Agent 的对话历史 |
| 工作目录 | ✅ 完全隔离 | 子代理在临时目录操作,不影响主目录 |
| 工具集 | ✅ 可按需配置 | 主 Agent 可以限制子代理只能用哪些工具 |
| LLM 模型 | ✅ 可独立配置 | 子代理可以用不同的模型(更快/更便宜的) |
| 终端会话 | ✅ 完全隔离 | 子代理的 shell 环境独立 |
| 迭代限制 | ✅ 独立 | 子代理有自己的 max_iterations |
四、信息流:父 Agent 怎么"知道"子 Agent 的结果?
子代理执行完后,不会把完整的对话历史返回给父 Agent——那样会浪费大量 token。它只返回一个摘要:
def _run_child_agent(goal, toolsets, context=""):
# 创建子 Agent
child = AIAgent(toolsets=toolsets, ...)
# 运行
result = child.run_conversation(context + "\n\n" + goal)
# 返回摘要
return {
"goal": goal,
"status": "completed",
"summary": result["final_response"], # 只返回最终回复
# 注:不返回完整对话历史!
}
父 Agent 拿到摘要后,可以根据需要决定下一步:
# 父 Agent 的视角
子代理A的摘要:"Go语言的优势...劣势..."
子代理B的摘要:"Rust语言的优势...劣势..."
子代理C的摘要:"性能对比:Go在并发方面占优..."
父 Agent:(看了三个摘要后)
"好,我来整合成一份完整的对比报告..."
五、子代理 vs 其他并发机制
Hermes 有多种"并行/后台"机制,它们各有用处:
| 机制 | 同步/异步 | 隔离度 | 适用场景 |
|---|---|---|---|
| delegate_task | 同步等待 | 高(全隔离) | 需要结果的并行子任务 |
| terminal(background) | 异步 | 中(仅终端隔离) | 启动服务器、跑长任务 |
| cronjob | 定时触发 | 高(完整会话) | 定时任务、每日报告 |
| spawn hermes | 独立进程 | 最高(完全独立) | 长时间独立任务 |
选择建议:
任务需要结果才能继续? → delegate_task
任务在后台跑,不关心结果? → terminal(background)
任务需要定时执行? → cronjob
任务需要完全独立运行几天? → spawn hermes process
六、一个完整的委派案例
为了展示子代理的威力,看一个真实场景:
用户需求: "帮我调研三个 RAG 框架,整理对比报告"
用户输入
│
▼
主 Agent 分析:需要调研 3 个框架,可以并行
│
▼
派发 3 个子代理(并行):
├── 子代理 A: "调研 LangChain 的 RAG 实现"
│ └── 独立搜索、独立分析
├── 子代理 B: "调研 LlamaIndex 的 RAG 实现"
│ └── 独立搜索、独立分析
└── 子代理 C: "调研 Chroma + 原生 RAG 实现"
└── 独立搜索、独立分析
│
▼
主 Agent 等待 3 个子代理完成
│
▼
收到三个摘要:
A: "LangChain 支持... 优点... 缺点..."
B: "LlamaIndex 支持... 优点... 缺点..."
C: "原生 RAG 实现方式..."
│
▼
主 Agent 整合、对比、撰写报告
│
▼
输出完整的对比报告给用户
总耗时:max(3个子代理耗时) + 整合耗时
而不是 3个子代理耗时之和
这就是子代理的价值:把串行变并行,把复杂变分解。
七、下一篇预告
从第1篇的技术原理,到第8篇的子代理机制。我们已经把一个 AI Agent 的里里外外都拆了一遍。
第九篇我来点实战内容——讲我实际用 Hermes 做什么。
作为全栈老程序员,我用 Hermes 来:
- 帮我写自媒体文章的初稿
- 辅助开发 Python 小工具
- 设置定时任务(每日信息整理)
- 把我的经验整理成"技能"
这些都是真实的使用案例,不是空谈理论。下篇见。
代码位置:
~/.hermes/hermes-agent/tools/delegate_tool.py
并发限制: 默认最多 3 个子代理并行(可配置)
注意: 子代理是同步等待的(主Agent会阻塞直到子代理完成)
元思未来 · 行稳致远,进而有为