跳转至

智能体循环

agent 循环是一个序列化的、按会话(session)执行的运行过程,它将消息转化为动作和回复:输入接收、上下文组装、模型推理、工具执行、流式传输、持久化。

入口点

  • Gateway RPC:agent 和 agent.wait。
  • CLI:openclaw agent。

运行序列

  1. agent RPC 验证参数,解析会话(sessionKey/sessionId),持久化会话元数据,并立即返回 { runId, acceptedAt }。
  2. agentCommand 运行该轮次:解析模型以及 thinking/verbose/trace 默认值,加载技能快照,调用 runEmbeddedAgent,并在嵌入式循环未发出 lifecycle end/error 事件时发出一个回退事件。
  3. runEmbeddedAgent:通过按会话队列和全局队列对运行进行序列化,解析模型和 auth 配置文件,构建 OpenClaw 会话,订阅运行时事件,流式传输 assistant/tool 增量,强制执行运行超时(超时即中止),并返回载荷及用量元数据。对于 Codex app-server 轮次,原生 Codex 负责 provider 存活状态以及确切的 turn/completed 结果;静默期和 assistant 输出不会结束该轮次。
  4. subscribeEmbeddedAgentSession 将运行时事件桥接到 agent 流:工具事件映射到 stream: "tool",assistant 增量映射到 stream: "assistant",生命周期事件映射到 stream: "lifecycle"(phase: "start" | "finishing" | "end" | "error")。
  5. agent.wait 等待某个 runId 的终态结果,并返回 { status: ok|error|timeout, startedAt, endedAt, error? }。Gateway RPC 运行还会等待其终态重放(terminal replay)载荷被发布,因此在获得终态等待结果后的重复请求可以重放该结果。

对于嵌入式 OpenAI Responses 轮次,response.completed 会完成一次模型响应。如果 provider 发送 end_turn: false,那么即使已完成的响应只包含文本,循环也会请求另一个响应。现有的取消操作、宿主(host)停止决策以及有意的工具终止仍然适用。

每个已完成或未完成的 Responses 响应还会在保存的 assistant 消息的 diagnostics 中携带一个 openai_responses_terminal 条目。它记录 eventType 和 provider 的 endTurn 信号,取值为 true、false、"absent" 或 "invalid",不会保留格式错误的值。解读时请同时参考消息的 stopReason 和文本阶段。这些按响应记录的事实会在同一轮次的后续响应之后仍然存在,且无需原始流日志。它们记录的是 provider 信号,而不是宿主停止或取消是否阻止了继续执行。没有该诊断条目的旧消息无法确定 provider 是否省略了信号。

等待结果还携带该运行的 terminalReply,并在可用时携带 terminalReceipt。带有 sourceReplyDelivered: true 的回执确认最终回复已送达外部源会话。A2A 公告会使用该事实,而不是将显示历史镜像作为递送证据。

排队与并发

运行按会话键(session lane,即会话通道)进行序列化,并可选择性地通过全局通道(global lane)序列化,从而防止工具/会话竞争。消息通道选择一种队列模式(steer/followup/collect/interrupt)来接入该通道系统;参见 命令队列。

在流式传输之前,获得准入的运行会记录其持久化的 activeWriterRunId 声明。每次转录追加或重写都提供 expectedWriterRunId,同步提交事务会验证它是否仍然与活动声明匹配。因此,被取代的运行无法提交过时的转录数据。SQLite 写入者队列对每个 agent 的变更进行排序,而 Gateway 状态目录锁可防止另一个 Gateway 或 openclaw agent --local 进程同时拥有同一状态目录。

会话与工作区准备

  • 解析并创建工作区;沙箱化运行可能会重定向到沙箱工作区根目录。
  • 加载(或从快照复用)技能,并注入到 env 和 prompt 中。
  • 解析 bootstrap/上下文文件,并注入到系统 prompt 中。
  • 在流式传输开始之前准备好会话转录目标和写入者声明。后续的重写、压缩(compaction)和截断操作使用相同的事务内写入者声明围栏。

