☰
Agent Skills 实战教程:SKILL.md 目录规范、渐进式加载与企业工程落地
2026/10/7 7:37:00 网站建设 项目流程

1. 从 Prompt 堆叠到 Agent Skills:企业级能力治理的起点

如果你正在做 AI Agent 落地,大概率经历过这个阶段:系统提示词越写越长,从最初的几百字膨胀到几千字,每加一个业务场景就往上叠一段规则,最后连自己都不敢改——改一处怕崩全局。这就是典型的 Prompt 堆叠困境。

Agent Skills 是什么?简单说,它是一套把 Agent 能力模块化封装的工程方案。每个 Skill 是一个独立目录,核心是 SKILL.md 文件,里面用 YAML 元数据描述能力边界,用 Markdown 正文定义执行流程。Agent 启动时只加载元数据索引,任务命中后才加载完整流程,执行中按需读取资源文件。这套机制叫渐进式加载,能把常驻上下文的 Token 开销压到原来的 10% 到 40%。

它适合谁?三类人最该关注:一是正在搭建私有 Agent 平台的工程团队,二是需要多项目复用同一套业务能力的开发者,三是被 Prompt 冲突和输出不稳定折磨的运维同学。如果你只是写个单轮对话机器人,Skill 体系可能过重;但只要涉及多步骤业务流程、团队协作、版本迭代,它就是绕不开的基础设施。

我试过在一个代码评审 Agent 上做改造,改造前系统提示词 3200 字,改造后常驻索引不到 400 字,评审规则全部下沉到 Skill 的 references 目录,按需加载。响应延迟从平均 4.2 秒降到 2.8 秒,规则更新也不用动主提示词了。

这篇教程会带你走完一次完整的工程化改造:从目录规范到 SKILL.md 配置,从渐进式加载验证到常见报错排查。每一步都有可复制的代码和配置,你可以在自己的项目里直接跑一遍。

2. TaoToken 前置准备:模型接入与 API Key 配置

在开始写 Skill 之前,你需要一个能稳定调用大模型的入口。Agent Skills 本身是能力封装规范,但执行流程时仍然需要模型来理解任务、匹配 Skill、生成输出。这里我用 TaoToken 作为模型接入层,它的 API 兼容主流格式,配置简单,适合做 Agent 工程的底座。

2.1 获取 API Key 与 Base URL

首先访问 TaoToken 控制台创建 API Key。地址是 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=api_key_config ,登录后点击创建新密钥,复制保存。注意 Key 只显示一次,丢了只能重建。

Base URL 固定为 https://taotoken.net/api ,这个地址不加任何 UTM 参数,直接用于代码里的 base_url 配置。模型 ID 根据你的场景选,做 Agent 流程调度建议用 claude-sonnet-4-20250514 或同等能力的模型,推理稳定、指令遵循好。

2.2 在 Agent 项目中配置环境变量

不要硬编码 Key。在项目根目录建 .env 文件,写入:

TAOTOKEN_API_KEY=sk-你的实际密钥 TAOTOKEN_BASE_URL=https://taotoken.net/api TAOTOKEN_MODEL=claude-sonnet-4-20250514

然后在 Python 加载器里读取:

import os from dotenv import load_dotenv load_dotenv() API_KEY = os.getenv("TAOTOKEN_API_KEY") BASE_URL = os.getenv("TAOTOKEN_BASE_URL") MODEL_ID = os.getenv("TAOTOKEN_MODEL")

如果你用 Node.js 或其它语言,配置逻辑一样,只是读取方式不同。关键是 Base URL 和 Key 要成对出现,缺一个都会报 401。

2.3 验证模型连通性

在写 Skill 之前,先确认模型能通。跑一段最小请求:

import requests import os from dotenv import load_dotenv load_dotenv() resp = requests.post( f"{os.getenv('TAOTOKEN_BASE_URL')}/v1/messages", headers={ "x-api-key": os.getenv("TAOTOKEN_API_KEY"), "anthropic-version": "2023-06-01", "content-type": "application/json" }, json={ "model": os.getenv("TAOTOKEN_MODEL"), "max_tokens": 64, "messages": [{"role": "user", "content": "回复 OK"}] } ) print(resp.status_code) print(resp.json())

返回 200 且内容里有 OK,说明接入层没问题。如果返回 401,检查 Key 是否复制完整;如果返回 404,检查 Base URL 是否多了斜杠或路径。

