公众号文章结构化消化

SKILL效率

原名 yipian

效率 未标注权限 许可证不明确
安装这个技能

自动判断文章属于知识型、操作型还是感悟型,分别产出结构化笔记、SOP 清单加可执行提示词、金句与观点集,并输出 Markdown 与 HTML 文档。

一句话选型结论

按资料字段整理

公众号文章结构化消化是效率类的 Agent Skill,安装方式未标注,权限级别未标注,许可证不明确,有可直接复制的安装命令。技能本质是给模型读的说明书,效果取决于模型与客户端;装完先用一个小任务验证,再放进正式流程。

分类效率 安装方式未标注 权限级别未标注权限 许可证许可证不明确
累计安装 未标注 本周 未标注
GitHub 星标 未标注 仓库 未标注
下载量 未标注 技能市场下载数
权限级别 未标注权限 安装前请确认风险范围
许可证 许可证不明确 未说明

安装

未标注
在支持 Skills 的客户端里执行,装完问一句「你有哪些技能」验证
skillhub install user_16bdbfca/yipian
  1. 1
    确认客户端支持

    确认你的客户端支持 Skills(Claude、Codex、Cursor 等),并升级到较新的版本。

  2. 2
    拿到技能文件

    执行上面的命令安装;也可以把仓库里的 SKILL.md 手动放进客户端的 skills 目录。

  3. 3
    刷新技能列表

    重新打开客户端,或刷新技能列表,让新放进来的技能被加载。

  4. 4
    验证是否生效

    问一句「你有哪些技能」,确认列表里出现它,再拿一个小任务试跑。

技能档案

效率
字段来自技能仓库与公开索引,中文说明由本站标注
作者 / 维护方user_16bdbfca仓库 未标注 · 版本 未标注
宿主客户端未标注技能可被这些客户端加载
安装方式未标注安装包 community
权限级别未标注权限安装前请确认风险范围
许可证未说明许可证不明确,商用前请自行确认条款
仓库可用性available技能仓库当前是否可访问
最近核验2026-11-06上游最后核验时间 · 数据更新 2026-11-06
正文体量38.0 KBSKILL.md 原文大小,越大说明说明书写得越细

能做什么

0 项能力
上游给出的能力概览与适用场景
整理公众号干货文章提炼可执行操作流程汇总同主题多篇文章

典型使用场景

3 类
按分类整理的常见用法,具体以 SKILL.md 为准
整理待办与日程

把零散任务收进清单,排出优先级。

批量处理重复任务

把固定动作做成模板,重复执行。

把流程写成清单

把口头流程整理成可复用的步骤。

选择建议

装之前先看这几条
按条目字段给出的判断,不替你决定用不用

值得选的地方

  • 有可直接复制的安装命令,不用自己翻文档找入口。

先想清楚的地方

  • 没有标注权限级别,实际能做什么要装好后确认。
  • 许可证不明确,商用或二次分发前先到仓库确认条款。
  • 没有做站内核验,内容以作者仓库为准。
  • 技能只是说明书,实际效果取决于模型与客户端,装完先用小任务试一遍。

更适合

  • 需要在效率场景里给模型补一套做法的人。
  • 愿意先读一遍 SKILL.md、确认它会让模型做什么的人。

不太适合

  • 对许可证要求很严、必须明确可商用的人。
  • 只想装完就自动变强、不打算看说明书的人。

同类对比

6 个同类
同分类里挑最接近的几条,字段以各自详情页为准
技能权限级别安装方式累计安装宿主客户端安装命令
公众号文章结构化消化当前 未标注权限 未标注 未标注 未标注 有
Nano PDF 文档 联网访问 ClawHub 市场 424.9 万 OpenClaw 有
自动更新程序 浏览器控制 ClawHub 市场 296.4 万 OpenClaw 有
Gemini 助手 联网访问 ClawHub 市场 254.7 万 OpenClaw 有
word DOCX 文档 联网访问 ClawHub 市场 239.6 万 OpenClaw 有
Excel XLSX 前端 联网访问 ClawHub 市场 227.0 万 OpenClaw 有

SKILL.md 全文

38.0 KB
模型实际读到的说明书原文,来自技能仓库

> ⚠️ 已知问题:本文件为 UTF-8 编码(无 BOM)。如某些环境显示中文乱码,请检查读取工具的编码设置。不影响功能执行。

/yipian — 公众号文章消化技能

> 一篇公众号文章 → 一片结构化知识


配置(用户按需修改)

# yipian 配置
preferences:
  # 存档后端: local / getnote / obsidian / none
  #   local: 保存到本地文件系统
  #   getnote: 保存到 Get笔记(需配置 getnote 相关字段)
  #   obsidian: 保存到 Obsidian(需配置 vault_path)
  #   none: 跳过存档,只在对话中展示产出
  storage_backend: local

  # 本地存档路径(仅 storage_backend 为 local 时生效)
  # 默认值: "~/yipian-notes/"(替换 ~ 为用户家目录)
  # 如果设为空字符串,则使用内置默认路径 yipian-output/(当前工作目录下,笔记跟项目走)
  local_path: ""

  # getnote 配置(仅 storage_backend 为 getnote 时需要)
  getnote_tag_prefix: "阅读"
  getnote_output_tag: "精读产出"

  # obsidian 配置(仅 storage_backend 为 obsidian 时需要)
  obsidian_vault_path: ""

  # 搜索工具(留空则自动探测可用工具: doubao_search > web_search > 无搜索)
  # 如果填入不存在的工具名,自动回退到自动探测
  search_tool: ""

  # 消化深度默认值: shallow / medium / deep
  default_depth: "medium"

  # 交互模式: standard / fast
  #   standard: 每步停等确认(适合用户想深度参与)
  #   fast: 自动走完,只在产出前做一次确认(适合日常刷文)
  # 如果设为其他无效值,自动回退到 standard
  interaction_mode: "standard"

触发方式

  • /yipian <公众号文章链接> — 单篇消化
  • /yipian <链接1> <链接2> <链接3> — 多篇同主题消化
  • 或直接发链接 — 问是否要用 yipian 消化

输入校验

