Files
reader/docs/notes/reading-pipeline-design-notes.md

477 lines
11 KiB
Markdown
Raw Permalink 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.
# 阅读流方案讨论纪要
## 参考来源
- Shawn Xie, 《Read Flow 2026》: <https://shawnxie.top/blogs/tools/read-flow-2026.html>
## 1. 当前目标
本项目的目标不是直接复刻文章里的整套实现,而是先按下面这条链路,逐步做出一套接近文章效果的阅读流系统:
1. 通过一层 RSS 聚合获取文章
2. 通过一个 `skill` 或 `mcp` 获取文章摘要
3. 基于规则对摘要结果进行过滤
4. 将过滤后的内容推送到知识库或其他文档组件
5. 完成消息推送
6. 最后再考虑与 OpenClaw 结合
这里的 `skill / mcp` 只是整条链路中的一个组件,当前讨论的重点是第 2 步“页面摘要能力”的设计,而不是一次性把整套系统全部落地。
---
## 2. 内容来源分类
为了便于后续实现,内容来源建议按两个维度分类:
- 按来源类型分类:这个内容来自什么渠道
- 按接入方式分类:系统用什么技术手段把它纳入统一流水线
这样做的原因是:
- 同一种来源类型,可能有不同接入方式
- 同一种接入方式,可能服务多种来源
- 后面的摘要、过滤、入库更适合依赖标准化后的 source schema,而不是依赖渠道名称
### 2.1 按来源类型分类
建议定义以下几类:
- `feed`
- 原生 RSS / Atom 源
- 典型例子:博客、新闻站、技术周刊、文档更新
- `converted_feed`
- 原本不是 RSS,但通过第三方或自建转换后变成 feed
- 典型例子:微信公众号、部分社区栏目、社交平台账号流
- `event_feed`
- 更偏事件流而不是传统文章流
- 典型例子:GitHub Releases、Commits、Discussions、Changelog
- `page_watch`
- 没有现成 feed,需要定时轮询某个页面的更新
- 典型例子:专题页、导航页、榜单页、产品更新页
- `custom_source`
- 无法直接归类,需要为特定站点写专门抓取逻辑
- 典型例子:结构特殊的网站、内部页面、非标准内容源
### 2.2 按接入方式分类
建议定义以下几类:
- `feed_direct`
- 直接消费 RSS / Atom
- `feed_converted`
- 通过 RSSHub、wewe-rss、RSS-Bridge 等方式先转成 feed,再统一消费
- `api_backed`
- 通过平台 API 获取内容,再标准化成内部 item
- `html_scraped`
- 直接抓取 HTML 页面,自己解析列表页和正文页
- `hybrid`
- 先消费 feed 元信息,再按需回源抓正文
### 2.3 你当前关心的几类内容如何归档
- RSS 文章
- 来源类型:`feed`
- 接入方式:`feed_direct`
- 优先级:最高
- 微信文章
- 来源类型:`converted_feed`
- 接入方式:通常是 `feed_converted`
- 优先级:高
- 其他文章
- 如果是社区栏目、论坛专题:通常归到 `converted_feed` 或 `page_watch`
- 如果是普通网站更新页:通常归到 `page_watch`
- 如果是 GitHub 项目更新:通常归到 `event_feed`
- 如果是特殊站点:归到 `custom_source`
结论:
“其他文章”不应作为最终系统里的正式分类,因为它过于宽泛,后续规则过滤会很难维护。
### 2.4 建议的实现级分类
如果进入实现阶段,建议系统内部只保留下面这 5 类 source type:
- `feed`
- `converted_feed`
- `event_feed`
- `page_watch`
- `custom_source`
同时再给每个 source item 增加一个 `content_kind` 字段,用来表达内容形态:
- `article`
- `thread`
- `release`
- `changelog`
- `video`
- `mixed`
这样可以把“来源渠道”和“内容形态”拆开,后面的摘要与过滤规则会更稳定。
### 2.5 当前阶段建议先支持的来源
为了降低复杂度,第一阶段建议只支持两类:
- `feed`
- `converted_feed`
原因:
- 它们最接近当前目标链路
- 接入成本最低
- 最容易验证第 2 步摘要和第 3 步过滤是否成立
- 暂时不需要引入复杂的页面轮询和定制抓取逻辑
---
## 3. 系统边界的核心结论
当前最重要的不是先决定做 `Skill` 还是 `MCP`,而是先定义一个可复用的“摘要内核”。
建议的能力分层:
`RSS 聚合 -> summary-core -> rule-engine -> sink -> push -> OpenClaw 编排`
其中:
- `summary-core`:负责抓取页面、抽取正文、生成结构化摘要
- `rule-engine`:负责根据规则和评分过滤内容
- `sink`:负责将结果写入知识库或文档系统
- `push`:负责消息通知
- `OpenClaw`:最后作为编排层或自动化入口接入
结论:先做“能力内核”,再决定用 `MCP` 还是 `Skill` 进行封装。
---
## 4. Skill 和 MCP 的定位
### Skill
适合:
- 人工触发
- 代理式工作流
- 提示词编排
- 让 Agent 在上下文里决定何时调用摘要流程
优点:
- 实现快
- 适合探索和半自动流程
缺点:
- 不适合做稳定的批处理基础设施
- 不适合承载缓存、重试、队列、状态管理
结论:
`Skill` 更像“编排层”,不适合作为底层基础能力的唯一实现。
### MCP
适合:
- 将摘要能力标准化成可调用工具
- 给 OpenClaw、ChatGPT、Claude 等 Agent 统一接入
- 为后续自动化编排预留稳定接口
优点:
- 接口标准化
- 易于被 Agent 调用
- 后续接 OpenClaw 更自然
缺点:
- 仍然需要自己实现抓取、抽取、摘要、缓存等能力
结论:
`MCP` 适合作为“能力接口层”。
### 当前建议
推荐顺序:
1. 先做 `summary-core`
2. 再暴露成 `MCP`
3. 最后根据需要加 `Skill`
不建议一开始只做 `Skill`。
---
## 5. 页面摘要能力应输出什么
页面摘要不要只输出一段自然语言摘要,而应该输出结构化结果,方便后面的过滤、入库和推送。
建议输出结构:
```json
{
"url": "https://example.com/article",
"title": "文章标题",
"source": "站点名",
"author": "作者",
"published_at": "2026-03-23T08:00:00Z",
"language": "zh",
"summary": "3-5句摘要",
"highlights": ["要点1", "要点2", "要点3"],
"keywords": ["rss", "mcp", "knowledge-base"],
"topics": ["AI tools", "workflow"],
"quality_flags": {
"is_paywalled": false,
"is_truncated": false,
"is_low_content": false
},
"scores": {
"readability": 0.84,
"novelty": 0.72,
"relevance": 0.91
},
"content_hash": "xxx"
}
```
这样做的价值:
- 规则过滤直接使用结构化字段
- 知识库入库更稳定
- 推送内容可按场景裁剪
- 后续可以追踪质量、去重和打分
---
## 6. 第 2 步“页面摘要能力”的技术拆分
页面摘要不是单一步骤,而是 3 个子能力:
1. 获取页面内容
2. 抽取正文
3. 生成摘要
### 5.1 获取页面内容
有两条常见路径:
- 直接使用 RSS item 里的 `content:encoded` 或 `summary`
- 根据 RSS item 的 `link` 回源抓取页面
建议策略:
- 先使用 RSS 已提供的内容
- 如果内容过短、质量不够,再回源抓页面正文
这样能平衡速度和完整度。
### 5.2 正文抽取
这是稳定性的关键环节。
候选技术:
- `trafilatura`
- 对博客、新闻、文档类页面较稳
- Python 生态成熟
- `readability-lxml`
- 经典轻量方案
- 覆盖面广
- `Playwright + Readability`
- 适合 JS 动态页面
- 成本最高,但兜底能力最强
建议组合:
- 第一层:`httpx/requests + trafilatura`
- 第二层:`Playwright` 作为兜底方案
不建议一开始就默认使用 Playwright。
### 5.3 摘要生成
有两类方向:
- 抽取式摘要
- 成本低
- 稳定
- 可读性通常一般
- 生成式摘要
- 更接近最终想要的“知识流”效果
- 可读性更好
- 需要更严格的结构化约束
建议:
- 采用生成式摘要
- 强制模型输出 JSON Schema
- 不允许自由散文式输出
这样后续流程会稳定很多。
---
## 7. 过滤层设计
过滤层不要完全依赖 LLM 的自由判断,建议采用“两层过滤”。
### 第一层:确定性规则
例如:
- 来源白名单 / 黑名单
- 关键词规则
- 主题规则
- 语言过滤
- 最低字数要求
- 重复检测
- 发布时间窗口
- relevance 阈值
### 第二层:可选的 LLM 判断
例如:
- 是否值得进入知识库
- 更偏资讯还是方法论
- 是否符合个人关注主题
- 是否值得推送
结论:
先规则,后智能,不要反过来。
---
## 8. 知识库与推送层设计
第 4、5 步可以统一抽象为 `sink` 和 `push`。
### 第一阶段建议优先做的 sink
- `Markdown sink`
- 输出到本地目录或 Git 仓库
- 适合第一版
- 可审计、易迁移
- `Webhook sink`
- 推送到 Telegram、企业微信、飞书、OpenClaw 或其他自动化入口
- 扩展性最好
### 后续可扩展的 sink
- Notion
- Obsidian Vault
- Logseq
- 数据库
结论:
第一版不建议直接绑死在单一平台上,优先本地 Markdown + Webhook。
---
## 9. 现在真正该先定义的接口
摘要内核建议先定义统一接口,而不是先定义 UI 或 Agent 提示词。
建议输入:
- `url`
- 可选 `raw_html`
- 可选 `rss_content`
- 可选 `user_profile`
- 可选 `summary_style`
建议输出:
- 结构化摘要 JSON
- 原始正文文本
- 元信息
- 错误状态
只要这个接口稳定:
- CLI 可以调用
- HTTP 服务可以调用
- MCP 可以调用
- OpenClaw Skill 也可以调用
这一步是整个系统里最关键的抽象。
---
## 10. 推荐的演进路线
建议按下面顺序推进:
1. `RSS 聚合`
- 先通过 FreshRSS 或现有聚合器获取文章链接
2. `summary-core`
- 输入 URL,输出结构化摘要
3. `rule-engine`
- 对摘要 JSON 执行过滤规则
4. `sink`
- 先落 Markdown,再做 webhook 推送
5. `MCP server`
- 暴露 `summarize_url`、`filter_summary`、`store_note` 等工具
6. `OpenClaw Skill`
- 让 OpenClaw 负责编排、人工确认或附加动作
7. `OpenClaw 集成`
- 最后再接入完整自动化流程
这样每一层都可以独立测试,不会过早被某个平台或代理框架绑定。
---
## 11. 当前阶段的明确判断
### 不建议的方向
- 一开始只做 Skill
- 一开始把 OpenClaw 当成底层依赖
- 一开始就直接绑死 Notion 一类单一知识库
- 让过滤完全依赖 LLM 自由发挥
### 建议优先做的方向
- 先定义 `summary-core` 的输入输出
- 摘要结果必须结构化
- 过滤规则优先基于确定性逻辑
- 入库优先选择 Markdown
- 对外优先暴露成 MCP,而不是先做纯 Skill
---
## 12. 后续讨论建议
后面可以基于这份文档继续讨论以下问题:
1. `summary-core` 的最小可用接口如何定义
2. `MCP` 版和 `Skill` 版分别暴露哪些能力
3. 正文抽取链路是否需要分层回退
4. 规则引擎使用配置文件还是代码实现
5. Markdown sink 的目录结构怎么设计
6. Webhook / 推送层先接哪一个目标
7. OpenClaw 在整条链路里承担“调度器”还是“人工审核入口”
---
## 13. 当前阶段的一句话结论
当前阶段最合理的方向是:先把“页面摘要”做成一个独立、结构化、可复用的能力内核,再优先封装为 `MCP`,最后再用 `Skill` 和 OpenClaw 做编排与集成。