Ironclaw 00 - 初见 Ironclaw

Ironclaw 是 Transformer 作者做的一个更加安全的 Openclaw。LLM 的 API 调用接触起来很简单,和其他 HTTP API 并没有什么太大的区别,但是 Agent 是如何实现的,整个系统如何设计?是我自己空想不出来并且很好奇的一点。我将用一个系列的 Blog 来记录阅读 Ironclaw 代码结构、实现方式分析和如何设计一个 Agent 的系统设计的笔记。

所有文章均 co-writer with LLM (但我自己会对内容的准确性负责),喜欢纯手写 Blog 的观众可以现在就关掉这个页面了 :)

组件之间的总体运行链路

  1. 用户消息进入:来自 CLI/Telegram/Web 等 Channel → 统一变成 IncomingMessage 送进 代理循环
  2. 代理循环处理一次请求:解析/路由(命令 vs 普通对话 vs job)→ 进入 agentic loop(LLM→工具→LLM…)→ 输出响应
  3. 并行与后台:需要后台跑的任务交给 调度器;真正执行 job 的是 工作器(Worker)
  4. 容器隔离(可选):需要 Docker 沙箱的 job 由 编排器(Orchestrator) 管容器生命周期、凭据注入、内部 API
  5. Web UI:Web 网关提供 HTTP API + SSE/WS 流,把聊天、job 事件、日志推到浏览器
  6. 记忆与检索:工作空间提供持久化文件+(FTS+向量)混合搜索,供 agent/工具使用
  7. 安全:安全层在“输入→LLM→工具→输出”的关键边界做注入防护、校验、脱敏、泄漏检测
  8. 定时任务:定时任务引擎按 cron/事件触发,轻量就地执行,重任务走调度器

下面逐个展开

1) 代理循环(主消息处理和任务协调)

它是什么:主控的事件循环,持续收消息、维护 session/thread/turn 状态、触发一次次“agentic turn”。

原理/机制(来自 src/agent/CLAUDE.md):

会话模型是 Session → Thread → Turn,每次用户输入通常对应一个 Turn(持久化/可回放/可压缩)。 真正“思考+调用工具”的循环,不是手写 while,而是复用一个共享引擎 run_agentic_loop()(后面第 4 点细讲)。 关键点:单个 thread 内顺序执行(保证对话一致性),而并行任务靠 scheduler/job。

2) 路由器(分类用户意图:命令、查询、任务)

它是什么:主要处理显式命令(以 / 开头)的解析,把它们变成内部的 MessageIntent(例如创建 job、取消 job、help 等)。

原理/机制:

文档明确写了:Router 只管 显式 /commands;自然语言消息绕过 router,直接走对话 dispatcher(这对“聊天就是聊天”很重要)。 见 src/agent/CLAUDE.md 里 “Command Routing (router.rs)” 的说明。

3) 调度器(管理带优先级的并行任务执行)

它是什么:并发/后台任务的管理器。把“需要后台跑的完整 job”与“轻量子任务(工具执行/后台任务)”分开管理。

原理/机制(文档给了实现模型):

维护两张表(在锁里): jobs:完整 LLM 驱动的后台 job(有 worker、消息通道) subtasks:轻量的 ToolExec / Background 任务 推荐入口是 dispatch_job():先建上下文/(可选)落库,保证后续 job_events/llm_calls 有外键,再 schedule。 这些都在 src/agent/CLAUDE.md “Scheduler” 小节写得很明确。 你可以把它理解成:把“对话主线程”从“长时间跑的工作”中解耦。

4) 工作器(执行包含 LLM 推理和工具调用的任务)

它是什么:真正跑一个 job 的执行单元。你们现在的 job 执行已经搬到 src/worker/job.rs,它实现了 LoopDelegate,复用共享的 agentic loop 引擎。

原理/机制(从 src/worker/job.rs + src/agent/CLAUDE.md 汇总):

共享引擎 run_agentic_loop() 的固定流程是:

