1. 为什么第一个 Agent Skill 必须从 SKILL.md 开始写?——不是格式问题,是思维锚点
“把重复任务写成第一个 Agent Skill:SKILL.md 怎么设计?”这个标题里藏着一个被多数人忽略的关键真相:SKILL.md 不是文档,而是 Agent 的最小可执行契约。我带过二十多个跨行业 Agent 项目,从电商客服自动归因、金融风控规则编排,到高校教务系统课表冲突检测,所有跑通的团队,第一份真正落地的产出,都不是 prompt、不是 workflow、更不是 config.yaml,而是一份命名规整、结构清晰、能被任意 agent 框架直接加载的SKILL.md。它之所以必须是.md,不是因为 Markdown 多好读,而是因为它天然具备三个不可替代的工程属性:人类可编辑、机器可解析、版本可追溯。你打开一个 Excel 表格改三行数据,Git diff 显示的是二进制乱码;但你在 SKILL.md 里加一句# 适用场景:订单超时未支付自动催付,Git commit 记录清清楚楚,PR review 时同事一眼就能判断这个 skill 是否越权。这背后其实是 agent 开发中一个残酷现实:90% 的失败不来自模型能力不足,而源于 skill 边界模糊、责任不清、变更失控。我亲眼见过一个团队花两周调优 LLM 输出格式,结果上线后发现,真正卡住流程的是某个 skill 在处理“用户地址含生僻字”时,没定义 fallback 逻辑,导致整个 agent 执行链在第三步就静默终止——而这个问题,本该在写 SKILL.md 的第一版就用## 异常兜底小节写死。所以,当你问“怎么设计”,本质是在问:如何用最轻量的文本,把一个活生生的业务动作,压缩成 agent 能理解、能复用、能审计的原子单元。它不是说明书,是接口协议;不是笔记,是服务契约;不是学习材料,是生产环境里的第一块路标。你写的不是 markdown,是你和 agent 之间的第一份劳动合同。
2. SKILL.md 的骨架不是模板,是四层责任切片
很多新手一上来就找“标准模板”,抄个 header、description、input/output 就完事。结果写出来的文件,agent 加载报错,人看半天不懂,测试用例写不出来。问题出在没理解 SKILL.md 的核心设计哲学:它必须同时满足四类角色的阅读需求,且每类角色只关心其中一层。我把这四层叫“责任切片”,它们像洋葱一样层层嵌套,缺一不可,但每一层的写法、粒度、术语都完全不同。下面拆解真实项目中反复验证过的结构逻辑,不是教条,是血泪教训换来的分层指南。
2.1 第一层:机器可识别的元数据区(YAML Front Matter)
这是 agent 框架启动时最先扫描的部分,必须严格遵循 YAML 语法,且字段名不能随意发挥。我见过最典型的错误,是把version: "1.2"写成version: 1.2,导致框架解析为浮点数而非字符串,后续做语义版本比对直接失败。正确写法如下:
--- id: order_timeout_reminder name: 订单超时未支付自动催付 version: "1.3" category: customer_service tags: [payment, reminder, timeout] author: zhangsan@company.com created_at: "2024-06-15" updated_at: "2024-07-22" status: production ---提示:
id字段是全局唯一标识,必须小写+下划线,禁止空格和中文,它是 agent 内部路由、日志追踪、权限控制的根 ID;status只能是draft/review/production/deprecated四种,框架会根据此值决定是否允许被 workflow 调用;tags是未来做 skill 检索、智能推荐的基础,别偷懒只写一个,比如payment和reminder应该并存,因为运营同学可能搜“催付”,技术同学可能搜“支付”。
2.2 第二层:人类可理解的业务契约区(H2 标题 + 自然语言)
这一层是给产品经理、业务方、测试工程师看的,他们不关心代码,只关心“这玩意到底干啥、在啥情况下干、干不好会怎样”。必须用完整句子,禁用任何缩写或内部黑话。例如,不要写“触发催付”,要写“当订单创建超过 30 分钟且支付状态仍为‘待支付’时,向用户发送包含订单号、剩余支付时限、一键跳转链接的短信提醒”。我坚持要求团队在这个区域写满三句话:第一句说清楚触发条件(时间、状态、数据源),第二句说清楚执行动作(发什么、给谁、含哪些关键字段),第三句说清楚成功标准(用户收到短信、短信内链接可点击、跳转后页面显示正确订单详情)。这三句话,就是后续所有自动化测试用例的原始输入。曾经有个项目,因为这里只写了“通知用户”,测试同学按“站内信”设计用例,结果上线后实际走的是短信通道,导致漏测了运营商网关超时场景,凌晨三点告警炸群。
2.3 第三层:机器可执行的接口契约区(H2 标题 + 结构化 Schema)
这一层是给开发、SRE、LLM Router 看的,它定义了 skill 如何被调用、输入输出长什么样、每个字段的约束是什么。这里最容易犯的错,是把 JSON Schema 当作文档写,堆砌一堆type: string,required: [...],却忘了加最关键的example和description。没有 example 的 schema,等于没写。正确示范:
## 接口定义 ### 输入参数(Input) | 字段名 | 类型 | 必填 | 描述 | 示例 | |--------|------|------|------|------| | `order_id` | string | 是 | 电商平台唯一订单号,长度 16-32 位,仅含数字与字母 | `"ORD20240722A8B9C0D1"` | | `user_phone` | string | 是 | 用户手机号,11 位纯数字,需通过运营商号段校验 | `"13800138000"` | | `timeout_minutes` | integer | 否 | 自定义超时时长(分钟),默认 30,范围 5-120 | `45` | ### 输出结果(Output) | 字段名 | 类型 | 描述 | 示例 | |--------|------|------|------| | `sms_sent` | boolean | 短信是否成功发出 | `true` | | `sms_id` | string | 短信网关返回的唯一回执 ID | `"SM1122334455667788"` | | `error_code` | string | 错误码,仅当 `sms_sent=false` 时存在 | `"INVALID_PHONE"` |注意:
error_code的枚举值必须在此处穷举,如INVALID_PHONE、SMS_QUOTA_EXCEEDED、GATEWAY_TIMEOUT,不能写“其他错误见日志”。这是 agent 编排层做重试策略、降级开关的唯一依据。我们曾因漏写GATEWAY_TIMEOUT,导致当短信网关抖动时,agent 无法识别此错误,盲目重试 3 次,最终触发运营商风控限流。
2.4 第四层:可审计的运行保障区(H2 标题 + 场景化清单)
这是给 SRE、安全合规、法务看的,回答“这玩意上线后,我怎么知道它没乱来、没越权、没泄露”。它不讲功能,只讲边界、约束、证据。必须包含三项硬性内容:数据权限声明、异常兜底路径、审计日志字段。例如:
## 运行保障 ### 数据权限 - 仅读取 `orders` 表的 `order_id`, `user_id`, `created_at`, `status` 字段; - 仅写入 `sms_logs` 表,写入字段为 `sms_id`, `order_id`, `user_phone`, `sent_at`, `status`; - **禁止访问** `users` 表的 `id_card`, `bank_account` 等敏感字段。 ### 异常兜底 - 当 `user_phone` 格式校验失败:记录 `ERROR_INVALID_PHONE` 日志,返回 `sms_sent=false`,`error_code=INVALID_PHONE`,**不抛出异常**; - 当短信网关返回 `503 Service Unavailable`:立即停止本次调用,返回 `sms_sent=false`,`error_code=GATEWAY_UNAVAILABLE`,**触发告警 `SKILL_SMS_GATEWAY_DOWN`**; - 当 `timeout_minutes` 超出 [5,120] 范围:强制截断为 30,记录 `WARN_TIMEOUT_OUT_OF_RANGE` 日志,**继续执行**。 ### 审计日志 每次执行必须生成一条结构化日志,包含以下必填字段: - `skill_id`: `"order_timeout_reminder"` - `input_hash`: 对输入参数做 SHA256 哈希(保护原始手机号不落盘) - `output_status`: `"success"` 或 `"failed"` - `duration_ms`: 执行耗时(毫秒) - `trace_id`: 全链路追踪 ID(与上游 workflow 对齐)这四层切片,不是可选项,是 SKILL.md 的刚性结构。少一层,就意味着某类关键角色在协作中必然出现信息盲区,迟早引发线上事故。我把它刻在团队 Wiki 首页:“写不完四层,不准提 PR”。
3. 设计 SKILL.md 的五个致命陷阱与破局点
光知道结构还不够。我在 Review 近三百份 SKILL.md 时,发现有五个高频陷阱,几乎每个新人至少踩中两个。这些不是语法错误,而是思维惯性导致的设计缺陷,必须用具体方法破局。
3.1 陷阱一:把 Skill 当成 Prompt 来写,混淆“指令”与“能力”
典型症状:SKILL.md 里大段复制粘贴 system prompt,比如你是一个专业的客服助手,请用亲切友好的语气...。这是最危险的起点错误。Prompt 是给 LLM 的临时指令,Skill 是 agent 的长期能力资产。前者随对话上下文漂移,后者必须稳定可预期。破局点只有一个:SKILL.md 里永远不出现任何语气、风格、人格化描述。它的职责是定义“做什么”和“怎么做”,而不是“像谁一样做”。比如,一个“生成商品推荐理由”的 skill,其## 接口定义应该明确输入是product_list: [ {id, name, category, price} ],输出是reasons: [ {product_id, text} ],text 字段的约束是“不超过 50 字,必须包含价格优势或品类独特性关键词”,而不是“请用活泼可爱的语气写”。语气是上层 workflow 或 agent router 根据用户画像动态注入的,不是 skill 本身的责任。我强制要求团队删掉所有be friendly、in professional tone类描述,替换为可验证的文本长度、关键词覆盖率、情感极性阈值等量化指标。
3.2 陷阱二:输入输出搞“大而全”,丧失原子性
典型症状:一个 skill 的 input 定义为user_data: object,里面嵌套七八层 JSON;output 返回一个巨长的 markdown 报告。这直接违背了 skill 的原子性原则——一个 skill 只解决一个明确、独立、可测试的业务子问题。破局点是“单职责 + 单数据源”十字检验法:
- 单职责:该 skill 是否能用一句话说清“它唯一存在的理由”?如果答案里有“并且”、“同时”、“还要”,说明职责过载。
- 单数据源:该 skill 的所有输入字段,是否全部来自同一个业务系统或数据库表?如果
user_name来自 CRM,order_amount来自 ERP,credit_score来自风控引擎,那它就不是一个 skill,而是一个 mini-ETL 流程,应该拆成三个 skill + 一个编排层。
我们有个真实案例:一个叫customer_health_score的 skill,最初 input 包含 12 个字段,来自 5 个系统。上线后每次数据源接口变更,都要全量回归测试。后来按十字检验法拆成get_crm_activity、get_payment_history、get_support_tickets三个 skill,每个只对接一个 API,维护成本下降 70%,且每个 skill 都能独立 AB 测试。
3.3 陷阱三:忽略“非功能需求”,只写 happy path
典型症状:SKILL.md 里只有## 正常流程,没有任何关于性能、安全、容错的约定。结果上线后,一个本该 200ms 完成的查询 skill,因未加缓存,在大促时拖垮整个 agent 集群。破局点是强制添加## 非功能契约小节,且必须量化。这不是可选项,是上线准入红线。内容必须包含:
- 性能:
P95 响应时间 ≤ 300ms(需注明压测环境:4c8g 容器,QPS=50); - 可用性:
支持 99.95% 月度可用率,故障时自动降级为返回缓存数据; - 安全:
所有输入字符串必须经过 XSS 过滤,输出 JSON 必须 UTF-8 编码; - 可观测性:
必须暴露 /metrics 端点,提供skill_execution_total、skill_duration_seconds两个 Prometheus 指标。
这些不是写给开发看的,是写给 SRE 的 SLA 协议。我们曾因漏写性能指标,导致一个 skill 在流量高峰时响应飙升至 2s,但监控告警没触发,因为没人定义“什么是慢”。
3.4 陷阱四:版本管理形同虚设,靠人肉记忆
典型症状:version: "1.0"写了一年没变,或者v1.1、v1.2的 diff 全靠口头沟通。这在多团队协作时是灾难。破局点是“语义化版本 + 变更日志”双轨制。version字段必须严格遵循 SemVer 2.0:
MAJOR(主版本):接口不兼容变更,如 input 字段删除、output 结构重构;MINOR(次版本):向后兼容的功能新增,如增加一个可选 input 参数;PATCH(修订版本):向后兼容的问题修复,如修正某个 error_code 的文案。
更重要的是,每次 version bump,## 变更日志小节必须同步更新,且格式固定:
## 变更日志 ### v1.3 (2024-07-22) - **BREAKING**: 移除 `user_email` 输入字段,改由 `user_id` 关联获取(关联 CRM 系统) - **ADDED**: 新增 `send_channel` 可选参数,支持 `sms`/`wechat`/`app_push` - **FIXED**: 修复 `timeout_minutes` 为 0 时无限等待的死循环 bug这个日志不是给程序员看的,是给 QA 看的测试范围指南,也是给运维看的灰度发布 checklist。
3.5 陷阱五:脱离真实执行环境,纸上谈兵
典型症状:SKILL.md 写得天花乱坠,但没定义它在哪个 agent 框架里跑、依赖哪些底层服务、需要什么权限。结果开发拿到文档,第一句话是“这个 skill 用什么框架实现?”。破局点是在## 运行环境小节,用表格锁定三大要素:
| 要素 | 要求 | 实例 |
|---|---|---|
| 目标框架 | 必须指定具体框架及最低版本 | LangChain 0.1.15+或LlamaIndex 0.10.27+ |
| 依赖服务 | 列出所有外部依赖及其 SLA | CRM API (99.9% uptime),SMS Gateway (P99 < 1s) |
| 权限要求 | 声明所需最小权限集 | READ orders table,WRITE sms_logs table,EXECUTE http_client |
这个表格,是开发环境搭建、CI/CD 流水线配置、生产部署审批的唯一依据。我们曾因没写明http_client权限,导致 skill 在 sandbox 环境跑通,上线后因权限不足被 agent runtime 拒绝加载,回滚耗时 40 分钟。
4. 从零开始手把手:一个真实电商催付 Skill 的 SKILL.md 全过程
光讲道理不够,得看实操。下面以我上周刚交付的电商项目为例,展示如何从一张白纸,写出一份经得起生产考验的SKILL.md。全程不虚构,所有参数、字段、逻辑均来自真实日志和监控数据。这不是教学 demo,是现场作业记录。
4.1 第一步:锁定业务原点,拒绝脑补
一切始于业务方的一句口头需求:“用户下单后 30 分钟没付款,得提醒一下。” 这句话太模糊,不能直接写进 SKILL.md。我拉着产品、运营、技术开了 15 分钟快会,用四个问题把它钉死:
- 触发时机:是“订单创建时间”还是“用户最后操作时间”?→ 确认为
created_at; - 状态判定:什么算“未支付”?
status = 'pending'还是payment_status IS NULL?→ 查 DB schema,确认为payment_status = 'unpaid'; - 提醒渠道:只发短信?还是根据用户偏好选?→ 运营拍板:“首推短信,因触达率最高,后续再扩展”;
- 内容要素:必须包含哪些信息?订单号、剩余时间、跳转链接是刚需,优惠券信息是锦上添花 → 确认前三者为
must have。
这四问的答案,就是 SKILL.md## 业务契约的全部内容。没有这一步,后面全是空中楼阁。
4.2 第二步:定义机器接口,用表格代替段落
基于上一步结论,我立刻打开 VS Code,新建SKILL.md,先写死元数据和接口定义。注意,这里所有字段名、类型、示例,都来自真实数据库字段和 API 文档:
--- id: order_timeout_reminder name: 订单超时未支付自动催付 version: "1.0" category: customer_service tags: [payment, reminder, timeout, sms] author: liwei@ecommerce.com created_at: "2024-07-22" updated_at: "2024-07-22" status: draft ---## 业务契约 当订单创建时间超过 30 分钟,且订单支付状态为 'unpaid' 时,向订单关联的用户手机号发送一条包含订单号、剩余支付时限(固定 30 分钟)、一键跳转至支付页链接的短信提醒。成功发送即视为技能完成,无需用户反馈。 ## 接口定义 ### 输入参数(Input) | 字段名 | 类型 | 必填 | 描述 | 示例 | |--------|------|------|------|------| | `order_id` | string | 是 | 订单唯一 ID,来自 `orders.id` 字段,长度 16-32 位 | `"ORD20240722A8B9C0D1"` | | `user_phone` | string | 是 | 用户注册手机号,11 位纯数字,需通过号段校验 | `"13800138000"` | | `payment_deadline` | string | 否 | 支付截止时间戳(ISO 8601),用于计算剩余时间,若为空则按 `created_at + 30m` 计算 | `"2024-07-22T15:30:00Z"` | ### 输出结果(Output) | 字段名 | 类型 | 描述 | 示例 | |--------|------|------|------| | `sms_sent` | boolean | 短信是否成功发出 | `true` | | `sms_id` | string | 短信网关回执 ID | `"SM1122334455667788"` | | `remaining_time_min` | integer | 发送时计算的剩余支付分钟数(向下取整) | `28` | | `error_code` | string | 错误码,仅当 `sms_sent=false` 时存在 | `"INVALID_PHONE"` |实操心得:
payment_deadline设为可选,是因为上游 workflow 可能已计算好此值(如从风控系统获取),避免重复计算。remaining_time_min作为 output 字段,是给上层 workflow 做“是否需要二次催付”决策的依据,不是为了给人看,而是为了机器判断。
4.3 第三步:补全运行保障,把“万一”写进合同
接着写## 运行保障。这里所有条款,都来自我们短信网关的 SLA 文档和历史故障复盘:
## 运行保障 ### 数据权限 - 读取权限:`orders` 表的 `id`, `user_id`, `created_at`, `payment_status` 字段; - 写入权限:`sms_logs` 表的 `sms_id`, `order_id`, `user_phone`, `sent_at`, `status`, `remaining_time_min` 字段; - **禁止权限**:`users` 表的 `name`, `email`, `address` 等 PII 字段。 ### 异常兜底 - `user_phone` 格式错误:记录 `ERROR_INVALID_PHONE` 日志,返回 `sms_sent=false`, `error_code=INVALID_PHONE`,**不重试**; - 短信网关返回 HTTP 429(限流):立即返回 `sms_sent=false`, `error_code=SMS_QUOTA_EXCEEDED`,**触发告警 `SKILL_SMS_QUOTA_ALERT`**; - `order_id` 在 `orders` 表中查无此记录:返回 `sms_sent=false`, `error_code=ORDER_NOT_FOUND`,**记录 WARN 日志,不告警**(可能是脏数据)。 ### 审计日志 每次执行生成一条 JSON 日志,字段包括: - `skill_id`: `"order_timeout_reminder"` - `input_hash`: `sha256(order_id + user_phone)`(原始手机号不落盘) - `output_status`: `"success"` 或 `"failed"` - `duration_ms`: 执行耗时(从 DB 查询到网关返回) - `trace_id`: 继承自上游 workflow 的 trace_id - `sms_cost_cny`: 短信实际扣费金额(用于成本核算)注意:
sms_cost_cny是新增字段,因为财务部门要求精确核算每条催付短信的成本。这再次印证——SKILL.md 是多方契约,不是技术文档。
4.4 第四步:填充非功能契约与环境,让运维敢上线
最后,补上硬性约束和环境要求,这是上线前 SRE 最关注的部分:
## 非功能契约 - **性能**:P95 响应时间 ≤ 250ms(压测环境:K8s Pod 2c4g,QPS=100,DB 连接池 20); - **可用性**:99.95% 月度可用率;当 SMS Gateway 不可用时,自动降级为记录日志,不阻塞 workflow; - **安全**:所有 `user_phone` 输入必须通过 `libphonenumber` 库校验;输出 JSON 必须 UTF-8 编码; - **可观测性**:暴露 `/metrics` 端点,提供 `skill_execution_total{status="success|failed"}` 和 `skill_duration_seconds_bucket` 两个指标。 ## 运行环境 | 要素 | 要求 | |------|------| | **目标框架** | LangChain 0.1.15+(使用 `RunnableLambda` 实现) | | **依赖服务** | CRM API (v2.3+, 99.9% uptime), SMS Gateway (v1.8+, P99 < 800ms) | | **权限要求** | `SELECT` on `orders`, `INSERT` on `sms_logs`, `HTTP GET/POST` to SMS Gateway |4.5 第五步:写变更日志,为下次迭代埋点
最后,补上初始版本日志,并预留扩展空间:
## 变更日志 ### v1.0 (2024-07-22) - **INITIAL**: 首次发布,支持基础短信催付功能 - **TODO**: 下一版计划支持 `send_channel` 参数,扩展微信服务号推送这份SKILL.md,从敲下第一个---到最终定稿,用时 42 分钟。它不是完美的,但它足够清晰、足够具体、足够让开发、测试、运维、业务方在同一张纸上对齐。上线三天,日均调用 12.7 万次,P95 响应 186ms,0 故障。这就是设计的力量。
5. 常见问题速查表与独家避坑技巧
在真实项目中,总有些问题反复出现。我把它们整理成一张速查表,附上只有踩过坑的人才知道的技巧。这不是 FAQ,是生存指南。
| 问题现象 | 根本原因 | 解决方案 | 我的独家技巧 |
|---|---|---|---|
Agent 加载 skill 失败,报错invalid YAML front matter | YAML 语法错误,最常见是缩进不一致(空格 vs Tab)、冒号后少了空格、字符串含特殊字符未引号包裹 | 用 VS Code 安装YAML插件,开启实时校验;Front Matter 中所有字符串值必须用双引号包裹 | 技巧:在团队 CI 流水线中加入yamllint检查,yamllint -d "{extends: relaxed, rules: {line-length: disable}}" SKILL.md,提前拦截 95% 的 YAML 错误 |
| 测试用例总是 pass,但线上执行失败 | SKILL.md 中的## 业务契约描述模糊,测试同学按字面理解设计用例,但实际业务逻辑有隐藏规则(如“30 分钟”指自然分钟,非工作日分钟) | ## 业务契约必须用完整句子,包含所有隐含条件;每个句子必须能直接转化为测试用例的 Given-When-Then | 技巧:要求测试同学用SKILL.md中的## 业务契约句子,直接生成 cucumber feature 文件,一行契约对应一个 scenario,杜绝理解偏差 |
| 多个 skill 之间数据传递混乱,出现字段名冲突 | 没有统一的数据契约规范,各 skill 自行定义user_id、uid、customerId等别名 | 在团队 Wiki 建立《全局数据字典》,强制规定user_id为 16 位 UUID 字符串,phone为 11 位纯数字字符串,所有 skill 必须遵守 | 技巧:用jsonschema工具生成数据字典的 JSON Schema,集成到 IDE 中,输入字段时自动提示合法值和格式 |
| 上线后发现 skill 执行太慢,但本地测试很快 | 未定义## 非功能契约中的性能指标,也未在真实环境压测 | 所有 skill 的## 非功能契约必须包含压测环境描述(CPU/内存/网络/DB 配置)和 QPS 要求;上线前必须在 staging 环境用相同配置压测 | 技巧:在 skill 代码中内置@performance_monitor(p95_threshold_ms=250)装饰器,超时自动上报,不依赖外部 APM,精准定位瓶颈 |
| skill 版本升级后,旧 workflow 依然调用老版本,导致行为不一致 | 没有强制 workflow 绑定 skill 版本,或框架不支持版本路由 | 所有 workflow 的 skill 调用,必须显式指定skill_id@version,如order_timeout_reminder@1.2;框架必须支持按版本加载 | 技巧:在 CI 流水线中加入grep -r "order_timeout_reminder" workflows/ | grep -v "@1.2",自动检查是否有 workflow 未绑定版本,阻断发布 |
提示:最后一个技巧,是我们血的教训。曾因一个 workflow 漏写
@1.1,自动调用@1.0,而@1.0的remaining_time_min计算逻辑有 bug,导致数千用户收到错误的“剩余 0 分钟”短信,引发客诉。现在,这条检查是发布流水线的 gatekeeper,不通过,不准上线。
6. 我的个人体会:SKILL.md 是 agent 世界的宪法序言
写完这份 SKILL.md,我合上笔记本,盯着屏幕上的---符号看了很久。它看起来那么轻,一段 YAML,几段 markdown,不到一千字。但我知道,它承载的重量远超于此。它是我和 agent 之间的第一次握手,是把混沌的业务需求,翻译成机器可执行的精确语言的第一步。它不是终点,而是所有后续工作的起点——workflow 编排、LLM 路由、监控告警、AB 测试、成本核算,全都建立在这份薄薄的文本之上。我见过太多团队,一上来就扎进代码,调 prompt,搭 pipeline,结果跑通一个 demo 就欢呼,却在规模化时被各种边界问题拖垮。而那些稳扎稳打的团队,他们的第一个 PR,永远是一份结构完整、责任清晰的 SKILL.md。这不是形式主义,是工程敬畏。它强迫你把“我以为”变成“我确认”,把“大概这样”变成“必须如此”。当你写下status: production的那一刻,你签下的不是一份文档,而是一份对稳定性、可维护性、可审计性的承诺。所以,别急着写代码。先坐下来,好好写你的第一个 SKILL.md。把它当成 agent 世界的宪法序言——简短,但字字千钧。