Files
reader/docs/design/summary-mcp-service-design.md
T

392 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Content Extract MCP Service 设计草案
## 状态说明
本文件主要记录早期 “content extract MCP” 的设计抽象。
它仍有背景参考价值,但不是当前生产事实入口。
当前生产能力已经演进为更完整的 workflow service,正式口径请优先看:
- `README.md`
- `docs/current/context-reset-brief.md`
- `docs/openclaw/README.md`
- `docs/openclaw/openclaw-handoff.md`
- `docs/openclaw/openclaw-orchestration-flow.md`
## 1. 文档目的
本文档用于定义当前仓库中已经落地的 MCP 服务设计,即“内容提取 MCP”。
目标是明确:
- 这个 MCP 服务当前真正负责什么
- 它对外暴露哪些 tool
- 每个 tool 的输入输出结构是什么
- validator 与 LLM 摘要如何接在 MCP 之后
- 当前 MVP 已完成到哪一层
这份文档主要记录当时实现阶段的设计取向,而不是当前生产阶段的唯一事实来源。
---
## 2. 服务定位
这个 MCP 服务的角色是:为上层 Agent、OpenClaw 或其他自动化流程提供统一的“文章内容提取”能力。
它在整条链路中的位置是:
`RSS 聚合 -> Content Extract MCP -> LLM 摘要 -> 校验 -> 规则过滤 -> 入库 -> 推送`
它不是完整阅读流系统,也不是摘要服务本身,而是阅读流中的“结构化正文提取层”。
### 2.1 服务负责的事情
- 接收 URL 或标准化 `item`
- 获取正文或补全正文
- 抽取文章标题
- 抽取正文纯文本
- 返回结构化文章对象
- 返回质量标记和结构化错误
### 2.2 服务不负责的事情
- 管理 RSS 订阅源
- 生成摘要
- 主题分类
- 价值判断
- 规则过滤
- 写入知识库
- 发送通知
- 执行全流程编排
结论:
当前 MCP 服务边界应保持干净,只负责把网页或 item 转成结构化文章数据。
---
## 3. 总体架构
建议并且当前实现采用的是:
```text
MCP Server Layer
-> Tool Handlers
-> extraction core
-> normalizer
-> content_loader
-> extractor
-> quality_checker
-> mapper
```
### 3.1 外层 MCP 层职责
- 注册 tools
- 接收和校验 tool 输入
- 调用内部 extraction core
- 将结果包装成 MCP tool 输出
### 3.2 内层 extraction core 职责
- 处理正文获取、标题提取、正文抽取、质量检查、结果映射
- 与具体 MCP SDK 解耦
### 3.3 为什么必须分层
如果把内容提取逻辑直接写死在 MCP handler 里,后续会出现这些问题:
- 业务逻辑难测试
- 协议层与抽取逻辑耦合
- 以后想补 CLI、批处理或其他入口时需要重复实现
因此当前原则是:
- MCP 是外壳
- extraction core 是内核
---
## 4. 当前暴露的 Tools
当前实现只暴露两个 tool:
- `extract_url_content`
- `extract_item_content`
### 4.1 `extract_url_content`
角色:
- 直接输入 URL
- 适合单条文章测试
- 适合手动调试或上层 Agent 直接调用
建议输入:
```json
{
"url": "https://example.com/post/1",
"language_hint": "zh"
}
```
建议输出:
```json
{
"success": true,
"article": {
"extract_id": "sha256:yyy",
"item_id": null,
"source_id": null,
"url": "https://example.com/post/1",
"title": "文章标题",
"language": "zh",
"content_kind": "article",
"plain_text": "抽取后的正文",
"quality_flags": {
"is_paywalled": false,
"is_truncated": false,
"is_low_content": false
},
"metadata": {
"content_source": "fetched_html",
"extractor": "trafilatura",
"char_count": 1234
},
"pipeline_state": "extracted"
},
"warnings": [],
"debug": {
"content_source": "fetched_html",
"extractor": "trafilatura"
}
}
```
### 4.2 `extract_item_content`
角色:
- 输入标准化 `item`
- 与上游 RSS 聚合层对接
- 保留 `item_id`、`source_id` 等追踪信息
建议输入:
```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"
}
}
```
建议输出:
```json
{
"success": true,
"article": {
"extract_id": "sha256:yyy",
"item_id": "sha256:xxx",
"source_id": "my-blog",
"url": "https://example.com/post/1",
"title": "文章标题",
"published_at": "2026-03-23T08:00:00Z",
"language": "zh",
"content_kind": "article",
"plain_text": "抽取后的正文",
"quality_flags": {
"is_paywalled": false,
"is_truncated": false,
"is_low_content": false
},
"metadata": {
"content_source": "item.raw_content",
"extractor": "inline",
"char_count": 1234
},
"pipeline_state": "extracted"
},
"warnings": [],
"debug": {
"content_source": "item.raw_content",
"extractor": "inline"
}
}
```
---
## 5. 统一输出规范
当前两个 tool 都复用同一个输出结构,即 `ExtractionOutput`。
成功时:
- `success = true`
- 返回 `article`
- 可附带 `warnings`
- 可附带 `debug`
失败时:
- `success = false`
- 返回结构化 `error`
- 不返回不完整的 `article`
这样做的好处是:
- 上层流程只需要消费一个稳定 schema
- 后续 LLM 摘要和 validator 可以独立接上
- OpenClaw 或其他 Agent 更容易编排
---
## 6. 错误返回规范
建议错误结构如下:
```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": []
}
```
当前错误码包括:
- `INVALID_INPUT`
- `CONTENT_FETCH_FAILED`
- `CONTENT_EXTRACTION_FAILED`
- `CONTENT_TOO_SHORT`
- `UNKNOWN_ERROR`
使用原则:
- 抓取失败不等于程序崩溃
- 可以重试的错误应标记 `retryable = true`
- 错误应指出具体阶段
---
## 7. 当前技术选型
当前 MVP 使用:
- Python
- MCP Python SDK (`FastMCP`)
- `httpx` 进行网页抓取
- `trafilatura` 进行正文抽取
- `beautifulsoup4` 作为 HTML 兜底解析
- `pydantic` 进行输入输出约束
设计原则是:
- 外层 MCP
- 内层 extraction core
- LLM 摘要和校验作为 MCP 之后的独立环节
---
## 8. 当前目录结构建议
当前实现大致如下:
```text
summary_mcp/
server.py
core/
normalizer.py
content_loader.py
extractor.py
quality_checker.py
mapper.py
pipeline.py
models/
item.py
document.py
summary_io.py
llm_result.py
validators/
llm_result.py
```
说明:
- `server.py`:MCP 服务入口
- `core/`:提取内核
- `models/`:提取和校验相关模型
- `validators/`:LLM 输出校验逻辑
---
## 9. 与 LLM 摘要层的关系
当前 MCP 不负责摘要,但它与摘要层的接口已经明确:
1. MCP 产出结构化 `article`
2. 上层 LLM 根据 `article.title`、`article.url`、`article.plain_text` 等生成摘要 JSON
3. validator 对摘要 JSON 做 schema 校验与业务校验
4. 若不合格,进入修复重试
这条闭环当前已经通过本地脚本验证:
- `scripts/run_summary_loop.py`
因此,当前正确的职责拆分是:
- MCP 负责提取
- LLM 负责摘要
- validator 负责验收
---
## 10. 当前阶段验收结论
当前 MVP 已完成以下验证:
- 真实 URL 可以提取为结构化文章 JSON
- 提取结果可以保存为文件
- 提取结果可以喂给 LLM 生成摘要 JSON
- 摘要 JSON 可以通过 validator 校验
- 整条“提取 -> 摘要 -> 校验”最小闭环已经跑通
---
## 11. 后续扩展方向
后续建议按下面顺序推进:
- 接入真实 RSS 聚合结果并映射为 `item`
- 定义规则过滤层 schema
- 设计知识库入库格式
- 增加批量处理能力
- 引入 Playwright 作为动态页面兜底方案
- 重命名包和项目名,使其与当前职责一致
---
## 12. 当前阶段一句话结论
当前仓库中的 MCP 已经从早期“摘要 MCP”演进为“内容提取 MCP”,它的职责是稳定产出结构化文章 JSON,并把摘要与验收环节留给后续的 LLM 和 validator 流程。