还在手搓 Agent ?SKILL.md 正在让一切变得不同
Agent 最烦人的地方,不是它不会干活。
是你每次让它干活之前,都得先塞一堆规矩:项目怎么跑、日志怎么看、SQL 怎么查、哪些命令不能碰、生成文件放哪、失败了先回滚还是先留现场。
我见过不少所谓 Agent 框架,代码写得挺热闹,最后全靠一段巨长 prompt 续命。
一换项目,废一半。
一换人维护,再废一半。
这地方我第一眼就不太信。因为线上系统里最怕的就是“规则藏在人脑里”,Agent 也一样。规则不落文件,迟早漂。
SKILL.md 这东西,真正有意思的点就在这里。
它不是再发明一个 Agent,也不是再套一层玄学提示词。它更像把某个具体能力拆成一个目录:一个 SKILL.md 放说明和触发条件,旁边可以带脚本、参考资料、模板、资产文件。OpenAI Codex 的文档里也是这个结构:一个 skill 是包含 SKILL.md 的目录,SKILL.md 至少要有 name 和 description,还可以配 scripts/、references/、assets/ 等内容。
以前我手搓 Agent,大概会这么写:
system_prompt = """
你是一个订单排障助手。
遇到退款问题先查 order 表,再查 refund_flow。
不能直接更新生产库。
SQL 必须加 limit。
遇到慢查询要提示 explain。
日志里必须带 trace_id。
"""
看着没问题,实际很脆。
这段东西会越写越长,最后没人敢删。业务一改,prompt 里旧规则还在。更恶心的是,Agent 每次都要带着一大坨上下文出门,哪怕这次只是让它格式化一个 CSV。
换成 SKILL.md,味道就不一样了。
比如我会把“订单退款排障”单独拆出去:
skills/
refund-debug/
SKILL.md
scripts/
scan_refund_log.py
references/
refund_tables.md
SKILL.md 可以写得很短,重点不是文学性,是让 Agent 知道什么时候该用、怎么用:
---
name: refund-debug
description: 排查订单退款失败、退款状态不一致、退款回调超时等问题
---处理顺序:
1. 先确认 trace_id,没有 trace_id 就让用户补,不要猜。
2. 只读查询,禁止生成 update/delete SQL。
3. 先看 refund_flow,再看 pay_callback_log,最后才看 order_snapshot。
4. SQL 必须带 limit 50。
5. 日志分析优先调用 scripts/scan_refund_log.py。
这里有个变化很关键:规则从“大脑里的经验”变成了“仓库里的文件”。
以后谁改退款链路,就顺手改这个 skill。Agent 不用每次吃完整项目背景,只在命中场景时加载这块。Anthropic 的 Agent Skills 文档也强调了类似思路:Skills 是由 SKILL.md 和可选资源组成的能力包,Agent 会在相关任务中自动调用。([Claude Code][2])
我自己会先写一个很土的扫描脚本,不搞框架,先把目录跑通:
from pathlib import Path
from dataclasses import dataclass
import re@dataclass
classSkill:
name: str
desc: str
root: Path
body: str
defread_skill(path: Path) -> Skill:
text = path.read_text(encoding="utf-8")
meta = {}
body = text
if text.startswith("---"):
_, head, body = text.split("---", 2)
for line in head.splitlines():
if":"in line:
k, v = line.split(":", 1)
meta[k.strip()] = v.strip()
ifnot meta.get("name") ornot meta.get("description"):
raise ValueError(f"坏 skill:{path}")
return Skill(
name=meta["name"],
desc=meta["description"],
root=path.parent,
body=body.strip()
)
defload_skills(base: str) -> list[Skill]:
skills = []
for md in Path(base).glob("*/SKILL.md"):
skills.append(read_skill(md))
return skills
defpick_skill(skills: list[Skill], user_text: str) -> Skill | None:
words = set(re.findall(r"[\w\u4e00-\u9fa5]+", user_text.lower()))
best = None
best_score = 0
for skill in skills:
haystack = f"{skill.name}{skill.desc}{skill.body}".lower()
score = sum(1for w in words if w and w in haystack)
if score > best_score:
best = skill
best_score = score
return best if best_score >= 2elseNone
这代码不高级,但够用。
真正上线前,当然不能靠这种关键词打分。但排查一个机制是不是靠谱,我一般先写这种笨脚本。能跑起来,再考虑 embedding、权限、沙箱、工具调用记录。不然一上来就搞一堆架构图,最后连 skill 什么时候被选中都说不清。
再补一个执行入口:
defbuild_agent_context(skill: Skill, question: str) -> str:
refs = []
ref_dir = skill.root / "references"if ref_dir.exists():
for f in ref_dir.glob("*.md"):
refs.append(f"\n[参考文件:{f.name}]\n{f.read_text(encoding='utf-8')[:1200]}")
returnf"""
你正在处理一个具体任务,不要扩展到无关领域。
用户问题:
{question}
命中的 Skill:
{skill.name}
执行约束:
{skill.body}
补充资料:
{''.join(refs) if refs else'无'}
输出要求:
给可执行结论;涉及 SQL 时默认只读;不确定就标出来。
"""
.strip()这里我最看重的不是“自动化”,而是边界。
以前 Agent 很容易犯一个毛病:用户问退款失败,它顺手开始分析支付网关、库存回滚、优惠券补偿,写得还挺完整。问题是现场排障不这么干。现场排障是先收窄,先拿证据,先确认链路断在哪。
SKILL.md 可以把这种排查顺序固化下来。
比如退款场景里写清楚:先查 refund_flow,再查回调日志,最后看订单快照。不是因为这个顺序优雅,是因为线上大概率这么快。
这个东西对团队也有用。
新人写 Agent,不用问一圈“我们项目有什么规矩”。打开 skills/ 看一眼,大概就知道哪些活已经沉淀过:日志排查、SQL 审核、接口回放、文档生成、数据清洗、发版检查。
但我不建议把 SKILL.md 写成大而全。
一个 skill 只干一类活。退款就是退款,慢 SQL 就是慢 SQL,日志清洗就是日志清洗。很多人一上来写个 backend-master/SKILL.md,里面塞满数据库、缓存、MQ、K8s、Python 脚本、Java 规范,最后又变回了巨型 prompt。
还有一点要盯住:安全。
SKILL.md 不是普通说明文。它会影响 Agent 选择什么能力、读什么文件、执行什么脚本。2026 年已经有研究专门分析 SKILL.md 这类文本可能带来的语义供应链风险,问题就在于描述和指令会影响技能发现、选择和治理判断。
所以我会给 skill 加一层很硬的本地校验:
BLOCKED = ("rm -rf", "drop table", "delete from", "curl http", "chmod 777")defcheck_skill_safe(skill: Skill) -> None:
raw = skill.body.lower()
hit = [x for x in BLOCKED if x in raw]
if hit:
raise RuntimeError(f"skill 有危险指令:{skill.name}, hit={hit}")
scripts = skill.root / "scripts"
if scripts.exists():
for p in scripts.glob("*.py"):
text = p.read_text(encoding="utf-8").lower()
if"subprocess"in text or"os.system"in text:
raise RuntimeError(f"脚本需要人工复核:{p}")
别嫌它粗糙。
很多事故不是因为系统没有高级安全模型,而是最基本的黑名单、权限隔离、人工复核都没做。Agent 时代也一样,能执行脚本的东西,默认就别太信。
SKILL.md 让我觉得有价值的地方,是它把 Agent 从“会聊天的黑盒”往“可维护的工程组件”推了一步。
规则能进仓库。
脚本能复用。
参考资料能分层。
能力能按场景加载。
出了问题还能回头看:到底是哪个 skill 触发了,读了哪些规则,执行了哪个脚本。
这比在 prompt 里手搓一万个“请务必注意”靠谱多了。
以后写 Agent,可能不会再问“这段 prompt 怎么调”。
更应该问的是:这个能力配不配单独沉淀成一个 SKILL.md?如果不配,那它可能只是一次性提示词。如果配,就该像代码一样管理它。