# Summary Core Interface 设计草案 ## 1. 文档目的 本文档用于定义 `summary-core` 的输入输出接口,目标是把“页面摘要能力”从概念讨论收敛成一套稳定、可复用、可封装的数据接口。 这份文档不限定实现语言,也不限定它最终通过 CLI、HTTP、MCP 还是 Skill 暴露,只关注能力边界和数据契约。 --- ## 2. 角色定位 `summary-core` 是阅读流系统中的摘要内核,负责把候选内容转换成可进入过滤、入库和推送阶段的标准化内容对象。 它在整条链路中的位置是: `RSS 聚合 / 页面抓取 -> summary-core -> rule-engine -> sink -> push` 它的职责是: - 接收一个候选内容对象或与之等价的输入 - 获取正文或补全正文 - 抽取正文纯文本 - 生成结构化摘要 - 输出标准化的 `document` 对象或与之兼容的结果 它不负责: - 维护 RSS 订阅源 - 执行规则过滤 - 写入知识库 - 执行消息推送 - 负责编排整个自动化流程 --- ## 3. 设计原则 ### 3.1 输入以 `item` 为主,补充字段为辅 `summary-core` 的标准输入应优先基于 `item`,这样可以和上游采集层稳定衔接。 但为了适配不同调用方式,也允许附带额外输入: - `raw_html` - `rss_content` - `user_profile` - `summary_style` ### 3.2 输出以 `document` 为中心 `summary-core` 的输出应该尽量直接映射到 `document`,而不是返回一段松散文本。 这样后续的规则引擎、知识库入库和推送层都可以稳定消费。 ### 3.3 失败也要结构化表达 正文抓取失败、内容过短、抽取结果异常、疑似付费墙等情况,不能只返回报错字符串,应该返回结构化错误或状态标志。 ### 3.4 同一套数据契约服务多种封装方式 不管未来是: - 本地 CLI - HTTP API - MCP tool - OpenClaw Skill 都应尽量复用同一套输入输出 schema,而不是每个入口都定义一套不同的数据格式。 --- ## 4. 处理阶段拆分 `summary-core` 内部建议拆成以下几个阶段: 1. 输入标准化 2. 正文获取 3. 正文抽取 4. 摘要生成 5. 质量标记 6. 输出映射 ### 4.1 输入标准化 目标: - 统一不同调用来源的数据结构 - 优先使用 `item` - 补齐缺失字段 ### 4.2 正文获取 目标: - 优先使用上游已提供的 `raw_content` - 若内容不足,再根据 `url` 回源抓取页面 - 如有 `raw_html`,优先直接使用 ### 4.3 正文抽取 目标: - 从 HTML 或富文本中抽取正文 - 生成稳定的纯文本内容 - 识别是否正文过短、被截断或包含大量噪音 ### 4.4 摘要生成 目标: - 基于正文生成结构化摘要 - 输出摘要、高亮点、关键词、主题和评分 - 保证结构化输出稳定,不允许自由散文式结果 ### 4.5 质量标记 目标: - 判断是否存在付费墙、截断、低信息密度等问题 - 为后续过滤器提供可用标志位 ### 4.6 输出映射 目标: - 将摘要结果映射为标准 `document` - 保持与上游 `item` 的可追溯关系 --- ## 5. 输入模型 ### 5.1 标准输入结构 建议标准输入命名为 `SummaryInput`。 ```json { "item": { "item_id": "sha256:xxx", "source_id": "my-blog", "title": "文章标题", "url": "https://example.com/post/1", "published_at": "2026-03-23T08:00:00Z", "raw_summary": "RSS 摘要", "raw_content": "RSS 正文片段", "content_kind": "article", "language": "zh" }, "raw_html": "...", "rss_content": "RSS 提供的正文内容", "user_profile": { "interests": ["AI", "workflow"], "languages": ["zh", "en"] }, "summary_style": "default" } ``` ### 5.2 输入字段说明 - `item` - 标准主输入 - 推荐必填 - `raw_html` - 可选 - 当调用方已经抓到网页 HTML 时可直接传入,避免重复抓取 - `rss_content` - 可选 - 当调用方希望优先使用 RSS 正文时可单独传入 - `user_profile` - 可选 - 用于后续个性化摘要、主题提取或兴趣打分 - 第一阶段可以不启用,但接口层建议预留 - `summary_style` - 可选 - 用于控制摘要策略,例如 `default`、`brief`、`detailed`、`bullet` ### 5.3 最小可用输入 第一阶段建议只要求: ```json { "item": { "item_id": "sha256:xxx", "title": "文章标题", "url": "https://example.com/post/1", "raw_summary": "RSS 摘要", "raw_content": "" } } ``` --- ## 6. 输出模型 ### 6.1 标准输出结构 建议标准输出命名为 `SummaryOutput`。 ```json { "success": true, "document": { "document_id": "sha256:yyy", "item_id": "sha256:xxx", "source_id": "my-blog", "url": "https://example.com/post/1", "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" }, "debug": { "content_source": "fetched_html", "extractor": "trafilatura", "model": "summary-model-name" }, "warnings": [] } ``` ### 6.2 输出字段说明 - `success` - 是否成功完成摘要流程 - `document` - 标准输出主体 - 结构应尽量与 `document` schema 对齐 - `debug` - 可选 - 记录本次实际用了什么正文来源、抽取器和模型 - 便于后续排查问题 - `warnings` - 可选 - 用于记录非致命异常,例如正文过短、内容疑似截断、元信息缺失 ### 6.3 最小可用输出 ```json { "success": true, "document": { "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" }, "warnings": [] } ``` --- ## 7. 错误模型 ### 7.1 为什么要有独立错误模型 如果 `summary-core` 只在失败时返回字符串错误,会导致: - 上层流程难以判断是否应该重试 - 过滤器和编排层难以做自动决策 - MCP 或 Skill 封装时难以标准化输出 因此建议引入结构化错误模型。 ### 7.2 建议错误结构 ```json { "success": false, "error": { "code": "CONTENT_FETCH_FAILED", "message": "Failed to fetch article content", "retryable": true, "stage": "fetch", "details": { "url": "https://example.com/post/1" } }, "warnings": [] } ``` ### 7.3 建议错误码 - `INVALID_INPUT` - `CONTENT_FETCH_FAILED` - `CONTENT_EXTRACTION_FAILED` - `CONTENT_TOO_SHORT` - `PAYWALL_DETECTED` - `SUMMARY_GENERATION_FAILED` - `UNSUPPORTED_CONTENT_KIND` - `UNKNOWN_ERROR` ### 7.4 建议错误字段 - `code` - 机器可识别错误码 - `message` - 人类可读错误信息 - `retryable` - 是否建议重试 - `stage` - 出错阶段,例如 `normalize`、`fetch`、`extract`、`summarize` - `details` - 补充调试信息 --- ## 8. 内容来源优先级策略 为了保证 `summary-core` 在不同输入条件下行为稳定,建议明确正文来源优先级。 推荐顺序: 1. `raw_html` 2. `item.raw_content` 3. `rss_content` 4. 根据 `item.url` 回源抓取 解释: - 如果调用方已经传入 `raw_html`,优先直接用,避免重复请求 - 如果 RSS 已经提供了高质量正文,可优先利用 - 如果 RSS 内容不足,再回源抓页面 这一策略需要在实现阶段进一步细化,例如通过长度阈值判断是否需要回源抓取。 --- ## 9. 与 `document` schema 的关系 `summary-core` 的输出不应另起炉灶,而应尽量直接映射到 `document`。 建议遵守以下原则: - `SummaryOutput.document` 与 `document` schema 保持高度一致 - `summary-core` 只负责生成 `document`,不负责直接写入知识库 - 上游保留 `item_id` 和 `source_id`,确保可追溯 这样做的好处是: - 下游过滤器只需要消费 `document` - 存储层无需理解摘要实现细节 - MCP 封装和 CLI 调用都可以复用同一结果结构 --- ## 10. 调用方式兼容性 同一套接口建议兼容以下几种封装方式: ### 10.1 CLI 适合: - 本地验证 - 单条 URL 调试 - 批处理脚本调用 建议形式: - `summary-core input.json` - `summary-core --url https://example.com/post/1` ### 10.2 HTTP API 适合: - 被其他服务调用 - 作为统一后端能力暴露 建议形式: - `POST /summarize` ### 10.3 MCP Tool 适合: - 给 OpenClaw、ChatGPT、Claude 等 Agent 调用 建议形式: - `summarize_item` - `summarize_url` ### 10.4 Skill 适合: - 让 Agent 在上下文中决定是否调用摘要流程 - 与其他工具组合编排 结论: 接口 schema 应保持统一,不因为调用方式不同而拆成不同语义模型。 --- ## 11. 当前阶段的最小可用接口 第一阶段建议只实现一个最小接口: 输入: ```json { "item": { "item_id": "sha256:xxx", "title": "文章标题", "url": "https://example.com/post/1", "raw_summary": "RSS 摘要", "raw_content": "" } } ``` 输出: ```json { "success": true, "document": { "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" }, "warnings": [] } ``` 这个阶段先不要求: - 个性化摘要 - 多风格摘要模板 - 批量接口 - 复杂质量评分 - 高级缓存策略 --- ## 12. 后续可继续讨论的问题 1. `SummaryInput` 是否必须包含完整 `item`,还是允许只传 URL 2. `summary_style` 是否应该在第一阶段就进入 schema 3. `debug` 字段是否应面向开发环境可选开启 4. 错误码是否需要更细分成抓取类、抽取类、模型类 5. `plain_text` 是否需要附带 token 数、字数等统计信息 6. `SummaryOutput` 是否需要保留原始抽取文本和最终摘要之间的映射信息 --- ## 13. 当前阶段的一句话结论 当前阶段最合理的做法是先固定 `summary-core` 的统一输入输出契约,让它稳定接收 `item`,稳定产出 `document`,再在此基础上封装 CLI、MCP 和 Skill。