IronClaw 学习笔记 02:Agent Loop
ReAct 循环的实现,消息模型设计,以及 JoinSet 并行工具执行
上一篇我们装好了"手"(Tool)和"脑"(LLM Provider),但它们还没有接线。这一篇把它们连起来,造一个真正的 Agent。
🎯 这一篇要解决什么问题?
Phase 01 结束时,tiny-claw 有了工具系统和 LLM 接口,但 REPL 依然只是 echo。工具没人调用,LLM 没人询问。就像一个有手有脑但没有神经系统的生物——零件齐全,但不会动。
这一篇要接的那根"神经"叫做 Agent Loop。它是 agent 的核心控制循环:
用户说话 → LLM 思考 → 调用工具(可能多轮) → LLM 总结 → 返回答案
这不是简单的"调一次 API 拿个回复"。LLM 可能决定先查时间、再查天气、最后综合回答——一个请求触发多轮工具调用。这种"思考→行动→观察→再思考"的循环就是 ReAct 模式。
🧠 ReAct:Reasoning + Acting
ReAct 是 2022 年提出的 agent 范式,核心思想是让 LLM 交替进行推理(Reasoning)和行动(Acting):
| 步骤 | 角色 | 内容 |
|---|---|---|
| 1 | User | "现在几点了?北京时间。" |
| 2 | LLM(推理) | 我需要获取当前时间,应该调用 time 工具 |
| 3 | LLM(行动) | → tool_call: time({ "operation": "now" }) |
| 4 | System(观察) | ← { "utc": "...", "local": "2026-03-09T18:30:00+08:00" } |
| 5 | LLM(推理+总结) | 根据工具返回的时间,现在北京时间是下午 6:30 |
| 6 | LLM(回复) | "现在北京时间 18:30。" |
关键在于步骤 2-4 是可重复的。如果用户问的问题需要多个工具,LLM 会连续发出多个 tool_call,每次都把工具结果追加到对话历史中,直到它认为信息足够了,才输出最终的文本回复。
循环的终止条件很简单——LLM 返回纯文本,不包含 tool_call。
🏛️ 从 OpenCode 到 IronClaw:Agent Loop 的概念迁移
| 维度 | OpenCode | IronClaw | 核心差异 |
|---|---|---|---|
| 循环位置 | agent.ts | dispatcher.rs | IronClaw 把 Loop 和 Dispatch 分离 |
| 消息模型 | Message[] 数组 | Session → Thread → Turn 三层 | IronClaw 有完整的状态管理 |
| 工具执行 | 顺序执行 | JoinSet 并行执行 | Rust 的并发优势 |
| 路由逻辑 | 无 | SubmissionParser + Router | IronClaw 区分命令和自然语言 |
🏛️ Dispatcher:ReAct 循环的实现细节
循环体逻辑:
loop {
若达到迭代上限 → 移除所有工具(Force text)
调用 LLM → 获得回复
若回复是纯文本 → 返回,循环结束
若回复包含 tool_calls → 并行执行(JoinSet)→ 结果追加到对话 → 继续循环
}
并行工具执行
IronClaw 利用 Rust 的 JoinSet 实现工具的并行执行。如果 LLM 一次返回多个 tool_call(比如同时查时间和搜索新闻),它们会被并行执行。
谁决定哪些工具可以并行?完全由 LLM 决定。 客户端不做任何依赖分析——LLM 若认为两个工具之间没有数据依赖,会在同一 response 中返回多个 tool_calls;若存在依赖,会先返回一个,等结果回来再下一轮返回另一个。tiny-claw 同样用 JoinSet 并行执行,通过 tool_call_id 匹配结果,不依赖消息顺序。
📝 tiny-claw Phase 02 的简化策略
| IronClaw | tiny-claw (Phase 02) |
|---|---|
| SubmissionParser + Router | REPL 直接判断 / 命令 vs 自然语言 |
| Session / Thread / Turn | 单个 Vec<ChatMessage> |
| Nudge + Force text + CostGuard | 简单 loop + max iterations |
JoinSet 并行 + 审批 + 安全过滤 | JoinSet 并行执行 |
| ContextMonitor | 无,可 /clear、/context 查看 |
目标:用最少的代码实现完整的 ReAct 闭环——用户提问,LLM 推理,调用工具,LLM 总结,返回答案。
🧪 动手试试
tiny-claw Phase 02 有 6 个工具:echo、time、web_search、list_dir、read_file、write_file。试试这几个 query,观察日志中的 iteration 次数和并行行为:
| 模式 | Query | 观察点 |
|---|---|---|
| 单轮 | "现在几点了?" | 1 次 tool_call,循环立即结束 |
| 多轮 | "看看 workspace 里有什么,然后读一下 notes.txt" | 2 次 tool_call,第二轮依赖第一轮结果 |
| 多轮 | "读一下 config.json,把 port 改成 8080" | read → write,先读再改的 coding 模式 |
| 并行 | "现在几点?顺便搜一下今天有什么新闻" | 日志显示 Executing 2 tool(s) in parallel,wall time ≈ max(各工具耗时) |
| 混合 | "看看 workspace 有什么文件,顺便告诉我几点" | 第一轮并行(list_dir + time),可追问"读一下第一个文件"触发多轮 |
📝 本篇总结
| 理解项 | 描述 |
|---|---|
| ReAct 模式 | 推理 + 行动的交替循环,tool_call 驱动多轮迭代 |
| 终止条件 | LLM 返回纯文本(无 tool_call)= 循环结束 |
| 并行工具执行 | Rust JoinSet 让同轮多个 tool_call 并行跑 |
| /context | 查看发给 LLM 的完整 messages 及本请求会带上的 tools |
下一篇:IronClaw 学习笔记 03:持久化 —— Workspace 文件系统设计、FTS + Vector 混合搜索原理、RRF 融合算法。