从一次 read 工具调用,看懂 OpenClaw Agent Runtime 的执行过程

1 阅读10分钟

很多人对 Agent 的印象就是:"写一段 Prompt,调一次模型,拿回一段文本"。但在 OpenClaw 里,一次 Agent 执行是一条完整的链路:Gateway 负责接入,Session 负责状态锁定,Agent Runtime 驱动模型循环,Tool Runtime 执行工具动作,Audit 和 Task 分别做审计和任务追踪。

fig1-pipeline.png

本文先按官方的分层设计,说明这条链路和理论上的执行时序;然后通过一次 "只读一个本地文件" 的实验(Mission 011)给出真实证据;最后总结 OpenClaw 的运行时设计。


1. Provider / Model / Agent Runtime / Channel 是四个不同层次

初学者最容易把 "Provider"、"Model"、"Runtime" 混为一谈,因为它们都出现在模型配置附近。官方把它们明确拆成不同的层(来源: Agent runtimes):

fig2-layers (1).png

层次职责示例
Provider认证、模型发现、请求传输deepseek-officialanthropicopenai
Model当前 Agent Turn 选定的具体模型deepseek-v4-flashclaude-opus
Agent Runtime驱动模型循环、处理 Tool Call、交回完成的 Turnopenclaw(内嵌)、codexclaude-cli
Channel消息进出 OpenClaw 的位置CLI、Slack、Telegram

说明:一个 Agent Runtime 独占一个"已准备好的模型循环"——接收 Prompt、驱动模型输出、处理原生 Tool Call,再把完成的 Turn 交还给 OpenClaw。提供某个 Runtime 具体实现的代码,官方称为 harness(如 openclawcodex内置Runtime)。

因此 deepseek-official/deepseek-v4-flash 只描述了 Provider 与 Model,它并不"运行" Agent;真正持有工具循环的是 Agent Runtime。本实验用的是内嵌 openclaw runtime——官方的 auto 策略在没有插件 harness 认领该 Provider/Model 时,会回退到这个内置运行时(Runtime selection)。

从源码结构也能看出这条运行时边界(来源:Agent runtime architecture):

模块主要职责
src/agents/embedded-agent-runner/内置 attempt loop、Provider 流适配、Compaction、模型选择、Session 接线
src/agents/runtime/@openclaw/agent-core 的门面与本地代理工具
packages/agent-core/可复用的 Agent core、消息、工具与 Session 契约
src/agents/sessions/Session 持久化、资源发现、Prompt、Skills
src/agents/agent-tools*.tsTool 定义、参数 Schema、Tool Policy、调用前后适配
src/agents/agent-hooks/Compaction safeguard、Context pruning
src/llm/Provider 注册、模型传输与流式实现

Agent 循环和工具执行都在 OpenClaw 这一侧;Provider 只负责模型通信。这也决定了后面所有证据的归属——工具不是模型自己调用,是运行时替它执行的。


2. 理论时序:一次 Run 是按 Session 串行的闭环

官方定义(转述):Agent Loop 是一次按 Session 串行的运行,把一条消息变成一系列动作与一个回复,依次经过接入、Context 组装、模型推理、Tool 执行、流式输出与持久化。入口有两个:Gateway RPC(agent / agent.wait)与 CLI(openclaw agent)。(来源: Agent loop)

sequenceDiagram
    autonumber

    participant C as CLI / Channel / Cron
    participant G as Gateway
    participant T as Task Ledger
    participant S as Session Store
    participant R as Agent Runtime
    participant P as Model Provider
    participant X as Tool Runtime
    participant A as Audit Ledger

    C->>G: 提交消息与 sessionKey
    G->>S: 解析或创建 Session
    G->>T: 为可追踪运行创建 Task
    G->>R: 启动 Agent Run

    R->>S: 获取 Session 写锁
    R->>R: 加载 Workspace / Skills / Tool Schema
    R->>R: 组装 System Prompt 与 Context

    R->>P: Model Request #1
    P-->>R: Tool Call (stopReason=toolUse)

    R->>A: tool_action.started
    R->>X: 执行 Tool
    X-->>R: Tool Result
    R->>A: tool_action.finished

    R->>P: Model Request #2 + Tool Result
    P-->>R: Final Answer (stopReason=stop)

    R->>S: 持久化消息、Usage 与运行状态
    R->>A: agent_run.finished
    R->>T: 更新 Task 终态
    G-->>C: 返回最终结果

三个设计要点:

  1. 串行与加锁:Run 通过"每个 Session 一个队列 + 一个全局队列"保证串行执行,避免 Tool 和 Transcript 的竞争。Transcript 写入额外受一把基于文件的 Session 写锁保护(进程感知,默认超时 60 秒,非可重入)。
  2. System Prompt 每 Run 重建:模型接收的不只是用户消息,还有 Tool Schema、Skills 元数据、Workspace 引导文件、运行时信息与对话历史。
  3. Tool Call 会触发一个完整的执行环路:只要模型返回 Tool Call,运行时就必须执行对应的工具,然后把工具的返回结果注入下一次模型请求。由此得到本文核心命题:

