Add FreshRSS ingestion and rule filtering

This commit is contained in:
zhuyongxin
2026-03-24 18:37:14 +08:00
parent c8d96705d1
commit ff3d15b8c4
21 changed files with 1460 additions and 21 deletions
+59
View File
@@ -0,0 +1,59 @@
# 文档索引
## 当前推荐阅读顺序
1. `context-reset-brief.md`
- 当前真实进度与下一步入口
2. `summary-mcp-service-design.md`
- 当前 MCP 服务的职责、接口和边界
3. `filter-rule-engine-design.md`
- 过滤层的输入输出、规则结构与当前实现
4. `source-schema-design.md`
- `source -> item -> document` 的对象设计
5. `reading-pipeline-design-notes.md`
- 更上层的阅读流方案与阶段划分
6. `summary-loop-explained.md`
- 当前 LLM 摘要校验闭环的解释
## 当前文档分层
### 1. 当前状态与导航
- `README.md`
- 仓库入口与脚本运行方式
- `TODO.md`
- 当前优先级、已完成项、下一阶段任务
- `docs/context-reset-brief.md`
- 当前阶段状态的最短摘要
- `docs/README.md`
- 文档索引与阅读顺序
### 2. 当前实现设计
- `docs/summary-mcp-service-design.md`
- 当前内容提取 MCP 的真实设计
- `docs/summary-core-interface-design.md`
- 摘要/提取内核的接口抽象
- `docs/source-schema-design.md`
- `source`、`item`、`document` 三层 schema
- `docs/summary-loop-explained.md`
- 提取 JSON -> LLM 摘要 JSON -> 校验 的闭环说明
- `docs/filter-rule-engine-design.md`
- 第一版规则过滤引擎设计与落地位置
### 3. 上下游方案设计
- `docs/reading-pipeline-design-notes.md`
- 整体阅读流、规则、sink、push 的方案笔记
### 4. 历史归档
- `docs/content-extract-mcp-mvp-archive.md`
- MVP 阶段归档,部分状态已被后续进展覆盖
## 当前文档维护原则
- `context-reset-brief.md` 记录当前最新状态
- `TODO.md` 记录任务优先级与下一步
- `content-extract-mcp-mvp-archive.md` 只当历史快照,不再作为最新事实来源
- 新增阶段性进展,优先更新 `README.md`、`TODO.md`、`context-reset-brief.md`
+42 -8
View File
@@ -7,9 +7,10 @@
- 只负责内容提取
- 不负责摘要、分类、价值判断
- 已完成 Python MCP 骨架
- 已实现两个 tool:
- 已实现三个 tool:
- `extract_url_content`
- `extract_item_content`
- `filter_summary_result`
- 已完成真实 URL 提取验证
- 已完成 LLM 摘要 prompt
- 已完成 LLM 摘要结果 schema 校验器
@@ -17,6 +18,12 @@
- 已完成 LLM 摘要校验 skill 封装
- 已清理旧的启发式 `summarizer.py`
- 已将旧的 MCP 设计文档更新为当前“Content Extract MCP”语义
- 已完成 FreshRSS `greader` API 接入
- 已完成 FreshRSS entry -> `item` 映射
- 已产出真实 `item` 样例文件
- 已跑通 `FreshRSS -> item -> content extraction` 单条链路
- 已定义过滤层输入输出 schema
- 已完成第一版规则引擎、本地脚本和 MCP tool
## 当前关键文件
@@ -28,12 +35,34 @@
- `src/summary_mcp/models/llm_result.py`
- LLM 校验器:
- `src/summary_mcp/validators/llm_result.py`
- 过滤模型:
- `src/summary_mcp/models/filtering.py`
- 过滤引擎:
- `src/summary_mcp/filters/engine.py`
- 默认过滤规则:
- `configs/filter_rules.json`
- 校验 CLI:
- `src/summary_mcp/validate_llm_result.py`
- 最小闭环脚本:
- `scripts/run_summary_loop.py`
- FreshRSS 拉取脚本:
- `scripts/pull_freshrss_items.py`
- FreshRSS 提取脚本:
- `scripts/run_freshrss_extract.py`
- 过滤脚本:
- `scripts/run_filter_rules.py`
- 当前提示词:
- `outputs/llm-summary-prompt.txt`
- FreshRSS 原始响应样例:
- `outputs/freshrss.raw.json`
- FreshRSS item 样例:
- `outputs/freshrss.items.json`
- FreshRSS 提取结果:
- `outputs/freshrss.extracted.json`
- 过滤结果样例:
- `outputs/filter-decision.json`
- 文档索引:
- `docs/README.md`
- MVP 归档:
- `docs/content-extract-mcp-mvp-archive.md`
- 当前 TODO:
@@ -45,21 +74,26 @@
- LLM 可根据提取结果生成摘要 JSON
- validator 可校验摘要 JSON
- 最小闭环脚本可直接调用 LLM 接口并产出通过校验的结果
- FreshRSS API 可拉取真实 entry
- 真实 entry 可映射为标准化 `item`
- 标准化 `item` 可继续进入内容提取流程
- 规则引擎可对结构化摘要结果输出 `keep / drop / review` 决策
- MCP tool 可承接“由上层 LLM/Agent 调用过滤”的模式
## 当前未开始的下一阶段
- 让 FreshRSS 作为主聚合池
- 设计 FreshRSS entry -> `item` 的映射
- 准备真实 `item` 样例
- 开始定义规则过滤层 schema
- 设计知识库入库格式
- 设计 webhook / 推送格式
- 把 FreshRSS 拉取与提取流程进一步批量化/调度化
- 迭代更细的过滤规则与个性化上下文
## 收束后建议从这里继续
优先从这两个问题继续:
1. FreshRSS 的 entry 字段如何映射成 `item`
2. 先做一个真实 `item` 样例文件,再讨论规则过滤
1. 先设计知识库 sink 的输入输出格式
2. 再决定先接 webhook / 推送还是继续细化过滤规则
## 一句话结论
当前 MVP 已完成,下一阶段不再是继续打磨 MCP,而是开始接入 FreshRSS 上游并建立 `item` 标准化入口。
当前 MVP 已完成,FreshRSS 上游和第一版规则过滤层都已接通,下一阶段应转向下游 sink 与推送设计。
+225
View File
@@ -0,0 +1,225 @@
# 规则过滤引擎设计
## 1. 目标
当前过滤层的定位是:
- 不让 LLM 直接做最终过滤决策
- 让 LLM 只产出结构化信号
- 由规则引擎输出最终 `keep / drop / review` 决策
当前链路是:
`item -> content extraction -> llm summary -> filter rule engine`
## 2. 输入输出
### 2.1 FilterInput
过滤层统一读取四类输入:
- `item`
- `article`
- `summary`
- `context`
其中:
- `item` 表示上游标准化候选条目
- `article` 表示正文提取结果
- `summary` 表示结构化 LLM 摘要结果
- `context` 表示额外的用户偏好或运行时上下文
### 2.2 FilterDecisionResult
过滤结果统一输出:
- `decision`
- `keep`
- `drop`
- `review`
- `matched_rules`
- `reasons`
- `labels`
- `priority`
- `matches`
这样后续的知识库 sink、推送层或人工审核都可以稳定消费。
## 3. 规则结构
当前规则文件位置:
- `configs/filter_rules.json`
单条规则结构为:
```json
{
"rule_id": "keep-worth-keeping-method",
"enabled": true,
"stop_on_match": false,
"conditions_all": [
{ "field": "summary.worth_keeping", "op": "eq", "value": true },
{ "field": "summary.category", "op": "in", "value": ["方法论", "工具实践"] }
],
"action": {
"decision": "keep",
"reason": "Structured summary marked the content as worth keeping in a durable category.",
"labels": ["summary", "durable"],
"priority": 80
}
}
```
## 4. 当前支持的操作符
- `eq`
- `ne`
- `in`
- `not_in`
- `contains`
- `overlap`
- `gte`
- `lte`
- `exists`
当前条件组合方式:
- `conditions_all`
- `conditions_any`
## 5. 动态上下文字段
规则支持从其他字段动态取值,例如:
```json
{ "field": "summary.topics", "op": "overlap", "value": { "from_field": "context.interest_topics" } }
```
这允许上层 Agent 在调用 `filter_summary_result` 时,把当前关注主题动态注入,而不必把偏好硬编码在规则文件里。
## 6. 当前默认规则思路
当前默认规则分为三类:
- 质量拦截
- 低质量正文直接 `drop`
- 疑似截断或付费墙进入 `review`
- 摘要价值判断
- `资讯 + worth_keeping=false` 直接 `drop`
- `方法论/工具实践 + worth_keeping=true` 直接 `keep`
- 个性化补充信号
- `summary.topics` 与 `context.interest_topics` 重叠时提升为 `keep`
- 其他 `worth_keeping=true` 的结果默认进入 `review`
## 7. 规则执行流程
当前过滤流程按以下顺序执行:
1. 上层先产出结构化 `summary`
2. 可选附带 `item`、`article` 与 `context`
3. 规则引擎按 `priority` 从高到低遍历规则
4. 每条规则根据 `conditions_all` / `conditions_any` 判断是否命中
5. 命中的规则被收集为 `matches`
6. 最终根据命中结果收敛为 `keep / drop / review`
当前决策收敛规则是:
- 只要命中任意 `drop`,最终结果就是 `drop`
- 否则只要命中任意 `keep`,最终结果就是 `keep`
- 否则如果命中任意 `review`,最终结果就是 `review`
- 如果没有任何规则命中,默认回落到 `review`
这样设计的原因是:
- `drop` 应该拥有最高约束力
- `keep` 只在没有硬性淘汰时生效
- 默认不自动放行未知内容
## 8. 为什么让 LLM 调用规则 tool,而不是直接裁决
当前架构故意拆成两层:
- LLM 负责生成结构化信号
- 规则引擎负责输出最终过滤决策
原因是:
- LLM 适合做语义理解、归类、摘要和价值信号提取
- 规则引擎适合做稳定、可复现、可审计的最终判断
因此推荐的调用方式是:
1. LLM 先调用提取 tool
2. LLM 或本地脚本产出 `summary_result`
3. LLM 再调用 `filter_summary_result`
4. 后续根据过滤结果决定是否入库、推送或人工审核
这意味着:
- LLM 是编排者
- rule engine 是裁决器
而不是让 LLM 在过滤阶段再次自由发挥。
## 9. 调用示例
### 9.1 本地脚本
```bash
python scripts/run_filter_rules.py ^
--summary outputs/result.json ^
--extracted outputs/read-flow-2026.extracted.json ^
--output outputs/filter-decision.json
```
### 9.2 带上下文的调用
```json
{
"interest_topics": ["个人信息管理", "阅读工作流"]
}
```
将这份上下文作为 `--context` 传入后,规则就可以根据当前关注主题做加权。
### 9.3 MCP tool
`filter_summary_result` 接收:
- `summary_result`
- 可选 `extracted_article`
- 可选 `item`
- 可选 `context`
返回:
- `decision`
- `matched_rules`
- `reasons`
- `labels`
- `priority`
- `matches`
## 10. 当前落地位置
- 过滤模型:
- `src/summary_mcp/models/filtering.py`
- 规则引擎:
- `src/summary_mcp/filters/engine.py`
- 默认规则:
- `configs/filter_rules.json`
- 本地运行脚本:
- `scripts/run_filter_rules.py`
- MCP tool:
- `filter_summary_result`
## 11. 当前阶段的设计结论
第一版过滤层采用“规则主导,LLM 提供信号”的架构:
- LLM 不直接做最终裁决
- 上层 Agent 可以决定是否调用过滤 tool
- 真正的过滤决策由规则引擎输出
- 灰区内容后续再考虑是否引入第二层 LLM 辅助判断