OpenClaw 源码解析(六):openclaw agent 如何触发一次 Agent 运行?
1. 本期要解决的问题
前几期我们已经从项目整体结构、CLI 命令体系、配置加载、Gateway 运行机制等角度理解了 OpenClaw 的基础框架。到了这一期,可以进一步进入 OpenClaw 最核心的使用动作:用户在终端中执行一条 openclaw agent --message "..." 命令后,系统内部究竟发生了什么?
从官方文档看,openclaw agent 的定位非常明确:它用于通过 Gateway 运行一次 agent turn,同时也可以通过 --local 参数强制走本地嵌入式执行路径;命令需要至少提供一个会话选择器,例如 --to、--session-key、--session-id 或 --agent。(OpenClaw)
也就是说,openclaw agent 并不是一个简单的“大模型问答命令”,它本质上是一个带有会话路由、Agent 选择、模型覆盖、回复投递和容错回退能力的任务调度入口。
2. 命令入口:agent 是核心 CLI 命令之一
在 CLI 命令描述文件中,agent 被注册为一个核心命令,其说明是 “Run one agent turn via the Gateway”,即通过 Gateway 运行一次 Agent 回合。这个定义说明,默认情况下 OpenClaw 倾向于让 Agent 请求经过 Gateway,而不是直接在当前 CLI 进程里裸跑模型调用。(GitHub)
可以把它理解为:
用户输入命令
↓
openclaw agent
↓
构造一次 Agent 请求
↓
优先交给 Gateway 执行
↓
得到回复后输出到终端,或投递到指定聊天渠道
这种设计的好处是,CLI 不需要自己承担全部运行状态。Gateway 可以统一管理会话、通道、插件、模型、工具、消息投递和长期运行状态。
3. 第一层校验:消息和目标会话必须存在
agent-via-gateway.ts 中的 agentViaGatewayCommand 首先会取出用户传入的 message,并进行空值校验。如果用户没有传入消息体,源码会直接抛出错误,提示使用 openclaw agent --message "..." --agent <id> 或者使用已有会话参数。(GitHub)
紧接着,它还会检查目标会话是否明确。源码要求用户至少提供 --to、--session-id、--agent 或 --session-key 中的一种,否则系统不知道这次消息应该发给哪个 Agent 或哪个会话。(GitHub)
这一步可以概括为:
message 为空? → 报错
没有任何会话选择器? → 报错
agent id 不存在? → 报错
这个设计很关键。因为 OpenClaw 支持多个 Agent,也支持把不同聊天渠道映射到不同会话。如果没有明确的目标选择规则,系统就无法判断这次 Agent turn 应该继承哪段上下文,也无法判断后续回复应该投递到哪里。
4. 第二层准备:解析 sessionKey、timeout、model override
通过基础校验之后,源码会继续解析运行参数。它会根据配置和用户传入的 agentId、to、sessionId、sessionKey 等信息解析出最终使用的 sessionKey。同时,它还会解析超时时间、消息渠道、幂等 key,以及可选的模型覆盖参数 --model。(GitHub)
这一段代码的核心作用不是“调用模型”,而是把用户输入的 CLI 参数转换为一次标准化的 Agent 运行请求。可以理解为:
用户参数
↓
规范化 agentId
↓
检查 agent 是否存在
↓
解析 sessionKey
↓
解析 timeout
↓
解析 model override
↓
生成 idempotencyKey
其中 idempotencyKey 很值得注意。它用于标识一次请求,避免同一个请求在 Gateway 中被重复执行。源码中还专门处理了 in_flight 状态:如果相同 run 已经在执行中,CLI 会提示当前 Agent run 已经在进行,而不是再启动一个重复任务。(GitHub)
5. Gateway 路径:真正向 Gateway 发起 agent 调用
参数准备完成后,OpenClaw 会通过 callGateway 向 Gateway 发起请求。这里的请求方法名是 "agent",请求参数包括 message、agentId、model、to、sessionId、sessionKey、thinking、deliver、channel、replyChannel、timeout、lane 和 idempotencyKey 等。(GitHub)
从这一点可以看出,openclaw agent 命令发出的不是一个简单文本请求,而是一个完整的 Agent 执行请求包。这个请求包同时携带了:
要说什么:message
由谁执行:agentId
使用哪个上下文:sessionKey / sessionId
是否覆盖模型:model
是否投递回复:deliver
投递到哪里:channel / replyChannel / replyTo
如何控制运行:timeout / thinking / lane
如何避免重复:idempotencyKey
因此,Gateway 不是被动转发文本,而是承担了 Agent 运行控制中心的角色。
6. 输出处理:JSON 输出与普通文本输出分开
Gateway 返回结果后,CLI 会根据用户是否传入 --json 选择不同输出方式。如果是 JSON 模式,源码会把 Gateway 响应写成结构化 JSON;如果不是 JSON 模式,则会提取 payloads,逐个格式化后输出到终端。(GitHub)
这说明 OpenClaw 同时面向两类使用方式:
人直接在终端使用
→ 输出可读文本
脚本或自动化系统调用
→ 输出可解析 JSON
这一点对于 CLI 工具非常重要。人类用户需要清晰的终端反馈,而自动化脚本需要稳定的结构化返回值。
7. 本地路径与回退路径:为什么有 --local?
官方文档说明,Gateway 模式失败时会回退到 embedded agent,而 --local 可以直接强制使用嵌入式执行;同时,--local 仍会预加载插件注册表,以便插件提供的 provider、tool 和 channel 在本地运行中仍然可用。(OpenClaw)
源码中的 agentCliCommand 也体现了这个分支逻辑:如果用户设置了 --local,系统会直接调用 agentCommand;如果没有设置 --local,则优先尝试 agentViaGatewayCommandWithTransientRetries,也就是 Gateway 路径。(GitHub)
可以概括成:
openclaw agent
↓
是否 --local?
├─ 是:直接 agentCommand 本地执行
└─ 否:优先 Gateway 执行
↓
Gateway 失败或超时?
├─ 是:embedded fallback
└─ 否:返回 Gateway 结果
这一设计增强了可用性。Gateway 正常时,系统走统一服务路径;Gateway 不可用或超时时,CLI 仍然有机会在当前进程中完成一次嵌入式 Agent 执行。
8. 嵌入式执行内部:agentCommand 做了什么?
当系统进入本地执行或 embedded fallback 时,会调用 agentCommand 相关逻辑。agent-command.ts 中的 prepareAgentCommandExecution 会再次检查消息体和会话选择器,并解析配置、Agent、会话、工作区、模型上下文等运行信息。(GitHub)
随后,系统会解析 session,得到 sessionId、sessionKey、会话存储、是否新会话、持久化 thinking / verbose 设置等信息;还会解析 Agent 所属工作目录、Agent 目录以及模型 manifest 上下文。(GitHub)
进入真正执行阶段后,源码会调用 acpManager.runTurn,并监听运行过程中的事件。对于 text_delta 事件,系统会把模型输出的增量文本累积起来,再通过运行时事件发送出去。(GitHub)
执行结束后,源码会整理最终文本,持久化 transcript,并调用投递逻辑 deliverAgentCommandResult 处理最终输出或消息发送。(GitHub)
因此,嵌入式执行链路可以写成:
agentCommand
↓
prepareAgentCommandExecution
↓
解析会话、Agent、工作区、模型上下文
↓
准备 skills snapshot
↓
acpManager.runTurn
↓
接收 text_delta / done 等事件
↓
累积最终回复
↓
保存 transcript
↓
输出或投递结果
9. 整体流程图
用户执行:
openclaw agent --agent ops --message "Summarize logs"
↓
agentCliCommand
↓
规范化参数、校验 sessionKey、生成 runId
↓
判断是否 --local
↓
┌─────────────────────────────┐
│ Gateway 优先路径 │
│ agentViaGatewayCommand │
└─────────────────────────────┘
↓
校验 message / session selector / agentId
↓
解析 sessionKey / timeout / model override
↓
callGateway({ method: "agent", params: ... })
↓
Gateway 执行 Agent turn
↓
返回 response
↓
JSON 输出或 payload 文本输出
↓ 如果 Gateway 失败或超时
┌─────────────────────────────┐
│ embedded fallback │
│ agentCommand │
└─────────────────────────────┘
↓
解析配置、会话、Agent、工作区、模型、技能
↓
acpManager.runTurn
↓
保存 transcript
↓
返回或投递结果
10. 本期小结
这一期我们重点分析了 openclaw agent 命令的执行链路。它表面上只是一个发送消息的 CLI 命令,但源码中实际包含了完整的 Agent 运行调度逻辑。
它的核心设计可以总结为三点:
第一,openclaw agent 是一次 会话化 Agent turn,不是简单的大模型 completion。系统会通过 sessionKey、sessionId、agentId 等信息明确上下文归属。
第二,OpenClaw 采用 Gateway-first 的执行策略。正常情况下,CLI 把请求交给 Gateway,由 Gateway 统一处理 Agent、模型、工具、通道和消息投递。
第三,OpenClaw 设计了 embedded fallback 机制。当 Gateway 路径失败或超时时,CLI 可以回退到本地嵌入式执行,从而提高命令可用性。
理解了这一期之后,我们就能更清楚地看出 OpenClaw 的核心思想:它不是把 LLM 调用简单包装成 CLI,而是围绕“多 Agent、多会话、多通道、可回退执行”构建了一套完整的 Agent 运行基础设施。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)