1. 从一次发票去重翻车说起:AI大模型Skills到底是什么
先说结论:AI大模型Skills是一套用SKILL.md文件把「某类任务该怎么做」固化下来的标准做法,它能让Agent在遇到同类任务时自动按你写好的流程执行,而不是每次靠临场提示词碰运气。它适合谁?适合那些反复处理同一类需求、又不想每次都重新解释一遍的开发者、运营和业务同学。核心检索词就三个:Skills、SKILL.md、Agent。
我拿自己踩过的坑开场。有段时间我经常要处理一批发票截图,里面有重复的,需要挑出来。第一反应当然是丢给模型做,结果它上来就搞了个视觉相似度匹配,把背景板一样的发票全判成重复,交易号完全不同的也被算进去了。问题不在模型笨,而在于我没告诉它「正确路径是什么」。视觉相似度这条路看起来合理,实际是错的;真正靠谱的是OCR提取交易号再做模糊匹配。
于是我换了个思路:与其每次临场纠正,不如把正确方法写成一个skill。写完之后再让它跑,它老老实实先OCR、再正则提交易号、再模糊匹配、最后分组输出,两组重复发票一次抓准。这就是Skills的价值——把正确路径写死,把错误路径封死。prompt临场对话很难稳定做到这一点,因为每次对话模型都可能重新「发挥」。
再往深一层看,Skills本质上是你给AI配置的一份最佳实践手册。它就是一个文件夹加一个markdown文件,文件名通常叫SKILL.md。文件开头是YAML元数据,声明这个skill叫什么、在什么场景下触发、能做什么;正文就是具体指令,一步步写清楚流程。你还可以在同级目录放参考文献、脚本、模板,让Agent按需读取。
为什么现在值得认真学?因为过去这套东西几乎都是平台私有的,你在这个IDE里调顺的流程,换个CLI、换个Agent框架就得重来。而近两个月,从Claude Code到Codex再到Google的Antigravity,越来越多工具开始向同一个目录结构和SKILL.md格式收敛。一次编写、多平台通用正在从口号变成现实。对个人来说,这意味着你积累的经验不再绑死在某个工具上;对团队来说,这意味着可以把业务SOP直接产品化成可复用的技能包。
下面我会从目录结构、SKILL.md模板、可复制配置,到在TaoToken统一Key通道下验证调用链路,一步步带你搭起来。全程可跟做,不需要你懂模型训练,甚至不需要你精通编程,能把经验写成可执行的SOP就够了。
2. Antigravity与Agent的Skills目录结构:项目级和全局级怎么放
要动手写skill,第一步是搞清楚文件放哪。目前主流平台对Skills的目录约定已经趋于一致,主要分两种:项目级和全局级。理解这两者的区别,能帮你决定哪些skill该跟着项目走、哪些该装在自己机器上到处用。
项目级Skills放在项目根目录下的固定路径里。以Antigravity为例,约定是<workspace-root>/.agent/skills/<skill-folder>/。假设你的项目叫my-project,那么完整结构就是:
my-project/ └── .agent/ └── skills/ └── my-skill/ └── SKILL.md这种放法的好处是能跟着git走。你把它提交到仓库,团队成员clone下来就自动获得了这套技能,不需要每个人单独配置。对于团队协作场景,项目级Skills特别适合放那些「和这个项目强相关」的规范,比如这个项目的代码风格检查流程、这个项目的接口文档生成规则。
全局Skills则放在用户目录下,配置一次,你电脑上所有项目都能用。Antigravity的全局路径是~/.gemini/antigravity/skills/<skill-folder>/。其他平台的全局路径大同小异,比如Claude Code习惯用~/.claude/skills/。全局级适合放那些「跨项目通用」的能力,比如你的通用代码审查流程、你的通用文档写作规范、你的通用数据处理套路。
Agent在开始对话时会做三件事:扫描所有可用skills、把你的任务和skill的description做匹配、匹配上就自动加载并执行。你也可以明确指定,比如直接说「用my-skill帮我做这个任务」,它就会跳过匹配直接调用。
这里有个容易踩的坑:目录层级不能错。很多人把SKILL.md直接放在skills根目录下,或者文件夹名和skill的name字段对不上,导致Agent扫描不到。记住,是skills/<skill-folder>/SKILL.md,中间必须有一层以skill名命名的文件夹。另外,文件夹名建议用英文小写加连字符,避免空格和中文,兼容性最好。
再补充一个组织技巧:一个skill文件夹里不只能放SKILL.md。你可以建references/放参考资料,建scripts/放可执行脚本,建templates/放输出模板。SKILL.md正文里可以引用这些文件,比如「参考references/format.md里的格式要求」。这样你的skill就从一份说明升级成了一个自带资源的小工具包。
理解了目录结构,接下来就是最核心的部分:SKILL.md本身怎么写。这是决定你的skill能不能被正确触发、能不能稳定执行的关键。
3. 可复制的SKILL.md模板与YAML配置:从零写一个能跑的skill
这一节给你一份可以直接抄的SKILL.md模板,以及配套的目录配置。我以「发票去重」这个真实场景为例,你可以照着改成自己的需求。
先看完整的目录结构:
.agent/ └── skills/ └── invoice-dedup/ ├── SKILL.md ├── references/ │ └── ocr-notes.md └── scripts/ └── extract_txn.py然后是SKILL.md的完整内容。注意开头必须是YAML frontmatter,用三个连字符包起来:
--- name: invoice-dedup description: 通过OCR提取交易号来识别重复发票。当用户上传多张发票截图并需要找出重复项时使用。适用于财务报销、票据核对场景。 --- ## 目标 从一批发票截图中找出重复的发票,按重复组输出。 ## 执行步骤 1. 对每张发票图片调用OCR,提取全部文本内容。 2. 用正则表达式提取交易号,交易号通常是20到30位连续数字。 3. 对提取到的交易号做模糊匹配,容忍OCR可能产生的个别字符误差。 4. 将匹配到相同交易号的发票分为一组,输出重复组列表。 ## 约束 - 禁止使用视觉相似度判断重复,背景相同不代表发票重复。 - 交易号提取失败时,标记为「无法判定」,不要猜测。 - 输出必须是纯JSON数组,不要包裹markdown代码块。 ## 参考 - OCR注意事项见 references/ocr-notes.md - 交易号提取脚本见 scripts/extract_txn.py这份模板里有几个关键点值得展开。第一,name字段要和文件夹名保持一致,这是Agent匹配的依据之一。第二,description字段极其重要,它决定了Agent在什么场景下会触发这个skill。写法上要包含「做什么」和「什么时候用」,最好把触发场景的关键词写进去,比如「上传多张发票截图」「找出重复项」。description写得模糊,skill就永远不会被触发。
第三,正文的步骤要具体到可执行。不要写「处理一下数据」这种模糊指令,要写「用正则提取20到30位连续数字」。步骤越具体,模型自由发挥的空间越小,结果越稳定。第四,约束部分专门用来封死错误路径。我那次翻车就是因为没写「禁止视觉相似度」,加上这条之后模型就不会再走弯路了。
如果你用的是支持JSON配置的工具,比如某些CLI的settings文件,可以这样声明skill的加载路径:
{ "skills": { "enabled": true, "paths": [ "./.agent/skills", "~/.gemini/antigravity/skills" ], "autoLoad": true } }如果是TOML风格的配置,等价写法是:
[skills] enabled = true auto_load = true paths = ["./.agent/skills", "~/.gemini/antigravity/skills"]配置里的paths数组把项目级和全局级都包含进来,autoLoad设为true表示对话开始时自动扫描。这样你既保留了项目专属技能,又能用上全局通用技能。
写skill有个心法:把它当成写给一个聪明但完全不了解你业务的新人看的SOP。新人不知道哪些路是坑,所以你要把正确路径和错误路径都写清楚。你写得越像操作手册,Agent执行得越稳。反过来,如果你写得像散文,模型就会自由发挥,结果不可控。
模板有了,配置也有了,下一步是验证它到底能不能跑通。这里我用TaoToken的统一Key通道来演示,因为它的接口兼容主流格式,验证起来最省事。
4. 在TaoToken统一Key通道下验证Skills调用链路
写完skill不验证,等于没写。这一节带你把调用链路跑通,确认Agent真的能识别、加载、执行你的skill。我用TaoToken作为统一入口,因为它提供兼容OpenAI格式的API,一个Key就能对接多种模型,验证Skills调用链路时不用来回换配置。
先拿Key。访问 https://taotoken.net/api-keys 创建你的API Key,然后到 https://taotoken.net/doc 看接入文档确认最新的Base URL和参数格式。Base URL是 https://taotoken.net/api ,注意这个地址不带任何查询参数。
拿到Key之后,先做一次最小请求,确认通道本身是通的。用curl测试:
curl https://taotoken.net/api/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -d '{ "model": "claude-sonnet-4-5", "messages": [ {"role": "user", "content": "回复两个字:通了"} ] }'如果返回正常的JSON,里面有choices数组,说明通道没问题。这一步很关键,因为后面skill调用失败时,你要能区分是通道问题还是skill配置问题。
通道通了之后,把skill目录准备好,然后发起一次带skill上下文的请求。很多Agent框架会自动扫描skills目录,但为了验证链路,我们可以手动把SKILL.md内容作为system消息注入,模拟Agent加载skill后的效果:
import os import requests api_key = os.environ["TAOTOKEN_API_KEY"] base_url = "https://taotoken.net/api" with open(".agent/skills/invoice-dedup/SKILL.md", "r", encoding="utf-8") as f: skill_content = f.read() resp = requests.post( f"{base_url}/v1/chat/completions", headers={ "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" }, json={ "model": "claude-sonnet-4-5", "messages": [ {"role": "system", "content": f"你可以使用以下skill:\n\n{skill_content}"}, {"role": "user", "content": "我有三张发票截图,交易号分别是 12345678901234567890、12345678901234567891、12345678901234567890,帮我找出重复的。"} ] } ) print(resp.json()["choices"][0]["message"]["content"])预期结果是模型按skill里定义的步骤,识别出第一张和第三张交易号相同,输出一个重复组。如果它输出的是纯JSON数组、没有多余解释,说明skill的约束生效了。如果它开始用视觉相似度之类的思路,说明你的约束没写到位,回去补强SKILL.md。
实测下来,这套链路跑通之后,你可以把同样的skill目录复制到Antigravity、Claude Code、Codex里,只要它们支持SKILL.md标准,行为基本一致。这就是「一次编写、多平台通用」的实际体感。
验证时建议准备几个边界用例:交易号提取失败的、只有一张发票的、全部不重复的。看模型在这些情况下是否遵守了「无法判定就标记、不要猜测」的约束。边界用例通过,才说明skill真的稳。
链路验证完,接下来聊聊实际使用中最容易遇到的报错,以及怎么快速定位。
5. 常见报错排查:401、local proxy failed与reading choices
Skills调用链路的报错,大致分三类:认证问题、网络通道问题、响应解析问题。我按真实遇到的顺序拆开讲。
第一类,401 Unauthorized。这个最常见,通常是Key没传对或者传了但格式错了。检查三件事:请求头里是不是Authorization: Bearer <你的Key>,Bearer和Key之间有一个空格;Key是不是复制完整了,有没有多出换行或空格;环境变量TAOTOKEN_API_KEY是不是真的被读到了,可以在代码里先print一下确认。如果Key本身没问题,检查是不是用错了Base URL,注意API地址是 https://taotoken.net/api ,不要自己拼错路径。
第二类,local proxy failed。这个报错通常出现在你本地配了某些网络转发工具,或者环境变量里残留了HTTP_PROXY、HTTPS_PROXY。Skills调用走的是标准HTTPS请求,如果本地有转发层拦截,就会报这个。排查方法是先清掉相关环境变量再试:
unset HTTP_PROXY unset HTTPS_PROXY unset ALL_PROXY然后重新跑一次最小请求。如果通了,说明就是转发层的问题。另外检查一下你的请求库有没有读取系统代理设置,requests默认会读环境变量,可以显式传proxies={"http": None, "https": None}来绕过。
第三类,reading choices 相关报错,比如KeyError: 'choices'或者list index out of range。这说明你拿到了响应,但响应结构和你预期的不一样。常见原因是请求本身失败了,返回的是一个错误对象而不是正常的completion结构。正确的做法是先判断状态码和响应体:
data = resp.json() if "choices" not in data: print("异常响应:", data) else: print(data["choices"][0]["message"]["content"])这样你能看到真实的错误信息,而不是被一个KeyError掩盖。如果错误信息里提到模型不存在,检查你的model字段拼写;如果提到参数错误,对照接入文档核对字段名。
还有一类和skill本身相关的「软报错」:模型没有触发skill,或者触发了但没按步骤走。这不是程序报错,但更隐蔽。排查方向是检查description是否包含足够的触发关键词,检查SKILL.md的YAML frontmatter格式是否正确(三个连字符、字段名拼写、缩进)。YAML对缩进敏感,多一个空格都可能解析失败。可以用在线YAML校验工具先验证一遍frontmatter。
如果你用的是Claude Code这类带OAuth登录的工具,遇到认证相关报错时,先确认登录态是否有效,再检查是不是同时配了OAuth和API Key导致冲突。一般建议二选一,用API Key的方式更可控,也方便在TaoToken统一管理。
排查的核心思路是分层:先确认通道通不通,再确认认证对不对,最后确认skill内容和响应解析。一层层排除,比盲目改代码高效得多。
6. 把Skills用起来:从验证模型到长期编码的落地路径
链路跑通、报错会排查之后,剩下的就是把它用起来。这里给你几条实际落地的路径,按使用频率从高到低排。
如果你只是想先感受一下Skills调用链路的效果,最直接的方式是打开模型对话页面,把SKILL.md内容贴进去,手动构造一次调用,看看模型是否按你的流程走。地址是 https://taotoken.net/chat ,适合快速验证skill的description和步骤写得对不对。
如果你打算把Skills用在日常编码里,比如让Agent按你的代码规范自动审查、按你的模板生成接口文档,那更适合用Coding Plan这类长期方案。它适合需要持续调用、把skill固化进工作流的场景,地址是 https://taotoken.net/coding-plan 。配置的时候记得把Base URL、API Key、Model ID三件套都填全,缺一个都会导致调用失败。
如果你要管理多个项目的Key和用量,控制台是必须熟悉的。在 https://taotoken.net/console 里可以创建、轮换、删除Key,也能看到调用记录,排查问题时特别有用。Key的管理页面在 https://taotoken.net/api-keys ,建议给不同项目分配不同的Key,方便隔离和追踪。
对于用Claude Code做开发的同学,接入文档里有专门的配置说明,地址是 https://taotoken.net/doc 。照着文档把Base URL和Key配好,再把skill目录放到约定位置,就能在编码过程中自动触发skill。
最后说个我自己的习惯:每写完一个skill,先别急着用到生产任务上,拿三个边界用例跑一遍。通过了再正式用。skill这东西,写的时候多花十分钟把约束写清楚,用的时候能省下几十次临场纠正。它不是什么高深技术,就是把你的经验老老实实写成可执行的步骤,然后让Agent照着做。真正难的不是写SKILL.md,而是想清楚「这件事的正确路径到底是什么」——想清楚了,写下来就是几分钟的事。