数据STUDIO

我用 SKILL.md 做了一个简历生成器

Image

“你养龙虾了吗?”——这是2026年春天最流行的社交寒暄。一个月前,我也跟风在阿里云上花68块买了台轻量服务器,一键部署了OpenClaw。那只可爱的小龙虾在我的服务器里安了家,24小时在线,能操控电脑、执行命令、发消息。然后呢?然后我发现,我根本不知道怎么“教”它干活。

这大概是所有“养虾人”的共同困惑:OpenClaw给你的是一个空转的引擎,它有能力做任何事,但它不知道你要它做什么。有人用它自动抓取公告、盯盘选股,有人用它一键发文到微信公众号,有人用它处理政务民生服务。区别在哪?区别在于,那些真正用起来的人,都学会了一件事:给AI写“说明书”。

今天就带你亲手写一份“说明书”。我们要做一个Resume Forge Skill——把你的Markdown简历一键转成PDF。全程不用写复杂的Agent代码,只用SKILL.md,搭配OpenClaw的Skills机制。

为什么选“简历生成”这个场景?

因为:

  • 零外部依赖:不需要API Key、数据库、网络。任何人克隆下来就能跑。
  • 输出直观:丢进去一个Markdown,出来一个漂亮的PDF,立刻能看到效果。
  • 天然覆盖Skill所有特性:元数据、三个渐进式披露层级、脚本、参考资料、资产、验证循环、陷阱清单、模板——全部用上。

我们的目标:构建一个能在Claude Code、VS Code with Copilot、Cursor或任何支持agentskills.io规范的Agent中安装运行的完整Skill。

最终目录结构

resume-forge/
├── SKILL.md                           ← 核心:Skill的“大脑”
├── scripts/                           ← Level 3: 可执行代码
│   ├── parse_resume.py                ← Markdown → 结构化JSON
│   ├── validate_resume.py             ← 检查缺失内容、质量问题
│   └── generate_pdf.py                ← JSON + 模板 → PDF
├── references/                        ← Level 3: 按需加载
│   ├── ats-optimization.md            ← ATS关键词策略和格式规则
│   └── industry-styles.md             ← 不同行业的风格建议(科技、金融等)
└── assets/                            ← Level 3: 静态资源
    ├── template_modern.html           ← 现代风格模板
    ├── template_classic.html          ← 经典风格模板
    ├── template_compact.html          ← 紧凑风格模板
    ├── report_template.md             ← 质量报告模板
    └── sample_input.md                ← 示例简历(供测试)

每个文件都对应着Part 1中讲到的概念。我们一步步搭建。

Step 1: 定义前置元数据(Frontmatter)

这是Agent在Level 1看到的“身份证”。启动时嵌入系统提示词,只消耗约100个token。描述得好不好,直接决定了LLM会不会把相关请求路由到你的Skill。

---
name:resume-forge
description:>-
  Convert a Markdown resume into a professionally formatted PDF with multiple
  style options and ATS optimization. Use this skill when the user asks to
  create a resume, format a resume, convert a resume to PDF, build a CV,
  generate a professional resume, make a resume ATS-friendly, or restyle an
  existing resume. Triggers include: 'resume', 'CV', 'curriculum vitae',
  'resume PDF', 'format my resume', 'ATS-friendly resume', 'professional resume',
  or any mention of creating career documents from markdown or plain text.
  Do NOT use for: cover letters, job searching, interview preparation,
  LinkedIn profile optimization, or portfolio websites.
license:Apache-2.0
compatibility:RequiresPython3.10+andweasyprint.Nonetworkaccessneeded.
metadata:
author:abvijaykumar
version:"1.0.0"
last-updated:"2026-03-17"
category:document-generation
tags:"resume, pdf, career, formatting"
allowed-tools:Bash(python3:*)Bash(pip:*)ReadWrite
---