这一步看起来简单,但很多 Skill 加载失败最后追查下来都是模型接入层没通。先把这层跑通,后面排障会省很多时间。

3. SKILL.md 目录规范与可复制配置模板

目录结构是 Skill 工程化的地基。我见过太多团队因为目录混乱导致加载器解析失败、资源引用错位、团队协作时互相覆盖。这一节给你一套经过验证的目录规范,以及可直接复制的 SKILL.md 配置。

3.1 标准目录结构

每个 Skill 独立成文件夹,文件夹名就是 slug 标识,全局唯一,统一小写加横杠。完整结构如下:

skill-data-validation/ ├── SKILL.md # 核心必选:元数据 + 执行流程 ├── scripts/ # 可选:辅助执行脚本 │ └── validate_tool.py ├── references/ # 可选:业务规范、校验标准 │ └──>--- slug: skill-data-validation name: 业务数据合规校验 description: 对结构化数据进行字段完整性、格式合规性、敏感信息检测,输出标准化校验报告。 triggers: - 数据校验 - 合规检查 - 格式检测 - 入库审核 version: 1.0.0 author: enterprise-dev-team tags: ["data", "validate", "compliance"] priority: 5 non_goals: - 不做数据修复与自动补全 - 不存储、不转发原始敏感数据 - 不校验业务逻辑合理性 output_contract: | 输出固定三部分: 1. 校验概述:总量、合规数、异常数 2. 异常明细:字段名、错误类型、违规依据、修复建议 3. 合规结论:通过 / 不通过 dependencies: - none --- # 执行流程 1. 接收用户传入的结构化数据与业务场景标识 2. 读取 references/data-rule.md 获取当前场景合规规范 3. 逐字段校验格式、完整性、敏感信息 4. 调用 scripts/validate_tool.py 执行批量规则校验 5. 基于 assets/report-template.md 生成标准化报告 6. 汇总异常信息,输出最终结果 # 异常处理规则 1. 无输入数据:终止流程,返回「未检测到有效待校验数据」 2. 场景不匹配:返回「暂无对应场景的校验规范」 3. 脚本执行异常:原样返回错误日志,不自行推断修复 4. 规范文件缺失:终止流程,提示检查 Skill 配置 # 执行约束 所有校验仅基于现有规则执行,不新增逻辑,不放宽标准。

3.3 关键字段说明

slug 是全局唯一标识,系统底层靠它识别 Skill,一旦确定不要改。description 决定 Agent 能否精准匹配任务,写法要直白,只讲解决什么、产出什么。triggers 是触发关键词数组,覆盖用户口语化指令和专业指令。non_goals 是边界定义,明确写出不做什么,这是防止 Skill 越权的关键。output_contract 强制统一输出结构,所有需要稳定输出的 Skill 都必须配。

priority 是执行优先级,数值越大越优先,用于多 Skill 同时命中时的排序。version 用于版本管控,团队协作时配合 Git 做灰度。

配置写完后,把整个目录放到项目的 .agent-skills 文件夹下,加载器就能扫描到。下一节我们写加载器代码,验证三级渐进式加载是否生效。

4. 渐进式加载验证:从索引到资源的完整请求链路

渐进式加载是 Agent Skills 的核心机制,也是企业级落地省 Token 的关键。这一节我们写一个可运行的加载器,然后跑一次完整请求,验证三个阶段是否按预期工作。

4.1 三级加载机制回顾

第一阶段是索引预加载。Agent 启动时扫描所有 Skill 目录,只解析 SKILL.md 的 YAML 元数据,提取 slug、name、description、triggers、priority,生成轻量索引池常驻上下文。此时 Agent 知道有哪些技能,但不知道具体步骤。

第二阶段是流程加载。用户任务输入后,匹配模块基于索引做语义打分,命中 Skill 后读取完整 Markdown 流程、异常规则、约束条件,载入当前会话。此时 Agent 有完整执行逻辑,但不加载外部资源。

第三阶段是资源动态加载。流程执行中需要读规范、跑脚本、套模板时,才单独加载对应文件。脚本只捕获输出,源码不进上下文;文档只读指定片段,不加载全文。任务结束,临时资源全部卸载,只保留索引。

4.2 加载器核心代码

下面这段代码实现了扫描、匹配、三级加载、安全校验、缓存卸载:

