IronClaw 学习笔记 07:Plan + Subagent
显式 Plan vs 隐式 ReAct、expected_outcome 自检、不达预期时的降级策略、Session 与 Sandbox 两种子 Agent 模式
上一篇我们让 Agent 能被浏览器、脚本、手机远程访问。但一个能触达的 Agent 如果只会一步步地等 LLM 告诉自己下一步做什么,面对真正复杂的任务就像一个没有计划的施工队——有人有工具,但不知道先挖地基还是先砌墙。
🎯 这一篇要解决什么问题?
Phase 06 之后,tiny-claw 已经具备完整的推理、工具调用、记忆、技能、多渠道访问能力。但它的工作方式始终是逐步反应——用户说一句话,LLM 想一步做一步。
当前 tiny-claw 的工作方式:
用户:"帮我清理三个月前的订单数据"
→ LLM 想了想,调了 bash_exec 看表结构
→ LLM 又想了想,直接开始 DELETE
→ 用户:"等等!你先备份了吗??"
❌ 不可逆操作没有提前展示计划
❌ 多步骤任务没有全局视角
❌ 复杂调研任务无法拆给多个 agent 并行做
❌ 执行用户代码没有沙箱隔离
这些"不行"揭示了两个缺失的能力:
- Plan——执行前展示完整计划,让用户和 LLM 都有预期
- Subagent——把子任务委派给独立的 agent 实例,支持并行和隔离
🧠 两种编排流派
在动手之前,先理解业界两种根本不同的多步骤编排策略。
显式 Plan:先想后做
┌─────────────────────────────────────────────────────────────┐
│ 显式 Plan + 顺序执行 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 用户:"分析 sales.csv 并生成可视化报告" │
│ │
│ Phase 1: Planning(一次 LLM 调用) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ { │ │
│ │ "goal": "分析 sales.csv 并生成可视化报告", │ │
│ │ "actions": [ │ │
│ │ { "tool": "bash_exec", │ │
│ │ "params": { "command": "head sales.csv" }, │ │
│ │ "expected_outcome": "获取 CSV 列名和行数" }, │ │
│ │ { "tool": "bash_exec", │ │
│ │ "params": { "command": "python analyze.py" }, │ │
│ │ "expected_outcome": "输出统计结果" }, │ │
│ │ { "tool": "write_file", │ │
│ │ "params": { "path": "report.md" }, │ │
│ │ "expected_outcome": "生成报告文件" } │ │
│ │ ], │ │
│ │ "confidence": 0.85 │ │
│ │ } │ │
│ └─────────────────────────────────────────────────────┘ │
│ │
│ Phase 2: Execution(按步骤机械执行) │
│ for action in plan.actions: │
│ execute_tool(action.tool, action.params) │
│ sleep(100ms) │
│ │
│ 代表项目: IronClaw (Rust) │
│ 优势: 可预览、可确认、省 LLM 调用 │
│ 劣势: 执行过程僵硬、不能根据结果调整方向 │
│ │
└─────────────────────────────────────────────────────────────┘
IronClaw 在 reasoning.rs 中完整实现了 ActionPlan——一次 LLM 调用生成 JSON 计划,然后 execute_plan() 逐步执行。每个 PlannedAction 都带有 expected_outcome 字段。
隐式 ReAct:边想边做
┌─────────────────────────────────────────────────────────────┐
│ 隐式 ReAct Loop │
├─────────────────────────────────────────────────────────────┤
│ │
│ 用户:"分析 sales.csv 并生成可视化报告" │
│ │
│ Round 1: LLM 决定先看文件结构 │
│ → bash_exec("head sales.csv") │
│ → 结果:4 列,10000 行 │
│ │
│ Round 2: LLM 看到列名,决定用 pandas 分析 │
│ → bash_exec("python analyze.py") │
│ → 结果:报错 "pandas not found" │
│ │
│ Round 3: LLM 调整方向,先安装依赖 │
│ → bash_exec("pip install pandas") │
│ → 结果:安装成功 │
│ │
│ Round 4: 重新运行分析 │
│ → bash_exec("python analyze.py") │
│ → 结果:统计数据输出 │
│ │
│ Round 5: 写报告 │
│ → write_file("report.md") │
│ │
│ 代表项目: Cursor, OpenClaw, 当前 tiny-claw │
│ 优势: 灵活、能自我纠错、每步都能根据实际结果调整 │
│ 劣势: LLM 调用次数多、用户无法提前看到全貌 │
│ │
└─────────────────────────────────────────────────────────────┘
注意 Round 2 → Round 3 的转折:ReAct 在 pandas not found 时自动调整了方向。如果是显式 Plan,这里要么中断计划,要么需要复杂的重规划逻辑。
一个关键发现
IronClaw 完整实现了 Plan + Execute,但
use_planning在整个代码库中全部设为false。
这不是 bug,而是工程判断——在大多数场景下,ReAct loop 已经足够好。Plan 是特定场景的优化,不是通用需求。
📋 什么时候真的需要 Plan?
Plan 的使用场景比直觉预期的少得多。
场景一:不可逆操作需要人工预览
任务:"清理生产数据库三个月前的订单"
× ReAct:直接开始执行,用户不知道会删什么
→ LLM 也许会先查再删——但你能保证它每次都这样做吗?
✓ Plan:先展示完整步骤
Goal: 清理 orders 表 90 天前的数据
┌─────────────────────────────────────────────────────┐
│ Step 1: 查询 orders 表记录数 │
│ 预期:获得当前数据量 │
│ Step 2: 备份到 orders_backup_20260316 │
│ 预期:备份完成,行数与源一致 │
│ Step 3: DELETE WHERE created_at < ... │
│ 预期:删除约 N 万条旧订单 │
└─────────────────────────────────────────────────────┘
⚠️ 警告:DELETE 操作不可逆
Plan 的价值:让用户在"不可逆"之前看到全貌。
场景二:步骤极多,上下文会撑爆
任务:"重构项目中所有 50 个文件的 import 语句"
× ReAct:每步都把前面所有文件的工具结果带进 context
→ 第 20 步时 context 可能超 128k token
✓ Plan:一次性规划好 50 个步骤
→ 执行时 context 只需 [Plan] + [当前步骤结果]
→ 上下文始终可控
场景三:执行环境无法调用 LLM
场景:离线批处理、网络受限的 Docker sandbox、低延迟要求的自动化
× ReAct:每步都要 LLM call,受限环境里跑不了
✓ Plan:在有 LLM 的阶段一次性规划好,执行阶段纯机械执行
其他场景呢?
都不需要 Plan。
- "任务有 5 步" → 不需要 Plan,ReAct loop 本来就是多步骤的
- "任务很复杂" → 不需要 Plan,复杂度不等于需要预先规划
- "想让结果更可靠" → 不需要 Plan,LLM 拿到实际结果比拿到计划预期更可靠
🔧 plan 工具设计:让 Plan 成为 ReAct 的一部分
既然 Plan 只在特定场景有用,那不要把 Plan 做成一个独立的执行引擎。而是把它做成 ReAct loop 里的一个普通工具——plan 展示计划,但执行仍然由 ReAct loop 驱动。
核心理念:Plan 是展示,不是执行
┌─────────────────────────────────────────────────────────────┐
│ Plan 工具 vs Plan 执行引擎 │
├─────────────────────────────────────────────────────────────┤
│ │
│ IronClaw 的做法(Plan 执行引擎): │
│ │
│ plan() → ActionPlan JSON │
│ → execute_plan(): │
│ for action in plan.actions: │
│ execute_tool(action.tool, action.params) │
│ → 问 LLM "完成了吗?" │
│ → 不够 → 继续 run_agentic_loop() │
│ │
│ 问题: │
│ • 需要独立的 plan 执行引擎代码(200+ 行) │
│ • 执行过程僵硬,中途不调整 │
│ • 所以 IronClaw 自己没开启这个功能 │
│ │
│ ───────────────────────────────────────────────────────── │
│ │
│ tiny-claw 的做法(Plan 工具 + Step Tracker): │
│ │
│ plan() → 展示计划 → AgentLoop 解析 steps → ActivePlan │
│ → 每轮 LLM 调用前,system prompt 末尾追加 plan progress │
│ → LLM 每轮都看到 Expected vs Actual 的对比 │
│ → ReAct loop 继续执行,Step Tracker 持续追踪 │
│ │
│ 优势: │
│ • plan 是一个普通工具,execute() 返回结构化 JSON │
│ • 不需要 plan 执行引擎,ReAct loop 负责执行 │
│ • Step Tracker 每轮刷新,LLM 不会"忘记"检查预期 │
│ • 代码量极小(< 150 行) │
│ │
└─────────────────────────────────────────────────────────────┘
两种触发方式
Plan 有两种触发场景,但共享同一套展示和写入逻辑:
/plan 命令 | plan 工具 | |
|---|---|---|
| 谁触发 | 用户(主动要求看计划) | LLM(判断任务需要多步骤编排) |
| 使用时机 | 用户知道任务复杂或危险 | LLM 识别到不可逆操作或高复杂度 |
| 底层机制 | slash 命令 → 注入 plan 提示 → LLM 调用 plan 工具 | LLM 自主决定调用 plan 工具 |
| 确认流程 | 无需确认,直接进入 ReAct loop | 无需确认,直接进入 ReAct loop |
为什么不需要用户确认(y/n)?因为 Plan 是信息展示,不是权限审批。用户已经发了请求(/plan 清理数据),Plan 是让他们看到全貌。如果需要权限控制,那是 Safety Layer 的责任(Phase 04),不是 Plan 的。
✅ expected_outcome + Step Tracker:持续验证机制
这是整个 Plan 设计中最精巧的部分。
问题:一次性 reminder 会被遗忘
如果只把 plan 作为 tool_result 写入 context,LLM 执行了 5 轮工具调用之后,plan 的内容已经被推到 context 很远的位置。LLM 可能忘记检查 expected_outcome,也可能跳步。
解法:Active Plan — 每轮动态追踪
AgentLoop 在检测到 plan 工具被调用后,进入 Active Plan 模式。核心机制是每轮 LLM 调用前,在 system prompt 末尾动态追加 plan progress:
┌─────────────────────────────────────────────────────────────┐
│ system prompt 末尾(每轮动态更新): │
│ │
│ ## Active Plan │
│ Goal: 清理 orders 表 90 天前的数据 │
│ │
│ ✅ Step 1: 查询 orders 表记录数 │
│ Expected: 获得当前数据量 │
│ Actual: 42857 rows │
│ │
│ → Step 2: 备份到 orders_backup_20260316 │
│ Expected: 备份完成,行数与源一致 │
│ │
│ Step 3: DELETE WHERE created_at < ... │
│ Expected: 旧记录被删除 │
│ │
│ Before proceeding to the next step, verify that │
│ the actual result matches the expected outcome. │
│ If it deviates significantly, adjust your approach │
│ or inform the user. │
└─────────────────────────────────────────────────────────────┘
关键区别:Plan progress 不是一次性写入 context 然后被遗忘的,而是每轮 LLM 调用前动态更新在 system prompt 中。LLM 每轮都能看到当前在第几步、前面步骤的实际结果、以及下一步的预期。
验证是上下文做的,不是代码做的
expected_outcome 对两种角色都有价值:
- 对用户:执行前知道每步预期产出什么,形成共同预期
- 对 LLM:每轮都看到 Expected vs Actual 的并排对比,自然做验证
传统验证需要硬编码检查逻辑(result > 0 ? continue : abort)。Active Plan 把验证变成了持续的上下文——只要 LLM 每轮都能看到预期和实际的对比,验证就自动发生。零验证代码,但比一次性 reminder 可靠得多。
↩️ 不达预期时:Step Tracker 让偏差无处可藏
当实际结果不符合预期时会发生什么?来看一个完整的执行流程:
plan({
goal: "清理服务器日志",
steps: [
{ action: "查看各目录日志大小",
expected_outcome: "获得占用空间列表" },
{ action: "备份最近7天日志到 /backup/",
expected_outcome: "备份文件已创建,大小与源一致" },
{ action: "删除30天以前的日志",
expected_outcome: "旧日志清除,磁盘释放 > 1GB" }
]
})
Step 1 顺利完成后,Step 2 执行失败:
LLM 在第 3 轮调用前看到的 system prompt 末尾:
┌──────────────────────────────────────────────────────┐
│ ## Active Plan │
│ Goal: 清理服务器日志 │
│ │
│ ✅ Step 1: 查看各目录日志大小 │
│ Expected: 获得占用空间列表 │
│ Actual: /var/log 占 8.2GB, /tmp/log 占 1.1GB │
│ │
│ → Step 2: 备份最近7天日志到 /backup/ │
│ Expected: 备份文件已创建,大小与源一致 │
│ Actual: No space left on device │
│ │
│ Step 3: 删除30天以前的日志 │
│ Expected: 旧日志清除,磁盘释放 > 1GB │
│ │
│ Before proceeding to the next step, verify that │
│ the actual result matches the expected outcome... │
└──────────────────────────────────────────────────────┘
LLM 判断: Step 2 Expected ≠ Actual → 需要调整
可能的决策:
├─ 清理 /backup 中的旧文件,腾出空间后重试
├─ 换备份路径(/tmp/backup/)
├─ 压缩率更高的方式备份
└─ 告知用户:备份空间不足,是否继续?
Step Tracker 比一次性 reminder 可靠的原因:LLM 不是在 context 的某个遥远位置找到 expected_outcome,而是在每轮 system prompt 的末尾直接看到 Expected vs Actual 的对比。这个偏差无法被忽略。
不需要硬编码决策树。 LLM 拿到清晰的对比上下文(预期 + 实际 + 偏差),它比任何 if/else 都更擅长判断该怎么办。ReAct loop 本身支持多轮工具调用,LLM 有足够的 turns 来自我纠错。
这正是 tiny-claw 的设计优势:Plan 提供预期,Step Tracker 提供持续对比,ReAct 负责执行和纠错。三者组合得到的灵活度和可靠性远超纯 Plan 执行引擎。
🤖 Subagent:什么时候才真的需要?
在讲实现之前,先纠正一个常见误区:不是所有多步骤任务都需要 Subagent。
ReAct 已经能做的事
用户:"帮我分析 sales.csv,生成报告"
→ 同一个 agent,ReAct loop 连续调用工具:
bash_exec("head sales.csv") ← tool call #1
bash_exec("python analyze.py") ← tool call #2
write_file("report.md") ← tool call #3
→ 这不需要 subagent——这只是连续工具调用。
ReAct loop 本身就处理:
- 顺序多步骤(A → B → C)
- 条件分支(工具失败 → LLM 换方式)
- 同轮并行(LLM 返回多个 tool_calls → JoinSet 并行执行)
真正需要 Subagent 的四个场景
场景一:需要不同的角色 / 系统提示
父 agent(系统提示:项目经理)
收到:"把这个功能实现一下"
→ spawn 子 agent(系统提示:后端工程师,只有代码工具)
→ spawn 子 agent(系统提示:测试工程师,只有测试工具)
父 agent 做协调,子 agent 做执行。
各自有不同 system prompt 和工具集——不会互相污染。
场景二:需要跨轮次并行
区分两种并行——同轮工具并行 vs 跨轮次 agent 并行:
┌─────────────────────────────────────────────────────────────┐
│ │
│ 同轮工具并行(JoinSet,已有): │
│ LLM 返回: [搜品牌A, 搜品牌B, 搜品牌C] │
│ → 三个工具同时执行,等全部完成 │
│ → 结果合并回 context,继续下一轮 │
│ → 每个并行项只是一次工具调用 │
│ │
│ ───────────────────────────────────────────────────────── │
│ │
│ 跨轮次 agent 并行(需要 Subagent): │
│ 任务:"同时让三个 agent 深度调研三个竞品" │
│ → 每个竞品调研需要 多轮 agent loop(搜→分析→写报告) │
│ → 不能用 JoinSet 并行一个 "多轮 loop" │
│ → 需要 spawn 三个独立子 agent,各跑自己的 ReAct loop │
│ │
│ subagent_1: 搜 A → 分析 A → 写 A 报告(多轮) │
│ subagent_2: 搜 B → 分析 B → 写 B 报告(多轮) │
│ subagent_3: 搜 C → 分析 C → 写 C 报告(多轮) │
│ 父 agent 收集三份结果 → 汇总对比 │
│ │
└─────────────────────────────────────────────────────────────┘
关键区别:并行的粒度是"一次工具调用"还是"一个完整的 agent loop"。
场景三:需要上下文隔离
子任务产生大量输出,不应污染主对话上下文。或者子任务的失败不应影响父 agent 的状态。
场景四:需要安全沙箱
执行用户提供的代码或脚本。主进程不能直接 exec——用户代码可能删文件、耗尽资源、访问敏感路径。需要 Docker 容器级别的隔离。
判断公式
需要 subagent 吗?
问题 1:任务能在同一个 context / 角色下完成吗?
→ 是 → 不需要,继续 ReAct loop
问题 2:需要多个 agent 各自跑多轮 loop 并行?
→ 是 → 需要 subagent(并行 spawn)
问题 3:需要独立的 system prompt / 工具集?
→ 是 → 需要 subagent(角色分工)
问题 4:需要执行不可信代码?
→ 是 → 需要 subagent(Docker 沙箱)
🔀 Subagent 两种模式
Mode: Session(同进程,轻量)
┌─────────────────────────────────────────────────────────────┐
│ Session 模式 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 父 AgentLoop.chat("同时调研三个竞品") │
│ │ │
│ └─→ LLM 同时调用三个 subagent(JoinSet 并行): │
│ subagent({ task: "调研A", mode: "session" }) │
│ subagent({ task: "调研B", mode: "session" }) │
│ subagent({ task: "调研C", mode: "session" }) │
│ │ │
│ 每个内部: │
│ new AgentLoop → .chat(task) → 返回结果 │
│ 多轮: web_search → 分析 → write_file → 总结 │
│ │ │
│ └─→ 父 LLM 收到三份结果 → 汇总对比 │
│ │
│ 特点: │
│ ✅ 毫秒级 spawn │
│ ✅ 通信零开销(同进程) │
│ ✅ 共享 provider 和 registry(Arc 克隆) │
│ ❌ 隔离弱(子 agent panic 可能影响主进程) │
│ ❌ 无法安全执行不可信代码 │
│ │
│ 适合: 角色分工、并行调研、上下文隔离 │
│ │
└─────────────────────────────────────────────────────────────┘
实现极其简洁:SubagentTool 的 execute() 方法只需要 new 一个 AgentLoop、set system prompt、调 .chat(task)、返回结果。全部代码不到 80 行。
Mode: Sandbox(Docker 容器,强隔离)
┌─────────────────────────────────────────────────────────────┐
│ Sandbox 模式 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 父 AgentLoop.chat("运行这段 Python 脚本") │
│ │ │
│ └─→ LLM 调用 subagent({ mode: "sandbox", task: "..." }) │
│ │ │
│ ├─ docker run │
│ │ --rm │
│ │ --network none # 默认断网 │
│ │ --memory 512m │
│ │ --cpus 1.0 │
│ │ --pids-limit 100 # 防 fork bomb │
│ │ -e OPENAI_API_KEY=$KEY # 直接注入 │
│ │ -v $dir:/workspace │
│ │ tiny-claw:latest │
│ │ --message "$task" --no-color │
│ │ │
│ ├─ 等待完成(timeout 10 min) │
│ │ │
│ └─→ 返回容器 stdout 给父 agent │
│ │
│ 特点: │
│ ✅ 强隔离:文件系统、网络、进程完全隔离 │
│ ✅ 安全:用户代码不影响主进程 │
│ ✅ 资源限制:内存、CPU、进程数受控 │
│ ❌ 冷启动慢(秒级) │
│ ❌ 资源消耗大 │
│ │
│ 适合: 执行用户代码、不可信输入、需要文件系统隔离 │
│ │
└─────────────────────────────────────────────────────────────┘
为什么 tiny-claw 不需要 ProxyLlmProvider
这是 tiny-claw 比 IronClaw 简单得多的地方。
IronClaw 的 Docker 容器不能直接调用 LLM——API Key 不在容器内。所以 IronClaw 实现了一套完整的代理层:
IronClaw:
容器 → ProxyLlmProvider → HTTP → 宿主 Orchestrator API → 真正的 LLM API
容器 → WorkerHttpClient → HTTP → 宿主 Orchestrator API → 凭据存储
需要:orchestrator HTTP API(6+ endpoints)、ProxyLlmProvider、TokenStore、
CredentialGrant 权限管理、worker token 认证
tiny-claw 的做法简单得多:
tiny-claw:
docker run -e OPENAI_API_KEY=$KEY ... tiny-claw --message "$task"
容器内的 tiny-claw 直接用注入的 API Key 调 LLM。
不需要 proxy、不需要 orchestrator API、不需要 token 认证。
安全权衡:tiny-claw 是个人工具,容器虽然拿到了 API Key,但容器本身是 --network none(默认断网)或受限网络。对于个人使用场景,这个权衡完全合理。
🛡️ 深度保护:防止无限递归
Subagent 能 spawn 子 agent,子 agent 理论上也能 spawn 孙 agent。如果不加限制,一个 LLM 幻觉就能触发无限递归。
max_depth = 3(默认)
depth 0: 主 agent(用户对话)
depth 1: 子 agent → 可以 spawn
depth 2: 孙 agent → 可以 spawn
depth 3: 超过限制 → 返回错误,不再 spawn
实现方式:每个 SubagentTool 在构造时记录 current_depth。spawn 子 agent 时,子 agent 的 SubagentTool 的 current_depth = parent_depth + 1。超过 max_depth 时,execute() 直接返回错误信息——LLM 看到错误后会调整策略。
有趣的是,IronClaw 没有实现深度保护。它的 Job 系统不支持 Job spawn 子 Job,所以递归不是问题。但 OpenClaw 有 callerDepth >= maxSpawnDepth 的检查——因为它的 Session 模式支持嵌套。
📊 完整对比
| 维度 | IronClaw | OpenClaw | tiny-claw (Phase 07) |
|---|---|---|---|
| Plan 触发 | Worker 内部 use_planning | 无显式 Plan | plan 工具 + /plan 命令 |
| Plan 执行 | 独立引擎 execute_plan() | — | ReAct loop + ActivePlan step tracker |
| Plan 状态 | 已实现但未启用 | — | 启用 |
| 预期验证 | expected_outcome(未使用) | — | Step Tracker 每轮注入 Expected vs Actual |
| Subagent 载体 | Docker 容器 | 同进程 Session | 两种都有 |
| LLM 访问 | ProxyLlmProvider 代理 | Gateway RPC | 直接注入 API Key |
| 深度保护 | 无 | maxSpawnDepth | max_depth=3 |
| 通信方式 | SSE + inject channel | Gateway 事件 | 同进程直调 / Docker stdout |
| 代码量 | ~1000 行 | ~500 行 | ~200 行 |
🧪 动手试试
Phase 07 之后 tiny-claw 能展示计划和委派子任务。试试这些场景:
| 场景 | 操作 | 观察点 |
|---|---|---|
| 用户主动要求计划 | /plan 帮我清理旧日志 | Agent 先展示计划再执行 |
| LLM 自动展示计划 | "清理三个月前的数据库记录" | LLM 判断不可逆,主动调用 plan 工具 |
| 计划不达预期 | 步骤执行报错 | LLM 对比 expected_outcome 与实际结果,自动调整 |
| 并行子 agent | "同时调研 A、B、C 三个竞品" | 三个子 agent 并行跑,父 agent 汇总 |
| 角色分工 | "让一个分析师和一个工程师协作" | 子 agent 有不同 system prompt |
| 深度保护 | 让子 agent 尝试 spawn 子子子 agent | 超过 max_depth 时返回错误 |
📝 本篇总结
| 理解项 | 描述 |
|---|---|
| 两种流派 | 显式 Plan(先想后做) vs 隐式 ReAct(边想边做) |
| Plan 真正需要时 | 不可逆操作预览、步骤极多上下文爆炸、离线执行 |
| Plan 作为工具 | 展示计划 → AgentLoop 解析 steps → ActivePlan → ReAct loop 继续 |
| Step Tracker | 每轮 system prompt 末尾追加 plan progress(Expected vs Actual) |
| expected_outcome | LLM 每轮看到预期+实际的并排对比,持续验证 |
| 不达预期降级 | 偏差在 step tracker 中无处可藏——LLM 自然处理 |
| 两种触发 | /plan 用户主动、plan 工具 LLM 主动,底层逻辑统一 |
| Subagent 判断 | 角色分工、跨轮次并行、上下文隔离、安全沙箱——其他场景不需要 |
| Session 模式 | 同进程 new AgentLoop,轻量、快速、共享资源 |
| Sandbox 模式 | Docker 容器,强隔离、直接注入 API Key、无需 proxy |
| 深度保护 | max_depth=3,每层 spawn 时 depth+1,超限返回错误 |
核心洞察
1. Plan 是信息展示,不是执行引擎
IronClaw 写了 200+ 行的 execute_plan() 来按步骤执行计划——然后没启用它。tiny-claw 的做法是:Plan 只负责"展示",执行仍然交给 ReAct loop。结果是代码更少、更灵活、而且真正启用了。
2. Step Tracker 让 expected_outcome 真正起作用
一次性把 plan 写入 context 的问题是 LLM 会"忘记"。Active Plan 通过每轮在 system prompt 末尾动态追加 Expected vs Actual 对比,把验证变成了持续的、不可忽略的上下文。零验证代码,但比硬编码 if/else 更可靠——因为 LLM 理解语义,而 if/else 只能比较字符串。
3. 不需要 Subagent 的场景远比需要的多
多步骤 ≠ 需要 Subagent。连续工具调用、同轮并行、条件分支——这些 ReAct loop 全都能做。只有角色分工、跨轮次并行、安全沙箱这几个场景才真的需要 spawn 独立 agent。过度使用 Subagent 只会增加复杂度。
下一篇:IronClaw 学习笔记 08:WASM 沙箱 —— WASM 安全模型、Capability-based 权限、Credential Injection、三层 Tool 体系。