# Source Schema 设计草案 ## 状态说明 本文件偏对象建模和来源抽象,主要用于解释早期 schema 设计思路。 它不是当前生产运行手册,也不是当前 workflow service 的唯一真相来源。 如果你关注当前 OpenClaw 集成或运行状态,应优先看: - `docs/current/context-reset-brief.md` - `docs/openclaw/README.md` - `docs/openclaw/openclaw-orchestration-flow.md` ## 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`、过滤器和入库流程。