import os import yaml from dataclasses import dataclass from typing import List, Dict, Optional SKILL_PATHS = [ os.path.expanduser("~/.agent-skills/private"), "./.agent-skills", "/opt/team-skills", ] SAFE_PATH_BLACKLIST = ["/etc", "/root", "/usr"] @dataclass class SkillMeta: slug: str name: str description: str triggers: List[str] version: str priority: int non_goals: List[str] output_contract: str @dataclass class SkillFull: meta: SkillMeta workflow: str exception_rule: str class SkillLoader: def __init__(self): self.skill_index: Dict[str, SkillMeta] = {} self.skill_full_cache: Dict[str, SkillFull] = {} self._scan_all_skills() def _scan_all_skills(self): for path in SKILL_PATHS: if not os.path.exists(path): continue for skill_dir in os.listdir(path): skill_path = os.path.join(path, skill_dir) md_path = os.path.join(skill_path, "SKILL.md") if not os.path.isdir(skill_path) or not os.path.exists(md_path): continue meta = self._parse_skill_meta(md_path) if meta: self.skill_index[meta.slug] = meta def _parse_skill_meta(self, md_path: str) -> Optional[SkillMeta]: try: with open(md_path, "r", encoding="utf-8") as f: content = f.read() if not content.startswith("---"): return None yaml_end = content.find("---", 3) if yaml_end == -1: return None meta_data = yaml.safe_load(content[3:yaml_end].strip()) return SkillMeta( slug=meta_data.get("slug", ""), name=meta_data.get("name", ""), description=meta_data.get("description", ""), triggers=meta_data.get("triggers", []), version=meta_data.get("version", "1.0.0"), priority=meta_data.get("priority", 0), non_goals=meta_data.get("non_goals", []), output_contract=meta_data.get("output_contract", "") ) except Exception as e: print(f"解析失败:{md_path}, 错误:{e}") return None def match_skill(self, user_query: str) -> Optional[str]: max_score = 0 target_slug = None query = user_query.lower() for slug, meta in self.skill_index.items(): score = 0 for trigger in meta.triggers: if trigger.lower() in query: score += 3 if meta.description.lower() in query: score += 2 score += meta.priority * 0.5 if score > max_score and score >= 2: max_score = score target_slug = slug return target_slug def load_full_skill(self, slug: str) -> Optional[SkillFull]: if slug in self.skill_full_cache: return self.skill_full_cache[slug] for path in SKILL_PATHS: md_path = os.path.join(path, slug, "SKILL.md") if not os.path.exists(md_path): continue with open(md_path, "r", encoding="utf-8") as f: content = f.read() yaml_end = content.find("---", 3) body = content[yaml_end+3:].strip() if "异常处理规则" in body: workflow, exception_rule = body.split("异常处理规则", 1) else: workflow, exception_rule = body, "" full = SkillFull( meta=self.skill_index[slug], workflow=workflow.strip(), exception_rule=exception_rule.strip() ) self.skill_full_cache[slug] = full return full return None def load_skill_resource(self, slug: str, res_path: str) -> str: for path in SKILL_PATHS: skill_base = os.path.join(path, slug) full_res_path = os.path.abspath(os.path.join(skill_base, res_path)) if any(full_res_path.startswith(b) for b in SAFE_PATH_BLACKLIST): return "资源访问拒绝:禁止访问系统敏感路径" if not full_res_path.startswith(os.path.abspath(skill_base)): return "资源访问拒绝:禁止跨目录访问" if os.path.exists(full_res_path) and os.path.isfile(full_res_path): with open(full_res_path, "r", encoding="utf-8") as f: return f.read() return f"资源文件不存在:{res_path}" def unload_cache(self): self.skill_full_cache.clear()

4.3 验证请求与成功结果

把上面的加载器保存为 skill_loader.py,然后在同目录建 .agent-skills/skill-data-validation/SKILL.md,内容用第 3 节的模板。跑一段验证:

from skill_loader import SkillLoader loader = SkillLoader() print("索引池大小:", len(loader.skill_index)) print("已加载 Skill:", list(loader.skill_index.keys())) slug = loader.match_skill("帮我做一次入库数据合规检查") print("匹配结果:", slug) if slug: full = loader.load_full_skill(slug) print("流程长度:", len(full.workflow)) print("异常规则长度:", len(full.exception_rule)) rule = loader.load_skill_resource(slug, "references/data-rule.md") print("资源读取:", rule[:80]) loader.unload_cache() print("缓存已清空,索引保留:", len(loader.skill_index))

