Files
reader/docs/design/summary-core-interface-design.md

11 KiB

Summary Core Interface 设计草案

状态说明

本文件记录的是较早期的摘要内核接口抽象。

它更适合用于理解背景设计,不应直接当成当前生产接口契约。 当前接口与编排真相请优先看:

  • README.md
  • docs/current/context-reset-brief.md
  • docs/openclaw/README.md
  • docs/openclaw/openclaw-handoff.md

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。

{
  "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": "<html>...</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 最小可用输入

第一阶段建议只要求:

{
  "item": {
    "item_id": "sha256:xxx",
    "title": "文章标题",
    "url": "https://example.com/post/1",
    "raw_summary": "RSS 摘要",
    "raw_content": ""
  }
}

6. 输出模型

6.1 标准输出结构

建议标准输出命名为 SummaryOutput。

{
  "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 最小可用输出

{
  "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 建议错误结构

{
  "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. 当前阶段的最小可用接口

第一阶段建议只实现一个最小接口:

输入:

{
  "item": {
    "item_id": "sha256:xxx",
    "title": "文章标题",
    "url": "https://example.com/post/1",
    "raw_summary": "RSS 摘要",
    "raw_content": ""
  }
}

输出:

{
  "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。