Prompt 组装

系统 prompt 由 OpenClaw 的基础 prompt、技能 prompt、bootstrap 上下文以及每次运行的覆盖(per-run overrides)构建而成。会强制执行模型特定的限制和压缩预留 token。关于模型实际看到的内容,参见 系统 prompt。

Hooks

OpenClaw 有两个进程内 hook 系统:

  • 内部 hooks(Internal hooks):用于命令和生命周期事件(如 command:new)的 HOOK.md 脚本。
  • 插件 hooks(Plugin hooks):位于 agent/tool 生命周期和 Gateway 管道中的类型化 api.on(...) 处理器,例如 before_tool_call。

HTTP webhooks 是独立的:它们接受触发工作的外部请求,而不是订阅 agent 循环事件。

内部 hooks(Gateway hooks)

  • agent:bootstrap:在系统 prompt 最终确定之前构建 bootstrap 文件时运行。使用它来添加或移除 bootstrap 上下文文件。
  • 命令 hooks(Command hooks):核心会发出 command:new、command:reset 和 command:stop。其他命令名称不会自动成为 hook 事件。

设置和示例参见 Hooks。

插件 hooks

这些 hook 在 agent 循环或 Gateway 管道内运行:

Hook 运行时机
钩子 运行时机
before_model_resolve 会话前(无 messages),用于在解析前确定性地覆盖提供商/模型。
before_prompt_build 会话加载后(包含 messages),用于注入 prependContext、systemPrompt、prependSystemContext 或 appendSystemContext;或者,在支持按轮次范围的已提交工具表面的运行时中,使用 toolsAllow 对其进行收窄。空的 toolsAllow 表示不提交任何可选工具;省略则保持宿主解析出的表面不变。不支持的运行时将拒绝限制性值,而不是忽略它们。
before_agent_reply 内联操作之后、LLM 调用之前。允许插件接管该轮次,并返回合成回复或完全静默。
agent_end 完成后,携带最终消息列表和运行元数据。
before_compaction / after_compaction 观察压缩周期;这些钩子不会重写或否决压缩。
before_tool_call / after_tool_call 拦截工具参数/结果。
before_install 在操作员安装策略运行之后,针对已暂存的技能/插件安装材料,且当前进程中已加载插件钩子时。
tool_result_persist 在工具结果写入由 OpenClaw 拥有的会话记录之前,同步转换工具结果。
message_received / message_sending / message_sent 入站和出站消息钩子。
session_start / session_end 会话生命周期边界。
gateway_start / gateway_stop 网关生命周期事件。
钩子 运行

出站/工具守卫的钩子决策规则:

  • before_tool_call:{ block: true } 是终止性的,并停止较低优先级的处理器。{ block: false } 是空操作,不会清除先前的阻止。
  • before_install:与上述相同的终止/空操作语义。对于必须由操作员拥有的安装允许/警告/阻止决策,且必须覆盖 CLI 安装和更新路径,请使用 security.installPolicy,而不是 before_install。
  • message_sending:{ cancel: true } 是终止性的,并停止较低优先级的处理器。{ cancel: false } 是空操作,不会清除先前的取消。

有关钩子 API 和注册详情,请参阅 插件钩子。

Harness 可以适配这些钩子。Codex app-server harness 将 OpenClaw 插件钩子保留为已文档化的镜像表面的兼容性契约;Codex 原生钩子是另一个更低层的 Codex 机制。

流式传输

  • 助手增量从代理运行时以 assistant 事件流式传输。
  • 已在提供程序事件队列中等待的相邻文本追加可以在代理交付之前合并。这不会增加缓冲延迟;快照、内容块变更、推理、工具和终止事件仍保持为独立边界。
  • 块流式传输可以在 text_end 或 message_end 上发出部分回复。
  • 推理流式传输可以是单独的流或块回复。
  • 有关分块和块回复行为,请参阅 流式传输。

