
1. Skills 到底是什么拆解一个被玩出新含义的词先说个直接感受。我最近一年多听到skills这个词的频率比过去十年加起来都高而且语境完全变了。以前聊 skills 基本是在说你要不要学个新技能这种鸡汤话题现在在智能体Agent开发的圈子里skills 指的是一个非常具体的技术概念把某类任务的处理能力封装成一个可描述、可调用、可复用的标准化单元。打个比方你就明白了。以前你让 AI 帮你干活是把所有步骤、规则、背景知识全部塞进一段长长的提示词里AI 每次都要重新阅读理解一遍像带一个什么都不懂的新人每个任务都要从头培训。而 skills 的思路是把任务拆解成几个固定模块每个模块有自己的操作手册和工具包AI 只需要识别当前场景匹配哪个模块然后直接调用像给团队里的老手一份 SOP他看一眼就能上手干活。我说的这个操作手册在具体实现里通常是一个叫SKILL.md的说明文件配上若干脚本和资源文件整整齐齐放在一个目录里。这个设计解决的核心问题是把 AI 的会话内临时能力转化为可持久化、可积累、可复用的组织资产。你的每一次实践成果、每一个踩坑后的修正都沉淀成一个独立的 skill 文件下次遇到类似问题直接调用不用重新解释。这篇内容适合谁适合三类人。一类是被碎片化提示词折腾到崩溃的开发者每次都要重写一大段 prompt 才能让模型稳定输出一类是做 Agent 应用的工程师正在为如何管理多任务模块发愁还有一类是对 AI 自动化有兴趣、想把自己的工作流沉淀成工具的运营和产品同学。我会把我实际跑通的经验、踩过的坑、调参的思路全部摊开讲不绕弯子。2. 一个 Skill 的标准结构从入门到能落地的完整拆解2.1 目录结构先看一份能直接参考的骨架一个成熟的 skill 在文件系统层面长什么样我习惯这样组织meeting_minutes/ ├── SKILL.md ├── requirements.txt ├── scripts/ │ ├── format_minutes.py │ └── extract_actions.py ├── resources/ │ ├── templates/ │ │ └── minutes_template.md │ └── examples/ │ ├── good_sample.md │ └── bad_sample.md └── tests/ ├── test_extract_actions.py └── fixtures/ └── sample_transcript.txt这里每个文件都不是摆设。SKILL.md是给模型看的使用说明书它不负责执行具体的代码逻辑而是告诉模型这个技能在什么场景下触发、输入应该是什么格式、输出应该长什么样、有哪些边界和禁忌。scripts/目录放真正干活的脚本resources/放模板和示例tests/放回归测试。我见过很多新手把逻辑全塞进 SKILL.md 里让模型生成一段代码来做某事这在简单场景下能用但一旦逻辑复杂一点模型输出的代码很容易出语法错误或逻辑漏洞远不如直接调用写好的脚本稳定。注意SKILL.md 的核心价值是让模型知道该不该用、怎么用而不是替模型写代码。能交给确定性脚本的活就不要让模型现场发挥。2.2 SKILL.md 的写作规范模型靠这个文件做决策这部分是 skill 设计里最容易被低估的一环。很多人以为 SKILL.md 就是功能说明随便写两句就行。实际上模型对 skill 的调用决策几乎完全依赖 name、description 和 when_to_use 这几个字段。写得好不好直接决定模型是该调你的 skill 还是自己瞎编答案。以我做的会议纪要整理skill 为例一份能稳定触发的 SKILL.md 长这样--- name: meeting_minutes description: 将会议录音转写文本整理为结构化会议纪要提取讨论要点、决策结论和待办行动项。适用于商务会议、项目同步、技术评审等场景。 when_to_use: 输入包含会议对话记录或逐字稿用户要求生成纪要、总结、行动项。 input: raw_transcript 纯文本UTF-8 编码每行对应一个说话片段 output: minutes JSON 对象包含 title/date/attendees/summary/decisions/action_items privacy: - 禁止将文本内容写入公开日志 - 输出不能包含与输入无关的虚构内容 --- # 会议纪要整理 本技能将原始转写文本转换为结构化纪要处理流程 1. 清理文本噪音重复、语气词、空白 2. 识别发言者角色主持人、决策者、执行者 3. 提取关键讨论点和最终决策 4. 从承诺性语句中抽取待办行动项 5. 按模板生成格式化纪要 ## 注意事项 - 行动项必须包含负责人若原文未指明置为 null 并在备注中标记待确认 - 决策结论必须与讨论内容一致禁止脑补 - 主持人开场白包含的本次会议主要讨论片段是摘要的重要来源你注意看这个设计的巧思description里写了触发场景when_to_use里写了边界条件privacy里写了数据红线注意事项里写了输出约束。这一整套下来模型每次触发 skill 时都有一个清晰的行为框架而不是自由发挥。2.3 依赖与环境让 skill 换台机器也能跑光有 SKILL.md 还不够一个可复用的 skill 必须解决环境依赖这个现实问题。我见过太多项目代码写得漂漂亮亮结果换台机器一跑缺包、缺环境变量、Python 版本不对全崩。所以现在的标配做法是每个 skill 目录下放一个requirements.txt把脚本依赖的第三方库锁死版本涉及 API 密钥的全部走环境变量注入禁止硬编码进脚本。依赖声明示例openai1.30.0 pydantic2.6.0 tabulate0.9.0环境变量走.env文件或运行时注入export MEETING_MINUTES_API_KEYyour-key-here还有一个隐藏很深的坑如果 skill 里要调用外部 API比如把转写文本送给大模型做摘要必须在 SKILL.md 里明确声明网络权限和接口白名单。有些运行时环境会对 skill 做沙箱隔离没有声明的域名默认不可访问。我在一次工程里有意识地在privacy字段下加了一行仅允许访问 api.speech-service.example.com结果排查半天漏掉的 API 请求都是因为这一行声明没加齐。3. 如何拆技能边界一个关键的设计思维3.1 粒度判断的三条标准这是我在整个 skill 开发过程中觉得最有价值的部分怎么判断一个功能该不该做成独立 skill标准可以归纳为三条可独立验收这个功能有明确的输入、明确的输出、明确的质量标准能单独测试和回归场景复用性这个功能在不同任务中会被反复调用而不是一次性临时逻辑状态边界清晰这个功能自身的状态变化可以被隔离不需要依赖外部会话的历史上下文。拿实际例子说明。解析一份 PDF 合同并抽取关键条款符合这三条标准适合做成独立 skill生成项目周报宽度跨度较大、依赖项目状态和团队数据拆成 skill 前要先把输入输出定义清楚回答你好这种没有业务逻辑的废话场景压根不值得封装。3.2 拆过了头也是问题模块化的反面陷阱拆 skill 不是越细越好。我见过有人把文本去重和文本分词拆成两个 skill结果每次调用要连续触发两个模块中间环节的数据传递反而引入了不稳定因素。我的经验是至少要让一个 skill 能独立完成一个对用户有价值的交付物。文本去重本身不是用户目标从文本中提取结构化销售线索才是。与其拆成 10 个原子技能不如做成 3 个组合技能每个技能输出一个完整的中间产品。3.3 组合技能用路由 子技能架构保持系统整洁当一个 skill 内部还需要调用其他 skill 时不要直接写在脚本里互相 import后面会退化成一团乱麻而是在 SKILL.md 的执行流程里明确写出需要先调用另一个 skill。例如## 执行流程 1. 调用 content_cleaner 技能清洗原始文本 2. 调用 entity_extractor 技能抽取关键命名实体 3. 由当前技能整合清洗结果与实体结果生成结构化输出这种路由 子技能的设计让每次调用都有清晰的层级出了问题也容易定位是content_cleaner输出脏了还是entity_extractor识别错了还是整合逻辑有 bug。比起一个巨无霸 skill 内部做所有事排查效率高出一个量级。4. 实操实录从零构建一个PDF 发票信息抽取Skill纸上谈兵没意思我把一个实际项目的完整实现过程放出来。这个 skill 的需求来自财务同事他们每个月要处理几百张电子发票逐个人工录入进财务系统效率低且易错。我要做的 skill 是输入一个 PDF 发票文件输出一份结构化 JSON包含发票号码、开票日期、购方信息、金额明细和校验码。这不是一个demo是一个真实跑了半年的生产级 skill。4.1 第一步定义输入输出契约任何 skill 开发的第一步都不是写代码是定接口。我们最终敲定的输入输出契约如下输入通过标准输入或文件路径传递{ file_path: /path/to/invoice.pdf, ocr_model: offline }输出{ invoice_no: 031001900111, issue_date: 2025-02-18, seller: 示例科技有限公司, buyer: 某某实业有限公司, total_amount: 12800.50, tax_amount: 1472.10, check_code: 12345678901234567890 }定义契约还有一层隐藏价值它相当于给模型一个预期管理。模型在调用 skill 前就知道会拿回什么后续任务调度质量会提升很多。事实上许多 agent 框架把 skill 的输入输出 schema 当作规划的输入来用。4.2 第二步编写核心逻辑脚本发票字段抽取最关键的一步是把 PDF 转成可解析的文本。电子发票 PDF 大多是文本型 PDF可以直接提取文本也有一部分是扫描件需要 OCR。这个 skill 我选择了写一个适应性脚本先尝试直接用文本层提取提取不到再进入 OCR 分支。# scripts/extract_invoice.py import json import sys import re from typing import Dict def extract_text_from_pdf(file_path: str) - str: 通用 PDF 文本提取优先文本层其次 OCR try: import fitz # PyMuPDF doc fitz.open(file_path) text \n.join(page.get_text() for page in doc) if text.strip(): return text raise ValueError(empty text layer) except Exception as e: print(f[debug] text extraction failed: {e}) return ocr_fallback(file_path) def ocr_fallback(file_path: str) - str: 离线 OCR 兜底用于扫描版发票 import pytesseract from pdf2image import convert_from_path images convert_from_path(file_path, dpi300) return \n.join(pytesseract.image_to_string(img, langchi_sim) for img in images) def parse_invoice(text: str) - Dict: 基于规则的字段抽取 result {} patterns { invoice_no: r发票号码[:]\s*(\d{8,20}), issue_date: r开票日期[:]\s*(\d{4}年\d{2}月\d{2}日), seller: r销售方名称[:]\s*([^\n]), buyer: r购买方名称[:]\s*([^\n]), total_amount: r价税合计(?:小写)?[:]?\s*[¥]?([\d,]\.\d{2}), tax_amount: r税额[:]\s*[¥]?([\d,]\.\d{2}), } for key, pattern in patterns.items(): match re.search(pattern, text) if match: result[key] match.group(1).strip() return result if __name__ __main__: file_path sys.argv[1] text extract_text_from_pdf(file_path) invoice parse_invoice(text) print(json.dumps(invoice, ensure_asciiFalse, indent2))写完脚本后我拿三张样票跑了一遍两张票字段齐全一张扫描件走了 OCR 分支耗时 8 秒文本结果里有一个字段因为发票格式差异没匹配上。这就是为什么必须有第三步。4.3 第三步异常分支和边界处理生产环境只有一条铁律失败也要有清晰的失败输出。我的做法是让脚本永远输出合法 JSON但允许字段为null同时增加warnings数组告诉调用方哪些字段存疑。代码如下invoice parse_invoice(text) invoice[warnings] [] if not invoice.get(invoice_no): invoice[warnings].append(发票号未匹配到请人工复核 OCR 结果) if not invoice.get(total_amount): invoice[warnings].append(金额未匹配到检查 PDF 是否加密或为红色发票) # 关键即使字段缺失也输出结构化结果 警告 out {k: invoice.get(k) for k in [invoice_no, issue_date, seller, buyer, total_amount, tax_amount]} out[warnings] invoice[warnings] print(json.dumps(out, ensure_asciiFalse, indent2))这一步看着不起眼却是demo和生产级之间的分水岭。很多 skill 在理想输入下跑得很漂亮一旦遇到格式差异就抛异常或输出空结果调用方完全不知道发生了什么。你输出的warnings信息本身有一次提示模型如何处理失败的作用模型看到警告就知道要请用户复核而不是自信地拿不完整 JSON 继续跑。4.4 第四步测试用例不是可选项tests/目录不是摆设。我针对发票 skill 建了四类测试用例标准电子发票样本、扫描件样本、格式不规范的旧版发票样本、加密 PDF 样本。每个用例都对应一个 fixture 文件和一个预期输出 JSON。跑测试的时候只需要一条命令python -m pytest tests/ -v这四类用例的设计思路可以复用标准件验证主逻辑扫描件验证兜底能力不规范件验证容错异常件验证错误报告。我后来修了好几个正则没有覆盖的发票版式都是靠回归测试发现的如果没有这套测试改一处逻辑可能悄悄弄挂另一处。5. 工程化的经典坑从一场线上事故说起任何 skill 上线都会遇到开发环境好好生产环境三连崩。我复盘过一个印象深刻的案例一个客户反馈分类skill 在测试环境准确率稳定在 92%上线第一周准确率突然跌破 60%而且没有任何报错。排查了两天最后发现原因非常朴素测试环境喂的是人工清洗过的干净文本生产环境喂的是从邮件系统直接拉出来的原始文本——里面夹杂了大量回复转发链条邮箱签名档自动免责声明。模型把这些噪音内容也做了分类自然全乱了。这类问题的解决思路很通用在 skill 的数据入口层做显式清洗。我把清洗逻辑独立成一段脚本放在正式处理前def clean_raw_input(raw_text: str) - str: 移除邮件回复链、签名档、免责声明等非正文内容 # 去掉转发链 text re.sub(r----------.*?----------, , raw_text, flagsre.DOTALL) # 去掉签名通常在退订字样之前的最后一段 cut text.find(退订) if cut ! -1: text text[:cut] # 去掉免责声明 text re.sub(r(?i)disclaimer:.*?$, , text, flagsre.DOTALL) return text.strip()数据清洗完成之后准确率回升到了 89%。这个教训让我养成了一个习惯自测时一定要使用真实业务数据而不是自己手敲的样例文本。模型能力再强也扛不住垃圾进、垃圾出。5.1 运行时依赖版本漂移的隐性坑还有一次事故更隐蔽。一个 skill 依赖的第三方 JSON 解析库从 1.x 升到了 2.x行为不兼容但没报错——输出格式从Decimal变成了str。下游流程认不出来整条链路数据错乱。这个事情的教训是requirements.txt里不要写松散的package1.0要锁死到精确版本package1.2.4甚至记录sha256哈希。另外强烈建议每个 skill 单独建虚拟环境或容器不要把依赖混在全局环境里不然版本漂移早晚找上门。5.2 权限边界给 Skill 设置行为红线一个值得单独拎出来说的点是权限声明。尤其当 skill 涉及文件读写、网络请求、邮件发送时没有提前声明权限边界运行时环境会拦截或者更糟糕——不拦截但没审计。我会在 SKILL.md 的permissions字段明确列出permissions: file_read: - **/*.pdf - **/*.txt file_write: - output/** network: - api.example.com这不仅是安全要求也是可观测性要求。当你把 skill 放到共享平台或团队内部分发时清晰的权限声明能减少大量协调成本。运行时的审计工具也会根据这个声明生成调用日志出了问题可以追溯是谁在哪个阶段越权了。6. 在业务里落地的几个关键判断6.1 什么样的场景最适合先用 Skill结合我的实践下面这些场景是 skill 红利最大、最适合先落地的场景特征举例为什么适合流程固定、规则明确发票信息抽取、日志分类、格式转换逻辑可稳定复用输出可测试需要反复解释背景会议纪要模板、周报生成一次沉淀免去重复教模型涉及多步内部决策客户投诉分诊、工单路由显式决策过程更容易调优数据清洗与标准化地址清洗、电话格式统一确定性逻辑零幻觉风险6.2 什么时候不要硬做 Skill相反有三类情况我会明确不建议做 skill需求还处于快速变动期今天一个逻辑明天一个思路沉淀下来只会变成改来改去的负债高度依赖实时个性化上下文的任务需要每次重新获取上下文Cache 成本极高还有不能接受模型幻觉的领域比如涉及复杂法律判断或医疗结论的场景skill 只能补充格式和流程不能替代领域审核。6.3 衡量 Skill 好坏的指标很多团队问我skill 做得好不好看什么我给你一套务实的指标体系调用准确率模型在正确的场景下触发了正确的 skill 的比例任务完成率调用 skill 后拿到完整可靠输出的比例人工修正频率输出有多少比例需要人工改完才能用调试效率一次稳定性问题的平均定位时间复用率一个 skill 在一个时间窗里实际被不同会话调用的次数。这套指标不是纸上谈兵我在实际项目中用过至少两轮迭代。最开始我只看任务完成率后来发现人工修正频率更能暴露问题——很多输出表面看有结果实际用起来还有偏差。7. 个人实践中的体会与建议做了这么久 skill 相关的工程实践我最大的体会是写 skill 本质上是在做知识工程的现代化版本。以前我们要把专家经验写成流程文档、做成标准作业程序现在换个形式把专家的判断逻辑、操作步骤、边界条件编码成模型可理解、可调用的单元。真正值钱的不只是那几行代码而是你通过反复踩坑、复盘、抽象沉淀出来的什么场景该怎么做、哪里容易出错、如何兜底这套隐性知识。从 tavily 到自动化操作从企业内网流程到个人助手skills 的想象空间远比提示词优化要大。它不是一个技术名词那么简单它代表的是 AI 应用从一次性对话走向可工程化交付资产的趋势。如果你想动手我的建议是别一开始就想着做一个完美的复杂 skill从你每周重复三次以上的任务开始把它拆干净写成第一版跑起来然后反复修改。你会在第三四个版本的时候突然感受到那种一次沉淀、长期复用的爽感那才是这个设计真正迷人的地方。