OpenClaw 时间旅行调试与执行回放
凌晨4点,Agent莫名其妙崩溃了——它调用了一个不存在的API,然后陷入了死循环。日志里只有"Error: timeout",鬼知道它当时脑子里装了什么。于是我打开了执行回放——像看监控录像一样,一帧一帧地看它当时在干什么、看到了什么、怎么做的决定。然后我找到了问题的根源:一个变量的值在某个时间点被覆盖了,而它被卡在了错误的状态。
什么是时间旅行调试
时间旅行调试(Time Travel Debugging)允许你:
- 记录执行轨迹:每一步工具调用、状态变化、模型输出都记录下来
- 回放任意时刻:跳转到任意时间点,查看当时的状态
- 单步调试:像IDE一样逐帧执行,定位问题发生的瞬间
- 反向追踪:从错误发生点反向查找根因
启用执行记录
配置执行追踪
# openclaw.config.yaml
execution_trace:
enabled: true
# 记录内容
capture:
- messages # 所有对话消息
- tool_calls # 工具调用(包括参数和返回值)
- state_changes # 状态变更
- llm_outputs # LLM生成内容
- errors # 错误和异常
# 存储配置
storage:
backend: file # file | memory | database
path: ~/.openclaw/traces/
max_size: 100MB # 单个trace文件最大100MB
retention: 7d # 保留7天
# 采样配置(可选,减少开销)
sampling:
rate: 1.0 # 1.0 = 100%记录
# 生产环境可以设置为0.1 (10%采样)
运行时启用追踪
// 为特定会话启用追踪
const session = await sessions_spawn({
task: "分析用户数据",
runtime: "acp",
trace: true // 启用执行追踪
});
// 会话ID即为trace ID
const traceId = session.sessionId;
console.log(`执行追踪ID: ${traceId}`);
回放执行轨迹
查看Trace概览
# CLI查看trace
openclaw trace list # 列出最近的trace
openclaw trace show # 查看trace概览
openclaw trace show --detail # 详细视图
# 输出示例
Trace ID: session_abc123
Duration: 45.2s
Steps: 23
Status: failed
Timeline:
├── [00:00] 开始任务: 分析用户数据
├── [00:05] 工具调用: web_search
├── [00:08] 搜索结果返回 (12条)
├── [00:10] 工具调用: web_fetch
├── [00:15] 解析网页内容
├── [00:20] ⚠️ 模型输出警告...
├── [00:35] 工具调用: exec (失败)
└── [00:45] ❌ 任务失败: timeout
时间点跳转
// API方式回放
// 跳转到特定时间点
const snapshot = await getTracePoint(traceId, {
timestamp: '00:20', // 跳转到20秒时的状态
// 或使用step索引
step: 12
});
// snapshot 包含当时的完整状态
console.log(snapshot.state); // Agent当时的状态
console.log(snapshot.context); // 当时的上下文内容
console.log(snapshot.toolCalls); // 正在进行的工具调用
console.log(snapshot.messages); // 当时的对话历史
可视化回放
# 在浏览器中可视化回放
openclaw trace view
# 打开 http://localhost:3000/trace/
# 特性:
# - 时间轴拖动
# - 消息流展示
# - 工具调用详情
# - 状态变化高亮
# - 错误点标记
问题定位实战
场景1:无限循环调试
// Trace中发现循环点
Steps 15-18 重复执行了5次:
├── [00:30] 工具调用: check_status
├── [00:31] 结果: "pending"
├── [00:32] Agent判断继续等待
├── [00:33] 工具调用: check_status
├── [00:34] 结果: "pending"
└── ... (重复)
// 根因:状态一直没有变成"completed"
// 原因:检查的API端点错误,永返回"pending"
// 修复:添加超时限制或检查正确的端点
场景2:错误消息追踪
// 从错误点反向追踪
Error at step 23: "Failed to parse JSON"
// 回到step 23
查看工具输出:
{
"data": "<html>...</html>" // 竟然返回了HTML
}
// 继续回到step 22
工具调用:web_fetch("https://api.example.com/data")
// 应该返回JSON的API,却返回了HTML错误页面
// 再回到step 21
查看Agent的决策:
"我决定从 https://api.example.com/data 获取数据"
// URL是动态拼接的,拼接逻辑有误
// 根因:URL拼接错误,使用了undefined变量
场景3:状态污染分析
// 对比不同时间点的状态
Step 10:
state.currentTask = "process_data"
Step 15:
state.currentTask = null // 什么时候被清空的?
// 追踪state变更
Step 12:
Action: setState
Change: currentTask: null
Trigger: 某个工具调用的清理逻辑
// 根因:工具调用后错误地清理了状态
高级功能
差异对比
// 对比两个时间点的状态差异
const diff = compareTracePoints(traceId, {
from: 10, // step 10
to: 20 // step 20
});
// 输出差异
{
"added": {
"searchResults": [...] // 新增的数据
},
"removed": {
"tempCache": null // 被清理的缓存
},
"changed": {
"step": "analyzing → complete"
}
}
搜索与过滤
# 在trace中搜索
openclaw trace search "error"
openclaw trace search "tool:web_fetch"
# 过滤显示
openclaw trace show \
--filter "tool_calls" \
--filter "errors"
导出分析
# 导出trace为JSON
openclaw trace export > trace.json
# 分析报告
openclaw trace analyze
# 输出分析报告
Analysis Report
===============
Total Steps: 45
Tool Calls: 23
Errors: 2
Time Distribution:
- LLM Calls: 60% (27s)
- Tool Calls: 30% (13.5s)
- Other: 10% (4.5s)
Bottleneck Analysis:
1. web_fetch (step 15): 8.2s - 慢!建议添加超时
2. llm_generate (step 30): 3.1x平均时长 - 可能context过长
Recommendations:
1. 为web_fetch添加30s超时
2. 压缩第30步前的上下文
性能开销
- 内存开销:约10-20%(取决于记录内容)
- 存储空间:每次执行约100KB-10MB(取决于复杂度)
- 延迟影响:约5%(可以接受)
- 建议:生产环境开启10%采样,开发环境全量记录
最佳实践
- 始终保留最近7天的trace——问题往往几天后才发现
- 关键任务全量记录——尤其是审批流程、数据迁移
- 定期清理trace——占用的空间不小
- 结合告警——错误发生时自动保存trace
- 不要看所有细节——聚焦错误点附近的步骤