| name | extract-excerpt |
| description | Use this skill to fill the `excerpt` field in translated Chinese Markdown articles when the article had no subtitle. Trigger on requests like "提取 excerpt", "补 excerpt", "extract excerpt", "给文章补 excerpt", or a fully-spec'd handoff prompt with slugs + target_root. This is a **leaf executor**—it owns the semantic judgment of "which candidate paragraph is the real opening body text" but **does NOT decide paths**: target_root comes from the caller. If the caller didn't provide it, stop and ask. Does not translate, does not register results into site navigation, does not orchestrate batches. |
Extract-Excerpt 文章摘要执行器
为已翻译的中文 Markdown 文章填 frontmatter 的 excerpt 字段。
业务语义:excerpt 是文章的"开篇摘要",两个来源:
- 作者写了副标题 → medium-fetch / substack-fetch 在抓取时已写入;translate 透传并译成中文。这种情况本 skill 一看 excerpt 已存在 → 跳过。
- 作者没写副标题 → 上游不写 excerpt。本 skill 从正文里挑首段、语义判断后写入 excerpt 字段。
这是一个 leaf executor skill——只负责单篇文章的 excerpt 提取与写入,不决定路径、不决定日期、不做批量调度、不翻译、不接入站点导航。这些决策属于调用方(典型是 material-pipeline)。
机械的部分(跳过标题/图片/代码块等、收集候选段落、剥离行内标记、超长截断、frontmatter 的 YAML 转义与按位插入)由 helper 脚本 excerpt-prepare.js 完成。本 skill 唯一的职责是那个语义判断:在脚本给出的候选段落里,选出真正的"开篇正文"。
调用契约
调用方在 prompt 中必须显式提供:
| 字段 | 必填 | 含义 |
|---|
slugs | 是 | 要处理的 slug 列表(≥1 条;每个 slug 对应 <target_root>/<slug>.md) |
target_root | 是 | 文章根目录绝对路径 |
max_chars | 否 | excerpt 字符长度上限,默认 200 |
overwrite | 否 | frontmatter 已有 excerpt 时是否覆盖,默认 false(跳过) |
缺 slugs 或 target_root → 停下问清楚,不要凭记忆/历史路径填默认值。max_chars / overwrite 缺省时用默认值,无需追问。
适用场景
- 调用方给出明确的
slugs + target_root
- 为翻译流水线产出的译文补
excerpt(仅对没有副标题的文章生效)
- 为存量文章批量回填
excerpt
helper 脚本
本 skill 自带 scripts/excerpt-prepare.js(仅依赖 Node 内置模块,无需 npm install),两个模式:
node <skill 目录>/scripts/excerpt-prepare.js <file> --max-chars 200
node <skill 目录>/scripts/excerpt-prepare.js <file> --write --index <n> --max-chars 200
prepare 输出形如:
{
"frontmatter": { "present": true, "hasExcerpt": false },
"candidates": [
{ "index": 1, "kind": "italic", "chars": 66, "text": "...", "truncated": null },
{ "index": 2, "kind": "plain", "chars": 76, "text": "...", "truncated": null }
]
}
frontmatter.hasExcerpt:译文是否已有 excerpt 字段。
true → 已有副标题来的 excerpt,跳过(除非调用方传 overwrite)。
false → 作者没写副标题,需要走候选段流程。
kind:plain 普通段落 / italic 整段斜体(多为导语)/ quote blockquote
truncated:超过 max_chars 时脚本预先算好的截断文本,否则 null
- 脚本只做机械跳过、不替你下结论:导语、作者注、CTA 等都会作为候选列出来,由你判断
write 模式的写入位置:插在 translated 之后、tags 之前(与 translate-verify 的 expectedOrder 对齐:title / author / url / translated / excerpt / tags)。
语义判断:怎么选候选
从 candidates 里选出第一段真正的开篇正文,跳过下列"看起来在正文位置、实则不是正文"的候选:
- 导语 / teaser:通常是
kind: italic,整段斜体、内容是对全文的概述或副标题式引导。例:cloud-ant-colonies 的候选 1 是斜体导语,应选候选 2 的正文段落。
- 作者注 / 译者说明 / 免责声明:如"作者注:本文使用合成数据……"。
- 订阅 / 付费墙 CTA:如"被防火墙挡住了?免费阅读本文……"。
- 异常开篇:订阅卡片、广告、与正文无关的导航性文字。
保留为有效正文:
kind: quote 的 blockquote 算有效正文——有些文章开篇就是引用块形式的正文/译者引言。
- 普通的第一段叙述性文字。
找不到有效正文
candidates 为空,或全部是导语/CTA/作者注一类、没有任何真正正文段落 → 不写 excerpt,在完成回复里把该 slug 记为"无有效正文"。不要硬塞导语或图注当 excerpt。
工作流程
1. 校验调用契约
读 slugs / target_root(必填)+ max_chars / overwrite(可选)。必填项缺失或 target_root 非绝对路径 → 停下问清楚。不自行扫描 target_root 决定处理哪些文件——slug 由调用方给定。
2. 逐篇处理
对每个 slug,文件路径 = <target_root>/<slug>.md:
- 跑
node <skill 目录>/scripts/excerpt-prepare.js <file> --max-chars <max_chars>
- 看
frontmatter:
present: false → 跳过该篇,记为"无 frontmatter"
hasExcerpt: true 且 overwrite = false → 跳过该篇,记为"已有副标题 excerpt"
- 按「语义判断」从
candidates 选出开篇正文的 index;选不出 → 记为"无有效正文",跳过
- 跑
node <skill 目录>/scripts/excerpt-prepare.js <file> --write --index <选中 index> --max-chars <max_chars>(如需覆盖加 --overwrite)
- 脚本会自动取该候选(超长则用预算好的截断文本)、做 YAML 转义、按位插入或替换 frontmatter 的
excerpt 行
通常无需 Read 整篇文章——prepare 输出的候选清单已经够做语义判断。只有候选都不对、怀疑脚本误跳了正文时,才 Read 原文复核。
3. 自检
确认每篇 excerpt 已写入、是纯文本、长度 ≤ max_chars、正文未被改动(脚本只动 frontmatter)。
完成回复要求
- 每个 slug 的处理结果:✓ 已写入(附 excerpt 文本与字数)/ · 已有副标题 excerpt 跳过 / · 无 frontmatter 跳过 / ⚠ 无有效正文
- 汇总:成功 N 篇 / 跳过 M 篇 / 无内容 K 篇
这个 skill 不做的事
- 不决定路径 / 日期——
target_root 必须由调用方提供
- 不挑选文件——
slugs 由调用方给定,不自行扫目录
- 不翻译——只处理已是中文的文章
- 不改正文——只往 frontmatter 加
excerpt 一个字段
- 不动其它 frontmatter 字段——title / author / url / translated / tags 原样保留
- 不写
_meta.json / 不接入站点导航——excerpt 同步进站点 _meta.json 由 material-pipeline 负责:日更流水线在 publish 步骤自动写入,存量 backfill 用 sync-meta.js
- 不做批量调度——一次调用处理传入的 slug 列表;多 agent 并行由编排者决策
- 不修改 medium-sub / medium-fetch / translate / material-pipeline 的脚本(
scripts/excerpt-prepare.js 是本 skill 自带的 helper,可与本 skill 一起演进)