子代理
delegate_task 工具可以生成子 AIAgent 实例,具有隔离的上下文、受限的工具集和独立的终端会话。每个子代理获得全新的对话并独立工作 - 只有最终摘要会进入父代理的上下文。
子代理(Sub-agent)是 Hermes Agent 的核心功能之一,允许你将复杂任务分解为多个并行执行的子任务。每个子代理:
- 拥有完全独立的对话上下文 - 不了解父代理的历史对话
- 可以使用受限的工具集 - 只获得完成任务所需的工具
- 拥有独立的终端会话 - 与父代理和其他子代理隔离
- 返回结构化摘要 - 只有最终结果进入父代理上下文
- 并行执行 - 最多 3 个并发子代理(可配置,无硬性上限)
- 上下文隔离 - 子代理从零开始,只接收你提供的
goal和context - 工具集控制 - 精确控制每个子代理可以访问的工具
- 深度限制 - 默认扁平结构,可选嵌套编排(最多 3 层)
- 中断传播 - 中断父代理会自动中断所有活动子代理
- 成本优化 - 可以为子代理配置更便宜的模型
在 ~/.hermes/config.yaml 中配置子代理:
delegation: max_iterations: 50 # 每个子代理的最大轮数(默认:50) max_concurrent_children: 3 # 每批并行子代理数(默认:3) max_spawn_depth: 1 # 树深度(1-3,默认 1 = 扁平) orchestrator_enabled: true # 禁用则强制所有子代理为叶子角色 child_timeout_seconds: 600 # 子代理超时时间(秒,默认:600)
# 可选:为子代理使用不同的模型 model: "google/gemini-flash-2.0" # 更便宜的模型用于子代理 provider: "openrouter" # 可选:将子代理路由到不同的提供商使用自定义端点
Section titled “使用自定义端点”delegation: model: "qwen2.5-coder" base_url: "http://localhost:1234/v1" api_key: "local-key" # api_mode: "anthropic_messages" # 可选,用于 Anthropic 兼容端点当 base_url 指向 Anthropic 兼容端点时,api_mode 会自动检测。
delegate_task( goal="调试测试失败的原因", context="错误:test_foo.py 第 42 行的断言失败", toolsets=["terminal", "file"])delegate_task(tasks=[ {"goal": "研究主题 A", "toolsets": ["web"]}, {"goal": "研究主题 B", "toolsets": ["web"]}, {"goal": "修复构建", "toolsets": ["terminal", "file"]}])同时研究多个主题并收集摘要:
delegate_task(tasks=[ { "goal": "研究 2025 年 WebAssembly 的当前状态", "context": "关注:浏览器支持、非浏览器运行时、语言支持", "toolsets": ["web"] }, { "goal": "研究 2025 年 RISC-V 的采用情况", "context": "关注:服务器芯片、嵌入式系统、软件生态系统", "toolsets": ["web"] }, { "goal": "研究 2025 年量子计算进展", "context": "关注:纠错突破、实际应用、主要参与者", "toolsets": ["web"] }])代码审查 + 修复
Section titled “代码审查 + 修复”将审查和修复工作流委托给新的上下文:
delegate_task( goal="审查认证模块的安全问题并修复发现的任何问题", context="""项目位于 /home/user/webapp。 认证模块文件:src/auth/login.py、src/auth/jwt.py、src/auth/middleware.py。 项目使用 Flask、PyJWT 和 bcrypt。 关注:SQL 注入、JWT 验证、密码处理、会话管理。 修复发现的任何问题并运行测试套件(pytest tests/auth/)。""", toolsets=["terminal", "file"])委托大型重构任务,避免淹没父代理的上下文:
delegate_task( goal="重构 src/ 中的所有 Python 文件,将 print() 替换为适当的日志记录", context="""项目位于 /home/user/myproject。 使用 'logging' 模块,logger = logging.getLogger(__name__)。 将 print() 调用替换为适当的日志级别: - print(f"Error: ...") -> logger.error(...) - print(f"Warning: ...") -> logger.warning(...) - print(f"Debug: ...") -> logger.debug(...) - 其他 prints -> logger.info(...) 不要更改测试文件或 CLI 输出中的 print()。 之后运行 pytest 验证没有破坏任何东西。""", toolsets=["terminal", "file"])嵌套编排(高级)
Section titled “嵌套编排(高级)”对于多阶段工作流,可以生成可以委托自己的工作人员的编排器子代理:
delegate_task( goal="调研三种代码审查方法并推荐一种", role="orchestrator", # 允许此子代理生成自己的工作人员 context="...",)role="leaf"(默认):子代理不能进一步委托role="orchestrator":子代理保留delegation工具集
注意:max_spawn_depth 默认为 1(扁平),需要将其提高到 2 才能允许编排器子代理生成叶子孙代理。
关键:子代理一无所知
子代理以完全全新的对话开始。它们对父代理的对话历史、先前的工具调用或委托之前讨论的任何内容一无所知。父代理必须在调用中传递所有子代理需要的信息:
# 错误 - 子代理不知道"错误"是什么delegate_task(goal="修复错误")
# 正确 - 子代理拥有所需的所有上下文delegate_task( goal="修复 api/handlers.py 中的 TypeError", context="""api/handlers.py 第 47 行有 TypeError: 'NoneType' 对象没有 'get' 属性。 process_request() 函数从 parse_body() 接收一个 dict, 但 parse_body() 在 Content-Type 缺失时返回 None。 项目位于 /home/user/myproject,使用 Python 3.11。""")toolsets 参数控制子代理可以访问的工具。根据任务选择:
| 工具集模式 | 使用场景 |
|---|---|
["terminal", "file"] | 代码工作、调试、文件编辑、构建 |
["web"] | 研究、事实核查、文档查找 |
["terminal", "file", "web"] | 全栈任务(默认) |
["file"] | 只读分析,不执行的代码审查 |
["terminal"] | 系统管理、进程管理 |
子代理被阻止的工具:
delegation- 叶子子代理默认被阻止clarify- 子代理无法与用户交互memory- 不写入共享持久内存code_execution- 子代理应该逐步推理send_message- 无跨平台副作用
每个子代理都有迭代限制(默认:50),控制它可以进行多少次工具调用轮次:
delegate_task( goal="快速文件检查", context="检查 /etc/nginx/nginx.conf 是否存在并打印前 10 行", max_iterations=10 # 简单任务,不需要很多轮次)如果子代理在 child_timeout_seconds 秒内没有响应,会被视为卡住并被终止。默认是 600 秒(10 分钟)。
delegation: child_timeout_seconds: 600 # 默认值对于快速本地模型可以降低;对于复杂问题上的慢速推理模型可以提高。
监控运行中的子代理 (/agents)
Section titled “监控运行中的子代理 (/agents)”TUI 提供 /agents 覆盖层(别名 /tasks):
- 实时树状视图显示运行中和最近完成的子代理
- 每个分支的成本、令牌和文件触摸汇总
- 终止和暂停控制
- 事后审查:逐步查看每个子代理的历史
经典 CLI 只打印 /agents 作为文本摘要;TUI 是覆盖层真正发挥作用的地方。
委托 vs execute_code
Section titled “委托 vs execute_code”| 因素 | delegate_task | execute_code |
|---|---|---|
| 推理 | 完整的 LLM 推理循环 | 仅 Python 代码执行 |
| 上下文 | 全新隔离对话 | 无对话,仅脚本 |
| 工具访问 | 所有非阻止工具带推理 | 通过 RPC 的 7 个工具,无推理 |
| 并行性 | 默认 3 个并发子代理(可配置) | 单脚本 |
| 最适合 | 需要判断的复杂任务 | 机械多步骤管道 |
| 令牌成本 | 更高(完整 LLM 循环) | 更低(仅返回 stdout) |
| 用户交互 | 无(子代理无法澄清) | 无 |
经验法则:当子任务需要推理、判断或多步骤问题解决时使用 delegate_task。当你需要机械数据处理或脚本化工作流时使用 execute_code。
使用 max_spawn_depth: 3 和 max_concurrent_children: 3 时,树可以达到 3×3×3 = 27 个并发叶子代理。每增加一层都会成倍增加花费 - 有意识地提高 max_spawn_depth。
生命周期和持久性
Section titled “生命周期和持久性”delegate_task 是同步的 - 不是持久的:
- 在父代理的当前回合内运行
- 阻塞父代理直到每个子代理完成(或被取消)
- 如果父代理被中断,所有活动子代理被取消
- 子代理在父代理回合结束后不会继续运行
对于持久的长时间运行工作,使用:
cronjob(action=create)- 安排单独的 Agent 运行terminal(background=True, notify_on_complete=True)- 长时间运行的 shell 命令
诊断零调用超时
Section titled “诊断零调用超时”如果子代理在零次 API 调用后超时(通常是:提供商不可达、认证失败或工具模式拒绝),delegate_task 会将结构化诊断写入 ~/.hermes/logs/subagent-timeout-<session>-<timestamp>.log,包含子代理的配置快照、凭证解析跟踪和任何早期错误消息。