YAML — 递归地代表“YAML Ain't Markup Language” — 通过缩进而不是括号或大括号来表示数据。一切内容都基于三个基本元素:标量值(字符串、数字、布尔值)、序列(有序列表,用 - 标记)和映射(键值对,用 : 标记)。通过进一步缩进来嵌套内容。在某个键下使用两个空格缩进意味着“这属于该键”。这正是使 YAML 一目了然的原因,同时也是当某些内容出错时令人抓狂的地方。除了这些基础内容,YAML 还支持锚点(&name)和别名(*name)以重复使用配置块、用 --- 分隔的多文档文件,甚至还有自定义标签用于类型提示。大多数人从不使用高级功能,但仅锚点就可以避免在五个地方维护相同的配置块。
如果你在 AI 基础设施领域工作,YAML 是无法避免的。Hugging Face 模型卡片是 YAML 的前置内容,用于描述模型的许可证、数据集、指标和预期用途 — 这就是 Hub 知道你的模型是什么以及如何显示它的方法。启动你的推理服务器、向量数据库和监控堆栈的 Docker Compose 文件都是 YAML。部署和扩展模型服务 Pod 的 Kubernetes 清单也是 YAML。Hydra、MMEngine 和 Axolotl 等框架的训练配置也是 YAML。GitHub Actions、GitLab CI 和 CircleCI 中的 CI/CD 管道同样是 YAML。甚至 Prometheus 报警规则和 Ansible 剧本也使用 YAML。这种格式之所以在配置战争中胜出,不是因为它完美,而是因为它在你凌晨两点盯着 200 行部署规范时,是读起来最不痛苦的东西。
YAML 最臭名昭著的陷阱是其隐式类型转换。值 no 会被解析为布尔值 false。值 3.10 会变成浮点数 3.1,这在它表示 Python 版本时会引发问题。挪威的国家代码 NO 会变成布尔值 false — 这个问题被称作“挪威问题”,并在生产数据集中引发了真实错误。还有制表符与空格的问题:YAML 禁止使用制表符进行缩进,句号结束。一个 500 行文件中的制表符会导致整个文件解析失败,通常错误信息会指向错误的行。多行字符串则通过 |(字面块,保留换行符)、>(折叠块,合并行)及其带有 - 和 + 的变体来控制尾随换行符,这又增加了另一层混淆。大多数人会选择一种风格并记住它,而不是学习所有组合。
JSON 是严格、无歧义且普遍支持的 — 但由于大量的引号和大括号,手动编写起来很痛苦,而且不支持注释。YAML 是 JSON 的超集(有效的 JSON 也是有效的 YAML),它以可读性换取严格性:你可以获得注释、更简洁的语法和锚点,但也因此获得了隐式类型转换和对空白字符的敏感性。TOML 处于两者之间 — 显式类型、支持注释、无缩进敏感性 — 并且适用于像 pyproject.toml 和 Rust 的 Cargo.toml 这样的扁平或浅层配置。实用规则是:使用 JSON 进行机器间通信和 API 负载,使用 TOML 进行保持扁平的应用配置,使用 YAML 进行需要人类频繁阅读和编辑的深层嵌套配置。如果你的配置超过三层深度,YAML 可能是你的最佳选择。如果只有两层或更少,TOML 会帮你避免头痛。
你能做出的最棒投资就是使用 YAML 格式化检查工具。yamllint 不仅能捕获语法错误,还能发现风格问题,如不一致的缩进和尾随空格。在 CI 管道中运行它,以确保不良的 YAML 永远不会进入生产环境。IDE 插件 — Red Hat 为 VS Code 提供的 YAML 插件是标准 — 会提供实时验证、基于 JSON Schema 的自动补全以及当你的缩进偏离时的即时反馈。对于包含重复块的大型配置,学习锚点和别名:用 &defaults 定义一个默认配置,然后通过 <<: *defaults 合并到特定条目中,仅覆盖需要更改的部分。如果你在使用 Kubernetes 或 Docker Compose,像 kubeval 和 docker compose config 这样的工具会根据实际模式验证你的 YAML,捕捉通用检查工具无法发现的错误。当所有方法都失败时,记住你可以随时将 YAML 转换为 JSON,验证后再转换回来 — 因为最终它们代表的是相同的数据。