逐项解读:

  • name:规范要求1-64字符,只能小写字母、数字和连字符。必须和目录名一致。这里用resume-forge,清晰明了。
  • description:这是最关键的字段。因为没有算法路由,LLM靠它决定是否激活Skill。所以描述必须做好三件事:
    1. 说明做什么:“Convert a Markdown resume into a professionally formatted PDF”
    2. 列出触发词:resume, CV, resume PDF, ATS-friendly resume……
    3. 划出负面边界:“Do NOT use for cover letters, job searching...”
      负面边界能防止“帮我准备面试”这类无关请求误触发Skill。
  • license:既是法律声明,也是信任信号。Apache-2.0表示开源、允许商用,别人用起来放心。
  • compatibility:告诉Agent环境需求。如果机器没有Python 3.10或WeasyPrint,Agent能提前知道。“No network access needed”是额外的安全加分项。
  • metadata:规范没定义内容,自由键值对。我用它来版本管理、作者、分类。由于规范没有内置version字段,这是推荐的版本追踪方式(配合Git)。
  • allowed-tools:声明Skill需要的工具权限。这里只有Python脚本、pip安装包、读写文件。如果某个简历Skill突然要curl或网络访问,就是危险信号。

至此,前置元数据用了规范里的所有字段,且每个都有实际意义。

Step 2: 编写SKILL.md正文

正文是Level 2——当LLM判断Skill相关时加载的内容。它是Agent遵循的完整指令。规范建议控制在500行以内、5000 token以内。我们这篇正文约186行,~1350 token,很安全。

下面提取几个关键模式。

模式1:清单式工作流

## Workflow
Progress:
- [ ] Step 1: Obtain the Markdown resume
- [ ] Step 2: Parse into structured JSON (`scripts/parse_resume.py`)
- [ ] Step 3: Validate structure and content (`scripts/validate_resume.py`)
- [ ] Step 4: Fix any validation issues (loop until clean)
- [ ] Step 5: Select style and generate PDF (`scripts/generate_pdf.py`)
- [ ] Step 6: Final review and delivery

这个清单给Agent清晰的进度感:它知道自己在哪里、下一步做什么、哪些已完成。每一步都指向具体脚本,Agent不用自己摸索“怎么做”。

模式2:验证循环

### Step 4: Fix and Re-validate
If validation reports issues:
1. Read the specific error messages — they explain what's wrong and how to fix it
2. Edit `resume.md` to address the issues
3. Re-run Step 2 (parse) and Step 3 (validate)
4. **Only proceed to Step 5 when validation passes with zero errors**

这是“执行-验证-修复-重复”的经典模式。Agent不通过验证就不进入下一步。验证器的错误消息设计成自解释的,让Agent能自我纠正。

模式3:计划-验证-执行

整体流程是:解析简历(计划)→ 验证结构(验证)→ 生成PDF(执行)。中间的parsed.json就是“计划”——一个结构化表示,可以在生成PDF之前先按规则校验,防止垃圾进垃圾出。

模式4:条件文件加载(渐进式披露Level 3)

For ATS-specific optimization, read
[references/ats-optimization.md](references/ats-optimization.md)
before generating — it contains keyword placement strategies and formatting
rules that affect how ATS parsers score the resume.

For industry-specific styling guidance, read
[references/industry-styles.md](references/industry-styles.md)
only if the user specifies a target industry.

这就是渐进式披露在行动。参考文件不在启动时加载,也不在Skill激活时加载,只在具体任务需要时才加载。一个只想“把我的简历转成PDF”的用户,永远不会为ATS指南付出token成本。但说“为科技岗位做一份ATS友好的简历”时,两个文件才会被按需加载。

模式5:“Gotchas”陷阱清单

## Gotchas
- **WeasyPrint font rendering**: WeasyPrint uses system fonts. If a template
  specifies a font not installed on the system, it silently falls back to
  a default serif font.
- **Page overflow**: Long resumes may push content onto a second page with
  awkward breaks mid-section.
- **Special characters in names**: Accented characters (é, ñ, ü) work fine
  in the PDF but may break some ATS parsers.