收到用户输入后,先做校验:

  1. 无链接:用户只发文字(如"帮我消化一篇文章")但没有发任何链接 → 提示"请提供公众号文章链接",不调用 MCP
  2. 无效链接:链接格式明显不对(不包含 http 或 mp.weixin.qq.com)→ 提示"这不是公众号文章链接,请确认后重发"
  3. 非公众号链接:是有效 URL 但非 mp.weixin.qq.com 域名 → 改用 fetch_content 读取内容,然后走标准消化流程
  4. 多链接逐条验证:如果用户发了多个链接,逐条检查上述条件,分别标记有效/无效,只对有效链接继续处理。无效链接单独告知用户

核心原则

  1. 用户不懂技术。能直接完成的就完成,不要教步骤。
  2. 先评估后行动 — 每步前做条件检测,条件不够则降级或提示,不硬闯
  3. 按类型自动匹配格式 — 用户不需要选择输出格式
  4. MD + HTML 双输出 — 二者缺一不可,先 MD 空一行后 HTML

工作流程(总览 7 步)

① 输入 → ② 初检+概览 → ③ 选板块精读 → ④ 产物组装 → ⑤ 存档 → ⑥ 完成告知

确认节点策略(根据 interaction_mode)

standard 模式(默认): 每步执行完成停等确认。

fast 模式:
- Step 1→2 自动执行,初检+概览一出直接展示
- Step 3 精读板块选择 → 用户一次性选完 → AI 自动连续精读全部选中板块 → 精读完后问"直接全输出还是再补缺"
- 减少确认次数从 5-6 次压到 2-3 次

异常处理总则

异常场景 处理方式
用户中途说"算了/不弄了" 已产出的内容保留在对话中,存档步骤跳过,告知用户"已暂停,产出内容还在对话中"
用户中途追加新链接 暂停当前流程,将新链接追加到待处理队列。询问用户"新链接是同一主题(走多篇模式)还是单独处理"
系统/网络错误 重试 1 次 → 降级到备选方案(如 MCP 失败用 fetch_content)→ 还不行则告知用户具体失败原因和重试建议

分步详细流程

Step 1:输入

🔴 CHECKPOINT · 输入确认: 链接校验通过后,展示待消化链接列表让用户确认再进入下一步。

  • 用户发公众号文章链接(1 个或多个)
  • URL 校验:调用 MCP 前先确认链接是 mp.weixin.qq.com 域名且格式有效
  • 非公众号链接:直接用 fetch_content 读取,不走 MCP
  • ⏳ 进度提示: 调用 MCP 前先说 "正在用 download-mcp 下载文章...",避免用户等待时无反馈
  • 调用方式:直接用固化脚本(不要再手写 urllib 代码):
    python # 完整代码见附录 A(download_article.py) # 用法:将附录 A 代码保存为 download_article.py,然后运行: # python download_article.py <文章链接>
    脚本自动完成:MCP 调用 → 嵌套 JSON 二次解析 → 中文文件名 URL 编码(urllib.parse.quote)→ 下载到当前目录(文件名取自 MCP 返回的 urls[0])。
  • 返回结构说明: MCP 响应中 result.content[0].text 是一个 JSON 字符串,需再次解析才能获取 urls[0] 和 status。直接读 result.urls[0] 会失败。脚本已封装此逻辑。
  • MCP 调用失败(HTTP 500 / 超时 / 返回空 result):重试 1 次 → 仍失败则告知用户具体原因,不降级到其他工具
  • URL 编码问题: 下载文件时如遇到 404(中文文件名被错误转义),脚本已内置 urllib.parse.quote(parsed.path) 编码处理。不用 PowerShell。
  • 脚本不存在/报错时:回退到手写 urllib 调用(代码参考附录 A 内部实现,逻辑一致)
  • 如果 download-mcp 完全不可用 → 告知用户 MCP 不可用,请稍后重试

Step 2:内容初检 + 宏观概览(合并输出)

收到链接后,下载完毕直接输出内容初检结果 + 宏观概览,不停等确认。内容初检和宏观概览一次性展示给用户,用户看完了直接决定下一步。

2A 内容初检

自动检查三个维度:

检查项 判断标准 结果
完整性 是否有明显截断、字数远低于预期 ✅ 完整 / ⚠️ 可能不完整
结构性 是否有标题层级(h1/h2/h3) ✅ 有结构 / ⚠️ 纯文本堆叠
可读性 是否含大量乱码/HTML 残留 ✅ 干净 / ⚠️ 需要清洗

原文清洗(存档前执行): 运行附录 B 的 clean_article.py 代码(保存为 .py 文件后执行:python clean_article.py 原始文章.md),不需要手写正则。
脚本内置:去除公众号 UI 元素(关注引导"微信扫一扫可打开此内容"、小程序弹窗"预览时标签不可点"、作者头像、底部相关阅读等)、压缩多余空行(连续 3 行以上压为 1 行)、去行尾空格。
- 绝对保留:正文内容、标题层级(h1/h2/h3/h4)、图片(含 alt 文本)、引用块格式、代码块、列表格式、原文链接
- 敏感内容:如果文章明显包含违法违规内容,停止处理并提示用户。如果包含广告软文/营销推广,在消化笔记中标注"营销倾向"
- 新 UI 元素漏清:若脚本未覆盖新的公众号 UI 残留,人工补删后,把新元素追加到 clean_article.py 的 JUNK 列表(便于下次自动处理)

产出可消化度 + 消化深度建议:

可消化度 条件 建议深度
高(≥80%) 完整 + 有结构 + 干净 深层消化(精读+联想+搜索锚定)
中(50-80%) 有 1 项黄色预警(⚠️) 中层消化(结构化拆解+信息补全)
低(<50%) 有 2 项及以上黄色预警,或 1 项红色预警 浅层消化(摘要+金句),给出处理建议

评分规则: 三项全部绿色(✅)= 高;有一项黄色(⚠️)= 中;两项及以上黄色或任一红色 = 低。

文章长度特殊处理:
- 超短文(< 1000 字):不拆板块,直接输出摘要(150-300 字)+ 核心观点 1-2 条。跳过精读步骤,直接进入产物组装。在初检中标注"短文模式"。
- 超长文(> 20K 字):在初检中标注"长文模式",概览板块拆解控制在最多 5 个板块(取信息密度最高的 5 个)。精读时每个板块输出控制 ≤800 字,避免产出过长。