一次 Agent Run ≠ 一次模型请求


3. 实验设计:用最小动作暴露完整链路

实验任务极简:

让 Agent 读取一个本地 Markdown 文件,返回文件中预先嵌入的唯一标识(Marker),并返回文件前三行。

/root/openclaw/lab/runtime-anatomy-mission-011/input/runtime-sample.md

Prompt 中没有写入 Marker 的实际值,所以只要 Agent 返回了正确的 Marker,就证明它确实读到了文件——这是验证工具是否真实执行的锚点。

运行环境:

字段实际值
OpenClawv2026.7.1-2
Agentmain
Provider / Modeldeepseek-official / deepseek-v4-flash
Agent Runtime内嵌(embedded,auto 策略,未走 ACP 远程 Runtime)
Toolread
Session Keyagent:main:mission011-20260724-135327
Session IDd1ca7f12-ca11-438d-98f8-91ecaf632511
成功 Run IDd996b4e1-305b-4791-bc7a-c60bcb07fee8

本次实验将从五个数据来源采集证据:

  • Agent 结果:agent-result.json
  • 会话记录:Session JSONL Transcript
  • 审计事件:audit-run.json / audit-tools.json
  • 运行时日志:Gateway / systemd 日志
  • 任务状态:tasks-after.json

4. 实验结果:模型 → Tool → 模型 的两阶段闭环

成功 Run 由两次模型请求构成,中间夹一次本地读文件:

阶段Input TokensOutput TokensStop Reason作用
Model Request #125163toolUse生成 read Tool Call
Model Request #216465stop依据 Tool Result 产出最终回答

Tool 调用参数与执行结果:

{ "path": "/root/openclaw/lab/runtime-anatomy-mission-011/input/runtime-sample.md" }
指标结果
调用次数 / 状态1 次 / succeeded
Tool 耗时(Audit)约 5 ms
Run 总耗时5,020 ms(durationMs: 5020)

实际循环:

sequenceDiagram
    autonumber

    participant R as Embedded Agent Runtime
    participant D as deepseek-v4-flash
    participant X as read Tool
    participant S as Session / Transcript

    R->>D: Request #1
    D-->>R: stopReason=toolUse · read(path)
    R->>X: 读取 runtime-sample.md
    X-->>R: 文件正文与 Marker
    R->>S: 记录 Tool Call / Tool Result
    R->>D: Request #2 + Tool Result
    D-->>R: stopReason=stop · 最终回答
    R->>S: 持久化 Assistant 消息与 Usage

两个可以直接读出来的结论:

其一,延迟由模型与运行时主导,而非本地 Tool。 read 仅约 5 ms,占 Run 总时长的千分之一;5.02 s 中绝大部分是两次模型请求与运行时处理。对本地工具做性能优化收益极低。

fig3-latency.png

其二,Tool Result 是第二阶段的模型输入。 Request #2 的 input(164)小于 Request #1(251),却已携带 Tool Result,说明注入 Tool Result 的同时,前序 Context 经过了运行时侧的裁剪/重组,而非简单累加。


5. Prompt 成本主要来自系统层

用户请求很短,但模型实际看到的输入是官方定义的一组固定内容拼接而成:基础 Prompt + Skills Prompt + Bootstrap Context + 每轮覆盖项,并在提交前强制执行模型上限与 Compaction 预留(来源:System prompt)。

从 agent-result.json 可以看到本次 Prompt 的组成:

组成数量
Workspace 文件7
Skill 条目20
Tool Schema23
System Prompt 总字符32,255

仅 Tool Schema 一项就占了 12,978 字符(约四成) ,其余部分(基础 Prompt / Workspace / Skills / 历史)合计 19,277 字符。

这里引出两条工程含义:

  • 短消息不代表低 Context 成本。  用户消息很短,但系统层组成的 Prompt 可能远大于用户输入。
  • Tool 数量增长会同时抬高文本说明与 JSON Schema 两项开销。  每增加一个 Tool,都要付出双倍代价。

值得注意的是,可用 Tool 数量本身受 Tool Policy 动态裁剪:Gateway 日志显示 crongatewaynodes 三个 Tool 被策略移除后才进入 Schema 组装。因此"23 个 Tool Schema"是策略过滤后的结果,而非 Agent 全量工具集。

本次 Context Budget 为 64,000 tokens、预留 24,000 tokens,报告 shouldCompact: false——没有证据表明该成功 Run 触发了 Compaction。


6. runtime=cli 与 Agent Runtime 是两个维度

本次实验的 openclaw agent 命令在前台 Shell 中执行,同步等待结果,但 tasks-after.json 中仍然生成了 Task 记录:

{
  "runtime": "cli",
  "runId": "d996b4e1-305b-4791-bc7a-c60bcb07fee8",
  "status": "succeeded",
  "terminalSummary": "completed"
}

这里需要区分两个 runtime 的含义:

  • Agent Runtime:执行模型循环的后端,本次使用的是 OpenClaw 内置运行时(embedded runtime);
  • Task runtime:Task 的来源类型,本次的来源是 CLI。