- **Markdown parsing edge cases**: The parser expects `##` for sections and
`###` for sub-entries. Using `**bold text**` as a section header will cause
  the parser to miss that section entirely.

陷阱清单是Skill里价值最高的内容之一。这些都是Agent不被告知就会踩的坑。每个陷阱都说明症状、原因、隐含的解决方法。Agent不需要通过试错去发现,直接获得“前人智慧”。

Step 3: 编写脚本(scripts/)

脚本是干活的主力。它们通过bash运行,只有输出进入Agent的上下文,脚本代码本身不加载,省token。

scripts/parse_resume.py —— 解析器

将Markdown转成结构化JSON。几个设计亮点:

内联依赖(PEP 723)

# /// script
# dependencies = [
#   "markdown-it-py>=3.0",
# ]
# ///

用uv run scripts/parse_resume.py就能自动安装依赖,不需要requirements.txt,不需要虚拟环境。Agent一个命令就跑起来。

结构化JSON输出

{
"status": "success",
"name": "Alex Chen",
"sections_found": ["Summary", "Experience", "Education", "Skills", "Certifications"],
"total_entries": 6
}

结构化输出比自然语言好解析得多。Agent可以程序化检查sections_found,而不是去理解一段文本。

友好的错误信息

Error: File not found: resume.md
       Check the path and try again.

错误信息直接告诉Agent该怎么做。同时提供--help让Agent能发现接口:

python3 scripts/parse_resume.py --input resume.md --output parsed.json
python3 scripts/parse_resume.py --input resume.md  # 输出到stdout

scripts/validate_resume.py —— 质量门禁

验证器让“计划-验证-执行”模式落到实处。下面是一个损坏简历的验证输出示例:

{
"status": "fail",
"errors": [
    {
"field": "name",
"message": "Missing name. Add an H1 heading (# Your Name) at the top of the Markdown."
    },
    {
"field": "contact.email",
"message": "Missing email address. Add it below your name: **Email:** [email protected]"
    },
    {
"field": "sections",
"message": "Missing required section: Skills. Add ## Skills to your Markdown."
    },
    {
"field": "sections.Experience",
"message": "Section \"Experience\" is empty. Add content or remove the heading."
    }
  ],
"warnings": [
    {
"field": "contact.phone",
"message": "No phone number found. Recommended for most job applications."
    },
    {
"field": "sections",
"message": "Consider adding a Summary section to strengthen your resume."
    }
  ],
"summary": {
"error_count": 5,
"warning_count": 5
  }
}

