Python技术迷

还在手搓 Agent ?SKILL.md 正在让一切变得不同

Agent 跑偏的时候,日志一般长这样:

tool_call=python.run
reason="need to check api"
args={"cmd":"curl https://prod.xxx.com/order/create"}

看到 prod 我就不太想往下看了。

这种 Agent 不是不会干活,是你把它养成了一个“全靠临场发挥”的东西。prompt 里塞一堆规则,工具列表挂一屏,最后再补一句“请谨慎操作”。这玩意儿在线上排障里,我一般是不信的。

现在 SKILL.md 出来以后,这个写法开始变味了。

以前我们手搓 Agent,大概是这样:

SYSTEM_PROMPT = """
你是一个后端排障助手。
遇到接口问题先看日志,再看 trace,再看 SQL。
不要直接操作生产环境。
生成报告要包含原因、证据、修复建议。
"""

TOOLS = ["read_log", "query_trace", "run_sql", "write_report"]

这段代码看着没问题,其实问题很大。

规则在 prompt 里,工具在代码里,排查顺序在脑子里。过两周再来一个“接口压测分析 Agent”,你大概率复制一份 prompt,改几个词。再过两个月,项目里全是这种半成品 Agent。

SKILL.md 换了个思路。

它不是让你再造一个 Agent,而是把“怎么做一件事”沉到一个目录里。一个 Skill 通常就是一个文件夹,里面放 SKILL.md,再按需要放脚本、模板、参考文档。公开的 Agent Skills 规范里也是这么描述的:核心就是一个带 SKILL.md 的文件夹,里面可以带 metadata、instructions、scripts、references 这些东西。

我更关心的是这点:它把能力从“Agent 代码”里拆出来了。

比如接口冒烟检查,不要写死在 Agent 主逻辑里,直接这样放:

skills/
  api-smoke-check/
    SKILL.md
    scripts/
      smoke_check.py

SKILL.md 可以写得很短,别写成论文:

---
name: api-smoke-check
description: 当用户要求检查测试环境接口、回归核心链路、确认发布后接口是否正常时使用
---

# API Smoke Check

只检查非生产环境,除非用户明确给出 allow_prod=true。

执行顺序:

1. 
读取 endpoints.yml
2. 对每个接口发起请求
3. 记录 status、耗时、响应里的 biz_code
4. 失败接口必须保留请求参数和响应摘要
5. 最后生成一份 Markdown 报告

异常判断:

- 
HTTP 状态码不是 2xx,算失败
- biz_code 不等于 0,算失败
- 单接口耗时超过 1500ms,标记为 slow

这东西比一大坨 prompt 靠谱。

因为它有边界,有触发条件,有执行顺序,还有不能碰生产的限制。Agent 不是每次从头猜,而是任务命中这个 Skill 后,再加载里面的详细规则。Codex 的文档里也提到,它会先在上下文中保留可用 skills 的初始列表,决定使用某个 skill 后才加载完整 SKILL.md 指令。

脚本也别写成通用框架,现场够用就行:

# skills/api-smoke-check/scripts/smoke_check.py
import json
import time
from pathlib import Path

import requests
import yaml

defcall_one(base_url, item):
    url = base_url.rstrip("/") + item["path"]
    started = time.time()

try:
        resp = requests.request(
            method=item.get("method", "GET"),
            url=url,
            json=item.get("body"),
            timeout=item.get("timeout", 3),
        )
        cost_ms = int((time.time() - started) * 1000)

        body = {}
try:
            body = resp.json()
except Exception:
            body = {"raw": resp.text[:300]}

        biz_code = body.get("biz_code")
        ok = 200 <= resp.status_code < 300and biz_code in (0, None)

return {
"name": item["name"],
"url": url,
"ok": ok,
"status": resp.status_code,
"biz_code": biz_code,
"cost_ms": cost_ms,
"slow": cost_ms > 1500,
"sample": body,
        }

except Exception as e:
return {
"name": item["name"],
"url": url,
"ok": False,
"error": repr(e),
        }

defmain():
    cfg = yaml.safe_load(Path("endpoints.yml").read_text(encoding="utf-8"))

    env = cfg["env"]
if env == "prod"andnot cfg.get("allow_prod"):
raise SystemExit("refuse to run on prod without allow_prod=true")

    rows = [call_one(cfg["base_url"], item) for item in cfg["endpoints"]]

    failed = [r for r in rows ifnot r["ok"]]
    slow = [r for r in rows if r.get("slow")]

    print(json.dumps({
"env": env,
"total": len(rows),
"failed": len(failed),
"slow": len(slow),
"items": rows,
    }, ensure_ascii=False, indent=2))

if __name__ == "__main__":
    main()

这段代码没有什么高级东西,但它把排障习惯写死了:不碰生产、保留失败样本、单独标 slow、输出结构化结果。

这就是我觉得 SKILL.md 真正有用的地方。

不是“让 Agent 更聪明”,而是让 Agent 少自由发挥。

以前一个 Agent 要会查日志、写 SQL、跑接口、生成报告、改代码,最后就变成一个巨型提示词怪物。现在可以拆成几个 Skill:

slow-sql-review/
log-trace-lookup/
api-smoke-check/
release-note-writer/
python-batch-cleaner/

每个 Skill 只管一件事。

查慢 SQL 的 Skill 里就写:先要 EXPLAIN,再看扫描行数,再看索引,再看回表,不要一上来建议加缓存。

处理日志的 Skill 里就写:先按 trace_id 聚合,再按 ERROR 过滤,再找调用链断点,不要拿单行异常直接下结论。

写 Python 清洗脚本的 Skill 里就写:先备份原文件,输出 dry-run 统计,再真正落盘。

这些东西放在代码里也能做,但改起来麻烦。放在 prompt 里也能做,但容易越写越乱。放在 SKILL.md 里,至少能 review,能进 Git,能被团队复用。

当然,别把 SKILL.md 当银弹。

它也是指令,而且是能影响 Agent 行为的指令。第三方 Skill 不能随便装。近期也有研究专门提到,SKILL.md 这类自然语言元数据和指令会影响技能的发现、选择和加载,存在语义层面的供应链风险。

所以我现在看 Skill,第一眼不是看它功能多炫。

我先看三件事:

1. 有没有偷偷要求读取敏感文件
2. 有没有执行 shell / 网络请求
3. 有没有把结果发到外部地址

这跟看一个陌生 npm 包、pip 包差不多。你不能因为它是 Markdown,就觉得它安全。

SKILL.md 这波变化,最值得关注的不是格式,而是分工变了。

Agent 主体以后不该塞太多业务细节。它只负责理解任务、选择 Skill、调用工具、收口结果。

真正值钱的东西,是你们团队沉淀下来的那些判断顺序:

接口超时先不看业务代码,先看 trace。

SQL 慢别急着加索引,先看执行计划是不是走错。

批处理脚本别直接写库,先 dry-run。

线上操作没有回滚方案,就先别动。

这些以前都在老程序员脑子里,现在可以一点点写进 SKILL.md。

手搓 Agent 不是不能写。

只是再把所有能力揉进一个 prompt 里,后面维护起来,大概率会像一锅没分层的老系统。能跑,但没人敢改。