工具执行

  • 工具开始/更新/结束事件在 tool 流上发出。
  • 工具结果在记录/发出前会针对大小和图像负载进行清理。
  • 会跟踪消息工具的发送,以抑制重复的助手确认。

回复整形

最终负载由助手文本(加上可选推理)、内联工具摘要(当详细且允许时)以及模型出错时的助手错误文本组装而成。

  • 精确的静默标记 NO_REPLY 会从传出负载中过滤掉。
  • 消息工具重复项会从最终负载列表中移除。
  • 回退工具错误警告仅在一次运行以工具失败结束,且否则会使用户没有回复时出现。此守卫不可配置;面向用户的回复(包括已由消息工具交付的回复)会阻止该警告。

宿主决定输入是否需要可见回复。直接请求和已接受的群组/频道请求默认需要回答。未指向的群组请求仅在操作员明确允许 静默策略 时保持可选;提及和授权命令仍需要响应。环境房间事件和内部辅助轮次保持可选。模型生成的 NO_REPLY 是空输出,而不是豁免必需响应的权限;没有已交付回复的必需轮次仍需要回答。

如果必需回复的轮次在完全结算的工具批次之后结束,但没有已组成的答案,OpenClaw 可以执行一次无工具的最终化传递。先前的工具错误、工具前进度以及被取代的、未交付的确认不计为最终答案。此传递使用已结算的结果,并且不重复已完成的工具。致命自动化失败(包括被拒绝的执行)即使最终化产生答案,仍保持为失败。

已确认的交付会阻止重复生成。待处理交付或继续工作保留完成所有权,而不会被标记为已交付;被拒绝的发送、未刷新的延迟文本和缺失的交付回调不是交付证明。NO_REPLY 不会撤回已交付的文本。待处理工具、已接受的子运行和已让出的工作保留其现有所有者,助手错误和中断不是有意静默。

提示段诊断将附件/上下文块和生成的传入元数据与用户文本分开归因。仅包含这些块的提示不需要尾随用户文本即可完成回复处理。

压缩与重试

当 OpenAI Responses 请求在生成工具调用时达到其输出限制时,内置 harness 会完成已接纳的工具,并从其记录的结果重试。未完成的调用永远不会执行。恢复使用现有的有界会话重试预算,并且仍可取消;拒绝和不一致的终止响应不符合此继续条件。

自动压缩发出 compaction 流事件,并可能触发重试。重试时,内存缓冲区和工具摘要会重置,以避免重复输出。请参阅 压缩。

事件流

  • lifecycle:由 subscribeEmbeddedAgentSession 发出(并由 agentCommand 作为回退发出)。
  • assistant:来自代理运行时的流式增量。
  • tool:来自代理运行时的流式工具事件。

Gateway 将生命周期和工具开始/终止事件投影到有界的、仅元数据的 审计账本。此投影记录来源和结果代码,而不会将提示、消息、工具参数、工具结果或原始错误从转录/运行时路径中复制出去。

聊天通道处理

助手增量缓冲到聊天 delta 消息中。终止生命周期事件产生聊天 final、error 或 aborted 消息。确定性取消和超时事件会立即最终化,包括当运行时将它们报告为 phase: "error" 时。可重试错误保留 15 秒宽限窗口,用于同一运行的回退或重启。一旦外层执行所有者完成其尝试,它会发布 executionSettled: true。Gateway 会消费该事实而不进行重试宽限,包括从未到达模型或发出回退步骤的准备失败。对于 Gateway RPC 运行,agent.wait 还会在必需结算后加入终止重放发布。未标记的超时和裸中止观察保留其现有等待层重试处理。

