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

7.7 KiB
Raw Blame History

Content Extract MCP Service 设计草案

1. 文档目的

本文档用于定义当前仓库中已经落地的 MCP 服务设计,即“内容提取 MCP”。

目标是明确:

  • 这个 MCP 服务当前真正负责什么
  • 它对外暴露哪些 tool
  • 每个 tool 的输入输出结构是什么
  • validator 与 LLM 摘要如何接在 MCP 之后
  • 当前 MVP 已完成到哪一层

这份文档描述的是当前真实实现,而不是早期“摘要 MCP”设想。


2. 服务定位

这个 MCP 服务的角色是:为上层 Agent、OpenClaw 或其他自动化流程提供统一的“文章内容提取”能力。

它在整条链路中的位置是:

RSS 聚合 -> Content Extract MCP -> LLM 摘要 -> 校验 -> 规则过滤 -> 入库 -> 推送

它不是完整阅读流系统,也不是摘要服务本身,而是阅读流中的“结构化正文提取层”。

2.1 服务负责的事情

  • 接收 URL 或标准化 item
  • 获取正文或补全正文
  • 抽取文章标题
  • 抽取正文纯文本
  • 返回结构化文章对象
  • 返回质量标记和结构化错误

2.2 服务不负责的事情

  • 管理 RSS 订阅源
  • 生成摘要
  • 主题分类
  • 价值判断
  • 规则过滤
  • 写入知识库
  • 发送通知
  • 执行全流程编排

结论:

当前 MCP 服务边界应保持干净,只负责把网页或 item 转成结构化文章数据。


3. 总体架构

建议并且当前实现采用的是:

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 直接调用

建议输入:

{
  "url": "https://example.com/post/1",
  "language_hint": "zh"
}

建议输出:

{
  "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 等追踪信息

建议输入:

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

建议输出:

{
  "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. 错误返回规范

建议错误结构如下:

{
  "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. 当前目录结构建议

当前实现大致如下:

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 流程。