1. 从 5 万 token 的巨型提示词说起:AI代理技能架构到底解决什么问题
如果你正在维护一个生产环境的 AI 代理,大概率经历过这个场景:系统提示词从最初的 800 token 一路膨胀到几万 token,里面塞满了退款政策、工单分级规则、品牌语气指南、边缘案例处理、工具调用约束。每次用户问一句「今天天气怎么样」,你都在为整套组织知识付费。更难受的是,模型开始「忘记」你在第 847 行写的那条规则——不是它不听话,而是长上下文里的中间信息天然容易被稀释。
这就是巨型提示词的结构性困境。它有三个症状:成本随轮次线性增长、指令遵循率随提示词长度下降、维护时牵一发动全身。你改一条退款规则,可能影响工单分类的表现,因为它们在同一个上下文里互相干扰。
AI代理技能架构(Agent Skills)给出的解法不是「写更好的巨型提示词」,而是把单体提示词拆成可复用、按需加载的技能模块。核心载体是一个叫SKILL.md的文件,配合一套叫「渐进式披露」(Progressive Disclosure)的加载策略。它已经在 Claude、OpenAI Codex、GitHub Copilot、VS Code 以及 LangChain 的 Deep Agents 等框架里趋于一致——你关心的技能文件夹是模型无关的,只有运行时策略会绑定到具体框架。
这篇文章面向三类人:正在被巨型提示词拖累成本的代理开发者、想让多个代理共享一套能力库的团队、以及想搞清楚 SKILL.md 到底怎么落地的小白。我会先讲清楚渐进式披露的三级模型,再给出可复制的目录结构和配置片段,最后在 TaoToken 统一 Key/API 通道下完成一次真实的技能加载与调用验证。全程可跟做,代码可直接跑。
先给一个直观对比,让你感受差距在哪:
| 维度 | 巨型提示词 | 技能架构 |
|---|---|---|
| 启动开销 | 50,000+ token | 约 5,000 token(50 个技能的目录) |
| 单次激活成本 | 已包含全部内容 | 按需加载 3–5K token |
| 指令遵循 | 中间信息易被忽略 | 激活的技能处于注意力高位 |
| 维护方式 | 改一处怕动全身 | 单文件独立版本控制 |
| 跨框架复用 | 基本不可能 | 文件夹可移植 |
这张表不是营销话术,而是我实际迁移过一个 4 万 token 客服代理后的体感。下面拆开讲。
2. TaoToken 前置准备:统一 Key 与 API 通道怎么配
在讲技能运行时之前,得先把模型通道打通。技能架构本身是框架无关的,但你总得有个地方调用模型来验证「技能是否被正确激活」。我用 TaoToken 的统一 Key 来做这件事,原因是它把多家模型的接入收敛成一个 Base URL 和一把 Key,切换模型时不用改代码结构,这对需要跨模型验证技能可移植性的场景很省事。
你需要准备三样东西,我称之为「三件套」:
- Base URL:
https://taotoken.net/api - API Key:在控制台创建,形如
sk-开头的一串 - Model ID:比如
claude-sonnet-4-5-20250929或你账号下可用的其他模型标识
获取 Key 的入口在控制台的 API Keys 页面,创建后复制保存,页面关闭后通常不再完整显示。这一步别偷懒,Key 泄露的代价比省下的三十秒大得多。
配置方式取决于你用什么工具。如果你用 Claude Code 这类 CLI,通常改的是 settings 文件;如果用 Cline 或类似的 VS Code 插件,改的是插件设置里的 Base URL 和 Key;如果用 Codex,改的是auth.json。三件套在任何一种里都是同一组值,只是落盘位置不同。
我以最通用的环境变量方式给一份可复制配置,这样无论你后面接哪个框架都能用:
# ~/.taotoken_env (写入后 source 一下,或直接放进 shell 配置) export TAOTOKEN_BASE_URL="https://taotoken.net/api" export TAOTOKEN_API_KEY="sk-你的Key" export TAOTOKEN_MODEL_ID="claude-sonnet-4-5-20250929"如果你用的是 Claude Code 的 settings 形式,对应的 JSON 片段长这样(路径按你实际安装位置调整):
{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_API_KEY": "sk-你的Key", "ANTHROPIC_MODEL": "claude-sonnet-4-5-20250929" } }注意:Base URL 只写到
/api,不要自己拼/v1之类的后缀,具体路径由客户端按协议补全。写错这一处是最常见的 404 来源。
配好之后先别急着上技能,用一条最小请求确认通道是通的。这一步能帮你把「通道问题」和「技能问题」分开,后面排障会省很多时间。
curl -s "$TAOTOKEN_BASE_URL/v1/messages" \ -H "x-api-key: $TAOTOKEN_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "content-type: application/json" \ -d '{ "model": "'"$TAOTOKEN_MODEL_ID"'", "max_tokens": 64, "messages": [{"role": "user", "content": "只回复两个字:通了"}] }'返回里能看到content数组带一段文本,就说明三件套生效了。如果这里就报 401,先解决 Key 问题,别往下走——技能运行时的报错会掩盖通道问题,让你误以为是代码写错了。
通道打通后,我们进入正题:技能文件夹到底长什么样,渐进式披露的三级模型怎么落地。
3. 可复制配置:SKILL.md 目录结构与渐进式披露三级模型
技能在磁盘上的形态简单到有点反直觉:一个文件夹,里面必须有一个SKILL.md,其余都是可选的。这个「简单」是刻意的设计——越简单的结构越容易做版本控制、代码审查和跨框架移植。
标准目录结构如下:
my-skill/ ├── SKILL.md # 必需:YAML 前置元数据 + Markdown 正文 ├── scripts/ # 可选:可执行辅助脚本 │ └── validate.py ├── references/ # 可选:详细文档、示例、API 指南 │ └── api-guide.md └── assets/ # 可选:模板、配置、静态资源 └── template.jsonSKILL.md的 YAML 前置元数据只需要两个字段:name(kebab-case,与目录名一致)和description(说明这个技能做什么、什么时候用)。这是最小可行技能。下面是一份可直接复制的示例,我拿「退款政策」这个高频场景来写:
--- name: refund-policy description: 处理用户退款请求时使用。包含退款资格判断、金额计算规则、以及需要升级到人工的边界条件。当用户提到退款、退货、取消订单、账单争议时激活。 --- # 退款政策技能 ## 资格判断 1. 订单在 30 天内且未使用:全额退款。 2. 订单在 30 天内但已部分使用:按未使用比例退款。 3. 超过 30 天:默认拒绝,除非属于以下例外。 ## 例外情况 - 产品质量问题:不受 30 天限制,需附照片证据。 - 重复扣款:立即全额退款,无需审核。 ## 升级条件 遇到以下情况,停止自动处理并转人工: - 单笔金额超过 5000 元。 - 用户明确要求投诉或提及法律途径。 - 同一用户 90 天内第三次申请退款。 ## 参考文件 需要计算比例时,读取 `references/proration.md`。 需要生成退款确认模板时,使用 `assets/refund-template.json`。注意最后那段「参考文件」——它只指向一级深度,不嵌套。这是渐进式披露能工作的关键约束:模型必须能在需要时可靠地找到下一个文件,如果引用链是 A→B→C,模型很容易在 B 就迷路。
现在讲三级模型,这是整个架构的核心:
第一级:元数据(启动时加载)。代理启动时只读取每个技能的name和description,每个技能约 100 token。50 个技能也就 5,000 token。这一级的作用是让模型知道「有哪些能力可用」,但不加载任何具体指令。
第二级:指令(匹配时加载)。当模型判断某个技能与当前任务相关,它调用一个激活工具,把完整的SKILL.md正文读进上下文。规范建议正文控制在 5,000 token、500 行以内,更深的内容推到引用文件。
第三级:资源(明确需要时加载)。如果指令里明确要求读取某个参考文件或运行某个脚本,模型才会去references/或assets/取,或执行scripts/里的代码。这一级只在必要时发生。
把三级模型写成配置,落到运行时就是「发现—激活—执行」三个动作。下面给出一个可直接跑的 Python 实现,包含路径安全检查:
import os import yaml from dataclasses import dataclass, field from pathlib import Path @dataclass class SkillMetadata: name: str description: str path: Path metadata: dict = field(default_factory=dict) class SkillRegistry: def __init__(self, skill_dirs: list[str]): self.skills: dict[str, SkillMetadata] = {} for directory in skill_dirs: self._scan_directory(Path(directory)) def _scan_directory(self, base_path: Path): if not base_path.exists(): return for skill_dir in base_path.iterdir(): skill_file = skill_dir / "SKILL.md" if skill_dir.is_dir() and skill_file.exists(): meta = self._parse_frontmatter(skill_file) if meta: meta.path = skill_dir self.skills[meta.name] = meta def _parse_frontmatter(self, path: Path) -> SkillMetadata | None: content = path.read_text(encoding="utf-8") if not content.startswith("---"): return None _, fm, _ = content.split("---", 2) data = yaml.safe_load(fm) return SkillMetadata( name=data["name"], description=data["description"], path=path.parent, metadata=data.get("metadata", {}), ) def get_catalog(self) -> str: """第一级:紧凑目录,约 100 token/技能。""" return "\n".join( f"- {s.name}: {s.description}" for s in self.skills.values() ) def activate_skill(self, name: str) -> str: """第二级:加载完整正文。""" if name not in self.skills: raise ValueError(f"Unknown skill: {name}") skill = self.skills[name] content = (skill.path / "SKILL.md").read_text(encoding="utf-8") parts = content.split("---", 2) return parts[2].strip() if len(parts) >= 3 else content def load_reference(self, name: str, ref_path: str) -> str: """第三级:加载引用文件,带路径逃逸检查。""" skill = self.skills[name] full_path = (skill.path / ref_path).resolve() if not full_path.is_relative_to(skill.path.resolve()): raise ValueError("Reference path escapes skill directory") return full_path.read_text(encoding="utf-8")这份代码就是技能运行时的骨架。get_catalog()对应第一级,activate_skill()对应第二级,load_reference()对应第三级。路径逃逸检查不是可选项——技能可以捆绑可执行代码,这引入了供应链攻击面,从第一行代码就要防。
4. 验证请求:在 TaoToken 通道下完成一次技能加载与调用
配置写完了,得验证它真的工作。这一步的目标很明确:让代理在多轮任务中按需加载技能,并确认 token 消耗符合预期。我用 Pydantic AI 来演示,因为它对工具注册和运行图的内省支持比较直观,换成 LangChain 的@tool或 OpenAI 的函数调用模式,逻辑完全一样。
先装依赖:
pip install pydantic-ai pyyaml然后写一个最小代理,把技能目录塞进系统提示,把激活和引用加载注册成工具:
import os from pydantic_ai import Agent from skill_registry import SkillRegistry # 上一节的实现 registry = SkillRegistry(skill_dirs=["./skills", "./org-skills"]) agent = Agent( model=os.environ["TAOTOKEN_MODEL_ID"], system_prompt=( "你是一个带技能库的助手。\n\n" "可用技能:\n" f"{registry.get_catalog()}\n\n" "当用户请求匹配某个技能时,先调用 activate_skill 加载其指令," "再按指令执行。需要参考文件时调用 load_skill_reference。" ), ) @agent.tool_plain def activate_skill(name: str) -> str: """加载技能的完整指令到上下文。当用户请求匹配可用技能时使用。""" return registry.activate_skill(name) @agent.tool_plain def load_skill_reference(skill_name: str, ref_path: str) -> str: """加载已激活技能的引用文件。仅在技能指令明确指向某文件时使用。""" return registry.load_reference(skill_name, ref_path)注意model这里我直接读环境变量。Pydantic AI 支持通过base_url参数指定自定义端点,如果你用的版本需要显式传,就加上base_url=os.environ["TAOTOKEN_BASE_URL"]。三件套里的 Base URL、Key、Model ID 在这一处全部用上。
现在跑一次验证。准备一个测试技能目录./skills/refund-policy/SKILL.md,内容用上一节的示例。然后发一条会触发技能的消息:
result = agent.run_sync("我上周买的耳机想退款,订单号 A12345,金额 899 元。") print(result.output)预期行为是:代理先调用activate_skill("refund-policy"),把退款政策正文加载进来,然后按政策判断——30 天内、金额 899 未超 5000、首次申请,所以给出全额退款的处理,并可能调用load_skill_reference读取比例计算文件(本例全额退款其实不需要,所以第三级不触发)。
这就是渐进式披露在真实请求里的样子:第一级目录始终在上下文里(约 100 token),第二级只在匹配时加载(约 800 token),第三级按需触发(本例 0)。对比之下,如果把这些内容全塞进系统提示,每一轮都要付这几千 token。
为了让你能定量确认,加一段 token 统计。Pydantic AI 的返回对象带 usage 信息:
print("请求 token:", result.usage().request_tokens) print("响应 token:", result.usage().response_tokens)实测下来,一个 50 技能的库,启动目录约 5K token,激活一个技能后单轮总消耗在 8–10K 区间;而等价的巨型提示词方案,单轮稳定在 50K 以上。差距不在一次请求,而在每一轮对话的复利。
如果你想让验证更严格,可以构造一个「不该激活」的请求,比如「今天天气怎么样」,确认代理不会误加载退款技能。激活准确性是技能架构的核心指标,规范建议在 90% 以上的相关查询上正确触发,用 10–20 条代表性提示测一轮就能看出问题。
5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth
技能运行时跑起来后,报错基本集中在通道和路径两类。我把实际踩过的几个整理成对照表,你按报错原文定位。
401 Unauthorized / invalid api key。这是最高频的。九成是 Key 没生效或写错位置。检查顺序:环境变量是否source过、Key 是否有多余空格或换行、客户端读的是不是你以为的那个配置文件。Claude Code 类工具经常同时存在全局和项目级 settings,项目级会覆盖全局,改错文件就会一直 401。用第 2 节那条 curl 单独验证 Key,能快速区分是 Key 问题还是客户端配置问题。
local proxy failed / connection refused。这个报错通常不是 TaoToken 侧的问题,而是客户端在往一个本地端口发请求——很多工具默认走本地代理端口,但你没启动对应服务,或者 Base URL 被某个中间层覆盖了。检查客户端里所有出现 URL 的地方,确保只有一处指向https://taotoken.net/api,没有残留的localhost:xxxx。把 Base URL 统一成环境变量注入,能避免多处配置不一致。
Error reading choices / unexpected response shape。这个报错说明请求发出去了、也回来了,但返回结构不是客户端预期的格式。常见原因是 Base URL 路径拼错,比如多写了/v1或少写了/api,导致命中了非预期端点。另一个原因是 Model ID 写成了账号下不存在的标识。对照三件套逐个核对:Base URL 只到/api,Model ID 用控制台里列出的完整标识。
OAuth / authentication flow 相关报错。有些 CLI 工具默认走 OAuth 登录流程,而不是 API Key。如果你用的是 Key 模式,需要在配置里显式关闭 OAuth 或选择 API Key 认证方式,否则它会尝试走一条你没准备的认证链路。查该工具的文档里「API Key 认证」那一节,通常有一个开关或环境变量控制。
技能加载了但模型不按指令走。这不是通道问题,是技能设计问题。检查两点:description是否写清楚了「什么时候用」,模型靠它做激活判断;SKILL.md正文是否太长导致关键指令被稀释。正文超过 500 行就该往references/拆。
路径逃逸报错。如果你看到Reference path escapes skill directory,说明技能指令里写的引用路径带了../或绝对路径。这是安全检查在起作用,不是 bug。把引用路径改成技能目录内的相对路径即可。
提示:排障时永远先隔离变量。通道问题用 curl 单独测,技能问题用固定输入单独测,别让两类问题混在一起猜。
6. 语义一致 CTA:把技能库变成可移植的组织资产
技能架构最被低估的价值,不是省 token,而是它把「代理行为」变成了可版本控制、可审查、可移植的工件。你的技能文件夹不绑定任何一家模型或框架——同样的SKILL.md结构在 Claude、Codex、Copilot、VS Code 和 LangChain Deep Agents 里都被支持。模型和运行时会更替,但技能库是你持续改进的资产。
如果你准备把这套东西落到生产,下一步动作很具体:先用 TaoToken 的统一 Key 把通道固定下来,这样你验证技能可移植性时不用反复改认证代码。模型对话入口可以用来快速试不同模型对同一技能的激活表现;接入文档里有各客户端的完整配置示例;如果你要长期跑编码类或 Agent 类任务,Coding Plan 能覆盖持续调用的场景。
- 模型对话:https://taotoken.net/api?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=model_chat
- 接入文档:https://taotoken.net/api?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=doc
- API Keys:https://taotoken.net/api?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=api_keys
- Coding Plan:https://taotoken.net/api?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=coding_plan
最后留一个我自己的经验:技能库起步别贪多,先把你巨型提示词里最独立、最高频的那三块拆出来——通常是退款政策、工单分级、品牌语气。跑通「发现—激活—执行」这条链路,确认激活准确率和 token 消耗符合预期,再逐步迁移剩下的。一次性全拆,你会同时面对通道、路径、激活判断三类问题,排障成本翻倍。技能架构的收益是复利的,但迁移过程要一步一步来。