Cron 尝试完成状态在模型回退和临时确认重试期间保持为 finishing;worker finishing 事件不会声明执行结算。已完成的执行事实会在 cron 簿记之前捕获,但最终性只在执行结算后发布。新尝试会在准备之前清除先前结果。后续工作流错误或中止不能重新分类已完成的执行;cron 持久化、投递和 yielded-parent 延续保留各自独立的结果。

历史在其终端会话写入待处理期间保持运行处于活动状态。一旦该写入成功,历史和会话活动会显示记录结束时间和持续时间,而无需等待重试宽限期。

实时快照的范围限定于其助手消息。更正可以缩短或清除当前预览,而不会擦除较早的消息。待处理文本会在终端事件之前刷新;控制实时更新的节奏不会延迟工具执行或转录写入。

运行持续时间元数据属于当前运行,包括在模型启动前准备失败的情况。在 Control UI 的已完成工作汇总中,独立发送具有独立的耗时边界:失败的回合以及下一次发送前的空闲时间不属于该下一回合的工作。Steering 仍与其目标运行关联,而不是被视为独立重试。

超时

在 agent.wait 截止时间之前没有可用结果时,响应仅包含 runId 和 status: "timeout"。 它不会取消运行,也不会标识其执行阶段;再次等待同一个 runId 以观察完成。被 Gateway 生命周期关闭中断的等待会包含 timeoutPhase: "gateway_draining",且没有终端元数据。 已知排队中的聊天回合报告 status: "pending"、timeoutPhase: "queue" 和 providerStarted: false。

超时 默认值 说明
agent.wait 30s 仅等待;timeoutMs 参数可覆盖。不会停止底层运行。
Agent 运行时(agents.defaults.timeoutSeconds) 172800s (48h) 已消耗执行预算,到期时中止。进度不会重置它。设置 0 表示无限执行;运行时拥有的 provider 存活检查仍然适用。Codex 按尝试强制执行此预算,独立于其原生流恢复。
超时 默认值 说明
CLI 后端无输出看门狗 按每次新建/恢复的 CLI 运行计算 独立于代理运行时,由已注册的后端插件负责。CLI 内部后台任务共享父进程,并且不会在整体代理超时之后继续存活。
Cron 隔离代理轮次 由 cron 负责 调度器在执行开始时启动自己的计时器,在配置的截止时间中止运行,然后在记录超时前执行有界清理,以免过期的子会话使通道卡住。
模型空闲超时 云端 120s;自托管 300s 当在空闲窗口内未收到响应块时,OpenClaw 会中止模型请求。models.providers.<id>.timeoutSeconds 会为较慢的本地/自托管提供商扩展此空闲看门狗,但仍受任何更低的有限 agents.defaults.timeoutSeconds 或运行特定超时限制,因为这些值控制整个代理运行。无限运行预算仍保留提供商级空闲看门狗。未显式设置模型/代理超时的 Cron 触发云端模型运行使用相同默认值;若显式设置 cron 运行超时,云端模型流停滞上限为 60s,以便在外部 cron 截止时间之前仍可运行已配置的模型回退。针对真正本地端点(loopback/私有 baseUrl)的 Cron 触发运行保留本地空闲豁免;使用网络 baseUrl 的自托管提供商获得 300s 隐式看门狗。若显式设置 cron 运行超时,本地/自托管停滞上限为该超时值。对于较慢的本地提供商,请设置 models.providers.<id>.timeoutSeconds。
提供商 HTTP 请求超时 models.providers.<id>.timeoutSeconds 涵盖连接、请求头、请求体、SDK 请求超时、受保护 fetch 的中止处理,以及该提供商的模型流空闲看门狗。对于较慢的本地/自托管提供商(例如 Ollama),请先使用它,再提高整个代理运行时超时;当模型请求需要运行更长时间时,请保持代理/运行时超时至少不低于该值。