CLAUDE.md Lines 59-68 run_agentic_loop(delegate, reasoning, reason_ctx, config)

  1. Check signals (stop/cancel) via delegate.check_signals()
  2. Pre-LLM hook via delegate.before_llm_call()
  3. LLM call via delegate.call_llm()
  4. If text response → delegate.handle_text_response() → Continue or Return
  5. If tool calls → delegate.execute_tool_calls() → Continue or Return
  6. Post-iteration hook via delegate.after_iteration()
  7. Repeat until LoopOutcome returned or max_iterations reached Job worker 会: 记录/持久化 job 状态与事件 通过 SSE 把 job 的消息、工具调用、结果、状态推给 Web UI(你在 Worker::log_event() 能看到它把事件映射成 SseEvent::JobMessage/JobToolUse/…) 直观理解:Worker=“把一个任务跑完的执行机”,loop 引擎=“LLM↔工具的迭代框架”。

5) 编排器(容器生命周期、LLM 代理、每任务认证)

它是什么:当 job 需要在 Docker 沙箱里跑时,编排器负责“起容器、管容器、给容器发任务、收回事件”,并且用 每个 job 的 bearer token 做内部 API 鉴权。

原理/机制(从 src/orchestrator/api.rs):

编排器内部 API(默认另一个端口)提供 /worker/{job_id}/… 一组端点:取 job 描述、代理 LLM 完成、上报状态、上报事件、拉取 follow-up prompt、获取 credentials。 安全关键点:所有 /worker/ 路由都套了 worker_auth_middleware,用 per-job token 做鉴权(防止别的容器/进程伪造)。 你可以理解成:Orchestrator=“沙箱执行的控制平面”。

6) Web 网关(浏览器 UI:聊天、记忆、任务、日志、扩展、定时任务)

它是什么:面向浏览器的 HTTP API + SSE/WS 实时流(Axum 实现),把系统的各子模块状态/事件以统一方式暴露给前端。

原理/机制(src/channels/web/CLAUDE.md):

/api/chat/send 把消息送进 agent loop;/api/chat/events 用 SSE 推流(也支持 WS) 事件契约由 SseEvent 枚举定义(后端与前端对齐的“协议”) 工具需要审批时会发 approval_needed,前端点批准再调用 /api/chat/approval 恢复执行 认证用 Authorization: Bearer <GATEWAY_AUTH_TOKEN>;考虑到 EventSource/WS 限制,少数端点也允许 query token 直观理解:Web 网关=“UI 的后端适配层 + 事件总线出口”。

7) 定时任务引擎(cron + 事件/webhook 触发)

它是什么:把“定时触发”和“事件触发(webhook/system_event/manual)”统一为 Routine 的触发机制。

原理/机制(src/agent/CLAUDE.md):

Routine = Trigger(cron/event/system_event/manual) + RoutineAction(lightweight/full_job) + guardrails RoutineEngine:cron ticker + 事件匹配器 轻量 action:就地执行(inline) full_job:丢给 Scheduler 变成后台 job(保证不阻塞主循环) 这就是典型的“触发器→执行策略”分层。

8) 工作空间(带混合搜索的持久记忆)

它是什么:给 agent 的“持久化记忆文件系统”,支持读写任意路径,并提供 FTS+向量 的混合搜索(RRF 融合)。

原理/机制(src/workspace/README.md):

“Memory is database, not RAM”:要记住就写入 workspace 文件 目录结构有约定文件:MEMORY.md(长期记忆)、HEARTBEAT.md(周期检查)、daily/(日记/压缩输出) 搜索用 RRF(Reciprocal Rank Fusion)把关键词排名与向量相似度排名融合: [ score(d)=\sum \frac{1}{k+rank(d)} ]

后端差异:Postgres 支持 FTS + pgvector;libSQL 目前偏关键词(向量线路尚未完全接通) 直观理解:Workspace=“可审计、可检索、可持久化的外部记忆”。

9) 安全层(提示注入防御和内容清理)

它是什么:在几个关键边界对内容与工具调用做“防御纵深”:输入过滤、工具参数与结果处理、输出脱敏/泄漏检测、策略约束。

原理/机制(从 src/agent/CLAUDE.md + src/worker/job.rs 可见调用点):

文档明确写了一个关键不变量:工具结果在回到 LLM 之前要过 SafetyLayer(sanitizer → validator → policy → leak detector)。 工具执行路径还有 execute_tool_with_safety() / process_tool_result() 这种共享管线(chat/job/container 都复用),核心目的就是:把“能触达外部世界的动作”统一收口到受控管线。 直观理解:SafetyLayer=“LLM 与外部世界之间的防火墙/净化器”。