注意几个点:

  • 错误 vs 警告:错误会阻止流程(退出码3),警告不会(退出码0)。正文说“只有零错误才能继续”。
  • 每条错误都包含修复建议:不只是“缺少姓名”,而是“添加H1标题 (# 你的姓名)”。Agent能直接执行,不需要再问用户。
  • 退出码有意义:0=通过,1=文件错误,3=验证失败。不同失败类型用不同码,便于脚本编排。

--strict 开关可以对每个项目符号检查动词(例如要求用“Led”“Developed”等行为动词开头)。严格程度可调节。

scripts/generate_pdf.py —— 渲染器

接收JSON和样式名,加载对应的HTML模板,用Jinja2渲染,再用WeasyPrint转PDF。

$ python3 scripts/generate_pdf.py --input parsed.json --style modern --output resume.pdf
{
"status": "success",
"output": "resume.pdf",
"style": "modern",
"pages": 1,
"overflow_warning": false
}

设计要点:

  • 三种样式:modern, classic, compact,分别对应assets/下的三个HTML模板。
  • 页面溢出检测:脚本会检查PDF是否超过推荐页数,发出警告但不失败。长简历是有效的,但Agent需要知道。
  • ATS可提取性:PDF由语义HTML生成,不是图片或Canvas绘图。像pdfplumber这样的工具能提取每一个单词,这对ATS兼容性至关重要。

Step 4: 定义参考资料(references/)

参考资料是Level 3,存放在文件系统,零token成本,只在需要时加载,提供特定场景的深度知识。

references/ats-optimization.md

只在用户明确要求“ATS友好”时加载。内容包括:

  • 主流ATS(Workday、Greenhouse、Lever、iCIMS)的工作原理
  • 格式规则(禁用表格、多栏,用标准标题)
  • 关键词策略(镜像职位描述,包含缩写和全称)
  • 章节排序建议
  • 可靠 vs 易出错的日期格式

没有这个文件,Agent只能给出泛泛的通用建议。有了它,Agent就有具体可操作的规则。

references/industry-styles.md

只在用户说“我想申请金融岗位”之类时加载。针对不同行业给出风格建议:

  • 科技:现代风格,技能优先,包含GitHub链接
  • 金融:经典风格,教育优先,严格一页
  • 学术界:经典风格,无页数限制,出版物列表
  • 设计:现代风格,突出作品集链接
  • 医疗:经典风格,执照和证书优先
  • 政府:经典风格,安全许可(如有)

Agent只读相关部分。瞄准科技行业的用户永远不会加载医疗行业的内容。

Step 5: 定义资产(assets/)

资产是Skill使用的原材料。

HTML+CSS模板

每个模板是一个自包含的HTML文件,内嵌CSS。生成器用Jinja2注入数据,WeasyPrint转PDF。以下是从现代模板截取的CSS片段:

.section-title {
font-size: 11pt;
font-weight: 700;
color: #1a1a2e;
text-transform: uppercase;
letter-spacing: 1.2pt;
border-bottom: 1.5pt solid #2563eb;
padding-bottom: 3pt;
margin: 14pt08pt0;
}

以及Jinja2模板标记:

{% for section in resume.sections %}
<div class="section">
  <div class="section-title">{{ section.title }}</div>
  {% for entry in section.entries %}
  <div class="entry">
    <div class="entry-header">
      <span class="entry-title">{{ entry.title }}</span>
      {% if entry.organization %}
        <span class="entry-org"> — {{ entry.organization }}</span>
      {% endif %}
      {% if entry.dates %}
        <span class="entry-dates">{{ entry.dates }}</span>
      {% endif %}
    </div>
  </div>
  {% endfor %}
</div>
{% endfor %}

三个模板,三种风格,全从同一份JSON数据生成。

assets/sample_input.md

一个完整的示例简历,方便用户立刻测试:

# Alex Chen
**Email:** [email protected] | **Phone:** 555-0142 | **Location:** San Francisco, CA
https://github.com/alexchen | https://linkedin.com/in/alexchen

## Summary
Full-stack engineer with 6 years of experience...

## Experience
### Senior Software Engineer — Acme Cloud Inc (2022–Present)
- Led migration of monolithic Node.js application to microservices...

端到端运行演示

现在我们用示例文件跑通整个流程。

解析

$ python3 scripts/parse_resume.py --input assets/sample_input.md --output parsed.json

输出:

{"status": "success", "name": "Alex Chen", "sections_found": ["Summary", "Experience", "Education", "Skills", "Certifications"], "total_entries": 6}

5个章节,6个条目被提取出来。解析器找到了姓名、联系方式,并结构化每个经历项(标题、组织、日期、项目符号)。

验证

$ python3 scripts/validate_resume.py parsed.json

输出:

{
"status": "pass",
"errors": [],
"warnings": [
    {"field": "sections", "message": "Consider adding a Projects section to strengthen your resume."}
  ],
"summary": {"error_count": 0, "warning_count": 1, "sections_found": ["Summary", "Experience", "Education", "Skills", "Certifications"], "total_entries": 6, "total_bullets": 11}
}

零错误,一个警告(建议添加Projects章节,但不阻止生成)。退出码0,可以继续。

生成

$ python3 scripts/generate_pdf.py --input parsed.json --style modern --output resume.pdf

输出:

{"status": "success", "output": "resume.pdf", "style": "modern", "pages": 1, "overflow_warning": false}

一页,无溢出。PDF是干净、专业的现代风格简历。

也可以生成另外两种样式:

$ python3 scripts/generate_pdf.py --input parsed.json --style classic --output resume_classic.pdf
$ python3 scripts/generate_pdf.py --input parsed.json --style compact --output resume_compact.pdf

全部生成有效、一页、ATS可提取的PDF。

验证ATS可提取性

import pdfplumber
with pdfplumber.open('resume.pdf') as pdf:
for page in pdf.pages:
        print(page.extract_text())

每个单词都能正确提取,没有乱码、没有缺漏章节。ATS系统读取无障碍。

渐进式披露计分卡

我们把构建的Skill映射到三个Level:

Level
内容
Token成本
何时加载
Level 1
前置元数据 (name, description)
~100
启动时
Level 2
SKILL.md 正文
~1,350
Skill激活时
Level 3
scripts/, references/, assets/
可变,按需
仅当需要时

如果用户安装了50个Skill,启动时开销为 50 × ~100 = ~5,000 token。只有用户说“格式化我的简历”时,~1,350 token的正文才加载。ATS指南、行业风格、模板只在任务需要时才加载。

如果我把所有内容(ATS规则、行业指南、三个模板、陷阱清单)都塞进一个SKILL.md,它会有3000多行,轻松超过任何合理的token预算。渐进式披露让这一切变得可控。

功能检查清单

下面这张表对照了SKILL.md规范的每个特性,并标注我们在Resume Forge中是否用到:

特性
是否使用
说明
name
✅
resume-forge
description
✅
包含触发词和负面边界
license
✅
Apache-2.0
compatibility
✅
Python 3.10+, weasyprint
metadata
✅
版本、作者、标签等
allowed-tools
✅
声明权限
清单式工作流
✅
Step 1-6
验证循环
✅
Step 4 循环
计划-验证-执行
✅
解析→验证→生成
条件文件加载
✅
ATS和行业指南按需
Gotchas章节
✅
列出常见陷阱
脚本
✅
三个Python脚本
参考资料
✅
ats-optimization.md, industry-styles.md
资产
✅
三个HTML模板,示例文件
帮助文档(--help)
✅
每个脚本支持
错误码
✅
解析器、验证器用退出码区分状态

这就是一个覆盖了规范所有特性的完整Skill。

安装Skill

Claude Code

# 项目级(推荐)
cp -r resume-forge/ .claude/skills/resume-forge/
# 或全局(所有项目可用)
cp -r resume-forge/ ~/.claude/skills/resume-forge/

由于实时文件监听,Claude Code会立刻发现新Skill。

VS Code with GitHub Copilot

cp -r resume-forge/ .github/skills/resume-forge/

Claude.ai

打包成zip,通过“设置 → 功能”上传:

cd resume-forge && zip -r ../resume-forge.zip . && cd ..

调试技巧

  • Skill没触发:直接问Agent:“你用了哪个Skill?”或“你用resume-forge Skill了吗?”如果没触发,可能是描述中缺触发词,添加上用户使用的短语。
  • 解析器漏了章节:检查标题层级。解析器期望##作为章节标题,###作为条目。用**加粗文本**当标题是无效的。
  • PDF显示不对:检查系统字体。WeasyPrint使用操作系统字体,如果模板指定的字体不存在,会静默回退到serif。
  • 验证太严/太松:用--strict开启全面检查,默认模式是实用检查。可以修改validate_resume.py里的ACTION_VERBS集合来适配领域。

一些hack

在开始写Skill之前,可以先用这些工具加速开发。

Hack #1:用Skill Creator来构建Skill

Anthropic官方Skills仓库里有一个元Skill叫skill-creator。它是一个专门创建其他Skill的Skill。

安装它:

cp -r skill-creator/ .claude/skills/skill-creator/

现在你对Claude Code说:“我想建一个Skill,把Markdown简历转成PDF。”skill-creator就会激活,带你走完整流程:它会采访你关于用例、写草稿SKILL.md、生成测试提示词、并行运行有/无Skill的测试、启动基于浏览器的评测视图让你对比输出、再根据你的反馈迭代。循环是:草稿→测试→评审→改进→重复。

它还包含一个描述优化器,会测试20多个触发查询,测量触发准确率,然后重写描述来最大化命中率、最小化误报。

最有用的功能:它会生成“基线运行”(无Skill)和“启用Skill的运行”,让你看到Skill真正增加了什么价值。还会记录每次运行的耗时和token消耗,让你知道Skill的真实成本。

用这个工具,构建Resume Forge的时间能砍半。

Hack #2:发布前验证 —— 用skills-ref

官方agentskills/agentskills仓库里有一个参考库skills-ref,可以验证Skill是否符合规范。

安装:

pip install -e path/to/agentskills/skills-ref

然后验证:

skills-ref validate path/to/resume-forge

它会检查前置元数据字段、目录命名规范、结构。捕获你在运行时才会发现的错误:name字段不匹配、缺少description、目录名和Skill名不一致等。

它还提供to-prompt命令,生成Agent用来发现Skill的<available_skills>XML块。这让你能预览Agent在Level 1看到的元数据——那个决定Skill是否被激活的内容。

Hack #3:从模板开始,别从零开始

Anthropic Skills仓库包含一个template/目录,里面是最小化的SKILL.md骨架。就一个name、一个description占位符、一个正文占位符。这正是关键——别对着空文件发呆想前置元数据语法,克隆模板填空就行。

cp -r template/ .claude/skills/my-new-skill/

编辑name字段匹配目录名,写你的描述,就可以开工。这消除了记住YAML格式的摩擦,保证你不会漏掉必填字段。

浏览示例Skill

在开始自己构建前,先看看官方仓库里的示例:github.com/anthropics/skills/tree/main/skills

里面有生成PDF、构建DOCX、创建PowerPoint演示文稿、设计Canvas布局、构建MCP服务器等现成Skill。即便没有一个完全匹配你的用例,它们也是学习如何组织脚本、参考资料、渐进式披露的绝佳参考。特别是docx和pdf这两个Skill,如果你和我们一样在做文档生成,值得深入研读。

核心回顾

  1. 原理:SKILL.md通过前置元数据(Level 1)、正文(Level 2)和按需加载的资源(Level 3)实现渐进式披露,让Skill既强大又轻量。LLM根据描述进行路由,无需手写路由逻辑。
  2. 实践:构建一个高质量Skill,关键在于写好description(触发词+负面边界),用清单式工作流、验证循环、计划-验证-执行模式组织正文,并合理划分脚本、参考资料和资产。
  3. 避坑:调试Skill时,先检查描述是否包含用户实际使用的词汇;遇到触发不准就加负面边界;正文中一定要有“Gotchas”部分,把隐式知识显式化。

写在最后

这个Skill我们从头构建,每一行代码、每一个文件都对应着规范中的某个设计。但它的意义不只是“能运行”,而是演示了如何把人类专家的经验——简历怎么写、ATS怎么优化、行业偏好是什么——封装进一个文件夹里,让AI去自动执行。

最让我感慨的是,构建这样一个功能完整的Skill,我们几乎没有写复杂的Agent逻辑,只是写了一个“说明书”和一些确定性脚本。AI负责判断什么时候该用、怎么按步骤执行,而我们把领域知识沉淀下来,一次编写,到处运行。

现在,你手头是不是也有那种“每次都要手动指导AI做”的重复性任务?把它封装成一个SKILL.md,会怎样?

在你的日常工作中,哪个任务最适合封装成Skill?是部署流程、代码审查清单、还是某种文档生成?你会怎么设计它的渐进式披露层级?欢迎在评论区分享你的想法,我们一起探讨。

🏴‍☠️宝藏级🏴‍☠️ 原创公众号『数据STUDIO』内容超级硬核。公众号以Python为核心语言,垂直于数据科学领域,包括可戳👉Python|MySQL|数据分析|数据可视化|机器学习与数据挖掘|爬虫等,从入门到进阶!

长按👇关注- 数据STUDIO -设为星标,干货速递ImageImage