YAML Frontmatter:文件开头的"元数据层"
一、YAML Frontmatter 是什么
YAML 是什么
YAML 是一种数据格式,用来表示结构化数据,和 JSON、XML 同类。
它跟文件类型无关,可以写在 .yaml 文件里,也可以写在程序字符串里。
它靠缩进表示层级,用 键: 值 写字段 ,用 - 写列表:
name: 张三
age: 28
skills:
- Python
- Go
address:
city: 北京
同一份数据,换 JSON 写要括号、引号、逗号:
{
"name": "张三",
"age": 28,
"skills": ["Python", "Go"],
"address": {"city": "北京"}
}
换 XML 写要成对标签,更冗长:
<person>
<name>张三</name>
<age>28</age>
<skills>
<skill>Python</skill>
<skill>Go</skill>
</skills>
<address><city>北京</city></address>
</person>
三者表达的是同一份数据,只是写法不同——JSON 用括号引号、机器友好;XML 用成对标签、严格冗长;YAML 用缩进和短横线、最接近人写字,代价是对缩进极其敏感(多一个空格或混进 Tab 就报错)。
Frontmatter
Frontmatter 是一种位置约定:规定"文件开头的两行 --- 之间放元数据",不关心这段元数据用什么格式写。
YAML Frontmatter
YAML Frontmatter 就是两者合起来——用 YAML 格式写的 Frontmatter,是 Markdown 生态里最常见的叫法。
所以严格说,YAML 是格式,Frontmatter 是约定,YAML Frontmatter 是约定 + 格式的组合。你写的文件仍然是 .md,只是开头那段被约定为元数据:
---
key: value
---
正文内容
补充一点:Frontmatter 不一定非用 YAML。Hugo 就支持 YAML、TOML、JSON 三种写法,只是 YAML 是事实标准,所以大家默认说 "YAML Frontmatter"。
事实标准:没有官方组织强制规定,但大家用得多了、慢慢默认都这么做,于是它就成了标准。
二、它用在哪里
核心思路只有一句话:机器先读头部,快速知道这个文件是干嘛的;正文只在需要时才完整读。
AI 技能(SKILL.md)
头部决定这个技能能不能被用上:
---
name: pdf-processing
description: 提取 PDF 文本、填写表单。用户处理 PDF 时使用。
allowed-tools:
- Read
- Write
---
# PDF Processing
...
description最关键:工具先扫所有技能的描述,和用户请求匹配,匹配上了才加载正文。allowed-tools等字段用来控制权限和管理信息。
好处是:技能再多,也不用把所有正文都塞进 AI 的上下文,省空间。
AI 记忆(memory 文件)
头部用来给记忆分类、排优先级:
---
type: feedback
priority: high
created: 2026-10-20
---
用户偏好:单元测试用 pytest,测试文件放 tests/ 目录。
type、priority 让工具决定先注入哪条记忆、后注入哪条。
其他常见场景
- AI Agent 定义:Claude Code、GitHub Copilot、Gemini CLI 等都用"Markdown + YAML 头部"来定义 agent,头部配模型、工具等参数。
- Hugging Face 模型卡:头部记录
license、tags、datasets等,平台靠它分类和筛选模型。 - 静态网站:Hugo、Jekyll、Docusaurus 早就这么用了,头部写标题、日期、是否草稿,正文是文章。
- Ansible:用
.meta.yml文件定义参数规则。
看出规律了吗?头部给机器快速读取,正文给人或模型慢慢看。 这不是 AI 时代的新发明,而是 Markdown 圈用了很多年的老约定,AI 工具只是把它发扬光大了。
三、怎么解析
解析就是"把头部和正文切开",但要处理各种意外:
def parse_frontmatter(text: str) -> tuple[dict, str]:
if not text.startswith("---\n"):
return {}, text # 开头没有 ---,说明没头部
parts = text.split("---", 2)
if len(parts) < 3:
return {}, text # 格式不完整
try:
metadata = yaml.safe_load(parts[1]) or {}
except yaml.YAMLError:
return {}, text # YAML 写错了
if not isinstance(metadata, dict):
return {}, text # 解析出来不是字典
return metadata, parts[2].lstrip()
四个关键点:
- 开头必须是
---\n,否则直接当普通文本。 - 最多切三段:空串、头部、正文。
- 任何一步失败都降级:返回空头部 + 原文,保证正文永远能读。
- 结果必须是字典,防止 YAML 解析出字符串或列表。
四、新手容易踩的坑
- 缩进只能用空格,不能用 Tab——这是报错第一大原因。
- 普通字符串不用加引号,只有含
:、#等特殊字符时才加。 - 列表写成
- 项目换行缩进,不要写成[a, b]那种 JSON 风格。 - 布尔值写
true/false,建议全小写。
五、一句话总结
Frontmatter 就是文件开头的"信息头":机器先读它,快速判断这个文件是什么、要不要用;正文是真正的内容,按需加载。
头部写坏了也不影响正文——这个"降级"设计,就是它好用的关键。