Refine OpenClaw payloads and reorganize docs
This commit is contained in:
@@ -0,0 +1,544 @@
|
||||
# Source Schema 设计草案
|
||||
|
||||
## 1. 文档目的
|
||||
|
||||
本文档用于定义阅读流系统中的来源与内容对象模型,目标是把“来源分类”的讨论收敛成一套可执行的数据结构,供后续的抓取、摘要、过滤、入库和推送流程统一使用。
|
||||
|
||||
这份文档关注的是数据抽象,而不是具体实现语言、数据库模型或 API 细节。
|
||||
|
||||
---
|
||||
|
||||
## 2. 设计目标
|
||||
|
||||
这套 schema 需要解决以下问题:
|
||||
|
||||
- 不同渠道的内容如何进入同一条流水线
|
||||
- RSS、微信文章、普通网页、GitHub 更新如何被统一表示
|
||||
- 摘要器应该接收什么输入对象
|
||||
- 过滤规则应该依赖哪些标准字段
|
||||
- 入库与推送如何复用统一元信息
|
||||
|
||||
核心目标是建立稳定的中间层,避免后续各模块直接依赖渠道差异。
|
||||
|
||||
---
|
||||
|
||||
## 3. 设计原则
|
||||
|
||||
### 3.1 分层而不是混合
|
||||
|
||||
建议将系统中的对象分成三层:
|
||||
|
||||
- `source`
|
||||
- `item`
|
||||
- `document`
|
||||
|
||||
每一层职责不同:
|
||||
|
||||
- `source`:描述一个来源渠道本身
|
||||
- `item`:描述一次抓取到的候选内容
|
||||
- `document`:描述经过正文抽取和摘要后的标准内容对象
|
||||
|
||||
### 3.2 先标准化,再智能化
|
||||
|
||||
在进入摘要、过滤、入库前,应该先把不同渠道的内容统一映射成相同的对象结构,而不是在每个处理阶段临时兼容各种来源格式。
|
||||
|
||||
### 3.3 渠道类型与内容形态分离
|
||||
|
||||
建议明确区分:
|
||||
|
||||
- 来源渠道是什么
|
||||
- 内容本身是什么形态
|
||||
|
||||
例如:
|
||||
|
||||
- 微信文章和 RSS 博文可能都属于 `article`
|
||||
- GitHub Release 和 changelog 可能来自不同渠道,但内容形态更接近事件流
|
||||
|
||||
---
|
||||
|
||||
## 4. 对象分层
|
||||
|
||||
### 4.1 `source`
|
||||
|
||||
`source` 是来源配置对象,用来描述某个渠道本身是什么、如何接入、是否启用。
|
||||
|
||||
特点:
|
||||
|
||||
- 偏静态
|
||||
- 由用户或系统配置维护
|
||||
- 不代表具体某一篇内容
|
||||
|
||||
### 4.2 `item`
|
||||
|
||||
`item` 是采集层产物,用来表示“这次抓回来的一个候选条目”。
|
||||
|
||||
特点:
|
||||
|
||||
- 来自 RSS、API、HTML 抓取或转换 feed
|
||||
- 可能只有标题、链接和摘要
|
||||
- 不保证已经拿到完整正文
|
||||
|
||||
### 4.3 `document`
|
||||
|
||||
`document` 是标准内容对象,用来表示“已经过正文抽取和摘要处理,可以进入过滤、入库和推送阶段”。
|
||||
|
||||
特点:
|
||||
|
||||
- 已经完成正文抽取
|
||||
- 已经有结构化摘要
|
||||
- 是后续规则引擎的主输入
|
||||
|
||||
---
|
||||
|
||||
## 5. 枚举设计
|
||||
|
||||
### 5.1 `source_type`
|
||||
|
||||
表示来源类型。
|
||||
|
||||
可选值:
|
||||
|
||||
- `feed`
|
||||
- `converted_feed`
|
||||
- `event_feed`
|
||||
- `page_watch`
|
||||
- `custom_source`
|
||||
|
||||
说明:
|
||||
|
||||
- `feed`:原生 RSS / Atom
|
||||
- `converted_feed`:由 RSSHub、wewe-rss、RSS-Bridge 等转换而来
|
||||
- `event_feed`:更像事件流,例如 GitHub Releases、Commits、Discussions
|
||||
- `page_watch`:轮询普通网页的更新情况
|
||||
- `custom_source`:为特殊站点定制的抓取源
|
||||
|
||||
### 5.2 `ingest_mode`
|
||||
|
||||
表示接入方式。
|
||||
|
||||
可选值:
|
||||
|
||||
- `feed_direct`
|
||||
- `feed_converted`
|
||||
- `api_backed`
|
||||
- `html_scraped`
|
||||
- `hybrid`
|
||||
|
||||
说明:
|
||||
|
||||
- `feed_direct`:直接消费 RSS / Atom
|
||||
- `feed_converted`:先转换成 feed,再统一消费
|
||||
- `api_backed`:通过 API 获取数据
|
||||
- `html_scraped`:直接抓 HTML 页面并解析
|
||||
- `hybrid`:先消费 feed 元数据,再按需抓正文
|
||||
|
||||
### 5.3 `content_kind`
|
||||
|
||||
表示内容形态。
|
||||
|
||||
可选值:
|
||||
|
||||
- `article`
|
||||
- `thread`
|
||||
- `release`
|
||||
- `changelog`
|
||||
- `video`
|
||||
- `mixed`
|
||||
|
||||
说明:
|
||||
|
||||
- `article`:标准文章
|
||||
- `thread`:串联式内容,例如帖子串、社交媒体长线程
|
||||
- `release`:版本发布说明
|
||||
- `changelog`:更新日志
|
||||
- `video`:视频内容
|
||||
- `mixed`:无法明确归入单一形态
|
||||
|
||||
### 5.4 `fetch_state`
|
||||
|
||||
表示候选内容的正文抓取状态。
|
||||
|
||||
可选值:
|
||||
|
||||
- `pending`
|
||||
- `fetched`
|
||||
- `failed`
|
||||
- `skipped`
|
||||
|
||||
### 5.5 `pipeline_state`
|
||||
|
||||
表示内容在整条流水线中的处理状态。
|
||||
|
||||
可选值:
|
||||
|
||||
- `ingested`
|
||||
- `summarized`
|
||||
- `filtered`
|
||||
- `stored`
|
||||
- `pushed`
|
||||
- `dropped`
|
||||
|
||||
---
|
||||
|
||||
## 6. `source` 对象设计
|
||||
|
||||
### 6.1 角色定义
|
||||
|
||||
`source` 用于描述一个来源本身,而不是某条具体内容。
|
||||
|
||||
它应该回答这些问题:
|
||||
|
||||
- 这个来源是什么
|
||||
- 它属于哪一类来源
|
||||
- 用什么方式接入
|
||||
- 是否启用
|
||||
- 默认语言和默认内容形态是什么
|
||||
|
||||
### 6.2 建议字段
|
||||
|
||||
```json
|
||||
{
|
||||
"source_id": "wechat-aiweekly",
|
||||
"name": "AI Weekly 微信源",
|
||||
"source_type": "converted_feed",
|
||||
"ingest_mode": "feed_converted",
|
||||
"content_kind": "article",
|
||||
"base_url": "https://example.com",
|
||||
"feed_url": "https://example.com/feed.xml",
|
||||
"language": "zh",
|
||||
"enabled": true,
|
||||
"tags": ["ai", "newsletter"],
|
||||
"priority": 80
|
||||
}
|
||||
```
|
||||
|
||||
### 6.3 字段说明
|
||||
|
||||
- `source_id`
|
||||
- 系统内部唯一标识
|
||||
|
||||
- `name`
|
||||
- 人类可读名称
|
||||
|
||||
- `source_type`
|
||||
- 来源类型
|
||||
|
||||
- `ingest_mode`
|
||||
- 接入方式
|
||||
|
||||
- `content_kind`
|
||||
- 默认内容形态
|
||||
|
||||
- `base_url`
|
||||
- 来源站点主域名
|
||||
|
||||
- `feed_url`
|
||||
- feed 地址;没有则可为空
|
||||
|
||||
- `language`
|
||||
- 默认语言
|
||||
|
||||
- `enabled`
|
||||
- 是否启用该来源
|
||||
|
||||
- `tags`
|
||||
- 来源级标签,供过滤和统计使用
|
||||
|
||||
- `priority`
|
||||
- 用于抓取调度、结果排序或推送优先级控制
|
||||
|
||||
### 6.4 最小可用版本
|
||||
|
||||
```json
|
||||
{
|
||||
"source_id": "my-blog",
|
||||
"source_type": "feed",
|
||||
"ingest_mode": "feed_direct",
|
||||
"feed_url": "https://example.com/rss.xml",
|
||||
"enabled": true
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. `item` 对象设计
|
||||
|
||||
### 7.1 角色定义
|
||||
|
||||
`item` 是采集层的标准对象,用来统一表示从不同渠道抓回来的候选内容。
|
||||
|
||||
它应该回答这些问题:
|
||||
|
||||
- 这条候选内容来自哪个 source
|
||||
- 它的标题、链接和发布时间是什么
|
||||
- RSS 是否已经提供摘要或正文
|
||||
- 当前是否已抓到完整正文
|
||||
|
||||
### 7.2 建议字段
|
||||
|
||||
```json
|
||||
{
|
||||
"item_id": "sha256:xxx",
|
||||
"source_id": "wechat-aiweekly",
|
||||
"external_id": "feed-entry-id-or-url",
|
||||
"title": "文章标题",
|
||||
"url": "https://example.com/post/123",
|
||||
"author": "作者",
|
||||
"published_at": "2026-03-23T08:00:00Z",
|
||||
"discovered_at": "2026-03-23T10:00:00Z",
|
||||
"content_kind": "article",
|
||||
"language": "zh",
|
||||
"raw_summary": "RSS 提供的摘要",
|
||||
"raw_content": "RSS 提供的正文或片段",
|
||||
"metadata": {
|
||||
"feed_title": "源标题",
|
||||
"categories": ["AI", "Tools"]
|
||||
},
|
||||
"fetch_state": "pending"
|
||||
}
|
||||
```
|
||||
|
||||
### 7.3 字段说明
|
||||
|
||||
- `item_id`
|
||||
- 内部唯一标识
|
||||
- 建议由 `source_id + canonical_url + published_at` 做 hash 生成
|
||||
|
||||
- `source_id`
|
||||
- 指向来源配置对象
|
||||
|
||||
- `external_id`
|
||||
- 来源平台本身的 id,例如 RSS entry id、原始 URL 或平台内容 id
|
||||
|
||||
- `title`
|
||||
- 候选内容标题
|
||||
|
||||
- `url`
|
||||
- 原始内容链接
|
||||
|
||||
- `author`
|
||||
- 作者信息;如果无法获取可为空
|
||||
|
||||
- `published_at`
|
||||
- 原始发布时间
|
||||
|
||||
- `discovered_at`
|
||||
- 被系统发现的时间
|
||||
|
||||
- `content_kind`
|
||||
- 当前条目的内容形态
|
||||
|
||||
- `language`
|
||||
- 当前条目的语言
|
||||
|
||||
- `raw_summary`
|
||||
- 来源提供的摘要内容
|
||||
|
||||
- `raw_content`
|
||||
- 来源直接提供的正文、正文片段或富文本转纯文本结果
|
||||
|
||||
- `metadata`
|
||||
- 保留来源特有元信息,例如 feed 分类、栏目名、标签
|
||||
|
||||
- `fetch_state`
|
||||
- 正文抓取状态
|
||||
|
||||
### 7.4 最小可用版本
|
||||
|
||||
```json
|
||||
{
|
||||
"item_id": "sha256:xxx",
|
||||
"source_id": "my-blog",
|
||||
"title": "文章标题",
|
||||
"url": "https://example.com/post/1",
|
||||
"published_at": "2026-03-23T08:00:00Z",
|
||||
"raw_summary": "摘要",
|
||||
"raw_content": "",
|
||||
"fetch_state": "pending"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. `document` 对象设计
|
||||
|
||||
### 8.1 角色定义
|
||||
|
||||
`document` 是经过正文抽取和摘要处理后的标准内容对象。
|
||||
|
||||
它应该回答这些问题:
|
||||
|
||||
- 最终正文是什么
|
||||
- 摘要和高亮点是什么
|
||||
- 这个内容值不值得进入下一步过滤或入库
|
||||
- 当前它处于流水线哪个阶段
|
||||
|
||||
### 8.2 建议字段
|
||||
|
||||
```json
|
||||
{
|
||||
"document_id": "sha256:yyy",
|
||||
"item_id": "sha256:xxx",
|
||||
"source_id": "wechat-aiweekly",
|
||||
"url": "https://example.com/post/123",
|
||||
"title": "文章标题",
|
||||
"author": "作者",
|
||||
"published_at": "2026-03-23T08:00:00Z",
|
||||
"language": "zh",
|
||||
"content_kind": "article",
|
||||
"plain_text": "抽取后的正文",
|
||||
"summary": "3-5句摘要",
|
||||
"highlights": ["要点1", "要点2", "要点3"],
|
||||
"keywords": ["rss", "mcp"],
|
||||
"topics": ["workflow", "knowledge-base"],
|
||||
"scores": {
|
||||
"relevance": 0.91,
|
||||
"novelty": 0.72,
|
||||
"readability": 0.84
|
||||
},
|
||||
"quality_flags": {
|
||||
"is_paywalled": false,
|
||||
"is_truncated": false,
|
||||
"is_low_content": false
|
||||
},
|
||||
"pipeline_state": "summarized"
|
||||
}
|
||||
```
|
||||
|
||||
### 8.3 字段说明
|
||||
|
||||
- `document_id`
|
||||
- `document` 的唯一标识
|
||||
|
||||
- `item_id`
|
||||
- 回溯到原始候选条目
|
||||
|
||||
- `source_id`
|
||||
- 回溯到来源配置
|
||||
|
||||
- `url`
|
||||
- 原始链接
|
||||
|
||||
- `title`
|
||||
- 标题
|
||||
|
||||
- `author`
|
||||
- 作者信息
|
||||
|
||||
- `published_at`
|
||||
- 发布时间
|
||||
|
||||
- `language`
|
||||
- 语言
|
||||
|
||||
- `content_kind`
|
||||
- 内容形态
|
||||
|
||||
- `plain_text`
|
||||
- 抽取后的正文纯文本
|
||||
|
||||
- `summary`
|
||||
- 结构化摘要中的主摘要
|
||||
|
||||
- `highlights`
|
||||
- 关键要点
|
||||
|
||||
- `keywords`
|
||||
- 关键词
|
||||
|
||||
- `topics`
|
||||
- 主题标签
|
||||
|
||||
- `scores`
|
||||
- 打分结果,例如相关性、新颖度、可读性
|
||||
|
||||
- `quality_flags`
|
||||
- 内容质量标志,例如是否付费墙、是否截断、是否低信息密度
|
||||
|
||||
- `pipeline_state`
|
||||
- 当前流水线状态
|
||||
|
||||
### 8.4 最小可用版本
|
||||
|
||||
```json
|
||||
{
|
||||
"document_id": "sha256:yyy",
|
||||
"item_id": "sha256:xxx",
|
||||
"title": "文章标题",
|
||||
"url": "https://example.com/post/1",
|
||||
"plain_text": "正文",
|
||||
"summary": "3-5句摘要",
|
||||
"highlights": ["点1", "点2"],
|
||||
"scores": {
|
||||
"relevance": 0.9
|
||||
},
|
||||
"pipeline_state": "summarized"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 三层对象之间的关系
|
||||
|
||||
建议关系如下:
|
||||
|
||||
- 一个 `source` 可以产生多个 `item`
|
||||
- 一个 `item` 在多数情况下对应一个 `document`
|
||||
- `document` 是 `item` 经过正文抽取和摘要处理后的结果
|
||||
|
||||
简化理解:
|
||||
|
||||
- `source` 解决“从哪里来”
|
||||
- `item` 解决“抓到了什么”
|
||||
- `document` 解决“它值不值得留下”
|
||||
|
||||
---
|
||||
|
||||
## 10. 为什么必须分层
|
||||
|
||||
如果不区分 `source`、`item` 和 `document`,后面会出现这些问题:
|
||||
|
||||
- 来源配置和运行时数据混在一起
|
||||
- RSS 字段、网页抓取字段和摘要字段混在一起
|
||||
- 过滤规则既要判断来源,又要判断内容质量,还要判断摘要分数
|
||||
- 不同来源的差异会不断渗透到下游模块
|
||||
|
||||
分层后的收益是:
|
||||
|
||||
- 采集层可以独立演进
|
||||
- 摘要器输入更稳定
|
||||
- 过滤器只依赖统一对象,不依赖来源细节
|
||||
- 入库和推送更容易复用
|
||||
|
||||
---
|
||||
|
||||
## 11. 当前阶段的最小实现建议
|
||||
|
||||
为了降低复杂度,第一阶段建议:
|
||||
|
||||
- 只支持 `feed` 和 `converted_feed`
|
||||
- 只实现 `source`、`item`、`document` 这三层基础对象
|
||||
- `item` 只要求能容纳 RSS 拉回来的标题、链接、摘要、正文片段
|
||||
- `document` 只要求能容纳正文、摘要、高亮点和基础打分
|
||||
|
||||
这样做的好处是:
|
||||
|
||||
- 结构足够稳定
|
||||
- 成本足够低
|
||||
- 可以直接支撑下一步 `summary-core` 设计
|
||||
|
||||
---
|
||||
|
||||
## 12. 后续可继续讨论的问题
|
||||
|
||||
1. 是否需要给 `source` 增加抓取周期、超时、重试策略字段
|
||||
2. `item_id` 和 `document_id` 的生成策略是否要统一
|
||||
3. `metadata` 是否需要按来源类型拆出专用字段
|
||||
4. `document` 的 `scores` 和 `quality_flags` 是否要定义更严格的 schema
|
||||
5. 这套 schema 最终是落成 JSON 文件、数据库表,还是 Pydantic 模型
|
||||
|
||||
---
|
||||
|
||||
## 13. 当前阶段的一句话结论
|
||||
|
||||
当前最合适的做法是先固定 `source -> item -> document` 这三层对象模型,再围绕它设计 `summary-core`、过滤器和入库流程。
|
||||
Reference in New Issue
Block a user