预期输出:索引池大小 1,匹配结果 skill-data-validation,流程长度几百字符,资源读取返回规范内容前 80 字,缓存清空后索引仍为 1。这说明三级加载按预期工作:索引常驻、流程按需、资源动态、用完卸载。

如果匹配返回 None,检查 triggers 是否覆盖了「合规检查」这个词;如果资源读取返回不存在,检查 references/data-rule.md 是否真的建了。这两个是最常见的验证失败点。

5. 常见报错排查:401、local proxy failed、reading choices、OAuth

Skill 加载器跑通后,真正调用模型执行流程时还会遇到各种报错。这一节对照真实错误信息,给出排查路径。

5.1 401 Unauthorized

报错原文通常是{"error": {"type": "authentication_error", "message": "invalid x-api-key"}}。原因三类:Key 没配、Key 复制不完整、Key 和 Base URL 不匹配。

排查步骤:先确认 .env 里 TAOTOKEN_API_KEY 有值且没有多余空格;再确认 Base URL 是 https://taotoken.net/api 而不是别的地址;最后用 curl 直接测:

curl -X POST https://taotoken.net/api/v1/messages \ -H "x-api-key: $TAOTOKEN_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ -d '{"model":"claude-sonnet-4-20250514","max_tokens":16,"messages":[{"role":"user","content":"hi"}]}'

返回 200 说明 Key 没问题,问题在代码读取环节。返回 401 就重新生成 Key。

5.2 local proxy failed

这个报错一般出现在你本地配了代理工具,但代理进程没启动或端口不对。报错原文类似Connection refused: local proxy failed to connect。

排查:检查你的 HTTP_PROXY / HTTPS_PROXY 环境变量是否指向了一个没运行的端口。如果你不需要代理,直接 unset:

unset HTTP_PROXY unset HTTPS_PROXY unset ALL_PROXY

然后重启你的 Agent 进程。很多团队在内网环境误配了代理变量,导致请求发不出去,追查半天以为是 Key 问题。

5.3 reading choices 相关报错

报错原文可能是Cannot read properties of undefined (reading 'choices')或reading choices of null。这是响应体解析失败,通常因为返回的不是标准 JSON,或者你按 OpenAI 格式解析但实际返回的是 Anthropic 格式。

排查:先打印原始响应print(resp.text),看返回结构。如果是 Anthropic 格式,取content[0].text而不是choices[0].message.content。如果你用的是兼容层,确认 Base URL 路径是否正确,/v1/messages 和 /v1/chat/completions 返回结构不同。

5.4 OAuth 相关报错

报错原文可能是OAuth token expired或invalid_grant。如果你用的是 Claude Code 或 Codex 这类工具,它们可能走 OAuth 流程而不是 API Key。排查:确认你的工具配置的是 API Key 模式还是 OAuth 模式。如果用 API Key,在配置里显式指定:

{ "baseURL": "https://taotoken.net/api", "apiKey": "sk-你的密钥", "model": "claude-sonnet-4-20250514" }

三件套 Base URL、Key、Model ID 必须同时出现,缺一个就会走默认 OAuth 或报配置错误。如果你用 CC Switch 或 Cline MCP,同样检查这三项是否填全。

5.5 Skill 匹配失败但模型正常

模型能通但 Skill 不触发,检查三点:triggers 是否覆盖用户实际用词;description 是否太模糊;priority 是否被其他 Skill 压过。把 match_skill 里的 score 打印出来,看每个 Skill 的打分,就能定位是哪个环节没命中。

6. 语义一致 CTA:把 Skill 体系接到你的工程流里

到这里,你已经有了目录规范、SKILL.md 模板、加载器代码、验证步骤和排障清单。接下来就是把它接到你真实的项目里。

如果你还在选模型接入层,可以先到模型对话页面跑几个业务场景的 prompt,确认模型对指令的遵循度:https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=model_chat 。做长期编码和 Agent 调度的团队,建议直接看 Coding Plan,把 Skill 加载器和模型调用整合到统一的工程流里:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=coding_plan 。

接入文档里有完整的 API 参数说明和示例,配置加载器时对照着看能少踩很多坑:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=api_doc 。如果你用 Claude Code 做开发,Anthropic 兼容配置参考这里:https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=claude_code 。

最后给一个实操建议:先把一个真实业务场景抽成 Skill,跑通三级加载,再复制目录结构批量改造其他场景。不要一上来就全量重构,先用一个 Skill 验证加载器和团队协作流程,确认没问题再铺开。这样风险最小,迭代最快。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询