feat: add async job entrypoint for freshrss pipeline

This commit is contained in:
root
2026-04-11 22:12:11 +08:00
parent 10f9cd088f
commit c58d8114cd
8 changed files with 841 additions and 29 deletions
+229
View File
@@ -0,0 +1,229 @@
# FreshRSS 主日报异步 job 方案
## 背景
当前 `run_freshrss_openclaw_pipeline` 虽然已经作为正式 MCP workflow 入口存在,但执行模型仍是**同步 MCP 调用**。这会带来几个现实问题:
1. OpenClaw / MCP wrapper 存在超时风险,尤其是 5-10 篇的正式日报批次。
2. wrapper timeout 与真实 run 是否已落地,容易出现语义分离。
3. 当前已有 `run-state.json`、`get_run_status`、`get_run_report`、`get_delivery_payload`,但**启动层**仍然是同步调用,不利于正式生产链路稳定运行。
4. 单篇总结已经验证了“最小 async job + 轮询状态 + 读取结果”模型可行,主日报 run 应收敛到同一套运行模式。
## 目标
将 FreshRSS 主日报 run 改造成与 article-summary 类似的**最小真异步 job**:
- 启动即返回 `job_id`
- 真正执行由后台子进程完成
- 状态可轮询
- 成功后可读取结构化结果
- 业务逻辑继续复用既有 `run_freshrss_pipeline(...)`
- 不推翻现有 run-state / result query 能力
## 非目标
本阶段不做:
- 分布式任务队列
- 多 worker 调度
- 任意 stage 的后台恢复编排
- 并发控制中心
- 主流程与 article-summary job 的通用抽象框架一次性大重构
先做最小可用。
## 设计原则
1. **启动层异步化,执行核心不重写**
- `run_freshrss_pipeline(...)` 继续是主业务逻辑真相。
- async job 只负责启动、状态持久化、结果回读。
2. **run truth 与 job truth 分层**
- job truth:这次异步任务有没有启动、运行到哪一步、是否成功。
- run truth:真正的 freshrss workflow 输出与 `run-state.json`。
3. **OpenClaw 正式生产默认改为 async start path**
- 启动走 async job
- 状态和结果优先先看 job
- 真正业务产物仍由现有 run 查询工具承接
4. **与 article-summary job 尽量同构**
- 目录结构
- `run-state.json` / `input.json` / `result.json` / `job-report.json`
- 后台 runner 脚本
## 拟新增能力
### MCP tools
新增 3 个工具:
- `start_freshrss_pipeline_job`
- `get_freshrss_pipeline_job_status`
- `get_freshrss_pipeline_job_result`
### job 目录
固定目录:
`outputs/freshrss/pipeline_jobs/<job_id>/`
至少包含:
- `run-state.json`
- `input.json`
- `result.json`(成功时)
- `job-report.json`
### 执行模型
- `start_freshrss_pipeline_job` 写入 input + 初始化 job state
- 后台 `subprocess.Popen(...)` 启动 runner
- runner 内部调用 `run_freshrss_pipeline(...)`
- 成功后把 `run_id`、核心产物路径、关键计数写入 `result.json`
## job 输入参数
与现有 `run_freshrss_openclaw_pipeline` 尽量对齐:
- `limit`
- `mark_read`
- `include_read`
- `debug_artifacts`
- `continuation`
- `timeout_seconds`
- `max_retries`
- `stream_id`
- `api_base_url`
- `username`
- `api_password`
- `llm_api_key`
- `llm_model`
- `llm_api_url`
- `context`
- `run_id`
- `date_value`
- `output_dir`
- `include_item_reports`
## 返回语义
### start
返回:
- `job_id`
- `workflow`
- `run_type`
- `status=running`
- `output_dir`
- `message`
### status
返回:
- `job_id`
- `status`
- `current_stage`
- `started_at` / `updated_at` / `finished_at`
- `progress`
- `artifacts`
- `error_summary`
- 若主 run 已创建,可附带 `linked_run_id`
### result
成功时返回:
- `job_id`
- `status=success`
- `run_id`
- `delivery_output`
- `report_output`
- `digest_brief_output`
- `pulled_count`
- `delivered_count`
- `marked_read_count`
- `artifact`
- `result`
## stages 建议
最小 job stages:
1. `prepare_job`
2. `load_input`
3. `run_pipeline`
4. `write_result`
其中 `run_pipeline` 内部仍由现有 freshrss workflow 自己写它的 run-state。
## 与现有同步入口的关系
### 保留
`run_freshrss_openclaw_pipeline` 暂时保留,作为:
- debug / light path
- 本地调试工具
- 向后兼容路径
### 正式语义调整
文档与 OpenClaw handoff 中,主日报正式生产默认启动入口改为:
- `start_freshrss_pipeline_job`
同步入口降级为:
- debug / fallback
- 小批量验证
## OpenClaw 编排建议
新的推荐路径:
1. `start_freshrss_pipeline_job`
2. `get_freshrss_pipeline_job_status`
3. 成功后 `get_freshrss_pipeline_job_result`
4. 后续仍用:
- `get_run_status`
- `get_delivery_payload`
- `get_run_report`
- `list_run_artifacts`
## 风险点
1. **job 成功但 run 部分失败**
- 允许,job 结果应以真实 `run_freshrss_pipeline(...)` 返回为准。
- `run_id` + `report_output` 仍是最终真相。
2. **runner 崩溃但来不及写 result**
- 需保证 `job-report.json` 至少能写下失败摘要。
3. **重复状态源导致混淆**
- 文档必须明确:
- job state 管“启动任务”
- run state 管“业务工作流真相”
4. **同步 / 异步双入口长期漂移**
- 必须要求 async job 内部直接复用 `run_freshrss_pipeline(...)`
- 禁止再实现一套平行主流程
## 验收标准
1. 能通过 MCP 启动一个主日报 async job 并立即返回 `job_id`
2. 能轮询到 `running -> success/failed`
3. 成功后 `result.json` 含 `run_id` 与核心产物路径
4. 对应 run 仍能通过既有 `get_run_status` / `get_run_report` / `get_delivery_payload` 正常读取
5. README / handoff / TODO / plans 同步更新
## 建议实施顺序
1. 复制 article-summary job 骨架到 freshrss pipeline job
2. 新增 runner 脚本
3. server.py 暴露 3 个新工具
4. 补 query/result 读法
5. 更新 README / handoff
6. 将 TODO 主任务切到“主日报 async job”