换句话说:Task 的 runtime: "cli",并不等于 Agent 用了 CLI 作为后端运行时。两者是完全不同的维度。

在 OpenClaw 的 Task 设计里,通过 Gateway 派发的 CLI 命令、Cron 任务、Subagent、ACP 请求,都会被统一纳入 Task 记录。Task 只记录工作的生命周期,不决定谁来调度,更不代表使用了哪种 Agent Runtime。本次前台 CLI 执行也进入了 Task 表,说明 Task 系统的入口比"只记后台任务"更宽。


7. 四类标识,四种作用域

标识作用域
Session Key应用层会话路由键
Session IDSession Store 内部标识(UUID)
Run ID一次 Agent 执行实例
Task IDTask Ledger 中的一条工作记录

这四种标识是从属关系,不是同义词:

Session Key ──映射──> Session ID
     │
     ├──包含多次──> Run ID
     │
     └──关联多条──> Task ID

Task ID ──可关联──> Run ID

在本次实验中,同一个 Session 内先出现一次被中止的 Run(aborted),紧接着又成功完成了一个 Run。这说明 失败的是某一次执行实例,Session 本身不受影响,仍然可以继续使用。而 Task 为可追踪的执行过程附加了独立的生命周期:

queued → running → succeeded / failed / timed_out / cancelled / lost

8. 可观测性:五个数据面各覆盖一部分事实

数据源保存内容含正文主要用途
Session会话状态、模型与 Usage 元数据恢复与管理会话
Transcript用户/Assistant 消息、Tool Call、Tool Result重放具体交互
AuditRun 与 Tool Action 的元数据事件审计顺序、状态、耗时
Gateway LogsProvider、策略、网络与运行时诊断有限故障定位
Tasks可追踪工作的生命周期有限查询运行状态与来源

Audit 只保存元数据,这不是本次实验偶然观察到的情况,而是官方明确的设计:Gateway 把生命周期和工具的起止事件记录到审计日志中,只保存来源和结果码,不复制 Prompt、消息、工具参数、工具结果或原始错误(来源: Agent loop · Audit projection)。本次实验所有 Audit 事件的 redaction 都是 metadata_only,与这个设计一致。

因此,观察一次调用的完整链路,不能只看单一数据源:

Task       → 任务是否完成
Audit      → Run 和 Tool 的执行过程
Transcript → 交换了什么内容
Logs       → 运行时为什么失败

结论:OpenClaw 是一个 Agent Runtime,不是模型 API 的封装

本次实验验证了 OpenClaw 内置 Runtime 的几个核心设计要点:

  1. Runtime 与 Provider 分层——Provider 负责模型通信,Agent Runtime 负责 Prompt、模型循环与 Tool 接线。
  2. 一次 Run 可含多次模型请求——本次成功 Run 由 toolUsestop 两个模型阶段组成。
  3. Tool 由 OpenClaw 执行——模型只产出结构化 Tool Call,本地文件读取由 Tool Runtime 完成。
  4. Tool Result 反哺为下一阶段输入——Tool Call 把 Agent 从单次生成变为闭环执行。
  5. Prompt 成本集中在系统层——Workspace、Skills 与 Tool Schema 可远大于用户消息,且受 Tool Policy 动态裁剪。
  6. Task 来源与 Agent Runtime 正交——runtime=cli 只标注 Task 入口,不代表使用 CLI backend。
  7. Session / Run / Task 生命周期不同——Session 持续存在,Run 是一次执行,Task 是一条可追踪的记录。失败只影响执行实例,Session 和 Task 仍然可用。
  8. 审计需要多个数据源——Task、Audit、Transcript、Logs 各自记录一部分事实,只看任何一个都不足以复现全貌。

综合来看,OpenClaw 在 Provider 之上自行提供了运行循环、状态管理、工具执行、任务追踪与审计能力——它是一个完整的 Agent Runtime,而非模型 API 的薄封装。理解它的关键,是始终区分"哪一层在做事、哪一份记录能证明它"。


OpenClaw 官方扩展阅读(简体中文)

  1. Agent runtime architecture · 智能体运行时架构 — 源码模块划分、运行时边界与 Harness 选择。
  2. Agent loop · 智能体循环 — 从 Gateway RPC 到 Context 组装、推理、Tool 执行与持久化的完整流程,含队列、写锁与超时。
  3. Agent runtimes · 智能体运行时 — Provider、Model、Agent Runtime、Channel 的分层与 auto 选择规则。
  4. System prompt · 系统提示词 — System Prompt 的构建层次、固定区段与运行时注入。
  5. Context · 上下文 — Context 组成、Workspace 注入、Skills 与 Tool Schema 开销。
  6. Tasks · 后台任务 — Task Ledger 与 CLI/Cron/Subagent/ACP 的创建条件与生命周期。
  7. Audit · 审计记录 — Audit Ledger 的数据范围、隐私边界与查询方式。
  8. Sessions · 会话 — Session Store 的查询语义及其与在线状态的区别。