Files
reader/docs/summary-core-interface-design.md
T
2026-03-24 17:01:35 +08:00

504 lines
11 KiB
Markdown

# 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": "<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 最小可用输入
第一阶段建议只要求:
```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。