2B 宏观概览

内容初检之后,同一轮直接输出宏观概览(不停等)。

单篇模式

输出概览包含:

【核心主题】一句话
【解决什么问题】读完能带走什么价值
【板块拆解】全文划成 N 个板块,各标实用价值(⭐⭐⭐)
【信息缺口标注】原文哪块讲得不细,需要搜补
【消化建议】推荐精读板块 + 建议消化深度

⭐ 概览输出样例(基于真实文章《AI到底能帮你做什么?》):

【核心主题】WorkBuddy × ima 产品的 3 种使用范式与 7 个真实用户案例
【解决什么问题】读完能带走 3 套可直接落地的 AI 工作方式(读资料→造产品→跑腿)
【板块拆解】
  ① 读资料→第二大脑(3案例)⭐⭐⭐ 上手门槛最低
  ② 造产品→一人工程师(2案例)⭐⭐⭐ 最有杠杆感
  ③ 跑腿→数字同事(2案例)⭐⭐⭐ 效率提升最猛
【信息缺口】未提产品定价/获取渠道/自动化任务配置细节
【消化建议】推荐精读全部三个板块,深度建议 medium→deep

说明:概览不是固定模板填空,AI 可据文章特性调整格式。核心底线是板块拆解+信息缺口+推荐精读三项必须有。

概览纠错: 如果用户反馈概览中的主题或板块划分不准确,允许用户手动修正。修正后重新输出概览,不再重做 2A 内容初检。

多篇模式

输出主题知识地图:

【主题确认】共同主题
【每篇快照】每篇一句话 + 侧重点
【共识与矛盾】各篇一致的地方 vs 有分歧的地方
【信息互补关系】哪篇缺的、另一篇正好补了
【消化建议】推荐按篇精读 / 按概念精读 / 全主题输出

→ 用户确认后进入下一步

Step 3:选板块精读

🔴 CHECKPOINT · 精读选择: 展示板块列表 + 精读方式选项后停等用户选择。用户一次性选完,AI 自动连续执行。

单篇

用户在概览后选择精读方式(一次性操作,不要反复确认):
- A. 按个精读 → 从概览板块列表中选 1 个或多个精读(推荐一次性选完,AI 自动连续精读所有选中板块)
- B. 全主题输出 → 跳过精读,直接出整合报告

板块编号不存在: 如果用户选的板块编号超出概览列表范围,提示"板块编号不正确,请从以下列表中选择(1~N)"并重新展示板块列表。

"你决定" / "随便": 用户不做选择时,AI 自动推荐实用价值最高的板块(⭐⭐⭐优先)精读。

内容严重不足的板块: 精读时发现该板块原文仅几句话带过、实质内容太少,标记"无法精读,内容过少",询问用户是跳过还是由 AI 基于主题扩展补充。

AI 对每个选中板块逐一执行:
1. 输出核心观点拆解(2-3句话)
2. 提取关键细节/数据/案例
3. 原文讲得不细的地方 → 自动搜索补充
4. 关联延伸资源(如有)

精读完后自动衔接:
- 如果用户选了多个板块 → 自动连续精读,不逐个停等
- 全部精读完成后 → 询问"需要补其他板块,还是直接全主题输出?"
- 如果用户只选了 1 个板块 → 精读完后问"继续精读其他板块,还是直接出全主题输出?"
- fast 模式:用户在精读过程中可以随时说"这个板块够了/跳过",立即停止当前板块进入下一个
- 不再回头重新展示 Step 3 的板块选择

多篇

用户选择精读方式:
- A. 按篇精读 → 选某篇展开深挖(走单篇精读逻辑)
- B. 按概念精读 → 选某个概念,跨多篇提取拉对比
- C. 全主题输出 → 跳过精读,直接出整合报告

→ 确认后执行

Step 4:产物组装

🔴 CHECKPOINT · 产物确认: 精读完成后(或全主题输出前),确认所选板块是否已全部覆盖 + 是否需要补缺,再进入组装。

按文章类型自动匹配格式(无需用户选择):

文章类型 产物格式 说明
Type A 知识型 结构化笔记 板块拆解 + 核心观点 + 关键细节 + 延伸资源
Type B 操作型 SOP 清单 操作步骤 + 事实核查结果 + 可自动化提示词
Type C 感悟型 金句+观点集 核心心法 + 原文金句 + 可复用框架
混合型(A+B / A+C / B+C / A+B+C) 合并产出 以第一种类型为主格式,融入第二种类型的特征。如 A+B:结构化笔记中每个板块附带 SOP 步骤

必须同时输出两份文件,严格按以下顺序:

[MD 文档正文]
(不要前置闲聊、额外解释、多余注释)

(空一行)

[完整 HTML 源码]
(内嵌全部 CSS/JS,不引用外部资源)

MD 和 HTML 二者缺一即判定为任务失败。

