从重复对话到Agent Skills:AI助手技能包的构建与调优指南
发布时间:2026/9/8 3:35:01 锦皓数字建站

最近被问得最多的问题居然是skills 到底是什么。翻开技术社区到处都在聊给 AI 助手加 skills翻招聘 JD不少 AI 产品岗都开始把会编写 Agent Skills写进要求里。我一开始也以为这只是又一个包装出来的营销概念直到自己在一个真实项目里从零搭了一套才对技能这两个字有了完全不一样的理解。简单说Skills 就是给 AI 助手配置的可复用能力模块有点像给新员工的一份标准作业手册外加一套趁手工具手册告诉它在什么场景按什么流程干活工具让它不用每次从零造轮子。跟传统 Prompt 最大的区别在于——它把原来每次都要重复交代的背景、步骤、规范固化成了可以被自动匹配和加载的资产。这篇文章不是官方文档的复读而是我从需求分析、目录设计、SKILL.md 编写、辅助脚本实现到触发测试和踩坑修复的完整记录。想给 AI 助手加技能的开发者或者正在做 Agent 产品的工程师可以参考这套思路。1. 先从一次重复对话说起我为什么要折腾 Skills1.1 那个让我抓狂的调试场景事情要从一次相当乏味的调试经历讲起。当时我在维护一个带登录鉴权的 Python Web 服务需要反复让 AI 帮忙排查接口报错。每次对话我都得把同一套背景信息重新粘贴一遍项目目录结构、依赖环境、代码风格约定、某个接口的入参出参定义然后才能进入真正的帮我看看这个 500 错误环节。一顿操作下来真正干活的时间不超过五分钟但来回铺垫上下文却花了快半小时。更让人烦躁的是这种重复不是一次两次而是每天都在发生。同一个项目同一个风格规范同一套目录说明我复制粘贴了不下二十遍。后来我试着把这些背景写进一个超长 Prompt想着一次性说清楚以后就不用重复了。结果更糟。Prompt 一长模型的行为就开始变得犹豫。它会把很多不相干的信息也当成指令去理解经常在我只问一个接口报错的时候突然开始给我讲解整个项目的架构设计。不同任务塞在同一个 Prompt 里还会互相干扰——比如我既写了代码风格用 Black 格式化又写了遇到问题时先解释原因再给方案模型常常抓不住重点答非所问。那一刻我意识到问题不在 AI而在我给它的信息组织方式。我一直在用对话这种一次性介质传递本应该固化的流程知识。1.2 从 Prompt 到 Skills 的转变逻辑想明白之后思路其实很简单为什么不把任务操作手册和具体执行分开现实工作中一个新员工入职你不会每天把公司规章制度从头到尾给他念一遍。你给他一本员工手册再让他跟着老员工做几次他就会了。手册是长期资产对话是临时沟通。Skills 解决的就是这个长期资产的问题。它把一套任务的背景知识、执行步骤、质量标准和辅助工具打包在一起放在一个固定目录里。AI 在对话中遇到相关需求时会自己去读取这个技能包按里面的流程执行而不是等用户把每个细节重复一遍。这个设计哲学跟我在现实工作中见过的SOP 加工具库如出一辙把经验显性化把操作空间交给执行者但边界和步骤由人来定。只不过这次执行者从人类同事变成了模型。1.3 Skills 和几个近亲概念的边界很多人会问Skills 跟普通 Prompt、Function Calling 到底有什么区别我刚开始也有这个疑惑实际用了之后才理清楚。下面这张表是我自己总结的对照维度普通 PromptFunction CallingSkills核心形态自然语言指令结构化 API 定义文档 脚本 目录触发方式每次对话手动交代模型按需求调用按任务语义自动匹配适用场景简单问答、风格设定外部系统交互多步骤工作流、专业流程上下文开销随需求线性增长取决于函数定义渐进式加载按需读取可复用性低复制粘贴高接口即契约高含过程和工具Function Calling 解决的是能不能调外部 API的问题核心是接口契约Skills 解决的是该不该按一套流程走的问题核心是流程和规范。两者可以配合使用——Skill 内部的脚本可以调用外部 API但 Skill 本身关心的不是单个函数而是一整套任务怎么完成。需要强调的是渐进式加载这个概念。传统 Prompt 是先把所有内容塞进上下文模型全量读取Skills 则是让模型先看一句话描述判断要不要加载加载的时候也是按块读取正文不一定把所有附件都读一遍。这意味着一个项目的十几个 Skill 可以共存而不会像十几个长 Prompt 那样直接把上下文撑爆。2. Skills 到底是什么拆开看就是一个文档加一个脚本目录2.1 一个最小 Skills 目录长什么样Skills 的构造比我想象中朴素得多。我自己建的最小可用技能包目录长这样skills/ └── web-audit/ ├── SKILL.md # 技能说明书核心入口 ├── scripts/ │ └── analyze_headers.py # 辅助脚本处理精确计算类工作 └── references/ └── checklist.md # 参考资料详细清单放这里这个结构里SKILL.md 是灵魂。它是一份 Markdown 文档里面写清楚了这个技能是干什么的、什么时候触发、按什么步骤执行、遵守什么规范。模型在对话中会先读到这个文档的简介部分决定是否加载整个技能。scripts 目录放的是可执行脚本比如抓取网页、解析数据、算指标这类模型不擅长但程序很擅长的事情。references 目录放参考资料比如一份很长的检查清单、术语表、或者领域知识。这样安排的好处是主文档保持精简模型加载起来快大块头内容按需读取。2.2 SKILL.md 的 frontmatter 和正文设计SKILL.md 的结构很有讲究。文件开头必须有一段 YAML 格式的 frontmatter里面至少包含name和description两个字段--- name: web-audit description: 当用户想要对一个网站进行技术健康检查或者分析网页的响应头、SEO 元标签、基础性能指标时使用。 ---我在反复测试后发现description的写法是整个文件里最需要花心思的地方。它就像是技能包的门面模型就是靠读这段描述来判断这个技能适不适合当前任务。写得太抽象模型不知道什么时候该用写得太啰嗦又被模型当成多余的说明文字忽略掉。我自己习惯用的句式是当用户需要 X 时使用把触发的动作、操作对象和应用场景都说清楚。比如上面这个例子检查网站 响应头 SEO 元标签 性能指标就是一个足够清晰的触发条件组合。frontmatter 之后是正文。正文就是纯 Markdown写执行步骤和行为规范。这里有一个特别重要的原则关键行为准则要放在正文开头。因为 SKILL.md 正文在被模型读取时并不是一个字不漏地全量进入上下文而是分块加载的。放在越前面的内容被完整读取的概率越高。如果你把核心规则埋在文档第 500 行模型很可能读不到。2.3 为什么不用 JSON 或 YAML 这种结构化格式刚开始我也疑惑过既然要写步骤要写规则为什么不直接用 JSON 或者 YAML 把结构化做到底后来实践中想明白了。SKILL.md 本质上不是给机器执行的数据而是给模型理解指令的文本。Markdown 的优势在于可读性好模型可以跳读关键标题文本本身就是指令不需要再经过一层解析而且维护成本低谁都可以改。脚本则恰恰相反——它要处理精确的逻辑必须用严格的语言来写。这就解释了为什么 Skill 要分成 SKILL.md 和 scripts 两个部分一个是自然语言层面的标准作业手册一个是机器层面的工具实现。什么该用自然语言什么该写代码是我在设计任何 Skill 时都会先问自己的问题。2.4 辅助脚本在 Skill 里扮演的角色辅助脚本解决的是模型算不准和看不见的问题。比如让模型自己去数一个 HTTP 响应头里有多少个安全相关的字段它可能会写得五花八门但如果让它跑一段 Python 脚本结果就是确定性的。脚本要放进 Skill需要满足几个条件首先它必须有明确的输入输出约定让模型知道怎么调用、怎么解析结果其次它必须容错输入非法 URL 时要能优雅退出而不是抛一堆堆栈最后它要足够短小把核心逻辑讲清楚就行不需要做成一个完整工程。我在设计脚本时一定会做的一件事是在文件开头写清楚它的用途和用法#!/usr/bin/env python3 analyze_headers.py - 分析指定 URL 的 HTTP 响应头与页面元信息。 用法: python analyze_headers.py url 输出: JSON 格式的诊断结果包含响应状态码、关键响应头、页面标题和 meta 描述。 这段注释不是写给人类的而是写给模型的。模型读到脚本时会先看这段说明决定要不要调用以及怎么调用。把脚本的说明书写在脚本内部比单独在 SKILL.md 里描述要有效得多。3. 手把手写一个网站分析Skill从零到可运行3.1 场景定义与需求拆解光讲概念容易飘下面我用一个实际能跑的案例把流程串一遍。场景是这样我希望 AI 能帮我快速对一个网站做技术体检检查它的 HTTP 响应头是否安全、SEO 基础元素是否齐全、页面有没有明显性能隐患。以前我需要手动用 curl 看响应再用浏览器开开发者工具最后人工整理报告现在要让 AI 一句帮我看看这个网站健康吗就能自动加载技能、跑脚本、输出结构化报告。需求拆解下来这个技能要做的事有三件根据用户给的 URL 发起 HTTP 请求拿到响应头和页面 HTML解析响应头里的安全字段如 Strict-Transport-Security、Content-Security-Policy解析 HTML 里的 SEO 元标签title、meta description、canonical按检查清单输出一份诊断报告标注出哪些项通过、哪些项缺失整个过程的关键信息来自外部网络模型自己访问不了所以必须依赖脚本。3.2 创建目录结构和 SKILL.md先创建目录mkdir -p skills/web-audit/scripts touch skills/web-audit/SKILL.md然后写 SKILL.md。正文部分我采用先定行为边界再给执行步骤的结构--- name: web-audit description: 当用户想要检查一个网站的技术健康状况或者需要分析某网页的 HTTP 响应头、SEO 元标签、基础性能指标时使用。适用于站长排查站点问题、SEO 优化前的技术体检等场景。 --- # 网站技术体检技能 你是一个网站技术分析师。收到用户的 URL 后按以下流程进行检查输出结构化报告。 ## 执行步骤 1. 请求用户提供完整 URL。如果用户只给了域名默认补全为 https:// 前缀。 2. 调用 scripts/analyze_headers.py 脚本传入完整 URL。 3. 接收脚本输出的 JSON 结果逐项解析。 4. 按照 references/checklist.md 中的清单逐项对比生成报告。 ## 行为规范 - 报告必须先给结论摘要再列详细检查项。 - 每个检查项必须标注状态[通过]、[警告]、[失败]。 - 不要臆测脚本没有输出的指标。脚本没有返回的字段在报告中标注未检测。 - 如果脚本执行报错请先检查 URL 格式是否正确再尝试一遍如果仍然失败向用户说明原因不要编造结果。这个 SKILL.md 的关键在于行为规范部分。它没有直接写具体的技术检查标准而是定义了 AI 怎么跟用户交互、怎么处理不确定情况、怎么输出报告。这些边界不写清楚模型很容易在结果不好看的时候开始自由发挥。3.3 编写辅助脚本接下来是辅助脚本。我使用 Python 的 requests 和 BeautifulSoup 来实现这两个库是通用的 HTTP 与 HTML 解析方案大部分环境里都能直接装#!/usr/bin/env python3 analyze_headers.py - 分析指定 URL 的 HTTP 响应头与页面元信息。 用法: python analyze_headers.py url 输出: JSON 格式的诊断结果。 import sys import json import requests from bs4 import BeautifulSoup def analyze(url): result {} try: resp requests.get(url, timeout10, headers{ User-Agent: SkillBot/1.0, }) except requests.RequestException as e: # 失败时也输出固定结构方便模型识别错误类型 return { error: str(e), url: url, status: failed, headers: {}, meta: {} } result[final_url] str(resp.url) result[status_code] resp.status_code # 关键安全响应头检查 security_headers [ Strict-Transport-Security, Content-Security-Policy, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, ] headers_snapshot {} for h in security_headers: headers_snapshot[h] resp.headers.get(h, None) # 解析页面元信息 meta_info {} try: soup BeautifulSoup(resp.text, html.parser) if soup.title: meta_info[title] soup.title.get_text(stripTrue)[:200] desc soup.find(meta, attrs{name: description}) meta_info[description] desc.get(content, ).strip()[:300] if desc else None canonical soup.find(link, relcanonical) meta_info[canonical] canonical.get(href, ).strip() if canonical else None except Exception as e: meta_info[parse_error] str(e) result[security_headers] headers_snapshot result[meta] meta_info result[status] success return result if __name__ __main__: if len(sys.argv) 2: print(json.dumps({error: no url}, ensure_asciiFalse)) sys.exit(1) target sys.argv[1] if not target.startswith(http://) and not target.startswith(https://): target https:// target print(json.dumps(analyze(target), ensure_asciiFalse, indent2))这个脚本做了几件事请求指定 URL提取 5 个常见安全响应头解析页面标题、描述和 canonical 标签最后全部打包成 JSON 输出。注意两个细节一是请求失败时也返回固定的 JSON 结构而不是让模型去读堆栈错误二是输出格式统一用 JSON因为模型解析 JSON 的成本远低于解析自由文本。脚本写完后我在本地先跑了一遍python analyze_headers.py https://example.com输出结果结构完整模型拿到这份 JSON 就能按 SKILL.md 的规范生成报告了。3.4 触发测试与效果验证把这个 Skill 放到模型可加载的 skills 目录后我直接在对话里测试了一句帮我看看 https://example.com 这个站的技术健康状况。模型读完描述后自动匹配了 web-audit 这个技能然后按部就班执行了脚本最终返回的报告包含结论摘要、检查项列表和未检测项说明。整个过程我没有额外交代任何背景或步骤这就是 Skills 该有的体验——它把怎么干活这件事封装进了技能包里用户只需要表达意图。4. 让 Skill 更好用的几个关键调优描述、上下文与版本4.1 描述怎么写加载才准一个技能会不会被正确触发很大程度取决于description的写法。我对比过两种写法差异非常明显写法效果网站分析技能过于抽象模型不清楚何时触发常出现漏触发当用户想要检查一个网站的技术健康状况或分析网页响应头、SEO meta 标签、基础性能指标时使用触发准确率高模型能快速判断是否加载写描述时要避免抽象名词堆砌尽量用动作 对象 场景的句式。检查网站技术健康是动作响应头、SEO 元标签是对象站长排查、SEO 体检是场景。三个要素齐全模型才有足够的依据做判断。4.2 上下文窗口的预算控制刚开始做 Skill 时我犯过一个典型错误想把所有参考资料都塞进 SKILL.md生怕模型不知道某个细节。结果文档写了上千行模型加载变慢而且指令被大量背景信息稀释输出质量反而下降。后来我给自己定了三条规则SKILL.md 正文控制在 300 到 500 行以内只保留流程、规则和判断标准大段参考资料放 references 目录由 SKILL.md 引导模型按需读取任何可以通过脚本动态获取的数据都不写死在文档里这三条规则的本质是把静态知识和动态数据分离。静态知识是指那些不会频繁变化的规则放文档里合适动态数据是每次执行都可能不同的必须靠脚本去拿。混在一起不但浪费上下文还容易让模型产生幻觉式填充——它会倾向于相信写死的旧数据而不是去重新获取。4.3 版本管理与团队协作Skills 是纯文本资产天然适合用 git 管理。但只有 git 还不够还需要一套轻量的版本约定。我现在每个 Skill 目录里都会维护一个 CHANGELOG.md同时在 SKILL.md 的 frontmatter 里加version字段--- name: web-audit description: 当用户想要检查一个网站的技术健康状况... version: 1.2.0 ---版本号跟着 git tag 走每次修改必须同时更新 CHANGELOG。这份 CHANGELOG 不是给 git 用的而是给模型和团队成员看的。模型在加载技能时如果能读到变更记录就知道哪些规则是最近更新过的团队协作时新成员看一遍 CHANGELOG 就能快速了解这个技能的演进过程。这套做法帮我避免过一次很尴尬的事故两个同事改的是不同版本的同名技能合代码时谁也不知道自己手里的是新是旧。5. 我踩过的四个坑和建议你的第一步5.1 坑一description 写得太抽象AI 根本不知道什么时候该用第一个坑就是在描述上翻的车。我最初的描述写的是网站分析技能四个字自以为言简意赅结果模型在我需要它的时候选择了沉默而在不需要的时候偶尔又跳出来刷存在感。后来把所有技能描述统一改成当用户需要 X 时使用的句式触发准确率才稳定下来。教训是描述是技能的入口入口不清晰技能再好也白搭。5.2 坑二把参考文档全塞进 SKILL.md导致指令漂移我在做第二个技能时把一整套安全响应头规范全写进了 SKILL.md想着这样模型就不需要外部资料了。结果它开始背诵规范条目把文档里的通用说明当成当前目标网站的实际情况来报告甚至忽略了用户给的真实 URL 和响应头。这是个典型的指令漂移问题。文档越长模型越容易把文档内容当成事实而不是背景。解决办法就是前面说的详细清单放 referencesSKILL.md 只保留流程和判断规则数据一律从脚本结果拿。5.3 坑三辅助脚本被沙箱拦截流程直接中断还有一次脚本里用了 requests 库去向外部 URL 发请求运行时被模型的沙箱安全策略拦住了。流程卡在半路模型也一脸懵最后只能让用户换个方式体验很差。这个坑的教训是在设计 Skill 时要提前考虑运行环境的限制。我现在的做法是在一个 Skill 里同时提供两条路径优先调用脚本如果脚本因权限问题失败则在 SKILL.md 里写明 fallback 方法比如让模型直接展示 curl 命令或提醒用户手动执行。同时我尽量在脚本开头用注释说明它的网络依赖让模型调用前心里有数。5.4 坑四改完不记录版本一塌糊涂这个前面提过但值得再强调一下Skills 落地到团队协作时没有版本记录简直是灾难。多个 Skill 同时维护每个人都改过但没人记得改了什么。后来强制所有 Skill 必须带 CHANGELOG.md 之后这个问题才根治。我的原则是任何一次修改无论大小都要在 CHANGELOG 里留下一行记录说明改了什么、为什么改。5.5 建议你的第一步如果你也想尝试我的建议是不要一上来就设计宏大复杂的技能而是找一个你已经重复了三遍以上的任务开始。比如你经常让 AI 帮你审查代码风格那就写一个 code-review 技能经常让 AI 帮你整理会议纪要那就写一个 meeting-notes 技能。关键判断标准是如果这个任务你已经重复过三次以上把它写成 Skill 的收益就超过了成本。写第一个 Skill 时参照这套最简模板就够了一个目录名字用短横线命名如web-audit一个 SKILL.md包含 frontmatter 和正文一个 scripts 目录只有需要脚本时才加一个 CHANGELOG.md不用急着追求完美先跑通再迭代。我第一个 Skill 只花了二十分钟就写完了远没有想象中复杂。如果你已经写过几个 Skill欢迎对照上面四个坑检查一下自己的项目——你的 description 足够具体吗SKILL.md 会不会太长脚本有没有考虑 fallback版本记录跟上没跟上这四个问题是我在实践中筛过一遍之后留下的经验清单希望也能帮你少走一段弯路。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。