1. 项目概述为OpenClaw构建一个轻量级的任务恢复层如果你用过OpenClaw这类AI智能体平台肯定遇到过这种头疼的情况一个需要跑好几个小时甚至通宵的复杂任务比如批量分析数据、生成长篇报告或者执行多步骤的代码审查跑着跑着就“卡住”了。第二天早上回来一看终端没响应了日志停在某个地方你完全不知道任务到底成功了没有中间产出的数据还在不在更别提从断掉的地方继续了。这种“黑盒”式的长任务执行是很多自动化工作流从“玩具”走向“生产可用”的最大障碍。openclaw-task-recovery这个项目就是为了解决这个痛点而生的。它不是要取代OpenClaw现有的技能Skill系统也不是要引入一个臃肿的工作流编排引擎。你可以把它理解成给OpenClaw工作空间Workspace加装的一个“行车记录仪”和“自动启停”功能。它的核心目标很简单让那些长时间运行、可以分阶段、有明确检查点的任务变得可观测、可恢复、可管理。当任务因为网络波动、资源限制或底层服务超时而意外中断时这个恢复层能自动检测到“失联”并尝试从最后一个安全点继续执行而不是让你从头再来。这个工具特别适合那些已经开始用OpenClaw处理严肃工作的开发者、数据分析师或运维工程师。它通过几个非常轻量的概念——运行卡片Run Card、检查点Checkpoint和技能适配器Skill Adapter——构建了一套完整的恢复机制。最棒的是你甚至不需要先有一个成熟的“技能”对于一次性的长任务它也能现场为你搭建一个临时的恢复适配器让你立刻享受到任务持久化的好处。接下来我会带你深入这套机制的内部看看它是如何工作的以及如何将它无缝集成到你自己的OpenClaw工作流中。2. 核心设计思路与架构解析2.1 问题根源为什么长任务在AI智能体环境中容易“失联”要理解openclaw-task-recovery的设计首先得明白它要解决什么问题。在传统的脚本或后台服务中我们可以用supervisord、systemd或者更现代的工作流引擎如 Airflow、Prefect来管理进程状态和重试。但OpenClaw这类AI智能体平台的工作模式有所不同任务往往是由AI根据自然语言指令动态生成和执行的执行路径可能不确定中间状态也常常是保存在内存或临时文件中的。这就导致了几个典型问题状态黑盒任务一旦开始其内部阶段Phase和进度对外不可见。是卡在数据下载还是报告渲染中断即丢失进程崩溃、会话超时或网络断开意味着所有中间状态和上下文全部丢失。恢复成本高想从断点继续你得手动去翻日志找到最后一个成功的输出文件然后重新构造执行命令和参数几乎等于重跑。静默故障任务可能因为某个子步骤卡死而不再输出任何日志但进程并未退出形成“僵尸任务”直到你手动发现。openclaw-task-recovery的核心理念是引入一个“持久化运行时状态”的薄层。它不试图接管OpenClaw的任务调度那是OpenClaw自己或cron的工作而是在任务执行的生命周期中插入记录点和恢复钩子。这个设计借鉴了Durable Execution持久化执行的思想类似Temporal或Inngest但极大地简化了目标是能直接放在一个OpenClaw工作空间目录里运行无需外部服务依赖。2.2 架构总览心跳、看门狗与恢复器整个恢复层的运转围绕一个核心的观察-决策-执行循环主要由三个脚本文件驱动[OpenClaw 主进程 / Cron任务] | v (定期触发) [心跳检查 HEARTBEAT.md] | v (调用看门狗) [task_runtime_watch.py] —— 检测“陈旧”任务 | v (发现需恢复的任务) [task_runtime_resume.py] —— 通用恢复调度器 | v (调用具体技能的逻辑) [技能适配器 (task_resume.py)] —— 安全恢复特定任务1. 心跳Heartbeat与看门狗Watchdog这是整个系统的触发器。OpenClaw 通常有一个HEARTBEAT.md文件或类似机制用于定期执行健康检查。install.py脚本会在其中添加一个“任务运行时恢复检查”部分。这个检查会定期比如每分钟调用task_runtime_watch.py。task_runtime_watch.py的工作是扫描所有标记为“可自动恢复”且状态为“运行中”的运行卡片Run Card。它会检查每个任务的last_checkpoint_at时间戳。如果当前时间距离最后一次检查点的时间超过了预设的“陈旧阈值”例如30分钟看门狗就判定这个任务“失联”了。2. 运行卡片Run Card任务的身份证和病历本这是持久化状态的核心。一个运行卡片就是一个JSON文件保存在data/task-runs/目录下以{task_id}.json命名。它记录了任务的完整元数据和运行时状态基础信息任务ID、类型、标题、创建时间。执行上下文当前阶段Phase、状态Status、上一次检查点时间。恢复策略是否允许自动恢复、陈旧阈值、最大重试次数。产出物Artifacts每个阶段生成的关键文件路径这是恢复的关键依据。检查点历史记录了任务推进过程中的各个里程碑。这个卡片文件是任务可恢复的基础。任务每完成一个安全阶段就应该更新卡片中的阶段和检查点时间。3. 恢复调度器与技能适配器当看门狗发现一个陈旧任务时它会调用task_runtime_resume.py并传入任务ID。恢复调度器是一个通用程序它只做三件事加载对应的运行卡片找到卡片中指定的resume_adapter技能适配器路径然后调用这个适配器。技能适配器是真正包含业务逻辑的地方。每个需要恢复能力的技能或临时任务都需要实现一个task_resume.py适配器。这个适配器就像一个“专科医生”它知道如何诊断自己这个特定任务在当前阶段比如“数据收集”阶段卡住后该如何安全地继续。它会读取运行卡片中记录的阶段和产出物从最后一个检查点重新执行逻辑并更新卡片状态。这种设计实现了关注点分离恢复层通用负责状态管理和调度触发业务层技能适配器负责具体的恢复逻辑。这使得系统非常灵活任何技能都可以通过实现一个适配器来获得恢复能力。2.3 设计原则轻量、非侵入、契约明确这个项目的设计遵循几个关键原则这也是它能在OpenClaw生态中保持简洁有效的秘诀轻量优先Lightweight First所有代码都放在工作空间内无需数据库、消息队列等外部依赖。状态用JSON文件存储调度依赖现有的心跳机制。安装即用卸载无痕。非侵入式集成Non-invasive Integration它不修改OpenClaw核心代码。通过向HEARTBEAT.md添加检查项和提供脚本、模板供技能调用它以“插件”方式融入现有工作流。明确的适配器契约Clear Adapter Contract技能适配器需要遵守的接口非常明确接受--task-id参数更新指定字段这降低了集成复杂度也使得编写临时任务的适配器模板成为可能。安全恢复边界Safe Resume Boundary系统强烈建议并通过设计引导只为等幂Idempotent且可安全重试的阶段启用自动恢复。对于发送邮件、部署服务等有副作用的操作适配器逻辑应包含确认或人工干预环节。信号导向的观测性Signal-oriented Observability看门狗的输出不是杂乱的日志而是归为三类清晰的信号alerts告警、recoveries已恢复、needs_attention需人工介入。这让用户一眼就能看清系统状态。3. 核心组件深度拆解与实操要点3.1 运行卡片Run Card状态持久化的基石运行卡片是整个系统的核心数据模型。理解它的结构对于调试和高级用法至关重要。一个典型的运行卡片JSON内容如下{ task_id: research_20250415_022034, task_type: overnight-research, title: 竞品分析报告生成, created_at: 2025-04-15T02:20:34Z, status: running, // 可能的值pending, running, completed, failed, stalled phase: data_aggregation, resume_adapter: /home/user/.openclaw/workspace/skills/research/scripts/task_resume.py, allow_auto_resume: true, stale_after_minutes: 30, max_retries: 3, retry_count: 0, last_checkpoint_at: 2025-04-15T03:45:12Z, artifacts: { phase_collect: { raw_data: /tmp/research/raw_data.json, source_list: /tmp/research/sources.txt }, phase_data_aggregation: { normalized_data: /tmp/research/normalized.json } }, checkpoints: [ { phase: collect, at: 2025-04-15T02:45:00Z, message: 已完成初始数据收集共获取20个来源。 }, { phase: data_aggregation, at: 2025-04-15T03:45:12Z, message: 数据标准化完成已去除重复项。 } ], metadata: { user: alice, priority: high, tags: [report, automation] } }关键字段解析与实操要点status与phase的协同status表示任务的宏观生命周期状态如运行中、完成。phase表示任务当前所处的具体业务阶段如数据收集、分析、渲染。恢复动作总是针对某个phase进行的。当适配器恢复执行时它需要知道从哪个phase开始以及这个phase是否允许重试。artifacts的结构这是一个嵌套字典键通常是阶段名phase_xxx值是该阶段产生的关键文件路径。为什么这是恢复的关键假设任务在“渲染”阶段失败恢复适配器需要找到“分析”阶段生成的analysis_result.json才能开始渲染。这个路径就记录在artifacts.phase_analysis下。实操建议在创建检查点时务必使用--artifact参数记录下阶段产出的绝对路径。避免使用相对路径因为恢复时的工作目录可能不同。allow_auto_resume与stale_after_minutes这两个字段共同定义了看门狗的行为。只有allow_auto_resume为true的任务才会被自动检测和恢复。stale_after_minutes需要根据任务类型谨慎设置。一个CPU密集型的模型训练可能2小时没更新也算正常而一个频繁心跳的API轮询任务可能5分钟没动静就算异常。设置过短会导致不必要的恢复中断设置过长则意味着故障发现延迟。checkpoints列表这不仅是一个日志更是恢复的“安全点”序列。每次调用checkpoint命令都会在这里添加一条记录。最佳实践在checkpoint的--message参数中记录足够的信息以便人工查看时能快速理解进度例如“已处理1000条记录中的250条”。注意运行卡片文件是系统的唯一状态源。切勿手动编辑此文件除非你完全清楚后果。所有更新都应通过task_runtime.py脚本的命令来完成以保证状态的一致性。3.2 技能适配器Skill Adapter的契约与实现模式技能适配器是连接通用恢复层和具体业务逻辑的桥梁。它不是一个复杂的框架而是一个符合简单契约的Python脚本。适配器必须遵守的基本契约可执行必须能通过命令行调用并接受--task-id参数。可加载运行卡片内部需调用task_runtime模块的load_run_card函数。状态驱动根据运行卡片中的phase和status决定恢复逻辑。安全恢复只对等幂的、安全的阶段执行恢复操作。对于有副作用的阶段应记录日志并等待人工干预。状态更新恢复执行后必须通过task_runtime模块的update_run_card函数更新卡片的phase、status、artifacts和last_checkpoint_at。输出协议尽可能将结果以JSON格式打印到标准输出stdout供上层调用者解析。一个适配器的典型代码骨架如下#!/usr/bin/env python3 import sys import json import argparse from pathlib import Path # 假设 task_runtime 模块已安装在 workspace 脚本路径下 sys.path.insert(0, str(Path.home() / .openclaw / workspace / scripts)) from task_runtime import load_run_card, update_run_card, RunCard def resume_phase_data_collection(run_card: RunCard): 恢复‘数据收集’阶段 print(f[INFO] 恢复阶段: data_collection for task {run_card.task_id}, filesys.stderr) # 1. 从 artifacts 中获取上一个检查点的输出如果有 last_artifact run_card.artifacts.get(phase_data_collection, {}).get(partial_list) start_from 0 if last_artifact and Path(last_artifact).exists(): with open(last_artifact, r) as f: start_from int(f.read().strip()) # 2. 执行具体的恢复逻辑例如从第 start_from 条开始继续收集 # ... 你的业务逻辑 here ... new_data_point start_from 100 # 模拟新收集了100条 # 3. 更新产出物 new_artifact_path f/tmp/{run_card.task_id}_partial_{new_data_point}.txt with open(new_artifact_path, w) as f: f.write(str(new_data_point)) # 4. 更新运行卡片关键步骤 run_card.artifacts.setdefault(phase_data_collection, {})[partial_list] new_artifact_path run_card.phase data_analysis # 进入下一阶段 run_card.last_checkpoint_at datetime.utcnow().isoformat() Z # 注意status 可能保持 running除非完成或失败 update_run_card(run_card.task_id, run_card) return {resumed_to: new_data_point, next_phase: run_card.phase} def resume_phase_data_analysis(run_card: RunCard): 恢复‘数据分析’阶段 # 此阶段依赖上一阶段的产出物 input_data_path run_card.artifacts.get(phase_data_collection, {}).get(final_data) if not input_data_path or not Path(input_data_path).exists(): print(f[ERROR] 依赖的输入文件不存在: {input_data_path}, filesys.stderr) run_card.status failed run_card.metadata[error] Missing input artifact for analysis update_run_card(run_card.task_id, run_card) return {error: Missing input artifact} # ... 分析逻辑 ... # 更新阶段和产出物 run_card.phase report_generation run_card.artifacts.setdefault(phase_data_analysis, {})[result_json] analysis_output_path update_run_card(run_card.task_id, run_card) return {status: analysis_completed} def main(): parser argparse.ArgumentParser(description恢复特定技能的长任务) parser.add_argument(--task-id, requiredTrue, help要恢复的任务ID) parser.add_argument(--timeout-seconds, typeint, default300, help恢复操作超时时间) args parser.parse_args() # 加载运行卡片 run_card load_run_card(args.task_id) if not run_card: print(json.dumps({error: fTask {args.task_id} not found}), filesys.stderr) sys.exit(1) # 根据当前阶段路由到不同的恢复函数 result {} if run_card.phase data_collection: result resume_phase_data_collection(run_card) elif run_card.phase data_analysis: result resume_phase_data_analysis(run_card) elif run_card.phase report_generation: # 报告生成可能涉及外部API调用需谨慎处理自动恢复 if run_card.metadata.get(report_sent): print(json.dumps({warning: Report already sent, skipping auto-resume to avoid duplicate.}), filesys.stderr) run_card.status needs_attention update_run_card(run_card.task_id, run_card) result {action: paused, reason: non-idempotent phase} else: result resume_phase_report_generation(run_card) else: print(json.dumps({error: fUnknown phase: {run_card.phase}}), filesys.stderr) sys.exit(1) # 输出结果JSON格式便于上游处理 print(json.dumps(result)) if __name__ __main__: main()适配器设计的心得与避坑指南阶段划分的艺术阶段Phase划分要足够细使得每个阶段都是相对独立、可恢复的单元。但也不能太细否则会产生大量检查点开销。一个好的经验法则是一个阶段应该对应一个清晰的、会产生持久化中间产出的子任务。等幂性是生命线确保每个允许自动恢复的阶段都是等幂的。即重复执行多次与执行一次的效果相同。数据下载、计算、转换通常是等幂的发送通知、创建订单则不是。对于非等幂阶段适配器内必须有防护逻辑如检查metadata中的标志位。依赖管理在artifacts中清晰定义阶段间的输入输出依赖。恢复时适配器首先要检查所需的上游产出物是否存在且有效。超时与资源清理适配器可能执行很长时间。利用--timeout-seconds参数或在内部实现超时逻辑避免恢复操作本身卡死。同时对于临时文件考虑在任务最终完成或失败后进行清理可以在metadata中记录需要清理的路径列表。3.3 看门狗Watchdog信号系统从“静默”到“可对话”task_runtime_watch.py不仅仅是检测陈旧任务更重要的是它提供了一套清晰的信号系统将底层状态转化为用户和上游系统能理解的语义。这是提升运维体验的关键。看门狗每次执行由心跳触发都会遍历所有运行卡片并生成一个包含三个“桶”的报告{ alerts: [ { task_id: task_abc, phase: data_fetch, stale_for_minutes: 45, last_checkpoint: 2025-04-15T01:00:00Z } ], recoveries: [ { task_id: task_xyz, from_phase: processing, to_phase: completed, recovered_at: 2025-04-15T02:05:00Z } ], needs_attention: [ { task_id: task_123, phase: send_email, reason: non_idempotent_phase_blocked, blocked_since: 2025-04-15T01:30:00Z } ] }这三个信号桶的解读与处理策略alerts告警任务已陈旧超过stale_after_minutes但尚未尝试恢复或恢复尚未完成。这通常意味着任务刚刚失联看门狗已经将其标记并可能即将触发恢复如果allow_auto_resumeTrue。监控系统可以订阅此信号用于触发告警如发送Slack通知但不必立即人工介入因为系统可能会自动恢复。recoveries已恢复看门狗在本轮检测中成功调用了恢复适配器并且适配器报告任务已推进到新的阶段或完成。这是一个积极信号表明恢复机制正在正常工作。你可以将此信号记录到审计日志中用于评估系统的自愈能力。needs_attention需人工介入这是最重要的信号。它表示看门狗遇到了无法自动处理的情况必须由人来决定下一步。常见原因包括任务处于非等幂阶段如send_email适配器主动暂停。自动恢复尝试次数已达到max_retries上限但任务仍未完成。恢复适配器本身执行失败或出错。运行卡片状态异常如status为running但resume_adapter路径无效。实操建议将needs_attention信号集成到你的运维仪表盘或通知系统中。这是避免任务长时间停滞无人知晓的关键。你可以配置一个自动化规则当needs_attention列表非空时自动创建一张工单或发送一条高优先级的告警消息给负责人。4. 完整集成与实操流程4.1 从零开始安装与基础配置假设你的OpenClaw工作空间位于~/.openclaw/workspace。集成openclaw-task-recovery只需要几步。第一步克隆仓库并安装cd ~/.openclaw/workspace git clone https://github.com/m0x14o/openclaw-task-recovery repos/openclaw-task-recovery cd repos/openclaw-task-recovery python3 install.pyinstall.py脚本会做以下几件事你可以打开它查看具体逻辑将scripts/目录下的核心脚本复制到工作空间的scripts/目录。将docs/下的文档复制到工作空间的docs/openclaw-task-recovery/目录。将适配器模板templates/task_resume.py复制到工作空间的templates/openclaw-task-recovery/目录。最关键的一步在HEARTBEAT.md文件的末尾添加或更新一个名为“Task Runtime Recovery Check”的章节。内容大致是调用task_runtime_watch.py脚本。脚本会确保这个添加是等幂的即重复运行安装脚本不会造成重复内容。第二步验证安装安装后检查以下内容文件是否存在ls -la ~/.openclaw/workspace/scripts/task_runtime*.py心跳文件是否更新tail -n 20 ~/.openclaw/workspace/HEARTBEAT.md你应该能看到类似python3 scripts/task_runtime_watch.py的调用。运行一次看门狗脚本确保无语法错误cd ~/.openclaw/workspace python3 scripts/task_runtime_watch.py。初次运行由于没有任务输出应该是一个空的JSON对象{}。4.2 为现有技能添加恢复能力以“数据报告生成”技能为例假设你有一个已有的OpenClaw技能位于~/.openclaw/workspace/skills/report-generator/它能根据一个主题进行网络搜索、数据分析并生成PDF报告。现在我们要让它支持断点续做。第一步创建技能适配器使用安装时提供的模板快速创建适配器。cp ~/.openclaw/workspace/templates/openclaw-task-recovery/task_resume.py \ ~/.openclaw/workspace/skills/report-generator/scripts/task_resume.py第二步分析技能阶段设计检查点你需要审视你的报告生成流程将其划分为可恢复的阶段。例如phase_1_collect从多个来源收集原始数据和文章。phase_2_analyze对收集的内容进行总结和关键信息提取。phase_3_render将分析结果渲染成PDF。其中phase_1_collect和phase_2_analyze是相对等幂的重复收集和分析同一数据源结果一致。phase_3_render生成最终文件也是等幂的。但假设渲染后有一个phase_4_email发送邮件这个阶段就是非等幂的不能自动恢复。第三步修改技能主逻辑插入检查点调用在你的技能主脚本例如report-generator/main.py中在完成每个安全阶段后调用task_runtime.py checkpoint命令。# 假设在 main.py 中 import subprocess import json import sys def run_report_generation(task_id, topic): # ... 你的原有逻辑 ... # 阶段1: 收集 raw_data_file do_data_collection(topic) # 创建检查点 subprocess.run([ sys.executable, f{workspace_path}/scripts/task_runtime.py, checkpoint, task_id, --phase, collect, --artifact, fraw_data{raw_data_file}, --message, f已完成‘{topic}’的原始数据收集文件位于 {raw_data_file} ], checkTrue) # 阶段2: 分析 analysis_result do_analysis(raw_data_file) analysis_file save_analysis(analysis_result) subprocess.run([ sys.executable, f{workspace_path}/scripts/task_runtime.py, checkpoint, task_id, --phase, analyze, --artifact, fanalysis_result{analysis_file}, --message, 数据分析完成已提取核心观点。 ], checkTrue) # 阶段3: 渲染 pdf_report render_pdf(analysis_file) subprocess.run([ sys.executable, f{workspace_path}/scripts/task_runtime.py, checkpoint, task_id, --phase, render, --artifact, ffinal_report{pdf_report}, --message, PDF报告渲染完成。 ], checkTrue) # 阶段4: 发送邮件 (非等幂不设置自动恢复检查点或设置一个特殊标志) # send_email(pdf_report) # 更新任务状态为完成但不在这个阶段设置自动恢复的检查点 # 或者设置一个 metadata 标志位让适配器知道邮件已发送第四步实现适配器恢复逻辑编辑刚创建的task_resume.py根据阶段实现恢复函数。核心是读取运行卡片中的phase和artifacts从中断的地方继续。 例如恢复analyze阶段的函数需要读取artifacts.phase_collect.raw_data文件然后执行do_analysis。第五步启动任务时创建运行卡片修改技能启动的入口在开始长任务前先创建一个运行卡片。# 在调用技能前或在技能初始化时执行 TASK_IDreport_$(date %s) python3 ~/.openclaw/workspace/scripts/task_runtime.py create \ --task-id $TASK_ID \ --task-type report-generation \ --title 生成关于‘AI代理’的报告 \ --phase collect \ --resume-adapter $HOME/.openclaw/workspace/skills/report-generator/scripts/task_resume.py \ --allow-auto-resume \ --stale-after-minutes 20 \ --max-retries 2然后将这个TASK_ID传递给技能的主函数。至此你的“数据报告生成”技能就具备了基本的任务恢复能力。如果它在分析阶段因为网络问题卡住20分钟后看门狗会检测到并自动调用你的适配器从分析阶段重新开始。4.3 处理一次性临时任务无需创建正式技能对于临时性的、一次性的长任务你不需要创建一个完整的技能目录。openclaw-task-recovery提供了更快捷的路径。使用“一线提示词”模式这是项目README中提到的精髓。你直接向你的OpenClaw发送这样一条指令“运行这个作为可恢复的长任务帮我扫描项目目录/home/user/myproject下的所有.py文件统计代码行数、函数个数并生成一个复杂度报告。如果中途中断请能从断点恢复。”一个配置良好的OpenClaw在接收到这条指令并识别出“可恢复的长任务”这个模式后可以自动执行以下操作在tmp/task-runtime/code-analysis-20250415/下创建一个临时目录。将适配器模板复制到那里并基于你的任务描述生成一个简单的、专门针对代码分析任务的task_resume.py适配器骨架。创建一个运行卡片resume_adapter指向这个临时适配器。开始执行任务并在关键步骤插入检查点。手动创建临时任务流程如果OpenClaw没有自动完成你也可以手动模拟这个过程# 1. 创建临时任务目录和适配器 TASK_SLUGadhoc_code_analysis TMP_ADAPTER_DIR$HOME/.openclaw/workspace/tmp/task-runtime/$TASK_SLUG mkdir -p $TMP_ADAPTER_DIR cp ~/.openclaw/workspace/templates/openclaw-task-recovery/task_resume.py $TMP_ADAPTER_DIR/ # 2. 编辑这个临时适配器实现你的代码分析逻辑和阶段恢复 # 用你熟悉的编辑器编辑 $TMP_ADAPTER_DIR/task_resume.py # 3. 创建运行卡片 python3 ~/.openclaw/workspace/scripts/task_runtime.py create \ --task-id $TASK_SLUG \ --task-type adhoc-analysis \ --title 临时代码分析任务 \ --phase file_discovery \ --resume-adapter $TMP_ADAPTER_DIR/task_resume.py \ --allow-auto-resume \ --stale-after-minutes 10 # 4. 开始你的任务脚本并在其中调用检查点 # 你的脚本 my_analysis.sh 或 my_analysis.py 内部需要知道 TASK_SLUG # 并在每个子步骤后调用 task_runtime.py checkpoint ...这种方式非常适合探索性工作或一次性数据处理任务。任务完成后你可以选择保留这个临时适配器以备后用或者直接删除tmp/task-runtime/下的目录。5. 常见问题、排查技巧与高级模式5.1 故障排查清单在实际使用中你可能会遇到以下问题。这里是一个快速排查指南问题现象可能原因排查步骤与解决方案看门狗没有检测到陈旧任务1. 心跳未正确执行。2. 运行卡片的allow_auto_resume为false。3.stale_after_minutes设置过长任务还未被判定为陈旧。4. 运行卡片状态不是running。1. 检查HEARTBEAT.md文件确认task_runtime_watch.py的调用行存在且无语法错误。2. 手动执行python3 scripts/task_runtime_watch.py查看输出和错误信息。3. 检查具体任务的运行卡片JSON文件确认allow_auto_resume、status和last_checkpoint_at字段值是否符合预期。4. 查看系统时间是否准确。任务被标记为needs_attention1. 适配器执行出错Python异常。2. 适配器找不到运行卡片或产出物文件。3. 任务处于非等幂阶段适配器主动暂停。4. 达到最大重试次数max_retries。1.查看适配器日志看门狗调用适配器时适配器打印到 stderr 的信息是关键。检查工作空间的标准错误输出或OpenClaw的日志聚合处。2. 检查运行卡片中resume_adapter的路径是否正确文件是否有可执行权限。3. 检查artifacts中记录的路径文件是否存在且可读。4. 查看运行卡片的metadata字段适配器可能会将错误信息写在这里。自动恢复后任务陷入循环1. 适配器恢复逻辑有bug未能推进phase或更新last_checkpoint_at。2. 阶段划分不合理恢复后立即失败然后又被看门狗检测为陈旧再次恢复形成死循环。1.调试适配器手动运行适配器python3 /path/to/adapter.py --task-id TASK_ID观察其逻辑和输出。2. 检查恢复后运行卡片的phase和last_checkpoint_at是否被正确更新。3. 考虑在适配器中增加更详细的日志记录恢复尝试的每一步。4. 对于容易失败的环境依赖步骤可以在适配器开头增加健康检查如果基础条件不满足直接失败并标记为needs_attention而不是无限重试。检查点checkpoint命令执行失败1. 指定的task_id不存在。2. 当前用户对运行卡片文件或data/task-runs/目录没有写权限。3.--artifact参数格式错误。1. 使用python3 scripts/task_runtime.py list查看所有任务确认task_id。2. 检查data/task-runs/目录的权限确保运行OpenClaw的用户有读写权。3.--artifact参数格式应为keyvalue多个时使用多个--artifact参数。心跳执行导致性能问题任务数量非常多例如上千个每次心跳都遍历所有JSON文件并检查可能造成延迟。1. 这是轻量级方案的权衡。对于超大规模任务调度应考虑专用工作流引擎。2. 可以适度调低心跳频率如果OpenClaw允许配置。3. 确保data/task-runs/目录只存放活跃任务对于已完成completed或最终失败failed且无需保留的任务可以定期归档或删除其运行卡片文件。5.2 高级模式与最佳实践1. 混合模式自动恢复与人工审批结合对于包含非等幂步骤的流程可以采用“自动恢复人工闸门”模式。例如一个发布流程构建可自动恢复- 测试可自动恢复- 部署需人工批准。在运行卡片中设置phase: deployment_pending。适配器检测到此阶段时不执行任何操作而是将任务状态改为needs_attention并在metadata中添加pending_action: “manual_deployment_approval”。看门狗会将其放入needs_attention信号桶。你可以在一个管理界面查看所有needs_attention的任务手动点击“批准部署”。批准操作触发另一个脚本该脚本执行部署并在成功后更新运行卡片状态为completed。2. 基于产出物校验的增强恢复简单的检查点时间戳可能不够。你可以在适配器中加入对产出物的校验逻辑。例如在恢复数据分析阶段前先检查上游的“数据收集”阶段产出的文件是否完整例如检查文件大小、校验和或文件头信息。如果产出物损坏则恢复失败并标记为needs_attention而不是基于一个损坏的中间结果继续执行。3. 任务依赖链虽然openclaw-task-recovery本身不是DAG引擎但你可以通过运行卡片的metadata字段实现简单的依赖。例如任务B的metadata中记录depends_on: [“task_a_id”]。在任务B的适配器开始恢复前先检查任务A的运行卡片状态是否为completed。如果不是则将自己标记为pending或needs_attention。你可以编写一个外部的协调脚本定期检查这些依赖关系并触发后续任务。4. 运行卡片的生命周期管理归档对于重要的已完成任务可以将运行卡片JSON文件连同其artifacts中记录的产出物一起打包存档用于事后审计或复现。清理定期清理旧的和失败的任务卡片避免data/task-runs/目录膨胀。可以写一个简单的清理脚本根据created_at时间和status来删除文件。备份由于运行卡片是纯JSON文件可以很容易地纳入你的工作空间备份策略中。5. 监控与可视化你可以利用看门狗输出的JSON信号构建简单的监控面板。例如用一个Python脚本定期调用task_runtime_watch.py解析其输出然后将alerts、recoveries、needs_attention的数量推送到Prometheus、Datadog或者简单地显示在一个静态网页上。这能让你对整个工作空间的长任务健康状态一目了然。这套轻量级的任务恢复层其威力在于它的简单和直接。它没有试图解决所有问题而是精准地解决了长任务执行中最令人头疼的“失联”和“不可恢复”问题。通过将持久化状态、定期检测和可插拔的恢复逻辑分离它既提供了足够的可靠性又保持了极致的灵活性。无论是集成到现有技能还是管理临时性任务它都能显著提升你在OpenClaw上运行自动化工作的信心和效率。最关键的是这一切都发生在一个目录里无需改变你现有的架构这种低成本的可靠性提升在实际工作中往往是最有价值的。