ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Wasp 博客的 AI 内容流水线:notion-to-blog Skill 如何把 Notion 页面转成可构建的 MDX 博客

2026/9/13 17:21:32 拓冰建站 浏览量
Wasp 博客的 AI 内容流水线:notion-to-blog Skill 如何把 Notion 页面转成可构建的 MDX 博客 Wasp 博客的 AI 内容流水线:notion-to-blog Skill 如何把 Notion 页面转成可构建的 MDX 博客【免费下载链接】waspThe batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away complex full-stack features like auth, background jobs, RPC, email sending, end-to-end type safety, single-command deployment, and more.项目地址: https://gitcode.com/GitHub_Trending/wa/wasp这篇文章解析 Wasp 官方仓库中的notion-to-blogClaude Code Skill——一条输入一个 Notion 页面 URL,输出一篇符合 Docusaurus 博客构建规范的 MDX 文章的 Agent 自动化流水线。读完后,你将掌握该 Skill 的完整五步工作流(抓取、解析、图片优化、MDX 生成、人工确认)、Wasp 博客的 frontmatter 与截断标记约定,以及这些约定在 Docusaurus 配置 和 ImgWithCaption 组件 中的源码级落点。Skill 定位:一条可被 Agent 直接执行的五步流水线Skill 的完整定义位于 web/.claude/skills/notion-to-blog/SKILL.md,其 frontmatter 声明了三个元信息:--- name: notion-to-blog description: Transfer a blog post from Notion to the Wasp blog. Fetches content, downloads and optimizes images, and creates a properly formatted MDX file. argument-hint: [notion-page-url] ---argument-hint表明该 Skill 接受一个参数——Notion 页面 URL,输入输出边界非常清晰:输入是 Notion URL,输出是web/blog/目录下的一篇格式化 MDX 文章。这不是一个泛泛的提示词模板,而是一份对 Agent 的精确操作规程:每一步规定了要调用哪个 MCP 工具、写什么路径的文件、用什么命令行工具、以及产物必须满足什么格式约定。从仓库结构看,这个 Skill 是 Wasp 博客内容生产链的一环。web/blog/CLAUDE.md 中定义的博客工作流为:草稿规划 → 撰写 → 审阅(含 GEO 检查) → 通过crosspostingSkill 分发到 DEV.to/Medium → 通过social-contentSkill 生成社媒内容。notion-to-blog覆盖的是外部草稿(Notion)→ 本站 MDX这一入口环节,其产物必须满足博客构建系统的全部硬约束,因此文档中大量条款实际上是构建系统要求的翻译。后文会逐一给出这些要求对应的源码证据。Step 1:抓取 Notion 页面(注意分页)第一步只有一行核心逻辑,但包含一个容易踩坑的细节:从 URL 中提取页面 ID——末尾的带连字符 UUID(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx);调用mcp__notion__API-retrieve-a-page获取页面元数据(标题、属性);调用mcp__notion__API-get-block-children抓取全部内容块。Notion API 每页返回 100 个块,必须检查响应中的has_more字段,并用next_cursor继续翻页,直到取完。这里的工程要点是第三点。博客全文的内容块数量很容易超过 100(正文段落、代码块、图片、列表项都算块),如果 Agent 只取第一页,产出的 MDX 会在文章中段断尾且没有任何报错。Skill 文档把分页检查写成显式步骤,就是为了让 Agent 把是否取完当作一个可验证的终止条件,而不是默认一次调用即完整。Step 2:解析内容——边界识别与块类型到 Markdown 的完整映射这一步分两个子问题:先确定正文边界,再做块级转换。确定正文起止位置Notion 页面里常常混杂 WIP/工作笔记,真正的博文并不占满全页。Skill 给出的启发式规则是:不确定时向用户确认正文的起止位置;正文通常从第一个heading_1块开始;WIP 段落通常带有## WIP标题,或出现在文末附近的divider分隔符之后。即能用信号判断就判断,不能判断就问人——这是该 Skill 设计中人机协作原则的体现,而不是让 Agent 擅自裁剪内容。为什么必须用脚本解析文档明确要求:用 Bash 运行 Python 脚本解析 JSON 块数据,因为数据可能非常大(50K token)。原因很直接:如果把整页块 JSON 读进模型上下文再让模型逐块翻译成 Markdown,不仅 token 成本极高,还会在长文本中丢失格式细节。把确定性的转换逻辑(块类型 → Markdown 语法)交给脚本,模型只负责需要判断力的部分(正文边界、图片命名、frontmatter 撰写),这是典型的Agent 编排确定性工具的模式。块类型 → Markdown 映射表(完整继承自 Skill 文档)脚本需按 Notion 块的type字段逐一转换:Notion 块类型Markdown 输出heading_1/heading_2/heading_3#/##/###paragraph纯文本(保留行内格式)bulleted_list_item-numbered_list_item1.code带语言标注的围栏代码块image单独收集 URL 和 caption,不在正文原位输出quotecallout {emoji}divider---table_of_contents跳过(Docusaurus 自动处理 TOC)两个细节值得展开:富文本格式必须逐字段保留:粗体(**)、斜体(*)、行内代码(反引号)、删除线(~~)、链接(text)。Notion 的rich_text是数组结构,每个 run 带bold/italic/code/strikethrough布尔标志和可选的link对象,脚本需要对每个 run 做包裹式转换。table_of_contents块被显式跳过,注释给出的理由是 Docusaurus 会自己渲染目录。这类目标框架已具备的能力,源平台块就不搬运的判断,避免了成文后出现双目录。此外脚本要把所有图片 URL 与 caption 单独收集成一份清单,供 Step 3 的下载与 Step 4 的正文插图使用——图片不在转换阶段内联,这正是下载-重命名-压缩-回填三步流水线的前提。Step 3:图片下载与 WebP 优化图片处理被拆成五个动作,顺序固定:创建目录:web/static/img/post-slug/;用curl下载全部图片;根据上下文/caption 给图片起描述性文件名;用cwebp -q 85把全部图片转为 WebP;删除转换前的原文件。第 2 步有一句关键注释:Notion 的图片 URL 是临时签名的 S3 URL,必须立即下载。这类 URL 带有过期时间,若先完成全文转换再回头下载,链接可能已失效——所以解析完立即下载不是风格偏好,而是由 URL 的生命周期决定的硬约束。第 3 步的描述性命名决定了 frontmatter 中 banner 图和正文ImgWithCaption的source路径是否语义自明。以仓库中真实存在的一篇文章为例,web/static/img/buzz-wasp-one-message-app/目录下的文件即按prompt.webp、crm.webp、podcast.webp这类与内容强相关的名字组织,与 Skill 描述的命名要求一致。第 4 步的cwebp -q 85是质量 85 的有损压缩参数,配合 WebP 容器能显著降低博客静态站的图片体积;第 5 步删除原图则保证web/static/img/下不残留冗余文件。一个值得注意的下游联动:crossposting Skill 在把文章发到 Medium 时,专门有一段把.webp转回.jpg的逻辑,因为 Medium 不支持 WebP——本步骤的 WebP 化优化是针对本站静态站的,分发链路会按需回转。Step 4:生成 MDX 文件——frontmatter 与内容规则产物路径为web/blog/YYYY-MM-DD-slug.mdx,即发布日期 短横线 slug的文件名约定(仓库中如web/blog/2026-07-30-buzz-wasp-one-message-app.mdx均为此格式)。这一步的规范分两部分:frontmatter 字段约定,和正文内容规则。frontmatter 完整约定(继承自 Skill 文档)--- title: Post Title authors: [authorHandle] # from web/blog/authors.yml image: /img/post-slug/banner.webp tags: [wasp, tag2, tag3] # 3-6 lowercase tags keywords: [keyword1, keyword2, ...] # 10 for SEO description: SEO summary under 160 chars. ---逐字段说明(可结合仓库佐证):authors:取值必须是 web/blog/authors.yml 中已有的 handle,例如martinsos、vinny、sodic等。该文件为每位作者维护name/title/url/socials等信息,Docusaurus 据此渲染作者卡片;image:指向 Step 3 生成的目录下的banner.webp,是文章的分享横幅图;tags:3~6 个小写标签,首标签惯例为wasp;keywords:10 个以上的长尾词,服务于 SEO/GEO 检索;description:160 字符以内的 SEO 摘要。web/blog/CLAUDE.md 的Frontmatter一节给出了完全一致的字段约定(另提及可选的last_update.date字段用于重大更新),说明 SKILL 文档并非孤立规范,而是全站博客规范的执行副本。内容规则与源码级证据SKILL 文档规定正文的四条规则,每一条都能在仓库中找到对应的构建/渲染系统证据:规则一:frontmatter 之后必须import { ImgWithCaption } from ./components/ImgWithCaption;,所有图片必须用该组件渲染:ImgWithCaption source... caption... alt... /caption没有就省略该 prop;caption 内含双引号时,prop 值改用单引号:captiontext with quotes in it。对应组件 web/blog/components/ImgWithCaption.tsx 的实现印证了这些细节:source经useBaseUrl解析(所以写/img/...站点路径即可),caption渲染为斜体、半透明的figcaption;组件还提供framed模式(带 Wasp 黄底黑框的展示型图片)及justifyContent、margin等布局 prop。外层figure-container类名把图片间距接入文档/博客的垂直节奏系统——这也是 Skill 要求所有图片统一走该组件而非裸img的原因:图片间距、居中对齐、字幕样式全部由这一处收口。规则二:开头 hook 段落之后必须加{/* truncate */}注释(它控制博客列表页的文章摘要截断位置)。这条规则的强制性有直接的构建配置证据:web/docusaurus.config.ts 中 blog 插件配置了:blog: { id: blog, path: ./blog, routeBasePath: blog, // ... onUntruncatedBlogPosts: throw, // ... }onUntruncatedBlogPosts: throw意味着构建时若某篇文章缺少截断标记,Docusaurus 会直接抛错中断构建。所以{/* truncate */}不是排版建议,而是 CI 级别的硬校验——这正是它被写进 Skill 内容规则的第一位的原因。规则三:所有指向 wasp.sh / wasp-lang.dev 的 URL 必须相对化。具体转换:https://wasp.sh/blog/...→/blog/...,https://wasp.sh/docs/...→/docs/...,https://wasp-lang.dev/docs/...→/docs/...,且适用于全文中任何位置的 markdown 链接或其他引用。相对化后链接同时兼容wasp.sh与wasp-lang.dev两个域名的部署,并让 Docusaurus 的静态资源/路由解析正常生效。规则四:外部 URL(GitHub、Twitter 等)保持原样——即相对化只针对本站域名,不可全局无差别改写。Step 5:与用户确认(人工验收清单)流水线最后一步是向用户汇报,内容包含四项:生成的文件路径;下载图片的数量以及 WebP 转换的总节省体积;过程中做的决策(标题取舍、被跳过的段落等);提醒用户检查 TL;DR 区块中的锚点链接。第四步提醒尤其有实践价值:Notion 中手写的 TL;DR 段落若包含指向文内小节的锚点链接,经过脚本转换后锚点文本可能已被改写(比如标题大小写或标点变化),锚点会失效——这是自动化转换链路中残留的最后一类人为检查项,Skill 选择显式声明它而不是假装全自动。小结:这条流水线的设计模式回到 web/.claude/skills/notion-to-blog/SKILL.md 全文,它作为一份 Agent Skill 有几个可复用的设计特征:每步都有可验证的完成条件(分页的has_more、图片保存成功、文件落盘、人工确认),Agent 可以自检推进;确定性逻辑外置给脚本和 CLI(cwebp、Python 解析脚本),模型只做边界判断和文案撰写;格式规范与构建系统对齐——frontmatter 字段、ImgWithCaption组件、{/* truncate */}截断标记分别对应 authors.yml、ImgWithCaption.tsx 和onUntruncatedBlogPosts: throw这三处仓库事实,规范不是凭空约定,而是构建产物的要求;人机边界明确:正文起止不确定时问人,TL;DR 锚点留给人复查,其余全自动。如果你的站点同样采用 Docusaurus 博客,这套抓取 → 脚本化解析 → 资源落地 → 构建规范对齐的 MDX 生成 → 人工验收结构,以及用构建配置强制约定(如throw模式)而非口头规范的思路,可以直接借鉴到自己的内容管线中。【免费下载链接】waspThe batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away complex full-stack features like auth, background jobs, RPC, email sending, end-to-end type safety, single-command deployment, and more.项目地址: https://gitcode.com/GitHub_Trending/wa/wasp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考