Add keyword cleanup governance workflow

This commit is contained in:
zhuyongxin
2026-03-27 16:59:52 +08:00
parent 100044e1f7
commit 32ad584348
25 changed files with 1915 additions and 18 deletions
+14 -8
View File
@@ -1,4 +1,4 @@
# 文档索引
# 文档索引
## 当前目录结构
@@ -31,17 +31,19 @@
- 过滤层的输入输出、规则结构与当前实现
7. `docs/design/filter-rule-engine-usage.md`
- 规则怎么写、怎么跑、结果怎么解读的使用说明
8. `docs/design/markdown-sink-design.md`
8. `docs/design/daily-keyword-index-design.md`
- 日报级词元库与周期性词元清洗 skill 设计
9. `docs/design/markdown-sink-design.md`
- 第一版 Markdown sink 的输入输出、目录结构与落地方式
9. `docs/openclaw/openclaw-daily-digest-refactor.md`
10. `docs/openclaw/openclaw-daily-digest-refactor.md`
- 为什么要从单篇入库改成 OpenClaw 日报聚合链路
10. `docs/openclaw/article-candidate-daily-digest-schema.md`
11. `docs/openclaw/article-candidate-daily-digest-schema.md`
- `ArticleCandidateRecord`、`OpenClawCandidateInput` 与 `DailyDigest` 的正式设计
11. `docs/design/source-schema-design.md`
12. `docs/design/source-schema-design.md`
- `source -> item -> document` 的对象设计
12. `docs/notes/reading-pipeline-design-notes.md`
13. `docs/notes/reading-pipeline-design-notes.md`
- 更上层的阅读流方案与阶段划分
13. `docs/design/summary-loop-explained.md`
14. `docs/design/summary-loop-explained.md`
- 当前 LLM 摘要校验闭环的解释
## 当前文档分层
@@ -71,6 +73,10 @@
- 第一版规则过滤引擎设计与落地位置
- `docs/design/filter-rule-engine-usage.md`
- 规则配置、调用方式与结果解读
- `docs/design/daily-keyword-index-design.md`
- 日报级词元库与周期性词元清洗 skill 设计
- `scripts/apply_term_suggestions.py`
- 人工确认后将建议写回 watchlist / change log 的脚本入口(见 `README.md` 用法)
- `docs/design/markdown-sink-design.md`
- 第一版 Markdown sink 设计与落地位置
@@ -103,4 +109,4 @@
- `TODO.md` 记录任务优先级与下一步
- `outputs/README.md` 记录当前输出目录约定
- `docs/archive/content-extract-mcp-mvp-archive.md` 只当历史快照,不再作为最新事实来源
- 新增阶段性进展,优先更新 `README.md`、`TODO.md`、`docs/current/context-reset-brief.md`
- 新增阶段性进展,优先更新 `README.md`、`TODO.md`、`docs/current/context-reset-brief.md`
+43 -2
View File
@@ -23,6 +23,10 @@ OpenClaw 应通过 MCP 工具 `run_freshrss_openclaw_pipeline` 调用这条链
- 已完成“仅在最终 payload 成功写盘后再标记已读”的语义
- 已完成 MCP 工具 `run_freshrss_openclaw_pipeline`
- 已完成默认精简输出模式,减少中间文件
- 已完成日报级 `keywords` 词元库与全局词频统计
- 已完成 `keyword-cleanup-review` skill 骨架与 review bundle 脚本
- 已完成低复杂治理层:`term_cleanup_policy` / `term_watchlist` / `term_change_log`
- 已完成采纳建议写回脚本 `scripts/apply_term_suggestions.py`
## 当前 MCP 工具
@@ -47,6 +51,10 @@ OpenClaw 应通过 MCP 工具 `run_freshrss_openclaw_pipeline` 调用这条链
- `src/summary_mcp/server.py`
- FreshRSS 统一工作流
- `src/summary_mcp/workflows/freshrss_pipeline.py`
- 词元统计核心
- `src/summary_mcp/core/keyword_index.py`
- 词元统计模型
- `src/summary_mcp/models/keyword_index.py`
- 摘要循环
- `src/summary_mcp/core/summary_loop.py`
- 提取主流程
@@ -63,6 +71,18 @@ OpenClaw 应通过 MCP 工具 `run_freshrss_openclaw_pipeline` 调用这条链
- `src/summary_mcp/models/openclaw_delivery.py`
- 生产脚本入口
- `scripts/run_freshrss_pipeline.py`
- 词元统计重建脚本
- `scripts/build_keyword_index.py`
- 词元清洗 skill
- `skills/keyword-cleanup-review/SKILL.md`
- skill review bundle 脚本
- `skills/keyword-cleanup-review/scripts/build_review_bundle.py`
- 采纳建议写回脚本
- `scripts/apply_term_suggestions.py`
- 清洗治理配置
- `configs/term_cleanup_policy.json`
- `configs/term_watchlist.json`
- `configs/term_change_log.json`
- OpenClaw 交接说明
- `docs/openclaw/openclaw-handoff.md`
@@ -74,6 +94,19 @@ OpenClaw 应通过 MCP 工具 `run_freshrss_openclaw_pipeline` 调用这条链
- `candidates/openclaw-delivery-payload.json`
- `run-report.json`
同时会更新本地运行数据:
- `data/term_index/daily/YYYY-MM-DD.json`
- `data/term_index/term_stats.json`
如果需要词元清洗审阅输入,可额外生成:
- `outputs/term_index/review/keyword-cleanup-bundle.json`
如果需要在人工确认后把建议正式写入 watchlist / change log,可使用:
- `scripts/apply_term_suggestions.py`
如果需要排障,可开启:
- `debug_artifacts=true`
@@ -90,6 +123,10 @@ OpenClaw 应通过 MCP 工具 `run_freshrss_openclaw_pipeline` 调用这条链
- MCP 工具入口可直接触发完整链路
- 微信公众号样本可直接使用 RSS 提供的 `summary` 内容提取,不再回源抓网页
- 精简输出模式已实际跑通
- 日报级词元统计已通过离线样例验证,确认别名、停用词、非 `drop` 过滤和 rerun 覆盖逻辑正常
- `keyword-cleanup-review` skill 已通过 `quick_validate.py` 结构校验
- review bundle 脚本已实际跑通
- `apply_term_suggestions.py` 已通过 dry-run 与临时副本写回验证
## 当前已知限制
@@ -98,6 +135,8 @@ OpenClaw 应通过 MCP 工具 `run_freshrss_openclaw_pipeline` 调用这条链
- 某些规则仍偏保守,部分内容可能落到 `review`
- `paywall` 相关启发式仍可能误判中文文本
- Webhook / 主动投递到 OpenClaw 外部接口尚未实现,当前是由 OpenClaw 通过 MCP 主动调用
- 词元清洗 skill 当前已支持“bundle 构建 -> 建议审阅 -> 人工确认写回 watchlist/change_log”,但尚未接入周期性调度
- 当前词元统计仍以前置 `OpenClawDeliveryPayload` 作为日报前代理输入,真实 `DailyDigest` 接入后还需切换上游
## 当前最建议的交接阅读顺序
@@ -105,8 +144,10 @@ OpenClaw 应通过 MCP 工具 `run_freshrss_openclaw_pipeline` 调用这条链
2. `docs/openclaw/openclaw-handoff.md`
3. `docs/openclaw/openclaw-candidate-input-field-spec.md`
4. `docs/openclaw/openclaw-delivery-payload-spec.md`
5. `TODO.md`
5. `docs/design/daily-keyword-index-design.md`
6. `skills/keyword-cleanup-review/SKILL.md`
7. `TODO.md`
## 一句话结论
当前仓库已经从“提取 MCP 原型”演进到“可供 OpenClaw 调用的 FreshRSS -> OpenClaw payload 上游处理器”,可以开始交接,但后续仍建议继续补 webhook / delivery 接线与规则收敛。
当前仓库已经从“提取 MCP 原型”演进到“可供 OpenClaw 调用的 FreshRSS -> OpenClaw payload 上游处理器”,并已补上第一阶段的日报级词元统计能力和词元清洗 skill 骨架;后续重点转向 skill 周期调度、知识库状态流转和 webhook 接线。
+461
View File
@@ -0,0 +1,461 @@
# 日报级词元库与词元清洗 Skill 设计
## 1. 设计目标
当前项目已经具备:
`FreshRSS -> extraction -> LLM summary -> rule engine -> OpenClaw payload`
下一阶段希望新增“词元库”能力,目标不是做全文级检索索引,而是解决两件事:
1. 为后续规则配置提供稳定、轻量、可读的词元来源
2. 为周期性的词元清洗、合并和兴趣词补充提供数据基础
因此这套设计的核心原则是:
- 词元来源轻量化
- 数据粒度日报化
- 主链路程序化维护
- 清洗治理由独立 skill 周期性执行
- LLM 不直接修改规则或兴趣词配置
## 2. 为什么不用逐篇词元库
逐篇记录每篇文章的词元事件,虽然可追溯,但当前阶段成本过高,收益不足:
- 生成文件会很多
- 存储和调试负担更大
- 后续真正调规则时,用户更关心“最近日报里反复出现什么词”,而不是“某一篇文章具体抽到了什么词”
- 当前目标是服务日报与规则配置,不是做文章级分析平台
所以本项目不采用:
`article -> term event -> term store`
而采用:
`daily digest -> keyword aggregate -> daily term index -> global term stats`
## 3. 为什么只保留 `keywords`
当前 LLM 摘要结果里已经有两个候选字段:
- `keywords`
- `topics`
本设计只使用 `keywords` 进入词元库,不使用 `topics` 作为主来源。
原因:
- `keywords` 更具体,适合后续规则配置
- 例如 `Java`、`Go`、`Python`、`MCP`、`RAG`、`Kafka`
- `topics` 更宽泛,适合摘要展示,不适合作为精确规则命中基础
- 例如“后端工程”“AI Agent”“前沿科技”过于宽泛
- 只保留一个字段可以显著控制词元库规模
- 当前项目的真实需求是“词元可配置”,不是“主题聚类”
结论:
- `keywords` 进入词元库
- `topics` 继续保留在单篇摘要结果中,但不纳入词元统计主流程
## 4. 词元库的上游边界
词元库不直接读取所有候选文章,而只读取“最终进入日报”的内容。
也就是说,真正的词元来源是:
- `DailyDigest`
- 或等价的“已被日报选中”的 candidate 集合
不纳入词元库的内容:
- 被 `drop` 的内容
- 仅在中间候选层出现、但未进入日报的内容
- 调试产物中的临时摘要结果
这样做的好处是:
- 词元库只反映真正进入日级产物的内容
- 高频词更接近长期兴趣,而不是临时噪声
- 数据量更可控
## 5. 总体链路
目标链路调整为:
`DailyDigest -> keyword normalization -> daily term index -> global term stats -> cleaning skill review -> human confirm -> config update`
职责拆分如下:
### 5.1 程序负责
- 从日报中提取 `keywords`
- 归一化词元
- 应用别名映射
- 应用停用词过滤
- 生成每日词频
- 更新全局累计统计
### 5.2 Skill 负责
- 周期性读取词频结果
- 识别重复词、近义词、大小写变体
- 识别泛词、噪声词、低价值词
- 建议哪些词应合并、停用、加入兴趣词配置
### 5.3 人工负责
- 审核 skill 输出的建议
- 决定是否更新:
- `term_aliases`
- `term_stopwords`
- `filter_context.personal.json`
## 6. 数据文件设计
建议新增以下文件:
- `data/term_index/daily/YYYY-MM-DD.json`
- 某一天日报的词元聚合结果
- `data/term_index/term_stats.json`
- 全局累计词元统计
- `configs/term_aliases.json`
- 词元别名归一配置
- `configs/term_stopwords.json`
- 词元停用词配置
- `configs/term_cleanup_policy.json`
- 清洗阈值与治理策略配置
- `configs/term_watchlist.json`
- 当前处于观察状态的词元列表
- `configs/term_change_log.json`
- 已确认生效的词元治理变更记录
说明:
- 不新增逐篇 `term_events.jsonl`
- 不新增文章级明细文件
- 默认只保留“日报聚合结果 + 全局统计结果”
## 7. 每日词元文件结构
文件路径示例:
- `data/term_index/daily/2026-03-26.json`
建议结构:
```json
{
"date": "2026-03-26",
"source": "daily_digest",
"digest_id": "digest-2026-03-26",
"generated_at": "2026-03-26T21:30:00+08:00",
"candidate_count": 8,
"terms": [
{
"term": "AI Agent",
"normalized_term": "AI Agent",
"count": 4
},
{
"term": "MCP",
"normalized_term": "MCP",
"count": 3
},
{
"term": "RAG",
"normalized_term": "RAG",
"count": 2
}
]
}
```
字段说明:
- `date`
- 日报日期
- `source`
- 固定标记为 `daily_digest`
- `digest_id`
- 日报对象唯一标识
- `generated_at`
- 该词元文件生成时间
- `candidate_count`
- 当天日报包含的条目数
- `terms[]`
- 当天词元聚合结果
其中单条 `terms[]` 只保留:
- `term`
- `normalized_term`
- `count`
不保留:
- 逐篇文章来源列表
- 逐条命中明细
- 本地文件路径
## 8. 全局统计文件结构
文件路径:
- `data/term_index/term_stats.json`
建议结构:
```json
{
"schema_version": "v1",
"generated_at": "2026-03-26T21:30:00+08:00",
"terms": [
{
"term": "AI Agent",
"total_count": 18,
"days_seen": 6,
"first_seen": "2026-03-20",
"last_seen": "2026-03-26"
},
{
"term": "MCP",
"total_count": 12,
"days_seen": 5,
"first_seen": "2026-03-21",
"last_seen": "2026-03-26"
}
]
}
```
字段说明:
- `term`
- 最终归一后的词元
- `total_count`
- 累计出现次数
- `days_seen`
- 出现过的天数
- `first_seen`
- 首次出现日期
- `last_seen`
- 最近出现日期
当前阶段不额外记录:
- 各 category 分桶统计
- keep/review/drop 分桶统计
- 文章级来源列表
原因是:日报级词元库的第一目标是轻量稳定,不是分析平台。
## 9. 标准化与归一规则
程序在写入日报词元前,应先做标准化。
### 9.1 基础标准化
- 去除首尾空白
- 保留中英文大小写风格中的稳定写法
- 去重
- 过滤空字符串
### 9.2 别名映射
通过 `configs/term_aliases.json` 做归一。
示例:
```json
{
"Agent": "AI Agent",
"智能体": "AI Agent",
"Postgres": "PostgreSQL",
"Model Context Protocol": "MCP"
}
```
### 9.3 停用词过滤
通过 `configs/term_stopwords.json` 过滤过泛词和噪声词。
示例:
```json
[
"技术",
"系统",
"方案",
"实践",
"文章"
]
```
## 10. 为什么不让 LLM 直接维护词元库
LLM 可以帮助做清洗建议,但不适合直接维护主词元库。
原因:
- 主词元库更新应该稳定、低成本、可复现
- 词频统计属于纯程序逻辑,没必要消耗模型调用
- 如果让 LLM 直接写词元库,会引入不稳定和难审计问题
因此主流程固定为:
- LLM 只负责在摘要结果里输出 `keywords`
- 程序负责归一、聚合、统计
## 11. 词元清洗 Skill 设计
新增一个周期性清洗 skill,定位是“治理器”,不是“实时生产者”。
### 11.1 Skill 输入
建议输入:
- `data/term_index/term_stats.json`
- 最近 N 天的 `data/term_index/daily/*.json`
- `configs/term_aliases.json`
- `configs/term_stopwords.json`
- `configs/filter_context.personal.json`
### 11.2 Skill 输出
skill 不直接修改配置文件,而是生成建议文件,例如:
- `outputs/term_index/review/term-cleanup-suggestions-YYYY-MM-DD.md`
- `outputs/term_index/review/term-cleanup-suggestions-YYYY-MM-DD.json`
低复杂治理层建议补充三类输入:
- `term_cleanup_policy`
- 用来定义 watch 和 interest 的最小证据阈值
- `term_watchlist`
- 用来记录“先观察、暂不升级”的词元
- `term_change_log`
- 用来记录已经确认落地的配置变更,避免后续遗忘上下文
建议项包括:
- 建议合并的别名词
- 建议新增的停用词
- 建议加入 `interest_keywords` 的候选词
- 建议降权观察的热点词
### 11.3 Skill 允许做什么
- 发现重复词
- 发现大小写变体
- 发现中英文混用的近义词
- 发现持续高频但尚未进入兴趣配置的词
- 发现明显过泛的词
### 11.4 Skill 不允许做什么
- 直接改 `filter_rules.json`
- 直接改 `filter_context.personal.json`
- 直接覆盖 `term_stats.json`
- 在无人工确认的情况下自动生效
## 12. 建议的清洗建议文件结构
建议 JSON 文件结构如下:
```json
{
"date": "2026-03-26",
"based_on_days": 7,
"alias_suggestions": [
{
"from": "Agent",
"to": "AI Agent",
"reason": "和现有高频词语义一致,建议归并。"
}
],
"stopword_suggestions": [
{
"term": "系统",
"reason": "出现频繁但语义过泛,难以作为规则命中词。"
}
],
"interest_keyword_suggestions": [
{
"term": "MCP",
"reason": "最近多日持续高频,且符合当前 AI Agent 学习方向。"
}
]
}
```
## 13. 周期与触发方式
建议触发周期:
- 词元统计更新:每天一次,跟随日报生成
- skill 清洗:每周一次,或人工手动触发
推荐流程:
1. 当天日报生成完成
2. 程序更新 `daily/YYYY-MM-DD.json`
3. 程序更新 `term_stats.json`
4. 每周或人工触发一次词元清洗 skill
5. skill 输出建议
6. 人工确认后再更新配置文件
## 14. 与规则引擎的关系
这套词元库设计不是规则引擎的替代品,而是规则配置的辅助层。
关系如下:
- `keywords`
- 是词元库来源
- `term_stats`
- 是观察与调优依据
- `filter_context.personal.json`
- 是真正给规则引擎使用的兴趣词配置
- `filter_rules.json`
- 是最终裁决逻辑
也就是说:
`日报词元统计 -> 清洗建议 -> 人工确认 -> 更新 interest_keywords -> 规则引擎命中`
而不是:
`日报词元统计 -> 自动改规则`
## 15. 分阶段落地建议
### Phase 1
先做最小可用版本:
- 只读取日报中的 `keywords`
- 生成每日词元文件
- 生成全局累计词频文件
- 支持 `term_aliases` 和 `term_stopwords`
### Phase 2
再补治理层:
- 增加词元清洗 skill
- 输出建议文件
- 人工确认后更新配置
### Phase 3
最后再考虑增强:
- 增加趋势分析
- 增加最近 7 天热点词视图
- 增加“建议加入兴趣词”的自动排序
## 16. 一句话结论
这套设计选择“只统计日报中的 `keywords`,由程序维护轻量词元库,再由独立 skill 周期性做清洗建议”,目的是在控制数据规模的前提下,为规则配置和长期兴趣演化提供稳定、可审计、可扩展的基础设施。