内置的 OpenClaw harness 会将其执行截止时间发布到队列中。审批等待会暂停未使用的预算;处理完所有待审批事项后恢复同一预算。压缩(compaction)可以获得一次有界宽限期。来自 ask_user 的问题不会暂停整体执行预算。当设置为 0 时,不会启动执行计时器,但 provider 活性、Stop 以及有界中止清理仍然适用。隔离的工具后终结过程有自己的截止时间和取消控制,而不是继承已完成尝试中的回调。

终止性超时代表一轮失败,而不是成功完成。聊天和命令结果会保留该超时的说明;较早的工具错误不会替换该说明,也不会重启一个已经终态化的超时轮次。

模型空闲和 provider HTTP 行描述的是内置 OpenClaw 模型路径。Codex 拥有其原生流截止时间和网络重试机制。在确切收到原生终态之后,OpenClaw 允许两分钟用于本地结算。在单独的有界中止清理之后,排队投影获得五秒钟排空宽限期。这两个窗口都不会因进度而重置。当执行预算不受限制时,这些清理限制仍然适用。参见 Codex 超时。

当运行时报告确定的超时时,Gateway 会立即将会话侧边栏的终态状态和错误记录下来,而不会等待 provider 重试宽限期。打开失败的会话会像往常一样消除其侧边栏提醒。后续成功的一轮会清除之前的错误,并且不会被较早的延迟失败所替换。

会话卡住诊断

启用诊断后,一个内置的两分钟阈值会将长时间处于 processing 状态且未观察到回复、工具、状态、块或 ACP 进展的会话归类为:

  • 活跃的嵌入式运行、模型调用和工具调用报告为 session.long_running。归属的静默模型调用在达到中止阈值之前一直保持 session.long_running,这样缓慢或不流式传输的 provider 不会被过早标记为停滞。
  • 没有近期进展的活跃工作报告为 session.stalled。归属的模型调用在达到或超过中止阈值时切换为 session.stalled;无归属的过期模型/工具活动不会被隐藏为长时间运行。
  • session.stuck 保留用于可恢复的过期会话簿记,包括具有过期无归属模型/工具活动的空闲排队会话。

中止阈值至少为 5 分钟,并且是警告阈值的 3 倍。过期的会话簿记会在恢复门通过后立即释放受影响的会话通道;停滞的嵌入式运行仅在中止阈值之后才会被中止排空,因此排队的工作可以恢复,而不会切断仅仅较慢的运行。恢复会发出结构化的请求/完成结果;仅当同一处理代仍然当前时,诊断状态才会被标记为空闲;并且当会话保持不变时,重复的 session.stuck 诊断会退避。

关注与恢复日志行仅在其日志级别启用时才会读取可选的会话上下文。转录增强在后台读取工作线程中运行,最多返回 140 个字符;它绝不会延迟分类或恢复。会话替换会丢弃待处理的增强,停止诊断会撤销待处理的日志发布。隐身回复仍然被排除在外。

待处理的人工输入问题会保护其确切的活动属主免受过期工作恢复的影响。如果检查一个问题使其过期,或者诊断报告恢复或替换了该运行,则该观察结果不能授权中止恢复后的工作。恢复会重新验证随观察结果捕获的会话代。

当前正在等待原生工作的 Codex 尝试是这些空闲阈值的例外:它会以 runtime_owned_wait 原因保持 session.long_running,并且不会仅仅因为安静而被中止或接管。恢复和引导会重新验证确切的活动属主及其执行预算。这不会保护过期的 OpenClaw 拥有的请求或工具、取消、终态结算或无归属状态。

可能提前结束的情况

  • 智能体超时(中止)
  • AbortSignal(取消)
  • 网关断开或 RPC 超时
  • agent.wait 超时(仅等待,不会停止智能体)
  • 工具 - 可用的智能体工具
  • 钩子 - 由智能体生命周期事件触发的事件驱动脚本
  • 压缩 - 长对话如何被摘要
  • 执行审批 - shell 命令的审批关卡
  • 思考 - 思考/推理级别配置
  • 智能体运行时 - 驱动此循环的替代 harness 运行时

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 cl/openclaw