Files
reader/TODO.md
T
root 590d050218 keyword cleanup: v2 engine, alias rule layer, LLM semantic suggestions
- build_review_bundle.py: 新增 _compute_percentile/_compute_growth,
  候选池从固定阈值改为百分位排名 + 增速因子 (v2 policy)
- term_cleanup_policy.json: 升级 v2 schema
- generate_term_cleanup_suggestions.py: 新增 _prepare_alias_suggestions,
  规则层输出 alias (大小写/单复数/分词变体)
- generate_term_cleanup_semantic_suggestions.py: 新增 LLM 语义建议脚本
  (DeepSeek API, 产出 semantic alias/stopword/promote)
- SKILL.md: 更新为 5 Phase 工作流程
- 首轮清洗 apply: interest 54, aliases 17组, stopwords 17个
- docs/design/keyword-cleanup-flow-overview.md: 流程文档
- plans/: 引擎设计方案
2026-05-14 17:17:49 +08:00

373 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# TODO - Reader MCP 正式化
> 本文件用于架构与 Codex 协作同步。
>
> 规则:
> - `TODO` = 未开始
> - `DOING` = 正在进行
> - `DONE` = 已完成
> - 每次只允许一个最高优先级主任务处于 `DOING`
## 0. 协作约束
开始编码前必须阅读:
1. `README.md`
2. `docs/README.md`
3. `docs/openclaw/README.md`
4. `docs/openclaw/openclaw-handoff.md`
5. `docs/openclaw/openclaw-orchestration-flow.md`
6. `plans/README.md`
7. `plans/reader-mcp-architecture-design.md`
8. `plans/reader-mcp-implementation-plan.md`
9. 本文件
10. `plans/issues/2026-04-06-reader-digest-sigterm.md`
---
## 1. 当前主任务
### [DONE][P0] 建立 run-state 运行态基础设施
目标:
- 给 freshrss pipeline 引入正式 run state
- 即使失败或中断,也能留下明确运行真相
要求:
- 新增 `RunState / StageState / ArtifactRecord` 模型
- 在 `outputs/freshrss/rerun/<run_id>/run-state.json` 持久化
- 至少覆盖以下 stages:
- `fetch_feed`
- `extract_articles`
- `generate_summaries`
- `apply_filters`
- `build_delivery_payload`
- `write_run_report`
- 失败时写入失败阶段与错误摘要
- 不破坏现有输出目录兼容性
建议文件:
- `src/summary_mcp/runtime/state_models.py`
- `src/summary_mcp/runtime/run_store.py`
- `src/summary_mcp/workflows/...`
完成标准:
- 跑一次 pipeline 后,无论成功失败,都存在 `run-state.json`
- 文件中可看出当前/最后阶段、整体状态、关键 artifacts
进展备注:
- 2026-04-07:架构设计文档已建立;开始进入实现阶段。
- 2026-04-07:已新增 `runtime` 包骨架,落地 `RunState / StageState / ArtifactRecord` 与文件存储接口。
- 2026-04-07:已将 `run-state.json` 接入 `freshrss` 主流程,按阶段持续写入状态与关键 artifacts。
- 2026-04-07:已完成成功/失败路径自检,确认 `run-state.json` 在两类路径下都保留且不改变既有对外返回字段。
---
## 2. 后续任务队列
### [DONE][P1] 增加 MCP 状态查询接口 `get_run_status`
目标:
- 可通过 MCP 查询 run 状态
要求:
- 输入 `run_id`
- 返回 status / current_stage / completed_stages / failed_stage / artifacts / recovery
完成情况:
- 已通过 MCP 暴露 `get_run_status`
- 优先读取 `run-state.json`;对无 `run-state.json` 的历史 run 兼容基于现有 run 目录与 `run-report.json` 推断状态
- 返回补充了 `progress` / `output_dir` / `state_source`,便于 OpenClaw 稳定消费且不必手拼路径
改动文件:
- `src/summary_mcp/runtime/query_service.py`
- `src/summary_mcp/runtime/run_store.py`
- `src/summary_mcp/runtime/__init__.py`
- `src/summary_mcp/server.py`
遗留风险:
- 历史 run 若缺少 `run-state.json`,其阶段状态只能基于现有目录与 `run-report.json` 做保守推断
---
### [DONE][P1] 增加 MCP 查询接口 `list_runs`
目标:
- 查看近期 runs
要求:
- 支持按 workflow / status / latest_n 过滤
完成情况:
- 已通过 MCP 暴露 `list_runs`
- 支持按 `workflow` / `status` / `latest_n` 过滤近期 runs
- 返回 `run_id`、`status`、`progress`、`recovery`、`output_dir` 与 `state_source`
改动文件:
- `src/summary_mcp/runtime/query_service.py`
- `src/summary_mcp/runtime/__init__.py`
- `src/summary_mcp/server.py`
遗留风险:
- 当前按文件系统扫描 `outputs/freshrss/rerun/` 聚合,规模继续增大时可能需要再评估缓存或索引,但本阶段先保持文件系统真相
---
### [DONE][P1] 增加 MCP 查询接口 `list_run_artifacts`
目标:
- 统一列出 run 下 artifact
完成情况:
- 已通过 MCP 暴露 `list_run_artifacts`
- 对新 run 优先返回 `run-state.json` 已注册 artifacts,并补充 run 目录扫描发现的标准产物
- 对历史 run 直接基于现有 run 目录发现标准产物,保持兼容
改动文件:
- `src/summary_mcp/runtime/query_service.py`
- `src/summary_mcp/runtime/__init__.py`
- `src/summary_mcp/server.py`
遗留风险:
- 当前仅补充扫描固定的一组标准产物;未注册且不在标准集合内的调试文件不会进入稳定 artifact 列表
---
### [DONE][P1] 增加 MCP 结果读取接口 `get_delivery_payload`
目标:
- 按 run_id 读取 delivery payload
完成情况:
- 已通过 MCP 暴露 `get_delivery_payload`
- 查询优先复用 `run-state.json` 已注册 artifacts,其次回退标准产物路径、`run-report.json` 引用和 run 目录扫描
- 返回补充了 `artifact`、`payload_schema_version`、`generated_at`、`delivery_date`、`candidate_count`、`stats`,并保留完整 `payload`
改动文件:
- `src/summary_mcp/runtime/query_service.py`
- `src/summary_mcp/server.py`
- `src/summary_mcp/runtime/__init__.py`
遗留风险:
- 历史 run 的 payload 若既未注册也不在标准路径下,只能依赖 `run-report.json` 引用或目录扫描做兼容发现
---
### [DONE][P1] 增加 MCP 结果读取接口 `get_run_report`
目标:
- 按 run_id 读取 run report
完成情况:
- 已通过 MCP 暴露 `get_run_report`
- 查询优先复用 `run-state.json` 已注册 artifacts,其次回退标准产物路径、历史 `run-report.json` 固定位置和 run 目录扫描
- 返回补充了 `artifact`、核心计数摘要、规范化后的关键产物路径与 `keyword_index`,并保留完整 `report`
改动文件:
- `src/summary_mcp/runtime/query_service.py`
- `src/summary_mcp/runtime/__init__.py`
- `src/summary_mcp/server.py`
遗留风险:
- 历史 run 的 `run-report.json` 内嵌路径仍保留原始绝对路径于 `report` 字段,当前仅在顶层摘要字段做规范化,避免改变历史文件真相
---
### [DONE][P2] 设计并实现 `resume_run`
目标:
- 基于 `run-state.json` 和现有中间产物继续执行
说明:
- 先做最小可用恢复
- 暂不追求任意 stage 任意重入
- 设计约束已补充到 `plans/resume-run-minimal-design.md`
- 第一版只支持 freshrss workflow 且仅支持有 `run-state.json` 的 run
- 第一版仅考虑从最近可恢复点继续;`fetch_feed` / `extract_articles` 暂不支持恢复
进展备注:
- 2026-04-07:已完成 `resume_run` minimal design 与现有 runtime/workflow/server 代码对齐分析,开始实现最小恢复链路。
- 2026-04-07:已完成 `resume_run` 最小实现编码,新增 runtime 恢复服务并接入 MCP server;当前进入设计对齐与本地自检。
- 2026-04-07:已完成 `resume_run` 架构对齐与本地自检;已验证 `write_run_report` 可恢复,且 `extract_articles` 会被明确拒绝恢复。
- 2026-04-14:已补 `inspect_resume_plan`、artifact-first 恢复判定,以及生产模式下稳定 `summary-batch` / `candidate-batch` artifacts;当前 `resume` 的剩余主问题不再是恢复点判断,而是同步执行模型仍可能让 OpenClaw 恢复阶段超时。
---
### [DONE][P1] 把 `resume_run` 升级为最小真异步 job
目标:
- 解决 `resume_run` 在 OpenClaw → MCP 同步链路里仍可能超时的问题
- 让恢复也具备“启动 / 轮询 / 读取结果”的正式控制面
要求:
- 新增最小异步接口:
- `start_resume_job`
- `get_resume_job_status`
- `get_resume_job_result`
- 状态目录固定落到:
- `outputs/freshrss/resume_jobs/<job_id>/`
- 至少包含:
- `run-state.json`
- `input.json`
- `result.json`(成功时)
- `job-report.json`
- 启动前必须先走 `inspect_resume_plan`
- 业务执行继续复用现有 `resume_service`,不要重写恢复主逻辑
- `resume_run` 保留为同步 debug / fallback 路径,但不再作为 OpenClaw 的默认恢复入口
完成标准:
- 可恢复 run 上,`start_resume_job` 能成功返回 `job_id`
- `get_resume_job_status` 能稳定反映恢复 job 生命周期
- `get_resume_job_result` 能稳定返回 `run_id`、`resume_from_stage`、最终状态与关键产物路径
- 恢复耗时超过单次 MCP 同步窗口时,OpenClaw 仍不会因为同步调用挂住
进展备注:
- 2026-04-14:已落地 `src/summary_mcp/runtime/resume_jobs.py` 与 `scripts/run_resume_job.py`,新增 `start_resume_job` / `get_resume_job_status` / `get_resume_job_result`
- 2026-04-14:启动前会先走 `inspect_resume_plan`;不可恢复 run 会在 job 输入校验阶段直接失败,不进入后台恢复执行
- 2026-04-14:后台执行复用现有 `_resume_freshrss_run(...)`,没有重写恢复主逻辑
- 2026-04-14:已完成本地 synthetic 验证:`write_run_report` 恢复可通过 `start -> poll -> result` 闭环成功收敛
---
### [DONE][P1] 单篇总结改为最小真异步 job
目标:
- 解决 `generate_article_summaries` 在 OpenClaw → MCP 同步链路里易 timeout 的问题
- 将单篇总结正式升级为可启动、可轮询、可读取结果的异步 job
要求:
- 新增最小异步接口:
- `start_article_summary_job`
- `get_article_summary_job_status`
- `get_article_summary_job_result`
- 状态目录固定落到:
- `outputs/freshrss/article_summary_jobs/<job_id>/`
- 至少包含:
- `run-state.json`
- `input.json`
- `result.json`(成功时)
- `job-report.json`
- 执行模型优先使用后台子进程,不使用线程
- 业务逻辑继续复用 `summarize_selected_articles(...)`,不要重写正文总结核心逻辑
- 对 OpenClaw / reader-digest-flow 而言,异步 job 成功后应可继续接 IMA 沉淀闭环
当前进展:
- 2026-04-10:已完成方案文档 `plans/article-summary-async-job-plan.md`
- 2026-04-10:已落地最小代码骨架:
- `src/summary_mcp/runtime/article_summary_jobs.py`
- `scripts/run_article_summary_job.py`
- `src/summary_mcp/server.py` 已新增 3 个 async job tools
- 2026-04-10:已用真实 extracted 文件验证最小异步链路可跑通,job 能成功进入 `running → success`,并可读回结果
- 2026-04-10:已补 README / OpenClaw handoff 文档,并对外统一为 `start_article_summary_job` / `get_article_summary_job_status` / `get_article_summary_job_result`
- 2026-04-10:已完成聚焦自检:
- MCP tool 注册名校验通过
- stubbed async job 成功路径通过
- stubbed async job 失败路径通过,`error_summary` 与 `job-report.json` 可回读
下一步:
- 将 `reader-digest-flow` 正式默认路径切到 async job
- 用真实 LLM 配置再做一次非 stub 的服务端冒烟验证
---
### [DOING][P0] FreshRSS 主日报 run 改为最小真异步 job
目标:
- 解决 `run_freshrss_openclaw_pipeline` 在正式生产链路里仍为同步 MCP 调用、易超时的问题
- 将 FreshRSS 主日报启动路径升级为可启动、可轮询、可读取结果的异步 job
要求:
- 新增最小异步接口:
- `start_freshrss_pipeline_job`
- `get_freshrss_pipeline_job_status`
- `get_freshrss_pipeline_job_result`
- 状态目录固定落到:
- `outputs/freshrss/pipeline_jobs/<job_id>/`
- 至少包含:
- `run-state.json`
- `input.json`
- `result.json`(成功时)
- `job-report.json`
- 执行模型优先使用后台子进程,不使用线程
- 主业务逻辑继续复用 `run_freshrss_pipeline(...)`,不要重写日报核心逻辑
- job 成功后结果中必须带回 `run_id` 与关键产物路径
- README / handoff / OpenClaw 生产建议路径需要同步改成 async start path
当前进展:
- 2026-04-11:问题定位完成,确认之前异步化的是 article-summary,不是主日报 run
- 2026-04-11:已新增方案文档 `plans/freshrss-pipeline-async-job-plan.md`
下一步:
- 复用 article-summary job runtime 骨架实现主日报 async job
- 新增后台 runner 脚本
- 暴露 3 个 MCP tools
- 用真实 MCP 冒烟验证 `start -> status -> result`
---
### [TODO][P2] 评估 `rerun_stage` 是否值得进入第一阶段
目标:
- 在 `resume_run` 之后评估是否继续增加更细粒度补跑能力
---
### [TODO][P2] 调整 `run_freshrss_openclaw_pipeline` 内部实现以复用 runtime
目标:
- 保持外部兼容
- 内部不再是黑箱长函数
---
### [DONE][P1] interest/watch 候选引擎从固定阈值改为百分位排名 + 增速因子
目标:
- 解决固定阈值(total_count>=3)不随数据量自适应的问题
- 引入趋势信号(growth 因子),识别近期集中爆发的词
- 支持 7 天、41 天、200 天数据量下取同样的 top 5%/5%-20% 而不需调阈值
要求:
- `build_review_bundle.py`:新增 percentile 和 growth 计算函数;候选池从固定阈值改为百分位 + 增速
- `configs/term_cleanup_policy.json`:升级为 v2 schema,percentile/growth 替代绝对阈值
- 不改 `generate_term_cleanup_suggestions.py` 和 `apply_term_suggestions.py`
- 全量跑一次对比新旧产出,确认差异合理
方案文档:`plans/keyword-cleanup-interest-watch-engine-improvement.md`
---
### [DONE][P3] 更新 README / handoff / docs,明确 MCP 为正式入口
目标:
- 把生产建议从 CLI 迁移到 MCP
- CLI 明确降级为 debug / fallback
完成情况:
- 已更新 `README.md`,补齐 reader 作为正式 MCP workflow service 的当前能力边界、推荐调用路径、已支持 tools 与最小 `resume_run` 范围
- 已更新 `docs/openclaw/openclaw-handoff.md`,明确 OpenClaw 应优先通过 MCP 读取 run 状态与结果,不再自己拼接 reader 输出路径
改动文件:
- `README.md`
- `docs/openclaw/openclaw-handoff.md`
遗留风险:
- 当前仍无独立 `get_digest_brief` tool;若下游确实需要该产物,仍应先通过 `list_run_artifacts` / `get_run_report` 发现,而不是写死路径
---
## 3. 记录区
### 已完成记录
- 2026-04-07:新增架构设计文档 `plans/reader-mcp-architecture-design.md`
- 2026-04-07:新增实施计划文档 `plans/reader-mcp-implementation-plan.md`
- 2026-04-07:完成 `freshrss` pipeline 的 run-state 基础设施,新增 `runtime` 包并覆盖关键 stages 状态持久化。
- 2026-04-14:已补 OpenClaw 文档导航、历史归档、design/notes/plans 导航,并统一当前正式口径为 async job 编排入口。
### 风险提醒
- 不要在第一阶段引入复杂任务队列
- 不要让 CLI 和 MCP 背后变成两套独立逻辑
- 若实现偏离架构,先更新 `plans/` 再改代码