feat: add async resume jobs and doc navigation

This commit is contained in:
root
2026-04-14 15:55:02 +08:00
parent 6705613aa4
commit b8727f1885
26 changed files with 2791 additions and 665 deletions
+167 -134
View File
@@ -2,79 +2,86 @@
## 1. 文档目的
本文档定义 OpenClaw 在正式环境中如何调用 reader 作为上游 MCP workflow service。
本文档只回答一个问题:OpenClaw 在正式环境里应该如何编排 reader。
目标不是描述 reader 内部实现,而是明确 OpenClaw 的编排动作:
这里不重复介绍 reader 内部实现,只定义正式控制面:
- 什么时候启动新 run
- 什么时候查询状态
- 什么时候读取结果
- 什么时候尝试恢复
- 什么时候直接新开 run
- 什么时候需要人工介入
- 如何启动日报
- 如何轮询 job
- 如何读取 run 结果
- 如何判断是否恢复
- 如何走异步恢复
- 什么时候直接新开 run 或人工介入
本文档基于 reader 当前**已真实落地**的能力编写,不描述尚未实现的未来接口。
## 2. 当前正式入口
---
### 2.1 新 run
## 2. 当前 reader 已正式支持的 MCP 能力
正式生产入口:
当前可用能力:
- `start_freshrss_pipeline_job`
- `get_freshrss_pipeline_job_status`
- `get_freshrss_pipeline_job_result`
同步入口:
- `run_freshrss_openclaw_pipeline`
同步入口只保留给 debug / fallback,不再是正式编排默认路径。
### 2.2 run 级读取
正式 run 级读取接口:
- `get_run_status`
- `list_runs`
- `list_run_artifacts`
- `get_delivery_payload`
- `get_run_report`
### 2.3 恢复
正式恢复入口:
- `inspect_resume_plan`
- `start_resume_job`
- `get_resume_job_status`
- `get_resume_job_result`
同步恢复入口:
- `resume_run`
其中:
- `run_freshrss_openclaw_pipeline` 是当前正式启动入口
- `get_run_status` / `list_runs` / `list_run_artifacts` 用于观测
- `get_delivery_payload` / `get_run_report` 用于读取正式结果
- `resume_run` 用于最小恢复能力
---
`resume_run` 只保留给 debug / fallback。
## 3. 编排基本原则
### 3.1 OpenClaw 不再手拼路径
### 3.1 OpenClaw 不手拼路径
OpenClaw 不应再自己拼 reader 输出路径来判断运行状态或读取核心结果。
OpenClaw 不应自己推导这些路径:
优先使用 MCP:
- `outputs/freshrss/rerun/<run_dir>/run-state.json`
- `outputs/freshrss/rerun/<run_dir>/candidates/openclaw-delivery-payload.json`
- `outputs/freshrss/rerun/<run_dir>/run-report.json`
- 查状态 → `get_run_status`
- 读 payload → `get_delivery_payload`
- 读 report → `get_run_report`
- 做恢复 → `resume_run`
需要路径时,只消费 MCP 返回值:
只有在排障/人工核查时,才回退到直接看 reader run 目录。
- `output_dir`
- `artifact.path`
- `delivery_output`
- `report_output`
### 3.2 reader 是上游 workflow engine
### 3.2 顶层 `status` 才是分支依据
reader 负责:
`get_run_status` 和 job status 接口都可能做状态收敛。
- FreshRSS 拉取
- 内容提取
- 摘要
- 过滤
- payload 生成
- run 状态记录
- 最小恢复
因此:
OpenClaw 负责:
- 优先使用顶层 `status`
- `status_source` 用来解释状态来自原始 state 还是收敛结果
- `state_conflict=true` 说明底层状态文件已经落后于真实产物
- 触发执行
- 轮询状态
- 读取结果
- 生成 digest markdown
- Hugo 发布
- 聊天汇报
- 用户确认精选
- IMA 编排
不要再拿旧的 `raw_status`、`raw_current_stage` 或早期阶段名重新做分支。
### 3.3 默认生产语义
@@ -82,19 +89,18 @@ OpenClaw 负责:
- `mark_read=true`
- `debug_artifacts=false`
- 只在 debug/test/validation 时显式放宽
---
只有 debug / test / validation 时才放宽。
## 4. 标准 Happy Path
### Step 1: 启动新 run
### Step 1: 启动新 job
调用:
- `run_freshrss_openclaw_pipeline`
- `start_freshrss_pipeline_job`
推荐参数示例:
推荐参数:
```json
{
@@ -107,131 +113,156 @@ OpenClaw 负责:
}
```
期望:
预期:
- 获得 `run_id`
- 获得 `output_dir`
- 获得初始结果摘要
- 立即返回 `job_id`
- 后续由 OpenClaw 轮询 job,而不是同步等待整条流水线
如果启动阶段直接抛错:
### Step 2: 轮询 job
- 直接判为启动失败
- 不进入后续查询
调用:
### Step 2: 查询运行状态
- `get_freshrss_pipeline_job_status(job_id=...)`
根据返回:
- `status=running`:继续轮询
- `status=success`:读取 job result
- `status=failed`:进入失败处理
额外规则:
- 如果 `status_source=linked_run_reconciliation`,说明 outer job state 已落后,但 linked run 已经给出可用终态
- 如果 `status_source=stale_job_state_timeout`,把它当成终态失败,不要继续无限轮询
### Step 3: 读取 job result
调用:
- `get_freshrss_pipeline_job_result(job_id=...)`
预期读取:
- `run_id`
- `output_dir`
- `delivery_output`
- `report_output`
从这一刻开始,`run_id` 是正式的稳定句柄。
### Step 4: 读取 run 级状态与结果
调用:
- `get_run_status(run_id=...)`
根据返回:
- `status=running` → 继续轮询
- `status=success` → 进入结果读取
- `status=failed` → 进入失败处理
- `status=partial` → 视为未完成,优先看 `recovery` 和当前阶段
### Step 3: 读取正式结果
成功后读取:
- `get_delivery_payload(run_id=...)`
- `get_run_report(run_id=...)`
后续 OpenClaw 编排应以这两个接口为正式结果源,而不是自己拼路径读取 JSON。
根据 `get_run_status`:
### Step 4: 进入下游编排
- `status=running`:继续观察
- `status=success`:继续下游 digest / 发布 / 汇报
- `status=failed`:进入恢复或重跑决策
- `status=partial`:优先检查 report、artifacts 和 recovery
OpenClaw 在拿到正式 payload / report 后,继续执行:
如果 `status_source=run_report_reconciliation`,说明 `run-state.json` 已经过期,但 reader 已经根据终态产物收敛出有效状态。
- public/internal digest 生成
- Hugo 发布
- 聊天汇报
- 用户确认精选
- IMA 沉淀
如果 `status_source=stale_run_state_timeout`,说明 reader 认为该 run 长时间未收敛且没有终态产物,应按失败处理。
---
## 5. 恢复决策
## 5. 状态 → 动作映射
### 5.1 先看预检,不要直接恢复
| reader 状态 | OpenClaw 动作 |
|---|---|
| `running` | 继续轮询 `get_run_status` |
| `success` | 读取 `get_delivery_payload` 和 `get_run_report` |
| `failed` 且 `recovery.resumable=true` | 评估是否调用 `resume_run` |
| `failed` 且 `recovery.resumable=false` | 直接判失败,通常新开 run 或人工介入 |
| `partial` | 先读状态详情和 recovery,再决定继续等 / 恢复 / 人工介入 |
恢复前固定动作:
---
- 先调用 `inspect_resume_plan(run_id)`
## 6. 失败处理与恢复决策
只在以下条件同时成立时才启动恢复:
### 6.1 什么时候优先尝试 `resume_run`
- `can_resume=true`
- `recommended_action=resume`
满足以下条件时,优先考虑恢复而不是新开 run:
重点字段:
- `get_run_status` 返回 `failed`
- `recovery.resumable=true`
- 当前 run 对应的是 freshrss workflow
- 当前失败点在 reader 第一版支持的恢复范围内
- `requested_resume_from_stage`
- `resume_from_stage`
- `resume_decision_source`
- `artifact_resume_from_stage`
- `artifact_snapshot`
### 6.2 `resume_run` 当前支持范围
### 5.2 正式恢复路径
当前最小实现仅支持:
正式恢复控制面:
- 仅对带 `run-state.json` 的 freshrss run
- 仅从最近可恢复点继续
- 支持的恢复点:
1. `start_resume_job(run_id)`
2. `get_resume_job_status(job_id)`
3. `get_resume_job_result(job_id)`
不要再把同步 `resume_run(run_id)` 当成正式恢复入口。
### 5.3 当前支持范围
当前只支持:
- 带有效 `run-state.json` 的 `freshrss_daily_digest` run
- 从以下阶段恢复:
- `generate_summaries`
- `apply_filters`
- `build_delivery_payload`
- `write_run_report`
明确不支持:
当前不支持:
- `fetch_feed`
- `extract_articles`
### 6.3 什么时候不要恢复,直接新开 run
正式生产恢复优先依赖:
以下情况不建议 `resume_run`:
- `summary/summary-batch.json`
- `candidates/candidate-batch.json`
- `recovery.resumable=false`
- run 没有 `run-state.json`
- 失败点是 `fetch_feed` 或 `extract_articles`
- 恢复所需关键产物缺失
- 恢复点语义不明确或结果存在明显漂移风险
### 5.4 什么时候不要恢复
这时更合理的动作通常是:
以下情况直接新开 run 更合理:
- 直接新开 run
- 或人工介入排查
- `recommended_action=start_new_run`
- `recommended_action=read_terminal_result`
- 没有有效 `run-state.json`
- 恢复所需关键 artifacts 缺失
- 连续恢复失败
### 6.4 什么时候需要人工介入
## 6. 状态到动作映射
出现以下任一情况时,建议人工介入:
| 接口 | 状态 | OpenClaw 动作 |
| --- | --- | --- |
| `get_freshrss_pipeline_job_status` | `running` | 继续轮询 job |
| `get_freshrss_pipeline_job_status` | `success` | 读取 `get_freshrss_pipeline_job_result` |
| `get_freshrss_pipeline_job_status` | `failed` | 结束本次 job,必要时读 linked run |
| `get_run_status` | `running` | 继续观察 run |
| `get_run_status` | `success` | 读取 `get_delivery_payload` / `get_run_report` |
| `get_run_status` | `failed` | 先看 `inspect_resume_plan` |
| `inspect_resume_plan` | `recommended_action=resume` | 启动 `start_resume_job` |
| `inspect_resume_plan` | `recommended_action=read_terminal_result` | 直接读 run 结果,不恢复 |
| `inspect_resume_plan` | `recommended_action=start_new_run` | 新开 run 或人工介入 |
## 7. 人工介入条件
出现以下任一情况时,建议不要自动编排:
- 连续恢复失败
- `get_run_status` 与实际产物明显不一致
- payload/report 结构不符合预期
- 恢复依赖的关键文件缺失且原因不明
- FreshRSS / LLM / 外部环境异常
- payload / report 结构不符合预期
- `get_run_status` 与实际产物长期明显冲突
- FreshRSS、LLM 或外部依赖异常
- 恢复判定结果和编排预期不一致
---
## 8. 结论
## 7. 读取结果的标准动作
当前 OpenClaw 的正式调用方式已经收口为两条异步控制面:
### 7.1 `get_delivery_payload`
- 主日报:`start_freshrss_pipeline_job -> poll -> get result -> run reads`
- 恢复:`inspect_resume_plan -> start_resume_job -> poll -> get result`
用途:
- 获取正式交付给 OpenClaw 的 payload
- 后续 digest 生成应以该返回为准
OpenClaw 应做:
- 读取后直接进入 digest 生成
- 不再自己拼 `candidates/openclaw-delivery-payload.json`
同步 `run_freshrss_openclaw_pipeline` 和 `resume_run` 仅用于 debug / fallback,不应再作为默认正式编排路径。
### 7.2 `get_run_report`
@@ -263,7 +294,9 @@ OpenClaw 应做:
1. 调 `get_run_status`
2. 若 `failed && recovery.resumable=true`:
- 调 `resume_run`
- 调 `start_resume_job`
- 轮询 `get_resume_job_status`
- 读取 `get_resume_job_result`
3. 恢复后再次:
- 调 `get_run_status`
- 若成功,再读 payload / report
@@ -279,7 +312,7 @@ OpenClaw 应做:
- 让 OpenClaw 直接长时间 `exec` reader CLI 作为主要生产入口
- 让 OpenClaw 自己拼 reader 输出路径来判断成功/失败
- 让 OpenClaw 自己读取 `outputs/.../*.json` 作为正式结果源
- 在未确认 `resume_run` 支持范围外的失败点上强行恢复
- 在未确认恢复支持范围外的失败点上强行恢复
CLI 现在的定位是:
@@ -293,7 +326,7 @@ CLI 现在的定位是:
## 10. 当前已知局限
- `resume_run` 仍是最小实现,不支持任意 stage 任意重入
- 恢复能力仍是最小实现,不支持任意 stage 任意重入
- 历史无 `run-state.json` 的 run 不支持正式恢复
- 极旧 run 的结果读取仍可能依赖保守目录扫描
- `write_run_report` 若涉及重新 `mark_read`,仍依赖 FreshRSS 环境和可用凭据
@@ -304,4 +337,4 @@ CLI 现在的定位是:
OpenClaw 当前应把 reader 当作正式 MCP workflow service 使用:
**启动用 `run_freshrss_openclaw_pipeline`,观测用 `get_run_status`,结果读取用 `get_delivery_payload` / `get_run_report`,恢复仅在 `resume_run` 最小支持范围内启用;不要再把 reader 当成长 CLI 任务和路径拼接仓库来驱动。**
**启动用 `start_freshrss_pipeline_job`,观测用 `get_run_status`,结果读取用 `get_delivery_payload` / `get_run_report`,恢复默认用 `inspect_resume_plan` + `start_resume_job`,不要再把 reader 当成长 CLI 任务和路径拼接仓库来驱动。**