OpenClaw 调试
本地测试、实时模型、提供者、代码模式、工具搜索、Crabbox、CI之间的行为各不相同:在猜测之前先获取调试信号。
首先阅读
docs/logging.md:日志文件,openclaw logs,有针对性的调试标志。docs/reference/test.md:本地测试命令。docs/tools/code-mode.md:代码模式执行/等待 + 工具目录规则。$openclaw-testing:测试通道选择。$crabbox:广泛的、Docker、包、Linux、实时密钥、CI 对等性证明。
默认循环
- 声明怀疑的边界:配置、工具构建、提供者负载、获取、 流/SSE、转录重放、工作器/运行时、包/分发、CI。
- 添加/启用能够证明边界的最窄信号。
- 使用相同的提供者/模型/配置重现。除非模型本身是测试变量,否则决不要随机切换模型。
- 将配置状态与实际运行激活状态进行比较。
- 修补根本原因。
- 重新运行确切的失败探针;仅在契约要求时扩大范围。
模型传输日志
模型请求形状/流时间:使用有针对性的环境标志而不是全局调试:
OPENCLAW_DEBUG_MODEL_TRANSPORT=1 openclaw gateway
OPENCLAW_DEBUG_MODEL_PAYLOAD=tools OPENCLAW_DEBUG_SSE=events openclaw gateway
OPENCLAW_DEBUG_MODEL_PAYLOAD=full-redacted OPENCLAW_DEBUG_SSE=peek openclaw gateway
有用的标志:
OPENCLAW_DEBUG_MODEL_TRANSPORT=1:请求开始、获取响应、SDK 标头、第一个 SSE 事件、流完成、传输错误(级别为info)。OPENCLAW_DEBUG_MODEL_PAYLOAD=summary:有界的负载摘要。OPENCLAW_DEBUG_MODEL_PAYLOAD=tools:所有面向模型的工具名称。OPENCLAW_DEBUG_MODEL_PAYLOAD=full-redacted:有上限、已编辑的 JSON 负载。 仅在调试时使用;提示/消息文本可能仍会出现。OPENCLAW_DEBUG_SSE=events:第一个事件 + 流完成时间。OPENCLAW_DEBUG_SSE=peek:前五个经过编辑的 SSE 事件。OPENCLAW_DEBUG_CODE_MODE=1:代码模式工具面诊断。
查看日志:
openclaw logs --follow
常见边界
- 配置与激活: 已启用的配置可能与禁用的运行工具、原始运行、空允许列表、缺失的模型工具支持共存。在强制执行提供者负载不变量之前,先检查实际可见的工具。
- 工具面: 检查最终模型可见的工具名称,而不仅仅是注册表/配置。代码模式在实际激活后恰好是
exec+wait。 - 提供者负载: 在 SDK 调用之前,记录字段、模型 ID、服务层、推理、输入大小、元数据键、提示缓存键存在性、工具名称。
- 获取与 SSE: 获取响应证明 HTTP 标头已到达;第一个 SSE 事件证明提供者主体已取得进展。此处的间隙 = 流/主体/提供者问题,而非工具执行。
- 工作器/分发: 涉及工作器、动态导入、包导出、懒惰运行时边界、发布路径 →
pnpm build。 - 实时密钥: 缺少提供者密钥 → 在声明实时证明受阻之前,先检查配置的密钥工作流程。环境检查仅检查存在性;绝不打印密钥。
获取会话和转录
聊天会话错误报告:获取实际的转录、发送者归属、附件作为证据。
首先使用CLI(需要配置安装;对实时网关安全):
openclaw sessions list --agent <agentId> --json
openclaw sessions tail
openclaw sessions export-trajectory
文档:存储布局 docs/reference/database-schemas.md;CLI https://docs.openclaw.ai/cli/sessions。
原始存储:当CLI不可用时(例如通过SSH远程主机)或需要事件级细节:
- 每个代理的数据平面:
~/.openclaw/agents/<agentId>/agent/openclaw-agent.sqlite。 规范模式:src/state/openclaw-agent-schema.sql。 - 托管/systemd状态:服务用户的主目录(例如
/home/openclaw/.openclaw/...), 而不是root。root可能拥有包含不同代理的单独孤立安装。 预期的代理目录丢失:使用以下命令定位真实数据库find / -maxdepth 6 -name openclaw-agent.sqlite。 - Web聊天URL会话ID片段:
/chat/<agentId>/<slug>-<hex>。 在session_nodes中解析:session_key LIKE '%<hex>%'→current_session_id,display_name。键的形状:agent:<agentId>:<surface>:<uuid>;子代理会话使用表面subagent。 - 转录:
transcript_events(session_id,seq,event_json)。event_json.message:role(user/assistant/toolResult)+content(字符串或类型为text/toolCall/image的部分)。 - 发送者来源:真实用户消息携带
message.__openclaw(senderId,senderName,senderIsOwner);运行时合成的输入则不包含。 使用此字段区分操作员编写的文本和注入的提示。 - 全文转录搜索:
session_transcript_fts。 - 附件:
media://inbound/<file>网址 →<state-dir>/media/inbound/<file>。 - 用户附带的图片 ≠
image内容部分。消息信封携带message.__openclaw.media[]条目:url(media://inbound/<file>)、contentType、kind、fileName。仅提取部分的提取器会错过所有图片; 在原始event_json中搜索media://。远程主机:本地 scp 文件 (每个 scp 参数一个远程路径);在那里读取。
没有 sqlite3 二进制文件的主机仍有 Node:node:sqlite 无需依赖。
node -e 'const {DatabaseSync}=require("node:sqlite");
const db=new DatabaseSync(process.argv[1],{readOnly:true});
console.log(JSON.stringify(db.prepare(
"SELECT seq,event_json FROM transcript_events WHERE session_id=? ORDER BY seq"
).all(process.argv[2])))' <db-path> <session-id>
- 始终以
readOnly: true打开实时存储;切勿写入正在运行的网关状态 (根AGENTS.md验证规则)。 - 使用真实数据工作:首先将数据库复制到开发状态目录。
代码指针
- 模型负载 + 响应流:
src/agents/openai-transport-stream.ts - 受保护的获取/时间:
src/agents/provider-transport-fetch.ts - OpenAI/Codex 提供者包装器:
src/llm/providers/stream-wrappers/openai.ts - 工具构建、工具搜索、代码模式激活:
src/agents/embedded-agent-runner/run/attempt.ts - 代码模式运行时和工作器:
src/agents/code-mode.tssrc/agents/code-mode.worker.ts - 工具搜索目录:
src/agents/tool-search.ts
证明选择
- 单个帮助器/负载错误:本地有针对性的 Vitest。
- 仅文档/日志:
pnpm check:docs+git diff --check。 - 工作器/分发/懒惰导入/包表面:有针对性的测试 +
pnpm build。 - 实时提供者/模型行为:相同的提供者/模型、调试标志、如有可用的真实密钥。
- Docker/包/Linux/CI 对等性:有能力时使用当前的专用 Linux 工作器;
否则使用
$crabbox。 - CI 失败:确切的 SHA、仅相关的作业;仅在失败/完成后查看日志。
输出习惯
报告:
- 测试的边界
- 确切的命令/环境形状(已编辑)
- 观察到的信号,例如工具名称或第一个 SSE 事件时间
- 修复位置
- 窄证明 + 剩余风险