docs: narrow resume_run minimal recovery design
This commit is contained in:
@@ -0,0 +1,276 @@
|
||||
# resume_run 最小恢复语义设计
|
||||
|
||||
## 1. 背景
|
||||
|
||||
前面已经完成:
|
||||
|
||||
- run-state 基础设施
|
||||
- 状态查询接口
|
||||
- 结果读取接口
|
||||
|
||||
reader 现在已经具备“运行真相 + 查询 + 结果读取”能力。下一步是补 `resume_run`,让失败后的 run 可以从已有中间产物继续,而不是完全重跑。
|
||||
|
||||
但 `resume_run` 是第一项真正涉及“重新进入执行流程”的能力,复杂度高于前几轮。因此本轮设计必须进一步收窄范围,避免一次性把任意 stage 重入、复杂后台管理、任务调度全拉进来。
|
||||
|
||||
---
|
||||
|
||||
## 2. 本轮目标
|
||||
|
||||
只做 **最小可用的 `resume_run`**:
|
||||
|
||||
> 基于已有 `run-state.json`,让 freshrss pipeline 能从“最近可恢复点”继续执行。
|
||||
|
||||
本轮不追求:
|
||||
|
||||
- 任意 stage 任意重入
|
||||
- 历史 run 的通用恢复
|
||||
- 无 `run-state.json` run 的恢复
|
||||
- 多任务后台管理
|
||||
- 队列 / 数据库 / worker
|
||||
|
||||
---
|
||||
|
||||
## 3. 支持范围
|
||||
|
||||
### 3.1 仅支持 workflow
|
||||
|
||||
只支持:
|
||||
|
||||
- `freshrss_daily_digest`
|
||||
- 或当前 `run-state.workflow` 对应的 freshrss pipeline workflow
|
||||
|
||||
不支持:
|
||||
|
||||
- article summary 独立恢复
|
||||
- 其他未来 workflow
|
||||
|
||||
### 3.2 仅支持有 run-state 的 run
|
||||
|
||||
调用 `resume_run(run_id=...)` 时,必须满足:
|
||||
|
||||
- run 对应目录存在
|
||||
- `run-state.json` 存在
|
||||
- `run-state.json` 可解析
|
||||
|
||||
否则直接返回不可恢复错误,而不是尝试猜目录结构。
|
||||
|
||||
---
|
||||
|
||||
## 4. 最近可恢复点定义
|
||||
|
||||
本轮采用保守定义:
|
||||
|
||||
### 4.1 恢复依据
|
||||
|
||||
优先使用 `run-state.recovery.resume_from_stage`。
|
||||
|
||||
如果没有该字段,使用:
|
||||
|
||||
- `state.status == failed` 时:失败 stage
|
||||
- 否则:拒绝恢复
|
||||
|
||||
### 4.2 只支持以下恢复点
|
||||
|
||||
第一版仅支持从下面几类 stage 恢复:
|
||||
|
||||
1. `generate_summaries`
|
||||
2. `apply_filters`
|
||||
3. `build_delivery_payload`
|
||||
4. `write_run_report`
|
||||
|
||||
### 4.3 明确不支持的恢复点
|
||||
|
||||
第一版暂不支持从以下位置恢复:
|
||||
|
||||
1. `fetch_feed`
|
||||
2. `extract_articles`
|
||||
|
||||
原因:
|
||||
|
||||
- 这两个阶段更依赖外部抓取与逐条内容处理过程
|
||||
- 恢复语义更复杂
|
||||
- 容易和 FreshRSS 读状态、副作用、原始输入不一致问题缠在一起
|
||||
|
||||
如果 run 停在这两个阶段:
|
||||
|
||||
- `resume_run` 返回 `resumable=false`
|
||||
- 并明确建议重新触发新 run,而不是恢复
|
||||
|
||||
---
|
||||
|
||||
## 5. 恢复前置条件
|
||||
|
||||
### 5.1 必须存在的中间产物
|
||||
|
||||
按恢复点要求最小前置产物:
|
||||
|
||||
#### 从 `generate_summaries` 恢复
|
||||
必须至少有:
|
||||
- raw output
|
||||
- extracted outputs(或足以驱动 summary 的当前输入)
|
||||
|
||||
#### 从 `apply_filters` 恢复
|
||||
必须至少有:
|
||||
- extracted outputs
|
||||
- summary outputs
|
||||
|
||||
#### 从 `build_delivery_payload` 恢复
|
||||
必须至少有:
|
||||
- filter / candidate 所需输入已齐备
|
||||
|
||||
#### 从 `write_run_report` 恢复
|
||||
必须至少有:
|
||||
- delivery payload 已存在
|
||||
|
||||
### 5.2 缺失产物处理
|
||||
|
||||
如果恢复点所需的关键产物缺失:
|
||||
|
||||
- 直接返回不可恢复
|
||||
- 返回字段中注明缺失产物名
|
||||
- 不自动降级到更早 stage
|
||||
|
||||
原因:
|
||||
|
||||
- 第一版先避免隐式魔法恢复
|
||||
- 让行为更可预测
|
||||
|
||||
---
|
||||
|
||||
## 6. 执行语义
|
||||
|
||||
### 6.1 resume_run 的行为
|
||||
|
||||
调用 `resume_run(run_id)` 后:
|
||||
|
||||
1. 读取 `run-state.json`
|
||||
2. 校验 workflow、status、recovery 信息
|
||||
3. 判断恢复点是否在本轮支持范围内
|
||||
4. 校验该恢复点所需产物是否齐备
|
||||
5. 复用现有 freshrss pipeline / runtime,从该恢复点之后继续执行
|
||||
6. 更新原 run 的 `run-state.json`
|
||||
|
||||
### 6.2 不新建 run_id
|
||||
|
||||
第一版恢复时:
|
||||
|
||||
- **继续沿用原 run_id**
|
||||
- 不新建子 run / shadow run / retry run
|
||||
|
||||
原因:
|
||||
|
||||
- 保持恢复行为简单直观
|
||||
- 避免多 run 关系管理复杂化
|
||||
|
||||
### 6.3 状态更新
|
||||
|
||||
恢复开始时:
|
||||
|
||||
- `status` 置回 `running`
|
||||
- `current_stage` 置为恢复点
|
||||
- `error` 清理为 null
|
||||
- 在必要时更新 `recovery` 信息
|
||||
|
||||
恢复成功时:
|
||||
|
||||
- 正常走到 `success`
|
||||
|
||||
恢复失败时:
|
||||
|
||||
- 正常写入新的失败信息
|
||||
- 保留新的 `run-state.json`
|
||||
|
||||
---
|
||||
|
||||
## 7. MCP 接口建议
|
||||
|
||||
### 7.1 新增 tool
|
||||
|
||||
建议新增:
|
||||
|
||||
- `resume_run`
|
||||
|
||||
### 7.2 输入
|
||||
|
||||
```json
|
||||
{
|
||||
"run_id": "freshrss-pipeline-20260407-010236"
|
||||
}
|
||||
```
|
||||
|
||||
第一版不加 `from_stage`,避免人为覆盖恢复点逻辑。
|
||||
|
||||
### 7.3 输出
|
||||
|
||||
建议输出:
|
||||
|
||||
```json
|
||||
{
|
||||
"run_id": "freshrss-pipeline-20260407-010236",
|
||||
"workflow": "freshrss_daily_digest",
|
||||
"resumed": true,
|
||||
"resume_from_stage": "build_delivery_payload",
|
||||
"status": "success",
|
||||
"output_dir": "outputs/freshrss/rerun/freshrss-pipeline-20260407-010236",
|
||||
"delivery_payload": {...},
|
||||
"run_report": {...},
|
||||
"message": "Run resumed from build_delivery_payload and completed successfully."
|
||||
}
|
||||
```
|
||||
|
||||
### 7.4 不可恢复时输出
|
||||
|
||||
```json
|
||||
{
|
||||
"run_id": "freshrss-pipeline-20260407-010236",
|
||||
"resumed": false,
|
||||
"status": "failed",
|
||||
"resume_from_stage": "extract_articles",
|
||||
"message": "This run cannot be resumed from extract_articles in the current minimal implementation.",
|
||||
"missing_artifacts": []
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 代码组织建议
|
||||
|
||||
优先在现有 runtime 体系内最小扩展:
|
||||
|
||||
- `run_store.py`
|
||||
- 补载入/更新辅助能力(如还缺)
|
||||
|
||||
- `query_service.py`
|
||||
- 可复用读取 run-state / artifact / report / payload 的查询能力
|
||||
|
||||
- 新增或补充 runtime service
|
||||
- 例如 `resume_service.py` 或在现有 runtime 层增加 resume 逻辑
|
||||
|
||||
- `server.py`
|
||||
- 新增 MCP tool `resume_run`
|
||||
|
||||
关键原则:
|
||||
|
||||
- 不要重写整条 freshrss pipeline
|
||||
- 应尽量让 pipeline 能接受“从某 stage 之后继续”的最小参数
|
||||
|
||||
---
|
||||
|
||||
## 9. 明确不做的事
|
||||
|
||||
本轮明确不做:
|
||||
|
||||
1. `rerun_stage`
|
||||
2. 指定任意 `from_stage`
|
||||
3. 多 workflow 通用恢复框架
|
||||
4. 恢复时自动修补缺失产物
|
||||
5. 历史无 run-state run 的恢复
|
||||
6. 后台异步恢复任务管理
|
||||
|
||||
---
|
||||
|
||||
## 10. 一句话结论
|
||||
|
||||
`resume_run` 第一版只做一件事:
|
||||
|
||||
**对已有 `run-state.json` 的 freshrss run,在恢复点和前置产物都满足时,从最近可恢复点继续执行;否则明确拒绝恢复,不做隐式魔法补救。**
|
||||
Reference in New Issue
Block a user