源码导览:从 CLI 到 Agent

源码导览:从 CLI 到 Agent

这是一份“自顶向下”的源码导览:不追求把所有细节一次看完,而是先建立一张可用的定位地图。

你要先记住的 6 个问题

  1. 程序从哪里启动?(CLI 入口、命令分发)
  2. Gateway 的“控制面”是什么?(连接、鉴权、方法调用、事件订阅)
  3. 消息怎么进来?(Channels → 统一入站信封)
  4. 路由怎么选?(agentId / sessionKey / DM 与群组隔离)
  5. 回复怎么生成?(agent loop:run → attempt → streaming)
  6. 工具怎么执行且可控?(sandbox + tool policy + approvals)

当你能把上面 6 个问题串成一条线,你就具备“顺着调用栈读源码”的能力了。

一句话端到端链路

Channel 收到消息 → Gateway 规范化并路由 → 进入按 sessionKey 排队的执行通道(lane)→ 触发 runEmbeddedPiAgent → 生成回复(可流式)→ 必要时调用工具(可能需要审批)→ 回写到原渠道。

对应的概念文档(建议按顺序读):

Gateway 的两层“平面”

1) 控制面(Control plane)

控制面负责“建立连接 + 调用方法 + 订阅事件”:

  • 客户端先 connect
  • 之后可以 call 方法(例如 chat.send
  • 并通过 subscribe 接收事件流(例如 chat/agent/presence/tick)

入口位置通常从这里开始理解:

2) 数据面(Data plane)

数据面承载实际消息与运行时状态:

  • chat.send 触发一次 run(非阻塞 ACK),结果通过事件流回传
  • logs.tail 用于 Control UI 跟踪文件日志

路由与隔离:为什么不会串线

路由的核心目标是两件事:

  • 确定性:从哪个渠道进来,就回到哪个渠道(模型不“选渠道”)
  • 隔离性:群组/频道用独立 sessionKey,避免和私信混在一起

你最需要掌握的是:如何从一条入站消息,得到 agentIdsessionKey,再映射到 “lane:session:<key>” 的队列语义。

参见:

工具执行:为什么“安全边界”分三层

OpenClaw 把风险拆成三层来管:

  1. Sandbox:隔离“在哪里跑”(文件系统/网络/进程边界)
  2. Tool policy:约束“允许跑什么”(允许列表、能力范围)
  3. Approvals:约束“什么时候需要人确认”(高风险动作、人类门控)

参见:

读源码的“最短路径”(实操)

  1. 跑一次可观察的对话:openclaw dashboard(Control UI)或任一渠道收发一条消息。
  2. openclaw logs --follow,记录一次 chat.send 的 runId。
  3. 回到文档按顺序读:TypeBox → Session tool → Agent loop → Queue。
  4. 需要定位具体实现时,再补:Gateway logging / troubleshooting / security。