230 lines
5.5 KiB
Markdown
230 lines
5.5 KiB
Markdown
# 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”
|