ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AI Agent Skills:从提示词到可复用技能封装的实践指南

AI Agent Skills:从提示词到可复用技能封装的实践指南 如果只用一个词概括我过去一年在 AI 应用开发里的最大变化我会选 skills。不是简历上那一串能力列表而是把智能体从会聊天变成真能干活的那一层封装——一组指令、脚本和资源打包成可随时被模型调用的能力单元。我最早接触这个方向时手头最头疼的问题就是提示词越来越长、复用性越来越差同一个功能换一个项目就要重写一遍。后来转向技能化封装之后很多重复劳动直接被消掉了。这篇文章适合正在做 AI Agent、提示词工程或者想把自己的工作流沉淀成可复用模块的人。我会把 skills 的核心思路、目录结构、搭建步骤、运行时的坑和团队治理经验都过一遍尽量做到不仅让你看懂还能直接照着搭一个自己的技能库。1. Skills到底解决了什么问题从对话式提示词到可执行技能先说一个反直觉的结论提示词工程做得越好你的系统越脆弱。这不是危言耸听。当一段提示词超过 1500 字的时候模型对中段指令的遵循率会明显下降你精心编排的步骤说明很可能排在闲聊历史和系统设定之后被执行效果大打折扣。我之前维护过一套接近 3000 字的任务提示词每次对话都要把它重新注入一次token 开销大不说用户稍微换一个说法提问整个流程就乱套。1.1 为什么说提示词工程已经不够用根本原因在于提示词是线性文本它不适合承载复杂任务的程序化逻辑。你可以在提示词里写先做 A再做 B最后输出 C但模型在生成时是逐 token 预测的长指令之间的关联容易丢失。尤其是中间步骤涉及外部工具调用、文件读写、脚本执行这一类操作时单纯的提示词根本拉不住这些状态。Skills 换了一个思路把模型要遵循的一段话变成一个可以被按需加载的模块。模块内部自带说明文档、执行脚本、参考资源和边界声明模型只需要根据用户意图判断该不该用这个技能然后调用它具体步骤交给技能内部的确定性逻辑去完成。人还是那个分工但把记忆和理解与执行和计算分开了这恰恰是智能体落地时最需要的一层解耦。1.2 技能、工具调用与工作流三者边界在哪里很多人会把 skills 和 Function Calling、工作流引擎搞混。我自己也绕了一阵子后来用一张对照表理清楚了形态核心机制触发方式确定性典型场景Function Calling模型输出结构化参数调用外部 API由模型自主判断函数内部高查天气、发消息、查库存Workflow预先编排固定步骤顺序执行由外部流程引擎驱动整体高定时报表、数据管道Skills指令脚本资源的组合包模型根据语义差异自主激活混合文档处理、代码审查、会议纪要从这张表能看出Skills 最特别的地方是意图触发的组合性。它内部的脚本是确定性的但什么时候用、用哪个技能又交给模型来判断所以它既保留了灵活性又补足了纯提示词在稳定性上的短板。打个不恰当的比方提示词是一张写满注意事项的便签工作流是一条固定的流水线而 skill 是一个工具箱箱子上写了用途里面的工具该怎么用是固定的但什么时候打开这个箱子由师傅说了算。我在实际项目里最常用它的场景是文档处理。以前要做提取合同关键条款并生成摘要提示词写得再细模型也会偶尔漏掉某个条款后来我把这个任务拆成一个 skill脚本负责文本抽取模型只负责对抽取结果做语义润色准确率一下子从七成拉到九成以上。我不只靠手感我是拿 200 份真实合同测过的技能化之前关键字段召回率平均在 72% 左右换成分层处理之后召回率稳定在 93% 上下这个差距已经足够改变产品形态了。2. 拆开一个 Skills 包目录结构、清单文件与依赖资源要理解 skills最快的方式是打开一个现成的技能包把它的文件结构过一遍。我以一个典型的文档总结技能为例展示它的组织方式skills/ ├── pdf-summary/ │ ├── SKILL.md │ ├── scripts/ │ │ ├── extract.py │ │ └── summarize.py │ ├── assets/ │ │ └── prompt_template.txt │ └── tests/ │ ├── sample1.pdf │ └── test_extract.py这个结构并不复杂但里面每个目录都有自己的用处。很多人第一次写技能时只放一个文本文件这样做虽然也能跑但脚本一多就会乱更没法在团队里共享。我建议从一开始就按这个骨架来省得后面返工。2.1 SKILL.md 是说明书也是触发器的核心整个技能包里最重要的文件是SKILL.md。它承担双重角色对外是给模型看的说明书决定模型什么时候激活这个技能对内是操作手册告诉执行环境这个技能怎么跑。它一般用 Markdown 写顶部带一段 YAML 格式的元信息下面才是正文说明。我见过不少失败案例问题几乎都出在描述部分。有人喜欢把描述写成本技能是一个用于处理 PDF 文档并提取文本的模块化解决方案这种话模型读起来完全无感。正确的写法应该像在跟人说话当用户需要从 PDF 中提取文字、总结内容或分析表格时使用此技能。如果输入的不是 PDF不要使用。 前一句说什么时候用后一句说什么时候别用这就是模型能否精确触发的关键。下面是我给一个技能写过的 frontmatter 示例--- name: pdf-summary description: 当用户需要解析 PDF 文本、生成文档摘要、提取关键条款时使用。不适用于图片格式的扫描件。 version: 1.2.0 license: MIT ---2.2 辅助脚本与资源的组织逻辑SKILL.md 负责思考scripts 目录里的脚本负责动手。我踩过的一个坑是把所有逻辑都塞进 SKILL.md 的正文里让模型自己脑补执行过程。这是大忌。正确的做法是把能用代码确定性完成的事情全部踢给脚本。比如提取 PDF 文本、计算统计指标、二次处理 JSON 这些活交给 Python 脚本处理速度和准确率都远超模型逐字判断。脚本之间用标准输入输出和 JSON 串联这样每一步都能单独调试。assets 目录放一些模板和参考数据。举个例子我在做一个会议纪要技能的时候发现模型生成的纪要格式每次都不一样有的用列表有的用表格用户反馈很不统一。后来我直接在 assets 里放了一个 output_template.md要求脚本必须按这个模板渲染问题迎刃而解。输出格式这种东西交给模板比交给提示词靠谱得多。2.3 元数据字段不是装饰版本、许可与平台约束在个人项目里你可能会觉得版本号写不写无所谓。一旦技能要给别人用、给别的项目引用元数据就是命根子。我最惨痛的一次经历是本地写了一个处理 CSV 的技能跑得好好的丢到服务器上之后突然所有字段都读不出来。排查了半天原来是服务器 Python 版本低而技能的依赖库用了新语法。如果当时在 SKILL.md 里写清楚要求 Python 3.10这个问题一眼就能看出来。我建议每个技能至少写明四样东西name技能名description触发描述version语义化版本号allowed-tools或require-environment依赖的环境与工具清单。有团队协作需求的话再加上license和author避免以后扯皮。3. 手把手搭建一个可复用的会议纪要与任务拆解技能理论讲再多不如动手做一个。我拿会议纪要与任务拆解这个场景举例因为它的逻辑足够简单又覆盖了大多数技能都会遇到的关键点解析输入、调用模型、结构化输出。3.1 第一步先写触发描述再写实现顺序很重要。我每次都是先写描述再写脚本因为描述确定了技能的行为边界。很多人反过来脚本写爽了回头再补描述结果描述跟脚本行为对不上模型经常在错误的时候激活技能。这是我写过的两个描述一个是反面教材一个是能用的版本# 反面教材 description: 会议纪要处理技能包含文本解析、要点提取、任务分类等功能模块。# 推荐写法 description: 当用户提供会议录音转写文本或人工整理的会议记录需要生成结构化纪要和待办任务清单时使用。输入应为纯文本输出为 Markdown 格式。写清楚输入是什么、输出是什么、什么情况下不要用模型才能准确判断。实测下来描述里明确输入形态之后误触发率能降低一半以上。3.2 第二步编写执行脚本与模板文件技能的脚本不用写得很复杂关键是职责单一、接口干净。下面这个 Python 脚本做的事情是读取传入的文本提取含有关键信号的任务句然后输出 JSON 给模型做进一步整理。#!/usr/bin/env python3 import json import re import sys TASK_KEYWORDS [负责人, 截止, 跟进, 下周, TODO, 待办] def extract_tasks(text: str) - list: tasks [] lines [line.strip() for line in text.splitlines() if line.strip()] for line in lines: if any(kw in line for kw in TASK_KEYWORDS): tasks.append({raw: line, source: keyword_match}) return tasks if __name__ __main__: raw_text sys.stdin.read() result extract_tasks(raw_text) print(json.dumps(result, ensure_asciiFalse, indent2))这段脚本的思路是先捞网再精选先靠关键词把候选任务句全部拎出来再交给模型统一润色整理。你别指望一次正则就完美命中所有任务那是不可能的但至少要保证召回率够高后续模型才有东西可整理。JSON 作为中间格式是因为它对模型非常友好既能保留字段语义又不丢结构。3.3 第三步本地测试的三种办法技能写完之后一定要测试而且是三种测试都要做功能测试直接跑脚本喂一份测试文本确认输出符合预期。触发测试把技能加载到智能体环境里用几段不同语气、不同措辞的用户请求试一下确认该触发的时候触发、不该触发的时候别乱动。回归测试改完技能之后重跑旧用例确认没有破坏历史行为。我把测试用例放在tests/目录下然后用一个简单的冒烟脚本跑起来#!/bin/bash for f in tests/cases/*.txt; do cat $f | python3 scripts/extract_tasks.py | python3 -m json.tool /dev/null || echo FAIL: $f done echo smoke test done这个冒烟测试花不了两分钟但能挡住 90% 的愚蠢回归。我在团队里把这条命令写进了提交前检查脚本谁改技能都得先跑一遍。3.4 踩坑记录为什么我的技能有时候看不见新手最容易遇到的问题是技能明明放进去了模型就是不调用。我总结过三类原因描述写得像内部文档没写清楚触发场景。目录放错了位置技能不在约定的 skills 根目录下加载器根本没扫到。SKILL.md 的前置元信息格式错了解析失败被静默跳过。排查链路其实很简单先确认加载器能看到技能再确认元信息能解析最后再检查描述有没有写清楚触发条件。我见过有人排查了一下午模型怎么不听话最后发现只是目录名写错了一个字母。4. 调试与质量保障技能运行时最常见的五个问题技能上线运行之后才会暴露出一堆只在真实调用场景下出现的问题。我把遇到过的高频问题整理了一下这些问题在样例测试里通常不会暴露但一旦暴露就很要命。4.1 上下文污染技能输出不该占据对话主线程第一个频发问题是脚本把太多中间过程打印到标准输出。你原本只想要最终的整理结果但脚本调试时留下的日志全被模型当成了事实吸收进去后续回答就开始胡说。我处理这个问题的办法是给每个技能定义明确的输出契约脚本的标准输出只允许最终结果调试信息一律写到日志文件里。用 Python 的话就是用logging模块写到/tmp/skill-debug.log而不是 print 到 stdout。这个约束看起来简单实际能省掉非常多的烂账。4.2 环境依赖换个机器技能就失效第二个高频问题是依赖环境不一致。本地开发用的 Python 版本和依赖库版本在服务器或者同事电脑上完全不是一回事。锁版本是必须的在技能目录里放一份requirements.txt把每个依赖都固定到具体版本号并在 SKILL.md 里写明环境要求。pypdf4.2.0 rich13.7.1还有更稳妥的做法是在脚本入口处做环境自检缺少关键依赖就直接报错并提示安装命令而不是运行到一半才崩溃。我吃过这个亏一个文本处理技能在客户机器上跑到第 7 步才发现缺一个库整个任务前功尽弃。4.3 路径假设你以为的当前目录其实不是第三个问题出在文件路径上。脚本里写相对路径假设当前目录就是技能目录但智能体在执行脚本时的工作目录很可能完全随机。正确做法是从脚本自身定位技能目录再拼出资源的绝对路径SCRIPT_DIR$(cd $(dirname ${BASH_SOURCE[0]}) pwd) ASSETS_DIR$SCRIPT_DIR/../assets这个写法我几乎在每个技能里都会用已经变成条件反射了。你永远不要相信执行环境的工作目录只信脚本自己所在的位置。4.4 并发冲突多个实例同时写一个临时文件第四个问题是并发。用户可能同时发起多个会话每个会话都调用了同一个技能如果技能都用同一个临时文件名就会出现互相覆盖。我的习惯是每个技能运行实例都创建一个带进程 ID 或时间戳的临时目录用完再清理保证实例之间物理隔离。import tempfile import uuid workdir tempfile.mkdtemp(prefixfskill-{uuid.uuid4().hex[:8]}-)这个问题在个人开发者那里体验不出来因为你自己一般不会同时开十个会话跑同一个技能。但只要上了生产环境并发冲突就是迟早的事。4.5 输出格式不稳定模型改动结构导致下游解析失败第五个问题最隐蔽技能最终输出是要给模型或后续流程解析的如果允许模型自由发挥格式下游脚本一解析就崩。解决办法我前面提过——用固定模板兜底。输出模板放在 assets 里脚本先把结构化数据渲染成模板再输出给模型做最后的润色。这样既保证了格式稳定也不牺牲模型表达的灵活性。5. 团队共享与技能库的迭代治理单机版的技能做到能用不难要让一个团队十几个人一起维护、互相引用彼此的技能就需要一些额外的约定。这部分是我从实际协作里逼出来的经验不是从文档里抄来的。5.1 命名规范与目录组织技能命名统一用小写字母、短横线连接、动词开头例如pdf-summary、meeting-notes、code-review-helper。这样一眼就能看出这个技能是干什么的。目录层级保持扁平最多两级不要搞出skills/data/processing/text/clean这种深嵌套加载器扫起来累人也找不着。我建议每个团队维护一份SKILLS_INDEX.md列出所有已注册技能的清单、用途、维护人。技能一多靠脑子记不现实。有同事问我你们那个表格提取技能叫啥来着的时候直接甩给他一份索引文件比翻目录高效得多。5.2 从个人用到团队库评审清单团队共享之后技能就不再是自己跑得通就行还要考虑别人能不能快速看懂、会不会用错。我们团队现在合并技能代码之前会过一遍评审清单主要看这么几条描述是否写清了触发条件和禁用场景。工具脚本是否有对应的冒烟测试。是否标注了版本号和环境依赖。输出模板是否稳定能否被后续流程自动解析。是否处理了异常输入比如空文件、非预期编码。这套清单看起来简单但每一条都对应着我们踩过的真实事故。比如是否处理异常输入就是因为有人传了个空 PDF 进来技能直接抛异常把整个会话干崩了。5.3 版本策略语义化版本和兼容性声明个人开发时技能改了就改了没人在意。但团队里下游应用可能引用了你技能的某个输出格式你随手一改下游直接崩。所以我们规定技能必须用语义化版本号修复问题更新 patch加功能不破坏兼容更新 minor改输出格式或触发逻辑这类破坏性变更必须升 major。每次破坏性变更都要在技能的CHANGELOG.md里写明白改了什么并在SKILL.md的 description 里加上v2 版本要求用户重新上传文件格式为 xxx这类提示。听起来繁琐但经历过一次上周还能用的技能这周集体失效之后你会由衷觉得这套管理是必要的。6. Skills 的边界与我对下一步的判断最后想说点务实的判断。任何技术都有适用边界Skills 也不是银弹。我基于自己的实践把哪些任务该技能化、哪些不该以及未来这个方向会怎么走都梳理了一遍。6.1 适合技能化的三类任务符合三个特征的任务最适合技能化高频、流程固定、结果可验证。高频比如文档转化、格式整理、代码审查几乎天天用值得投入成本封装。流程固定同样输入进去期望的产出路径是明确的用确定性脚本兜底收益大。结果可验证能写测试用例断言输出对不对这样技能质量有保障改了也不会崩。文档处理是我最推荐的入门场景因为 PDF、Word、CSV 这些格式转换逻辑几乎是完全确定性的模型只需要做最后的润色出错概率天然低。6.2 不适合技能化的场景反过来有一些场景硬套技能化反而会拖累系统需要实时获取大量临时信息的任务技能里的静态资源帮不上忙不如直接用工具调用加实时 API。高度依赖多轮对话上下文的任务技能的一次性加载设计会让你反复传递中间状态反而比普通对话还繁琐。安全敏感操作比如直接删除数据、修改生产配置这些动作不应该被封装成模型意图触发的技能风险太大。我的原则是技能负责快而稳的事模型负责活而杂的事需要高权限的事永远握在人手里。6.3 我对技能生态的几个期待我对这个方向保持乐观因为它的复用价值太明显了。一个团队积累了几十个高质量技能之后新项目起步就是站在过去的肩膀上。我更期待三个变化第一技能能互相调用形成组合能力第二出现成熟的技能注册中心和搜索机制不用再像现在靠 GitHub 仓库和目录约定来分发第三技能的行为描述能被自动评测和优化让触发准确率不再是玄学。如果你也想开始搭建自己的技能库我的建议很简单别追求一开始就做个完美的通用技能挑一个你每天都在做的重复任务把它封装成第一个 skill跑通之后你自然会发现它值不值得投入。我那个会议纪要技能最初不过 30 行 Python现在已经迭代到第 4 个大版本一直在帮我实时整理每周的例会记录。小事积累起来就是体系。
返回列表