你让 AI Agent 帮你跑一个需要 30 分钟的数据迁移脚本,然后就没了下文——它在干嘛?卡住了?还是已经跑完了?你不知道,因为 Agent 只会在任务彻底完成或彻底失败时才告诉你结果。
这是 Agent 系统中一个普遍的痛点:耗时任务的进度黑洞。用户发出指令后陷入等待,没有进度条,没有中间反馈,只有最终的成功或失败。在 OpenClaw 的实际使用中,我总结了四种适用于不同场景的进度上报方案,每种都有其最佳适用场景。
问题的本质
传统的"请求-响应"模式天然不适合耗时任务。Agent 收到指令后开始执行,整个过程中用户端是空白的。这带来几个实际问题:
- 焦虑感:用户不确定任务是否还在正常运行
- 失控感:无法判断是否需要介入或调整
- 资源浪费:如果任务早已失败,用户可能白等了很久才知道
核心挑战在于:Agent 在执行过程中如何"抬头"汇报,而不打断手头的工作?
方案一:直接写代码 + 后台进程
适用场景:一次性短任务(< 10 分钟)
最简单直接的方案。Agent 编写一个执行脚本,将其放到后台运行,然后通过轮询或回调机制检查结果。
典型例子:批量压缩项目中的图片
你告诉 Agent:“把 static/images/ 下所有大于 500KB 的图片压缩到 500KB 以内。”
Agent 的执行策略:
#!/bin/bash
# compress-images.sh
# 批量压缩图片,将进度写入状态文件
IMAGES=$(find static/images/ -size +500k -name "*.png" -o -name "*.jpg")
TOTAL=$(echo "$IMAGES" | wc -l)
DONE=0
echo '{"status":"running","total":'$TOTAL',"done":0}' > /tmp/compress-status.json
for img in $IMAGES; do
convert "$img" -quality 85 -resize '1920x1920>' "$img"
DONE=$((DONE + 1))
echo '{"status":"running","total":'$TOTAL',"done":'$DONE'}' > /tmp/compress-status.json
done
echo '{"status":"completed","total":'$TOTAL',"done":'$TOTAL'}' > /tmp/compress-status.json
# generated by hugo's coding agent
Agent 启动这个脚本后,可以每隔一段时间读取 /tmp/compress-status.json,向用户汇报:“已完成 23/47 张图片压缩”。
方案特点
| 维度 | 评价 |
|---|---|
| 实现复杂度 | 低——写个脚本就行 |
| 适用时长 | < 10 分钟 |
| 进度精度 | 可以精确到每一步 |
| 容错性 | 一般——进程挂了就没了 |
| Agent 占用 | 需要 Agent 保持在线轮询 |
局限:Agent 必须在整个过程中保持会话,不断轮询状态文件。如果任务超过 10 分钟,这种"Agent 干等着"的模式就显得浪费了。
方案二:状态文件 + Cron 检查
适用场景:长程复杂任务(10 分钟 ~ 数小时)
对于耗时更长的任务,让 Agent 全程盯着是不经济的。更好的方式是:任务自己跑,定时来检查。
典型例子:全站 SEO 审计
你让 Agent 对博客所有文章做一次完整的 SEO 审计——检查每篇文章的标题长度、meta description、内链密度、图片 alt 文本、死链等。博客有 135 篇文章,每篇需要抓取和分析,整个过程可能需要 30-60 分钟。
Agent 的执行策略分为两步:
第一步:启动任务,写入初始状态
Agent 编写并启动审计脚本,脚本将进度持续写入状态文件:
import json, os, time
from pathlib import Path
STATUS_FILE = "/home/node/.openclaw/workspace/tasks/seo-audit-status.json"
def update_status(status, current=0, total=0, message="", results=None):
data = {
"task": "seo-audit",
"status": status,
"current": current,
"total": total,
"message": message,
"updated_at": time.strftime("%Y-%m-%d %H:%M:%S"),
"results_file": "/home/node/.openclaw/workspace/tasks/seo-audit-results.json"
}
if results:
data["summary"] = results
os.makedirs(os.path.dirname(STATUS_FILE), exist_ok=True)
with open(STATUS_FILE, "w") as f:
json.dump(data, f, indent=2)
posts = list(Path("content/post").rglob("*.md"))
update_status("running", 0, len(posts), "Starting SEO audit...")
for i, post in enumerate(posts):
# ... 分析每篇文章的 SEO 指标 ...
update_status("running", i + 1, len(posts), f"Analyzing {post.name}")
update_status("completed", len(posts), len(posts), "Audit complete", {
"issues_found": 42,
"critical": 3,
"warnings": 15,
"info": 24
})
# generated by hugo's coding agent
第二步:配置 Cron 定期检查
Agent 告诉 OpenClaw 每 5 分钟检查一次任务状态:
{
"cron": {
"seo-audit-check": {
"schedule": "*/5 * * * *",
"prompt": "读取 /home/node/.openclaw/workspace/tasks/seo-audit-status.json,如果任务还在运行,向用户汇报进度百分比和当前步骤。如果任务已完成,发送结果摘要并删除这个 cron 任务。如果状态文件超过 10 分钟没更新,告警任务可能已挂起。",
"sessionTarget": "main"
}
}
}
用户会每隔几分钟收到这样的消息:
“SEO 审计进行中:67/135 篇文章已分析(49.6%),当前正在分析
108-compression-is-intelligence.md”
任务完成后:
“SEO 审计已完成。共发现 42 个问题:3 个严重(死链)、15 个警告(标题过长)、24 个建议。详细报告已保存。需要我逐一修复吗?”
方案特点
| 维度 | 评价 |
|---|---|
| 实现复杂度 | 中等——需要状态文件协议 + Cron 配置 |
| 适用时长 | 10 分钟 ~ 数小时 |
| 进度精度 | 取决于 Cron 频率(通常 5-10 分钟) |
| 容错性 | 较好——状态文件可检测挂起 |
| Agent 占用 | 低——只在 Cron 触发时短暂占用 |
核心优势:任务和检查解耦。脚本只管跑,Agent 只管查。即使 Agent 会话中断,任务仍在执行;即使任务挂了,Cron 也能发现并告警。
方案三:多 Agent + 状态文件
适用场景:需要人工干预的长任务
有些任务不是一路跑到底的——中间可能需要人类做决策。比如遇到模棱两可的情况需要确认,或者某个步骤失败后需要人类选择恢复策略。这时需要一个 Agent 执行任务,另一个 Agent 与用户保持对话。
典型例子:服务器迁移
你要把一个 VPS 上的全部服务迁移到新机器。这个过程涉及:数据库备份和恢复、配置文件迁移、DNS 切换、服务启动和验证。每一步都可能遇到需要人类决策的情况。
架构如下:
┌─────────────┐ 状态文件 ┌─────────────┐
│ main Agent │ ◄──────────────► │ coding Agent │
│ (对话交互) │ migration- │ (执行迁移) │
│ │ status.json │ │
└──────┬──────┘ └──────┬──────┘
│ │
▼ ▼
用户通道 远程服务器
(DingTalk等) (SSH 执行命令)
coding Agent 负责执行迁移步骤,每完成一步或遇到问题时更新状态文件:
{
"task": "server-migration",
"status": "waiting_for_input",
"phase": "database-restore",
"completed_phases": ["backup", "config-sync"],
"current_issue": {
"type": "decision_required",
"message": "目标数据库已存在同名表 user_sessions,包含 2,847 条记录。",
"options": [
"drop:删除旧表,用备份数据覆盖",
"merge:合并两边数据(可能有主键冲突)",
"rename:将旧表重命名为 user_sessions_bak,再恢复"
]
},
"updated_at": "2026-03-10T14:32:00+08:00"
}
main Agent 通过 Heartbeat 或 Cron 定期检查状态文件。当发现 waiting_for_input 状态时,立即将问题转发给用户:
“服务器迁移进行到数据库恢复阶段,需要你做一个决策:
目标数据库已存在
user_sessions表(2,847 条记录)。选择处理方式:
- 删除旧表,用备份覆盖
- 合并数据(可能有主键冲突)
- 将旧表重命名为
_bak后恢复”
用户回复"3"后,main Agent 将决策写入状态文件,coding Agent 读取后继续执行。
方案特点
| 维度 | 评价 |
|---|---|
| 实现复杂度 | 较高——需要双 Agent 协调 + 状态协议 |
| 适用时长 | 数十分钟 ~ 数小时 |
| 人工介入 | 原生支持,核心设计目标 |
| 容错性 | 好——状态文件记录了完整的阶段信息,可断点恢复 |
| Agent 占用 | 分工明确——一个跑任务,一个保持对话 |
核心优势:执行和交互解耦。用户不需要等在终端前,Agent 遇到需要人类判断的节点会主动通知,用户回复后任务继续。这模拟了现实中"下属请示领导"的工作模式。
方案四:OpenClaw Cron + 状态文件
适用场景:周期性任务
有些任务不是一次性的,而是需要定期执行。比如每天检查 SSL 证书到期时间、每周生成流量报告、每月清理过期日志。这些任务本身可能很快(几秒到几分钟),但需要持续跟踪状态变化。
典型例子:多域名 SSL 证书到期监控
你有多个域名,需要在证书到期前自动告警并续期:
#!/bin/bash
# check-ssl-certs.sh
# 检查 SSL 证书到期时间,结果写入状态文件
DOMAINS=("blog.hugozhu.site" "api.hugozhu.site" "git.hugozhu.site")
STATUS_FILE="/home/node/.openclaw/workspace/tasks/ssl-check-status.json"
RESULTS="["
for domain in "${DOMAINS[@]}"; do
EXPIRY=$(echo | openssl s_client -servername "$domain" -connect "$domain":443 2>/dev/null \
| openssl x509 -noout -enddate 2>/dev/null \
| cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s 2>/dev/null)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
RESULTS+='{"domain":"'$domain'","expiry":"'$EXPIRY'","days_left":'$DAYS_LEFT'},'
done
RESULTS="${RESULTS%,}]"
cat > "$STATUS_FILE" <<STATUSEOF
{
"task": "ssl-cert-check",
"status": "completed",
"checked_at": "$(date -Iseconds)",
"certificates": $RESULTS
}
STATUSEOF
# generated by hugo's coding agent
OpenClaw Cron 配置:
{
"cron": {
"ssl-cert-monitor": {
"schedule": "0 9 * * *",
"prompt": "执行 /home/node/.openclaw/workspace/cron/check-ssl-certs.sh,然后读取状态文件。如果任何域名的证书在 30 天内到期,告警用户并建议续期。如果在 7 天内到期,标记为紧急。如果所有证书都正常,回复 HEARTBEAT_OK。",
"sessionTarget": "isolated"
}
}
}
平时用户不会收到任何消息(HEARTBEAT_OK 被静默丢弃)。只有当某个证书即将到期时,用户才会收到通知:
“SSL 证书告警:
api.hugozhu.site证书将在 12 天 后到期(2026-03-22),建议立即续期blog.hugozhu.site和git.hugozhu.site证书正常(剩余 247 天)需要我自动执行 certbot 续期吗?”
方案特点
| 维度 | 评价 |
|---|---|
| 实现复杂度 | 低——脚本 + Cron 配置 |
| 执行频率 | 分钟级到天级 |
| 进度概念 | 不是"进度"而是"状态变化" |
| 容错性 | 好——每次独立运行,上次失败不影响下次 |
| Agent 占用 | 极低——隔离会话中短暂运行 |
核心优势:完全自动化的"哨兵"模式。没问题时用户无感知,有问题时立即告警。结合 OpenClaw 的投递通道机制,告警可以精确送达正确的通道和设备。
四种方案的选型指南
| 维度 | 方案一:后台进程 | 方案二:Cron 检查 | 方案三:多 Agent | 方案四:周期 Cron |
|---|---|---|---|---|
| 任务类型 | 一次性短任务 | 一次性长任务 | 需人工介入 | 周期性任务 |
| 典型时长 | < 10 分钟 | 10 分 ~ 数小时 | 不确定 | 持续运行 |
| 实现复杂度 | 低 | 中 | 较高 | 低 |
| 进度汇报方式 | Agent 轮询 | Cron 定期读状态文件 | Agent 间状态协调 | 状态变化时告警 |
| 人工介入支持 | 不支持 | 有限 | 原生支持 | 不需要 |
| 典型场景 | 图片压缩、代码格式化 | SEO 审计、数据迁移 | 服务器迁移、复杂部署 | 证书监控、日志清理 |
决策流程很简单:
任务是否需要定期执行?
├─ 是 → 方案四:Cron + 状态文件
└─ 否 → 任务预计耗时?
├─ < 10 分钟 → 方案一:后台进程
└─ > 10 分钟 → 是否需要人工介入?
├─ 是 → 方案三:多 Agent
└─ 否 → 方案二:Cron 检查
更好的方案?状态文件协议的标准化
以上四种方案有一个共同的核心组件:状态文件。它是任务进程和 Agent 之间的"通信协议"。如果我们把状态文件的格式标准化,就可以构建一个统一的任务监控层。
我正在考虑在 OpenClaw 中引入一个标准化的任务状态协议:
{
"$schema": "openclaw-task-status-v1",
"task_id": "seo-audit-2026-03-10",
"task_type": "one-shot",
"status": "running | waiting_for_input | completed | failed",
"progress": {
"current": 67,
"total": 135,
"percentage": 49.6,
"eta_seconds": 1200
},
"message": "当前步骤的人类可读描述",
"requires_input": null,
"history": [
{"at": "2026-03-10T14:00:00Z", "event": "started"},
{"at": "2026-03-10T14:15:00Z", "event": "phase_completed", "phase": "crawl"}
],
"updated_at": "2026-03-10T14:32:00Z"
}
有了统一的协议,Gateway 层可以提供通用的任务仪表盘——一个地方看到所有正在运行的任务、它们的进度、是否需要人工介入。Agent 不需要写自定义的状态检查逻辑,只需要读取标准格式的文件即可。
这也为未来的任务编排打下基础:一个任务完成后自动触发下一个任务,形成 Pipeline。但这是后话了。
结论
Agent 执行耗时任务时的进度上报,本质上是一个异步通信问题。四种方案对应四种异步模式:
- 方案一是最原始的轮询——简单但浪费资源
- 方案二是定时采样——平衡了开销和实时性
- 方案三是事件驱动 + 人类参与——处理不确定性的最佳选择
- 方案四是哨兵模式——适合"没有消息就是好消息"的场景
选择哪种方案,取决于两个核心变量:任务的持续时间和是否需要人工介入。在实际使用中,这四种方案往往会组合使用——比如一个复杂的部署任务可能同时用到方案二(长流程进度追踪)和方案三(关键节点人工确认)。
最终的目标是让用户对 Agent 的工作有恰到好处的可见性:不是事无巨细地播报每一步,也不是完全黑盒地等到最后——而是在对的时间,告诉你对的信息。