📄 文档列表
🎬 口播文案
✏️ 编辑文档
标题
工具栏
加粗
H2 标题
H3 标题
引用
无序列表
有序列表
代码块
📷 上传图片
点击或拖拽上传图片
支持 PNG, JPG, GIF, WebP 格式
内容 (Markdown 格式)
# 实测 Hermes 的 delegate_task:一个命令,AI 同时帮你做三件独立的事 你有没有遇到过这种情况——同时想让 AI 做三件互不相关的事,结果只能一件一件来,等完一个再等下一个? 今天要说的这个功能,就是来解决这个痛点的。 Hermes 的 `delegate_task` 工具(不是 slash command,就是普通工具),可以让你同时派发出最多 3 个独立的子 Agent,各自拿到独立的任务、独立的上下文、独立的工作目录,然后并行去干活。 听起来挺美好的,但实际用起来有哪些坑?性能提升到底有多少?什么场景适合用、什么场景不该用? --- ## 一、核心原理:它到底是怎么工作的 `delegate_task` 是 Hermes 内置的一个工具,不是 slash command,意味着你可以在对话里直接调用它。 调用方式分两种模式: **单任务模式**——只派一个子 Agent: ``` delegate_task( goal="审查 src/auth/ 目录的安全问题", context="项目在 /home/user/webapp,使用 Flask + PyJWT,测试命令是 pytest tests/auth/ -v", toolsets=["terminal", "file"] ) ``` **批量模式**——最多同时派 3 个: ``` delegate_task(tasks=[ {"goal": "研究 WebAssembly 现状", "toolsets": ["web"]}, {"goal": "研究 RISC-V 服务器芯片", "toolsets": ["web"]}, {"goal": "研究量子计算进展", "toolsets": ["web"]} ]) ``` 这里有个硬性限制:**tasks 数组最多 3 个元素**,超过的会被截断。这个数字是由 `MAX_CONCURRENT_CHILDREN = 3` 决定的,写死在源码里,想改就得改代码。 底层用的是 Python 的 `ThreadPoolExecutor`,单个任务不经过线程池(直接跑以减少开销),2 个及以上才走线程池并发执行。 --- ## 二、每个子 Agent 拿到的上下文到底是什么样的 这是最重要的理解点:**子 Agent 对父对话一无所知**。 它们拿到的东西只有: 1. 你传的 `goal`(任务目标) 2. 你传的 `context`(背景信息) 3. 当前工作目录的绝对路径(Hermes 会自动从 `TERMINAL_CWD`、`terminal_cwd`、`cwd` 等路径里选一个真实存在的注入进去) 4. 你指定的 `toolsets`(工具集) 不在 context 里的东西,子 Agent 统统不知道。所以如果你跟 Hermes 讨论了半天"那个 bug",然后直接 `delegate_task(goal="fix the bug")`,子 Agent 收到的是一个它完全不理解的"fix the bug"字符串——它没有你的对话历史。 正确的做法是把所有必要信息都塞进 context: ``` # ❌ 错误:子 Agent 不知道 "那个错误" 是什么 delegate_task(goal="修复这个错误") # ✅ 正确:所有上下文都显式传入 delegate_task( goal="修复 TypeError", context="""api/handlers.py 第 47 行报错:'NoneType' object has no attribute 'get' 函数 process_request() 从 parse_body() 拿到的是 None 项目路径 /home/user/myproject,Python 3.11""" ) ``` 子 Agent 的系统提示词是这么构建的(源码 `_build_child_system_prompt` 函数): ``` You are a focused subagent working on a specific delegated task. YOUR TASK: {goal} CONTEXT: {context} WORKSPACE PATH: {workspace_path} Complete this task using the tools available to you. When finished, provide a clear, concise summary of: - What you did - What you found or accomplished - Any files you created or modified - Any issues encountered ``` --- ## 三、工具集限制:子 Agent 不能用什么 不是所有工具都能传给子 Agent,以下工具被硬编码阻止(`DELEGATE_BLOCKED_TOOLS`): | 工具 | 原因 | |------|------| | `delegate_task` | 防止无限递归 | | `clarify` | 子 Agent 不能向用户提问 | | `memory` | 不能写共享的持久记忆 | | `send_message` | 不能产生跨平台副作用 | | `execute_code` | 子 Agent 应该用完整推理而不是写脚本 | 另外,子 Agent 的 `toolsets` 还会和父 Agent 的有效工具集做交集——**子 Agent 永远拿不到父 Agent 没有开启的工具**。 常用工具集组合: | 任务类型 | toolsets | 说明 | |----------|----------|------| | 网络研究 | `["web"]` | 只有搜索和网页提取 | | 代码工作 | `["terminal", "file"]` | Shell + 文件读写 | | 全栈任务 | `["terminal", "file", "web"]` | 全部可用工具 | --- ## 四、并发模型与资源隔离 每个子 Agent 有自己独立的: - **Terminal Session**(独立的工作目录和状态) - **Conversation Context**(全新对话,无历史) - **Iteration Budget**(默认 50 步,可通过 `max_iterations` 调整) 深度限制为 2——父 Agent(深度 0)可以派子 Agent(深度 1),但子 Agent 不能继续派孙 Agent。这个限制由 `_delegate_depth` 字段控制,写在源码里: ```python depth = getattr(parent_agent, '_delegate_depth', 0) if depth >= MAX_DEPTH: # MAX_DEPTH = 2 return error("Delegation depth limit reached") ``` **结果顺序有保证**:虽然子 Agent 是并发执行的,但最终返回前会按 `task_index` 排序,保证结果顺序和你的 tasks 数组顺序一致。 --- ## 五、凭证继承与跨 provider 派发 子 Agent 默认继承父 Agent 的 API Key 和 Provider 配置。但如果你在 `~/.hermes/config.yaml` 里配置了独立的 delegation 凭证,就能让子 Agent 跑在完全不同的模型上: ```yaml delegation: model: "google/gemini-flash-2.0" provider: "openrouter" ``` 或者直接指定一个本地 endpoint: ```yaml delegation: model: "qwen2.5-coder" base_url: "http://localhost:1234/v1" api_key: "local-key" ``` 当父 Agent 和子 Agent 使用相同 provider 时,子 Agent 会共享父 Agent 的凭证池(credential pool),这样在遇到速率限制时可以自动切换 Key,而不是整个派发失败。 --- ## 六、实战场景:这些用法是真正有效的 ### 场景 1:并行研究 三个话题同时调研,汇总后分析: ``` delegate_task(tasks=[ {"goal": "调研 WebAssembly 浏览器外运行现状", "context": "关注 Wasmtime、Wasmer、WASI 进展", "toolsets": ["web"]}, {"goal": "调研 RISC-V 服务器芯片采用情况", "context": "关注服务器芯片、云厂商、软件生态", "toolsets": ["web"]}, {"goal": "调研量子计算实际应用突破", "context": "关注纠错进展、实际用例、主要公司", "toolsets": ["web"]} ]) ``` ### 场景 2:代码审查 + 修复 给一个完全独立的上下文去做安全审查,避免父对话的 bias: ``` delegate_task( goal="审查 authentication 模块并修复发现的问题", context="""项目路径 /home/user/webapp Auth 文件:src/auth/login.py, src/auth/jwt.py, src/auth/middleware.py 技术栈:Flask, PyJWT, bcrypt 测试命令:pytest tests/auth/ -v 关注点:SQL 注入、JWT 验证、密码处理、会话管理""", toolsets=["terminal", "file"] ) ``` ### 场景 3:分文件重构 大型重构按模块拆分,每个子 Agent 处理不同的代码区域: ``` delegate_task(tasks=[ {"goal": "重构 API handlers 使用新响应格式", "context": "文件:src/handlers/users.py, src/handlers/auth.py", "toolsets": ["terminal", "file"]}, {"goal": "更新客户端 SDK 适配新响应格式", "context": "文件:sdk/python/client.py, sdk/python/models.py", "toolsets": ["terminal", "file"]} ]) ``` --- ## 七、踩坑记录:这些坑我真真实实踩过 ### 坑 1:context 不足导致子 Agent 完全跑偏 **表现**:子 Agent 返回的结果完全不是你想要的,但它声称已经完成了。 **原因**:子 Agent 对话历史为零,它只能根据你给的 context 推理。如果你只说"修复那个 bug",它根本不知道是哪个 bug、哪段代码、什么错误信息。 **解决方法**:强制自己把所有信息都写入 context——文件路径、错误信息、项目结构、测试命令。宁可冗余,也不能遗漏关键信息。 ### 坑 2:派了 4 个任务以为会并发,实际只有前 3 个在跑 **表现**:提交了 4 个并行任务,发现第 4 个完全没有执行。 **原因**:`tasks` 数组被硬截断到 3 个(`task_list = tasks[:MAX_CONCURRENT_CHILDREN]`),不会报错,只是静默丢弃第 4 个。 **解决方法**:如果确实需要更多并行任务,分两批调用。 ### 坑 3:子 Agent 跑完后父 Agent 的工具名被污染 **表现**:派生子 Agent 后,`execute_code` 开始报奇怪的工具找不到错误。 **原因**:子 Agent 构建时会调用 `get_tool_definitions()`,这个调用会修改一个全局的 `_last_resolved_tool_names`,如果子 Agent 构建失败,全局状态没有被恢复。 **解决方法**:源码里有 `_parent_tool_names` 保存逻辑,在 `try/finally` 里强制恢复。但在某些极端情况下可能还会遇到,可以手动重置会话。 ### 坑 4:认为子 Agent 会记住会话历史 **表现**:期望子 Agent 能看到之前派发的另一个子 Agent 的结果。 **原因**:每个子 Agent 都是完全独立的对话,父子之间、兄弟之间的历史互不可见。 **解决方法**:需要共享的信息必须在父 Agent 这一层汇总后,再通过 context 传给下一个子 Agent。 --- ## 八、delegate_task vs execute_code:什么时候用哪个 | 对比维度 | delegate_task | execute_code | |----------|--------------|-------------| | **推理能力** | 完整 LLM 推理循环 | 纯 Python 执行,无推理 | | **上下文** | 全新隔离对话 | 无对话,只有脚本 | | **工具访问** | 所有非阻塞工具+推理 | 7 个工具 via RPC | | **并行能力** | 最多 3 个并发 | 单脚本 | | **适用场景** | 需要判断力的复杂任务 | 机械性数据处理 | | **Token 消耗** | 较高(完整 LLM 循环) | 较低(只返回 stdout) | **实操经验**:大多数场景下,`execute_code` 适合做数据收集(比如循环爬取 10 个页面),然后把结果交给 `delegate_task` 做推理分析——这是性价比最高的组合。 --- ## 九、配置参数一览 在 `~/.hermes/config.yaml` 中的完整配置项: ```yaml delegation: max_iterations: 50 # 每个子 Agent 最大步数,默认 50 default_toolsets: ["terminal", "file", "web"] # 默认工具集 model: "" # 子 Agent 使用的模型,为空则继承父 Agent provider: "" # 子 Agent 使用的 provider base_url: "" # 直接指定 API endpoint api_key: "" # endpoint 对应的 key ``` --- ## 写在最后 `delegate_task` 解决的核心问题是:**让多个独立任务真正同时跑,而不是排队等**。在调研、代码审查、分模块重构等场景下,效果是立竿见影的。 但它不是银弹——子 Agent 隔离的上下文既是优势也是限制,你需要花更多时间在构建 context 上。如果任务本身依赖父对话的上下文,或者需要不断和用户确认,直接在主对话里做反而更高效。 **你在用 delegate_task 吗?遇到过什么奇怪的坑?评论区聊聊。**
摘要
标签
多个标签用逗号分隔
分类
技术文章
教程指南
工具测评
项目实战
行业观察
默认
💾 保存修改
← 返回查看
返回列表