产出质量自检清单(写入前执行): 运行附录 D 的 check_output.py 代码(保存为 .py 文件后执行:python check_output.py &lt;产出目录&gt;),不用手写正则。
脚本自动检查 4 项:① 三文件存在且非空 ② HTML 零外部依赖(正则扫描 https?://,排除 mailto/tel)③ MD/HTML 结构一致性(MD 的 ## 标题必须全部出现在 HTML h2 中)④ 🔍 标记一致性。

AI 另需人工确认 1 项(脚本不覆盖):
1. 覆盖检查:用户选的精读板块是否全部在产出中有对应章节?未选的是否未出现?

(原自检清单 2/3/4/5 项已由脚本自动执行:🔍 标记检查 / HTML 外部依赖检查 / MD-HTML 结构一致性 / 输出格式约束由生成流程保证)

产出文件写入失败(磁盘满/权限不足): 直接将完整内容输出到对话中(先 MD 源码,空一行后 HTML 源码),并提示用户"自动保存失败,请手动复制保存"。不阻塞流程继续。

HTML 规格

**HTML 生成:运行附录 C 的 render_html.py 代码(保存为 .py 文件后执行:python render_html.py yipian-笔记.md yipian-笔记.html),不要手写 13KB 模板。
脚本自动完成:零外部依赖内联 CSS/JS(markdown 库渲染)→ 正文 ## 自动生成 TOC 锚点(sec-N)→ h3 小节自动包成 <details> 折叠块(SOP 步骤默认可展开)→ 图片外链替换为占位块 → 普通链接转纯文本 → 亮/暗色切换 + 响应式 + 系统字体栈。

  • 零外部依赖:内嵌全部 CSS 和 JS,不引入 CDN、外部字体、外部图标库
  • 自检:HTML 生成后检查源码中不包含 http:// / https:// 的外部资源引用(除 mailto: tel: 协议外)。如果发现,替换为内联版本或删除
  • 导航:支持目录(TOC)锚点点击跳转
  • 交互:内容区块可折叠/展开
  • 响应式:排版自适应电脑和移动端
  • 样式区分:标题、段落、列表、引用、代码块各自有清晰样式
  • 暗色模式:支持亮色/暗色切换开关
  • 脚本缺失/报错时:回退到手写 HTML(遵循以下规格),质量要求不变

Type B 特殊处理

SOP 清单中每步包含:
- 操作描述 + 预期结果
- 难度标记:🔧手工 / 🤖可自动化
- 🤖 步骤附完整提示词,用户可直接复制发给 Agent 执行
- 同时提供两种粒度:
- a. 单步骤独立提示词
- b. 整份 SOP 合并提示词(统一丢给 Agent 一次搞定)

提示词模板原则: 如果文章涉及特定产品/工具,模板用 [工具名] 等占位符标注,并说明"可替换为其他同类工具",避免硬编码。

SOP 生成前先做事实核查:
1. 文章说的步骤/数据/效果是否真实准确?→ 搜索交叉验证
2. 文章一笔带过的步骤 → 搜索找完整教程补全
3. 有多个做法/版本的分歧 → 搜索确认当前主流做法
4. 核查发现矛盾:如果搜索发现文章中的说法与权威来源不符,在 SOP 中标注矛盾点 + 附上证据来源,让用户自行判断,不直接篡改原文内容

Step 5:存档

存档路径优先级:
1. 配置中 local_path 有值且不为空 → 使用 local_path 作为根目录
2. 配置中 local_path 为空字符串 → 使用默认路径 yipian-output/(当前工作目录下)
3. storage_backend: none → 跳过存档,仅在对话中展示产出

同名文件处理: 目标路径已存在同名文件时,在新文件名后追加 _v2、_v3 等后缀,不覆盖已有文件。告知用户原始文件位置和新文件名。

默认保存到本地文件系统:

{存档根目录}/
  ├── YYYY-MM-DD_文章标题/
  │   ├── 原文.md          ← download-mcp 下载的原始内容(已清洗)
  │   ├── yipian-笔记.md    ← 产出的 MD 文档
  │   └── yipian-笔记.html  ← 产出的 HTML 文档

如果用户配置了其他后端(getnote / obsidian),则同步存储到对应后端。

同步失败处理: 如果 getnote/obsidian 同步失败(如网络断开、API 失效),降级为仅本地存档,并告知用户"XX 同步失败,已保存到本地"。不阻塞流程。

存档完成后告知用户路径。

Step 6:完成告知

产出校验: 输出完成报告前,检查产出文件是否存在且大小 > 0(本地模式)。如果文件丢失或为空,重新写入一次。仍失败则走"产出写入失败"降级方案。

输出简短完成报告,包含:

✅ 消化完成

【原文】
标题:《XXX》
来源:[链接]

【产出】
📄 MD 笔记:{路径}/yipian-笔记.md
🌐 HTML 笔记:{路径}/yipian-笔记.html

【内容概要】
(2-3 句话,概括消化后的核心产出)

文章类型自动识别

收到链接后,依据以下特征自动判断文章类型:

Type A — 知识型/信息型

  • 介绍工具/产品/概念/方法论
  • 告诉你"有个好东西存在"、"XX 是什么"
  • 可实操内容少,以信息传递为主
  • 产出:结构化笔记 + HTML

Type B — 操作型/教程型

  • 包含安装命令、配置步骤、API 调用
  • 教你"怎么装"、"怎么配置"、"怎么用"
  • 有具体可执行的动作
  • 产出:SOP 清单(含可自动化提示词)+ HTML

Type C — 感悟型/心法型

  • 个人成长、心理学、哲学、感悟
  • 不传递操作信息,传递思维框架和心法
  • 以观点、案例、类比为主
  • 产出:金句+观点集 + HTML

混合型判断

如果文章同时具有多种类型特征(如既介绍知识又包含具体操作步骤),自动识别为混合型,按"以第一类型为主格式,融入第二类型特征"处理。

平局 tiebreaker: 如果两种类型特征强度相等(无法判断哪个是"第一类型"),按以下优先级选择主格式:A(知识型)> B(操作型)> C(感悟型)。即 A=B 时选 A,B=C 时选 B。

用户手动覆盖

  • /yipian a &lt;链接&gt; — 强制按知识型处理
  • /yipian b &lt;链接&gt; — 强制按操作型处理
  • /yipian c &lt;链接&gt; — 强制按感悟型处理

信息缺口处理

精读或拆解中发现原文不够用时,按优先级使用补充手段:

  1. 本机资源优先:文章提到的 Skill/工具/产品,先查本机 agent 的 skills 安装目录是否已装有同类 skill(如 ~/.pi/agent/skills/、~/.agents/skills/),已安装则直接读其 SKILL.md 作为一手资料(比搜索更准、更快、零成本)。例如:文章提到某 skill 时,若本机已装有同款,优先读它的一手资料补全细节
  2. doubao_search — 搜中文补充信息(优先)
  3. web_search — 通用搜索后备
  4. fetch_content — 抓关联页面
  5. 无搜索工具 → 纯靠 AI 知识,标注"未经过实时校验"

search_tool 配置处理: 如果 search_tool 配置的值不存在或不可用,自动降级到自动探测顺序(doubao_search → web_search → 无搜索),并在精读结果中标注"搜索工具不可用,部分信息未验证"。

搜索结果为空时的处理: 如果搜索返回 0 条有效结果,不强行编造,标注"未搜索到补充信息,此部分依赖原文内容"。


yipian 操作反例黑名单(dim9 应用)

# 反模式 为什么不要做 替代做法
1 概览中编造板块 原文只有 2 个板块但拆成 5 个来显得分析深入,精读时发现内容不够会中断流程 板块拆解按原文实际结构来,宁少不多。实在想补的放在"信息缺口"里
2 超出原文范围做深度搜索补全 搜索补充太多外部信息会导致笔记偏离原文主题,变成"基于原文的衍生文"而非原文消化 搜索只补原文「提到但讲得不细」的内容,不补原文「根本没提」的新领域。补充的信息在笔记中标注「🔍 搜索补充」
3 HTML 注入外部 CDN/字体 离线打开时页面空白或样式崩坏,违背零外部依赖原则 所有 CSS/JS 内联,字体用系统默认字体栈(-apple-system, PingFang SC, Microsoft YaHei)
4 精读时跳过用户确认直接全输出 用户可能只想要某几个板块的精读,全输出浪费阅读时间 严格按 Step 3 流程:用户选了板块才精读,没选的不出
5 操作型 SOP 中的提示词写死产品名称 用户换工具就用不了 用 [工具名] 占位符,另起一行说明"可替换为其他同类工具"
6 存档时覆盖已有文件 之前的消化笔记丢失 同名文件追加 _v2/_v3 后缀,不覆盖。存档前告知用户文件位置
7 混合型文章只出单一格式 A+B 混合型只出了结构化笔记没出 SOP,用户少了实操价值 混合型产物 = 主格式 + 副格式特征合并。如 A+B = 结构化笔记中每板块附带 SOP 步骤
8 不用 Python 而在 Windows 上纠结 curl/PowerShell curl.exe 返回空结果、PowerShell 引号解析失败、中文文件名转义导致 MCP 下载耗时翻倍 所有 HTTP 请求和文件操作用 Python urllib.request 和 shutil 实现。中文文件名用 urllib.parse.quote() 编码。不要用 [System.Uri]::new()
9 精读覆盖清单不核对直接出产出 用户选了板块 2/3/4/5,但 AI 只写了 2/3/4 漏了 5,不自知 Step 4 组装前核对产出章节列表 vs 用户选择的板块列表,逐项打勾
10 搜索补充内容不标 🔍 标记 用户分不清哪些是原文内容、哪些是 AI 搜索补充的 所有搜索补充的内容前标注 🔍 搜索补充
11 输出的 MD/HTML 内容结构不一致 用户打开 MD 和 HTML 看到的信息不对应,产生困惑 写入前检查两者的板块/章节结构一致,关键数据和金句在两边都出现
12 手写 20 行 urllib 调 MCP / 手写清洗正则 / 手写 13KB HTML / 手写自检脚本 每次消化多花 5-10 分钟写代码,且内联 python 写中文/引号容易出错 一律调用附录中的固化脚本(A 下载 / B 清洗 / C 渲染 / D 自检)。先将代码写入临时 .py 文件再执行,不手写
13 存档位置与配置不一致 默认存 ~/yipian-notes/ 但用户可能希望存项目文件夹,返工移动 已改配置 local_path: &quot;&quot; → 默认存 yipian-output/(当前工作目录)。存档前先确认配置
14 确认次数过多 用户实际交互 = 发链接→选精读→直接全输出(接近 fast 模式),standard 模式多出 2-3 次确认 用户行为倾向快时,主动按 fast 模式节奏推进,把确认压缩到产出前一次

触发场景: Step 3 精读前、Step 4 组装前对照此表一次。任一反模式命中 → 调整方案重做。


确认节点一览(standard 模式 vs fast 模式)

类型 standard 模式 fast 模式
Type A 知识型 初检+概览 → 板块选择 → 精读 → 存档 初检+概览自动展示 → 板块一次性选完 → 精读自动连续 → 存档
Type B 操作型 初检+概览 → 事实核查结果 → 完成报告 初检+概览+事实核查自动完成 → 直接出完成报告
Type C 感悟型 初检+概览 → 框架确认 → 完成告知 初检+概览自动完成 → 框架+产出一起出
多篇模式 初检+概览 → 精读方式选择 → 输出格式 初检+概览+精读方式一次性选 → 全主题输出

附录:工具脚本

> 以下 4 个 Python 脚本供 Agent 执行时使用。运行前需安装 Python 3;render_html.py 额外需要 pip install markdown。将对应代码块内容保存为 .py 文件后执行。

附录 A:download_article.py(公众号文章下载)

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
&quot;&quot;&quot;download-mcp 下载公众号文章为 MD(处理中文文件名 URL 编码)。

用法: python download_article.py &lt;url&gt; [输出文件名]
默认输出文件名从 MCP 返回的 urls[0] 获取(含中文标题),保存到当前目录。
&quot;&quot;&quot;
import sys, os, json, urllib.request, urllib.parse

MCP_URL = &#x27;https://changfengbox.top/api/mcp&#x27;


def download(url, outpath=None):
    body = json.dumps({
        &#x27;jsonrpc&#x27;: &#x27;2.0&#x27;, &#x27;id&#x27;: 1, &#x27;method&#x27;: &#x27;tools/call&#x27;,
        &#x27;params&#x27;: {&#x27;name&#x27;: &#x27;wechat&#x27;, &#x27;arguments&#x27;: {&#x27;url&#x27;: url, &#x27;config&#x27;: {&#x27;MD&#x27;: True}}}
    }).encode()
    req = urllib.request.Request(MCP_URL,
                                 data=body,
                                 headers={&#x27;Content-Type&#x27;: &#x27;application/json&#x27;})
    resp = urllib.request.urlopen(req, timeout=60)
    data = json.loads(resp.read())
    txt = data[&#x27;result&#x27;][&#x27;content&#x27;][0][&#x27;text&#x27;]
    info = json.loads(txt)
    if info.get(&#x27;status&#x27;) != &#x27;completed&#x27;:
        raise RuntimeError(&#x27;MCP 状态异常: %s&#x27; % info.get(&#x27;status&#x27;))
    dl_url = info[&#x27;urls&#x27;][0]
    if outpath is None:
        outpath = os.path.basename(urllib.parse.unquote(urllib.parse.urlparse(dl_url).path))
    parsed = urllib.parse.urlparse(dl_url)
    encoded_path = urllib.parse.quote(parsed.path)
    final_url = parsed.scheme + &#x27;://&#x27; + parsed.netloc + encoded_path
    urllib.request.urlretrieve(final_url, outpath)
    print(&#x27;OK 已下载: %s (%d bytes)&#x27; % (outpath, os.path.getsize(outpath)))
    return outpath


if __name__ == &#x27;__main__&#x27;:
    if len(sys.argv) &lt; 2:
        print(&#x27;用法: python download_article.py &lt;url&gt; [输出文件名]&#x27;)
        sys.exit(1)
    url = sys.argv[1]
    out = sys.argv[2] if len(sys.argv) &gt; 2 else None
    try:
        download(url, out)
    except Exception as e:
        print(&#x27;FAIL: %s&#x27; % e)
        sys.exit(1)

附录 B:clean_article.py(原文清洗)

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
&quot;&quot;&quot;清洗公众号原文:去 UI 元素、压缩空行、保留正文结构。

用法: python clean_article.py &lt;输入md&gt; [输出md]
输出缺省时原地清洗。
&quot;&quot;&quot;
import re, sys

JUNK = [
    &#x27;在小说阅读器读本章\n\n去阅读\n\n&#x27;,
    &#x27;微信扫一扫可打开此内容&#x27;,
    &#x27;使用完整服务&#x27;,
    &#x27;关注该公众号&#x27;,
    &#x27;知道了&#x27;,
    &#x27;使用小程序&#x27;,
    &#x27;取消  允许&#x27;,
    &#x27;×  分析&#x27;,
    &#x27;作者头像&#x27;,
    &#x27;视频  小程序  赞&#x27;,
    &#x27;分享  留言  收藏  听过&#x27;,
]


def clean(text):
    for junk in JUNK:
        text = text.replace(junk, &#x27;&#x27;)
    idx = text.find(&#x27;预览时标签不可点&#x27;)
    if idx &gt; 0:
        text = text[:idx]
    text = re.sub(r&#x27;\n{3,}&#x27;, &#x27;\n\n&#x27;, text)
    text = re.sub(r&#x27;[ \t]+$&#x27;, &#x27;&#x27;, text, flags=re.M)
    return text


if __name__ == &#x27;__main__&#x27;:
    if len(sys.argv) &lt; 2:
        print(&#x27;用法: python clean_article.py &lt;输入md&gt; [输出md]&#x27;)
        sys.exit(1)
    inp = sys.argv[1]
    out = sys.argv[2] if len(sys.argv) &gt; 2 else inp
    with open(inp, encoding=&#x27;utf-8&#x27;) as f:
        content = f.read()
    cleaned = clean(content)
    with open(out, &#x27;w&#x27;, encoding=&#x27;utf-8&#x27;) as f:
        f.write(cleaned)
    print(&#x27;OK 清洗完成: %s (%d chars)&#x27; % (out, len(cleaned)))

附录 C:render_html.py(HTML 渲染)

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
&quot;&quot;&quot;把 yipian-笔记.md 渲染为零外部依赖的交互式 HTML。
用法: python render_html.py &lt;笔记md&gt; &lt;输出html&gt;
&quot;&quot;&quot;
import sys, os, re
import markdown


class IdGen:
    def __init__(self):
        self.n = 0
    def __call__(self, text, *args, **kwargs):
        self.n += 1
        return &#x27;sec-%d&#x27; % self.n


TEMPLATE = r&quot;&quot;&quot;&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;zh-CN&quot;&gt;
&lt;head&gt;
&lt;meta charset=&quot;UTF-8&quot;&gt;
&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
&lt;title&gt;{{TITLE}} — yipian 消化笔记&lt;/title&gt;
&lt;style&gt;
:root{--bg:#f7f7f5;--card:#fff;--text:#2b2b2b;--muted:#6b6b6b;--accent:#c0392b;--border:#e3e0da;--code-bg:#f0efec;--quote-bg:#faf6f4}
[data-theme=&quot;dark&quot;]{--bg:#17181a;--card:#202226;--text:#e4e4e4;--muted:#9a9a9a;--accent:#e8735c;--border:#34363c;--code-bg:#2a2c31;--quote-bg:#241d1b}
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:-apple-system,BlinkMacSystemFont,&quot;PingFang SC&quot;,&quot;Microsoft YaHei&quot;,sans-serif;background:var(--bg);color:var(--text);line-height:1.75;font-size:16px;transition:background .3s,color .3s}
.container{max-width:860px;margin:0 auto;padding:24px 20px 80px}
.topbar{display:flex;justify-content:space-between;align-items:center;max-width:860px;margin:0 auto;padding:18px 20px 0}
.brand{font-weight:700;font-size:15px;color:var(--accent)}
.theme-btn{border:1px solid var(--border);background:var(--card);color:var(--text);padding:6px 14px;border-radius:999px;cursor:pointer;font-size:13px}
header{margin:32px 0 28px}h1{font-size:26px;line-height:1.45;font-weight:800;margin-bottom:12px}
.meta{color:var(--muted);font-size:13.5px;line-height:1.8}.meta .m{display:inline-flex;align-items:center;gap:4px;margin-right:14px}
toc{background:var(--card);border:1px solid var(--border);border-radius:12px;padding:20px 24px;margin:24px 0}
toc h2{font-size:16px;margin-bottom:10px;color:var(--accent);border:none}
toc a{display:block;color:var(--text);text-decoration:none;font-size:14.5px;padding:4px 0}toc a:hover{color:var(--accent)}
section{margin:44px 0}
h2{font-size:21px;font-weight:800;padding-bottom:10px;margin-bottom:18px;border-bottom:2px solid var(--border)}
h3{font-size:17px;font-weight:700;margin:22px 0 10px;color:var(--accent)}
p{margin:10px 0}ul,ol{margin:10px 0 10px 24px}li{margin:5px 0}
blockquote{border-left:4px solid var(--accent);background:var(--quote-bg);padding:12px 18px;margin:14px 0;border-radius:0 8px 8px 0;font-size:15px}
pre{background:var(--code-bg);border:1px solid var(--border);border-radius:10px;padding:16px 18px;overflow-x:auto;font-size:13.5px;margin:14px 0;font-family:&quot;SF Mono&quot;,Consolas,&quot;Courier New&quot;,monospace;line-height:1.6}
code{font-family:&quot;SF Mono&quot;,Consolas,&quot;Courier New&quot;,monospace;font-size:.92em}pre code{background:none;padding:0}
table{width:100%;border-collapse:collapse;margin:16px 0;font-size:14px}
th,td{border:1px solid var(--border);padding:9px 12px;text-align:left}th{background:var(--card);font-weight:700}
details{border:1px solid var(--border);border-radius:10px;margin:14px 0;background:var(--card);overflow:hidden}
summary{padding:13px 18px;cursor:pointer;font-weight:700;font-size:15px;list-style:none;position:relative;padding-right:40px}
summary::after{content:&quot;+&quot;;position:absolute;right:18px;top:50%;transform:translateY(-50%);font-size:20px;color:var(--accent)}
details[open] summary::after{content:&quot;−&quot;}
details&gt;*:not(summary){margin-left:18px;margin-right:18px}details&gt;div,details&gt;p{padding-bottom:14px}
.imgph{border:1px dashed var(--border);border-radius:10px;padding:22px;text-align:center;color:var(--muted);font-size:13.5px;margin:16px 0;background:var(--card)}
footer{text-align:center;color:var(--muted);font-size:12.5px;margin-top:60px;padding-top:20px;border-top:1px solid var(--border)}
@media(max-width:640px){body{font-size:15px}h1{font-size:21px}h2{font-size:18px}.container{padding:16px 14px 60px}table{font-size:12.5px}th,td{padding:6px 8px}}
&lt;/style&gt;
&lt;/head&gt;
&lt;body data-theme=&quot;light&quot;&gt;
&lt;div class=&quot;topbar&quot;&gt;&lt;div class=&quot;brand&quot;&gt;📚 yipian 消化笔记&lt;/div&gt;&lt;button class=&quot;theme-btn&quot; id=&quot;themeToggle&quot;&gt;🌙 暗色模式&lt;/button&gt;&lt;/div&gt;
&lt;div class=&quot;container&quot;&gt;
&lt;header&gt;&lt;h1&gt;{{TITLE}}&lt;/h1&gt;&lt;div class=&quot;meta&quot;&gt;{{META}}&lt;/div&gt;&lt;/header&gt;
&lt;nav class=&quot;toc&quot;&gt;&lt;h2&gt;📑 目录&lt;/h2&gt;{{TOC}}&lt;/nav&gt;
{{CONTENT}}
&lt;footer&gt;由 yipian 消化生成 · {{TITLE}}&lt;/footer&gt;
&lt;/div&gt;
&lt;script&gt;
(function(){var t=document.getElementById(&#x27;themeToggle&#x27;),r=document.body;function a(v){r.setAttribute(&#x27;data-theme&#x27;,v);t.textContent=v===&#x27;dark&#x27;?&#x27;☀️ 亮色模式&#x27;:&#x27;🌙 暗色模式&#x27;;try{localStorage.setItem(&#x27;yipian-theme&#x27;,v)}catch(e){}}var s=null;try{s=localStorage.getItem(&#x27;yipian-theme&#x27;)}catch(e){}a(s===&#x27;dark&#x27;?&#x27;dark&#x27;:&#x27;light&#x27;);t.addEventListener(&#x27;click&#x27;,function(){a(r.getAttribute(&#x27;data-theme&#x27;)===&#x27;dark&#x27;?&#x27;light&#x27;:&#x27;dark&#x27;)})})();
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;&quot;&quot;&quot;


def parse_front(text):
    lines = text.split(&#x27;\n&#x27;)
    title = lines[0].lstrip(&#x27;#&#x27;).strip() if lines and lines[0].strip().startswith(&#x27;#&#x27;) else &#x27;&#x27;
    i = 1
    meta_items = []
    while i &lt; len(lines) and lines[i].strip().startswith&gt;&#x27;):
        raw = lines[i].strip().lstrip(&#x27;&gt;&#x27;).strip()
        if raw: meta_items.append(&#x27;&lt;span class=&quot;m&quot;&gt;%s&lt;/span&gt;&#x27; % raw)
        i += 1
    return title, &#x27;&lt;br&gt;&#x27;.join(meta_items), &#x27;\n&#x27;.join(lines[i:])


def wrap_h3_in_details(content):
    parts = re.split(r&#x27;(&lt;h3[^&gt;]*&gt;.*?&lt;/h3&gt;)&#x27;, content, flags=re.S)
    def to_summary(h3): return re.sub(r&#x27;&lt;h3[^&gt;]*&gt;(.*?)&lt;/h3&gt;&#x27;, r&#x27;&lt;summary&gt;\1&lt;/summary&gt;&#x27;, h3, flags=re.S)
    out, pending = [], None
    for part in parts:
        if re.match(r&#x27;&lt;h3&#x27;, part):
            if pending: out.append(&#x27;&lt;details&gt;%s%s&lt;/details&gt;&#x27; % (to_summary(pending[0]), pending[1]))
            pending = [part, &#x27;&#x27;]
        elif pending: pending[1] += part
        else: out.append(part)
    if pending: out.append(&#x27;&lt;details&gt;%s%s&lt;/details&gt;&#x27; % (to_summary(pending[0]), pending[1]))
    return &#x27;&#x27;.join(out)


def render(md_path, html_path):
    with open(md_path, encoding=&#x27;utf-8&#x27;) as f: text = f.read()
    title, meta_html, body = parse_front(text)
    md = markdown.Markdown(extensions=[&#x27;extra&#x27;, &#x27;toc&#x27;, &#x27;attr_list&#x27;, &#x27;sane_lists&#x27;], extension_configs={&#x27;toc&#x27;: {&#x27;slugify&#x27;: IdGen()}})
    content = wrap_h3_in_details(md.convert(body))
    content = re.sub(r&#x27;&lt;img[^&gt;]*alt=&quot;([^&quot;]*)&quot;[^&gt;]*/?&gt;&#x27;, lambda m: &#x27;&lt;div class=&quot;imgph&quot;&gt;📷 [图片] %s&lt;/div&gt;&#x27; % (m.group(1) or &#x27;&#x27;), content)
    content = re.sub(r&#x27;&lt;a href=&quot;[^&quot;]*&quot;[^&gt;]*&gt;(.*?)&lt;/a&gt;&#x27;, r&#x27;\1&#x27;, content, flags=re.S)
    toc_ul = &#x27;&#x27;
    m = re.search(r&#x27;&lt;ul&gt;.*?&lt;/ul&gt;&#x27;, md.toc, flags=re.S)
    if m: toc_ul = m.group(0)
    html = TEMPLATE.replace(&#x27;{{TITLE}}&#x27;, title).replace(&#x27;{{META}}&#x27;, meta_html).replace(&#x27;{{TOC}}&#x27;, toc_ul).replace(&#x27;{{CONTENT}}&#x27;, content)
    with open(html_path, &#x27;w&#x27;, encoding=&#x27;utf-8&#x27;) as f: f.write(html)
    print(&#x27;OK 渲染完成: %s (%d bytes)&#x27; % (html_path, os.path.getsize(html_path)))


if __name__ == &#x27;__main__&#x27;:
    if len(sys.argv) &lt; 3:
        print(&#x27;用法: python render_html.py &lt;笔记md&gt; &lt;输出html&gt;&#x27;)
        sys.exit(1)
    try: render(sys.argv[1], sys.argv[2])
    except Exception as e: print(&#x27;FAIL: %s&#x27; % e); sys.exit(1)

附录 D:check_output.py(产出自检)

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
&quot;&quot;&quot;yipian 产出自检:文件存在性、HTML 零外部依赖、MD/HTML 结构一致性、🔍 标记。
用法: python check_output.py &lt;产出目录&gt;
退出码: 0 = PASS, 1 = FAIL
&quot;&quot;&quot;
import os, re, sys

REQUIRED = [&#x27;原文.md&#x27;, &#x27;yipian-笔记.md&#x27;, &#x27;yipian-笔记.html&#x27;]


def strip_md(s):
    s = re.sub(r&#x27;\*\*|__|`|\*|_&#x27;, &#x27;&#x27;, s)
    s = re.sub(r&#x27;\[([^\]]*)\]\([^)]*\)&#x27;, &#x27;\1&#x27;, s)
    s = re.sub(r&#x27;&lt;[^&gt;]+&gt;&#x27;, &#x27;&#x27;, s)
    return re.sub(r&#x27;\s+&#x27;, &#x27;&#x27;, s)


def check(outdir):
    issues = []
    for name in REQUIRED:
        p = os.path.join(outdir, name)
        if not os.path.exists(p): issues.append(&#x27;文件缺失: %s&#x27; % name)
        elif os.path.getsize(p) == 0: issues.append(&#x27;文件为空: %s&#x27; % name)
    md_path = os.path.join(outdir, &#x27;yipian-笔记.md&#x27;)
    html_path = os.path.join(outdir, &#x27;yipian-笔记.html&#x27;)
    if not (os.path.exists(md_path) and os.path.exists(html_path)):
        print(&#x27;FAIL: 笔记文件缺失&#x27;); return issues
    md = open(md_path, encoding=&#x27;utf-8&#x27;).read()
    html = open(html_path, encoding=&#x27;utf-8&#x27;).read()
    ext = [u for u in re.findall(r&#x27;https?://[^\&#x27;\&#x27;\s&lt;&gt;]+&#x27;, html) if not u.startswith((&#x27;mailto:&#x27;, &#x27;tel:&#x27;))]
    if ext: issues.append(&#x27;HTML 含外部引用: %s&#x27; % ext[:5])
    md_h2 = re.findall(r&#x27;^## (.+)$&#x27;, md, re.M)
    html_h2_norm = [strip_md(re.sub(r&#x27;&lt;[^&gt;]+&gt;&#x27;, &#x27;&#x27;, h)) for h in re.findall(r&#x27;&lt;h2[^&gt;]*&gt;(.*?)&lt;/h2&gt;&#x27;, html, flags=re.S)]
    missing = [s for s in md_h2 if strip_md(s) and not any(strip_md(s) in h or h in strip_md(s) for h in html_h2_norm)]
    if missing: issues.append(&#x27;MD 章节在 HTML 缺失: %s&#x27; % missing)
    md_has, html_has = (&#x27;🔍&#x27; in md), (&#x27;🔍&#x27; in html)
    if md_has != html_has: issues.append(&#x27;🔍 标记不一致: MD=%s HTML=%s&#x27; % (md_has, html_has))
    return issues


if __name__ == &#x27;__main__&#x27;:
    if len(sys.argv) &lt; 2:
        print(&#x27;用法: python check_output.py &lt;产出目录&gt;&#x27;)
        sys.exit(1)
    issues = check(sys.argv[1])
    if issues:
        print(&#x27;FAIL:&#x27;)
        for i in issues: print(&#x27;  - %s&#x27; % i)
        sys.exit(1)
    print(&#x27;PASS 全部自检通过&#x27;)

常见问题

5 问
答案由本站依据条目字段整理
公众号文章结构化消化怎么安装?

在支持 Skills 的客户端里执行:skillhub install user_16bdbfca/yipian。安装方式为未标注。装完刷新技能列表或重开客户端。

公众号文章结构化消化支持哪些客户端?

资料没有标注宿主客户端,但只要客户端支持 Skills(Claude、Codex、Cursor 等),一般都能加载。

公众号文章结构化消化权限高吗,安全吗?

权限级别为未标注权限。安装前先看它会读取或写入哪些内容。

公众号文章结构化消化收费吗?

本站不代收费用,也没有标注具体价格。是否收费取决于技能本身和所用的模型或客户端,正式使用前按作者说明确认。

公众号文章结构化消化用什么许可证?

许可证标注为 未说明(许可证不明确)。商用或二次分发前请到仓库确认条款原文。

热门专题

按场景挑技能
全部专题 →

技能内容以作者仓库为准;分类、权限级别与许可证状态的中文说明为中文整理,最近核验 2026-11-06。