最近这大半年如果你稍微关注一下 AI 编程工具和 Agent 生态肯定绕不开一个词Skill。Claude Code 在推 SkillsCodex 在讲 skillTrae、Cursor 也在跟进就连一些写文案、做 PPT 的赛道都冒出“Skill 推荐”“Skill 脚本”这种搜索词。热度是真的高但我去翻了不少讨论之后发现一个挺尴尬的现象绝大多数人把 Skill 用成了“提示词收藏夹”甚至把一整套业务逻辑塞进一个 Skill 里最后做出来的东西既不像提示词、也不像工具、更不是 Agent四不像。我做了快十年的工程化相关的工作从最早的脚本自动化、到低代码平台、再到现在的 Agent 工程看到 Skill 这个概念被反复提起来其实心里是有点感慨的。这不是什么玄学概念它本质上就是一个工程产物有明确的边界、结构、生命周期和验收标准。这篇文章我想从工程视角把 Skill 彻底拆开它到底解决什么问题为什么这么多人用不好以及一个合格的 Skill 从设计到落地到底要走哪些步骤。如果你正准备在自己的项目里引入 Skill或者已经被“到底该不该用 Skill、该用在哪”这个问题卡住了这篇文章应该能给你一套可以落地的判断标准。1. Skill 的本质它不是提示词是能力单元1.1 先把几个概念理清楚Skill、Prompt、Tool、Agent 到底什么关系很多滥用 Skill 的问题根源在于概念混为一谈。Prompt、Tool、Skill、Agent 这些东西表面看都在指挥模型干活实际在工程里的定位完全不同。Prompt 是一次性指令它没有状态、没有外部依赖、没有可重复性保证。你让模型“帮我读一下这个日志找出报错原因”这是一次对话行为模型理解多少取决于上下文窗口里塞了什么信息。Tool 是确定性函数比如一个可以查询天气的 API、一个执行 SQL 的接口它不负责判断只负责执行。Agent 是一个策略体它决定下一步调用谁、调用完怎么处理它承载的是决策逻辑。而 Skill 恰好站在这些概念的中间它是一段可以被复用的、围绕特定任务组织起来的“能力单元”里面既有给模型看的指令也可以挂载给模型用的脚本和参考材料。我常用的一个类比是这样的Prompt 是你贴在冰箱上的一张便利贴写着“记得买牛奶”Tool 是你厨房里的那把刀Agent 是那个会看菜下碟的厨师Skill 则是一本菜谱。菜谱里不光写着“做鱼香肉丝”还写了主料辅料、每一步的火候、最后怎么收汁、甚至成品图长什么样。它把模糊的意图固化成可复现的流程。所以把 Skill 只当成 Prompt 来写等于你把菜谱当成便利贴那么做出来的菜好不好吃只能全凭当天模型的心情。1.2 拆开看任何真正的 Skill 都包含能力三要素一个设计合格的 Skill解剖开来看永远包含三个部分意图定义、执行路径、验收标准。三件事缺一不可。意图定义回答“这个 Skill 干什么用的”。它不是一句话含糊概括而是要清楚到让一个从未看过代码的协作模型能判断“现在这个任务该不该调用它”。执行路径回答“怎么干”是让模型按步骤推理还是先跑一个脚本做预处理还是查一张参考表再决策。验收标准回答“干完没有”输出格式是什么、正确结果应该长什么样、有哪些常见坑必须避掉。我见过特别多失败的 Skill翻来覆去就一句话“帮我优化这段文案”。这句话作为 Prompt 没问题作为 Skill 就完全不及格。因为模型不知道“优化”是指缩短篇幅还是调整语气不知道要不要保留原意不知道交付格式是给一版还是给三版。当一个 Skill 连验收标准都没有的时候它输出的质量就是随机游走这次能用、下次不能用看起来是模型不稳定的锅其实是定义不完整的锅。1.3 Skill 的边界它适合解决什么问题不适合解决什么问题与其纠结“怎么写 Skill”不如先想清楚“该不该写 Skill”。我摸索出的判断逻辑是高频、重复、有明确成功标准的任务适合做成 Skill一次性、探索型、依赖大量实时多轮交互的任务不适合。举例来说“把一段 Python 日志解析成结构化摘要并给出故障原因”就适合做 Skill因为任务高频、步骤明确、输出可以被校验。“陪我把一个模糊的商业想法聊清楚”就完全不适合。后一种任务的核心价值在于多轮对话中的随机碰撞和灵感发散你把流程固化了反而限制了模型的发挥空间。还有一类任务介于中间比如“生成单元测试用例”它看起来有标准模板但每个业务场景的边界差异很大。这类任务也能做 Skill但 Skill 里写的是“如何分析被测代码结构、如何设计用例覆盖矩阵、如何校验用例有效性”而不是写死“必须生成 10 条包含某某断言”的教条。简单总结Skill 的本质是把确定性带进 Agent 的不确定性执行过程它是工程化思维的产物不是灵感的产物。你在给自己的 Agent 编排能力时先拿边界这把尺子量一下已经能筛掉一半的伪需求。2. 为什么大家会滥用 Skill行为模式与背后原因2.1 典型滥用一把 Skill 当成“提示词收藏夹”最常见的滥用方式就是把平时觉得好用的一整段提示词直接存成 Skill 文件。有人收藏了几百个 Skill里面装的全是“你是文案大师”“你是财务专家”“你是心理顾问”之类的角色设定。这种行为看起来在整理资产实际上是在囤积负担。原因很简单提示词收藏夹的核心是“人在场”。你打开收藏夹翻到合适的提示词贴进对话框然后人工判断输出合不合适。这是一个以人为中心的工作流。但 Skill 是给 Agent 用的Agent 的调用机制决定了它只能靠 description 来判断是否触发。你把一堆角色扮演套话塞进 Skill 里Agent 面对真实任务时根本不知道什么时候该加载它结果就是要么永远不被调用要么被乱调用。我见过有人给 Agent 挂了 20 个 Skill结果每个任务触发三四个上下文被无关指令塞得满满当当输出质量反而断崖式下跌。2.2 典型滥用二把 Skill 当成“万能工具包”第二种滥用更隐蔽把一个 Skill 写得巨大无比试图让它覆盖某个领域的所有问题。比如有人做了一个“数据分析 Skill”里面既要求模型做数据清洗、又要求做可视化、还要输出分析报告、顺手再做预测建模。听起来很强大用起来基本是灾难。Skill 一大的问题在于它在加载时候不太分轻重所有指令都会被扔进上下文。你写了两千行说明文字无论当前任务只涉及其中 10% 的内容模型都得先读完这 2000 行才能开始干活。这不仅浪费 token更糟糕的是稀释了真正关键指令的权重。我建议成熟的做法是把大 Skill 拆成多个小 Skill比如“数据清洗 Skill”“异常检测 Skill”“报告生成 Skill”通过命名和描述让 Agent 按需加载而不是一个炸弹出场。2.3 滥用背后的真实原因不是懒是没想清楚问题域说了这么多冷静下来分析为什么大家会集体走到滥用这条路上我不认为是写 Skill 的人偷懒核心原因是大家压根没想清楚“问题域”和“能力单元”的区别。问题域是一个大的业务范围比如“电商运营”能力单元是范围内一个具体的、可独立验收的动作比如“生成商品标题”。很多人写 Skill 时把“电商运营”整个塞进去试图让 Skill 覆盖所有电商问题最终得到一个什么都想干、什么都干不好的怪物。正确路径是先划定问题域里哪些环节是高频、重复、有明确输出标准的然后针对这些环节一个个做 Skill。先有边界再有实现顺序反了必然翻车。3. Skill 的工程实现目录、结构、元信息与 token 控制3.1 标准目录结构与文件命名规范先给一套可以直接复用的标准姿势。当前主流的 Skill 实现不管你是跑在 Claude Code、Codex 还是自研 Agent 里都遵循一个类似的目录约定skill-name/ ├── SKILL.md ├── scripts/ │ ├── preprocess.py │ └── parse_logs.py ├── assets/ │ └── template_report.md └── README.mdSKILL.md 是入口Agent 会优先读取它来决定要不要加载整个 Skill。scripts 目录放需要确定性执行的脚本assets 放模板、参考文档这类素材。README 写给人看记录版本、维护人、变更记录跟代码仓库的 README 定位一致。目录命名我用小写加连字符比如log-analysis而不是LogAnalysis这样在文件系统里排序、搜索都比较干净。SKILL.md 只放“让模型理解如何完成任务”的指令不要把大段参考文档堆进来参考文档放 assets 里按需引用。至于版本我强烈建议在 README 和元信息里都写上版本号因为 Skill 的迭代速度很快没有版本管理改坏了你都不知道原来是好的。3.2 元信息设计description 写得好不好决定 Skill 会不会被调用SKILL.md 开头一般会有一段 YAML 格式的元信息至少包含 name、description、version 这几项。这里最容易被忽略的是 description。它对人类来说是摘要对 Agent 来说是“是否触发这个 Skill”的匹配依据。你 description 写得模糊Agent 该用的时候不用不该用的时候瞎用。我总结的 description 写法是“触发场景任务目标关键条件”三段式。比如写一个日志分析 Skill我不会写“可以分析日志并找出问题”这太泛了。我会写“当用户提供应用日志文件支持 txt/log/json 格式并要求定位报错、统计异常或梳理时序时使用。适用于多行堆栈追踪、日志级别混杂的场景。若日志已被结构化且用户仅要求单一数值提取不需要读取本 Skill。”最后那句排除条件特别重要它帮 Agent 避免了过度触发。3.3 执行正文的分块写法目标、输入、流程、输出与质量检查SKILL.md 的正文部分我习惯分五块写Objective目标、Input输入定义、Procedure执行流程、Output输出格式、QA质量检查和异常处理。Objective 写清楚这个 Skill 存在的目的一到两句话即可不要抒情。Input 块列出该 Skill 可能接收的输入形式包括文件路径、用户描述、结构化参数写清楚每一种情况怎么处理。Procedure 是核心按数字编号写出执行步骤每一步都要具体比如“第一步运行 scripts/parse_logs.py 解析文件传入参数为原始日志路径第二步根据脚本输出的 JSON 摘要定位 stack trace 关键字第三步按时间顺序还原故障链路”。步骤之间要有依赖关系让模型知道哪些是串行、哪些可以并行。Output 块明确交付格式能用模板就给模板能用表格就给表格。QA 块写“如果脚本报错怎么办”“如果日志没有明显异常怎么办”等边界情况。这套结构的本质是把“怎么做”从模型临场发挥变成既定流程相当于给模型一张操作手册同时保留它在具体细节上的弹性判断空间。烂 Skill 往往只有 Objective 和 Output中间的过程全靠模型自由发挥那它本质上就是一个带壳的 Prompt。3.4 脚本与工具的嵌入口什么时候必须用脚本不是所有 Skill 都需要脚本但凡是涉及精确计算、格式解析、重复性文本处理的步骤我强烈建议用脚本而不是让模型硬算。模型的文本推理能力强但遇到时间戳排序、正则匹配、大文件切分这些活既慢又容易出错。脚本在 Skill 里的定位是“确定性执行器”它的输入输出要让模型容易理解。最简单有效的做法是脚本只做机械性工作把结果输出成结构化 JSON然后在 SKILL.md 里告诉模型每个字段的含义。例如 Python 脚本解析完日志后输出一个 JSON里面包含 error_count、warn_count、top_exceptions、时间范围等字段模型拿到的是一份地图而不是一堆原木后续的归因分析基于这份地图来做效率和准确率都高得多。写脚本时有一个铁律尽量只用标准库。日志解析用 re 和 collections 就够不要让用户去 pip install 一堆依赖。Skill 的传播成本越低别人越愿意用。而且脚本的可重入性也要考虑同样的输入必须得到同样的输出不要在脚本里写随机采样或者带有状态缓存逻辑否则调试起来会非常痛苦。3.5 token 控制Skill 一旦加载每一行都在烧钱最后说一个非常现实的话题token。Skill 和普通提示词的最大区别是它会常驻在后续所有请求的上下文里。也就是说只要你加载了一个 Skill它里面的每一个字都在跟后续对话竞争注意力窗口。context 不是无限的你加载了三个大 Skill留给实际对话和内容的窗口就被挤占了一大截。控制 token 我有几个实战习惯。第一SKILL.md 控制在 60 行以内能分到脚本里做细节的不写在正文里。第二正文只写“原则、步骤、验收标准”细节放 assets 目录按需读取比如常用正则表达式放 examples.md。第三少堆示例宁缺毋滥。示例可以让模型照猫画虎但同样的效果用一句话描述加上一个迷你示例也能达到三个示例就是重复开销。第四建立“轻量模式”概念在 Skill 开头写一行“若用户只需快速摘要跳过步骤 3-5直接基于脚本 JSON 输出”。让模型在具体场景下自己判断裁剪到哪一步可以显著减少长任务下的重复耗时。4. 完整实操从零构建一个日志分析 Skill4.1 需求定义为什么先写使用文档再写实现用一个我最近在项目里落地的实际例子走一遍全流程这个 Skill 叫 log-analysis。业务背景是我们有一个微服务应用日志分散在多台机器上开发同学每次排查线上问题都要手动 grep、翻堆栈、对时间线特别费劲。我设计的 Skill 目标是给定一份原始日志自动完成日志级别统计、异常栈抽取、时间线还原并生成故障分析报告。动手写代码前我先花十分钟写了一段使用文档模拟“别人拿到这个 Skill 会怎么用”。这段使用文档不需要多正式但它能逼我想清楚边界条件日志文件多大、支持什么格式、没有堆栈怎么办、空文件怎么办、是否需要外部依赖。事实证明这个步骤非常值钱因为它提前暴露了三分之一的边界问题如果直接上手写后面调试要花两三倍时间。4.2 编写 SKILL.md一份可以直接套用的模板我的 SKILL.md 大致长这样--- name: log-analysis description: 当用户提供应用日志文件并要求定位故障、分析异常堆栈、统计错误数量或还原请求时序时使用。支持 txt/log/json 格式。若用户仅要求简单关键字搜索且不需要故障归因可不需要加载本 Skill。 version: 1.2.0 --- # Log Analysis Skill ## Objective 分析应用日志识别异常类型、统计严重程度、还原故障时间线并输出结构化的故障归因结论。 ## Input - 日志文件路径必填支持 .txt/.log/.json - 可选参数service_name服务名、time_range时间范围如 last_30m ## Procedure 1. 运行 python3 scripts/parse_logs.py --input path --format auto|json|txt 解析日志。 2. 读取脚本输出的 JSON 摘要确认 error_count / warn_count / info_count。 3. 若 JSON 摘要内 top_exceptions 非空提取每个异常的堆栈首行、出现次数、首次与末次时间戳。 4. 按时间戳还原故障链路error 前后 2 秒内的 warn 或 timeout 记录视为链路节点。 5. 若用户要求快速摘要直接跳到第 6 步不需要展开完整链路。 6. 输出分析报告格式见 Output。 ## Output 输出 Markdown 报告包含 - 概览时间范围、总日志量、级别分布 - 异常清单异常名称、次数、首次/末次时间 - 故障时间线按时间排列的关键日志 - 归因结论给出最可能的失败原因和下一步排查建议 ## QA - parse_logs.py 报错先检查文件路径是否存在、格式是否支持不修改脚本。 - 日志中无异常输出“未发现明显异常”并给出日志级别分布供人工判断。 - 单条异常全链路缺失标注“该异常缺少完整上下文建议补充请求 ID 后再分析”。这套正文的最大特点是每个步骤都在减少模型的决策负担它不需要思考“日志分析该从何入手”只需要按照编号往下执行。QA 段提前把最容易出现的三类边界故障写进去模型遇到这些情况时就不会硬着头皮编答案。4.3 辅助脚本只做机械活把判断留给模型辅助脚本我用了不到一百行 Python 实现核心思路是只做四件事按行读取、分类统计、提取堆栈、输出 JSON。关键代码大概是这样#!/usr/bin/env python3 import argparse, json, re from collections import Counter, defaultdict LEVEL_RE re.compile(r\b(INFO|DEBUG|WARN|ERROR|FATAL)\b) STACK_RE re.compile(r^\s*at\s([\w.$])\(([^:]):(\d)\)) def parse_line(line): timestamp line[:23] if len(line) 23 else line[:10] level_match LEVEL_RE.search(line) level level_match.group(1) if level_match else UNKNOWN return {timestamp: timestamp, level: level, raw: line.strip()} def main(): parser argparse.ArgumentParser() parser.add_argument(--input, requiredTrue) args parser.parse_args() counts Counter() exceptions defaultdict(list) lines [] current_exc None with open(args.input, r, errorsignore) as f: for raw in f: rec parse_line(raw) lines.append(rec) counts[rec[level].upper()] 1 if rec[level].upper() in (ERROR, FATAL): current_exc rec[raw][:200] exceptions[current_exc].append(rec[timestamp]) elif current_exc and rec[level].upper() WARN: current_exc None sorted_lines sorted(lines, keylambda x: x[timestamp]) result { total: len(lines), level_counts: dict(counts), time_range: [sorted_lines[0][timestamp] if sorted_lines else None, sorted_lines[-1][timestamp] if sorted_lines else None], top_exceptions: [ {message: exc, count: len(ts), first: min(ts), last: max(ts)} for exc, ts in sorted(exceptions.items(), keylambda x: -len(x[1]))[:10] ] } print(json.dumps(result, ensure_asciiFalse, indent2)) if __name__ __main__: main()这里有个很刻意的设计脚本的输出是严格格式化的 JSON但不包含任何归因建议。为什么因为归因是判断性工作模型更擅长从异常堆栈里联想下一步排查方向而脚本若强行写规则去归因会非常僵化。脚本和模型的协作边界是脚本负责还原事实模型负责解释事实。这条边界画得越清晰整个 Skill 越稳。4.4 调试与迭代我踩过的三个坑再好的设计落地过程也会踩坑。这个 Skill 在调试过程中我遇到过三个典型问题顺手分享出来。第一个坑是脚本输出被截断。当日志量很大时top_exceptions 可能输出超长内容直接塞进上下文既浪费 token 又可能导致后续输出截断。解决办法是我在脚本里对异常信息做了截断只保留前 200 字符并在 SKILL.md 里注明“完整堆栈需查看原文件第 N 行”。让模型知道信息从哪来它就能在需要时让用户补充完整段落而不是自己脑补。第二个坑是时间格式不统一。线上日志有的带时区有的不带有的用毫秒级时间戳有的用微秒级。我写的排序逻辑遇到混合格式时直接乱序。后来在脚本里加了一个时间归一化函数把能识别的格式统一转成 ISO 格式识别不了的保留原样并标记 unparsed。这一步工程量不大但直接决定了时间线还原的可用性。第三个坑是模型会跳过脚本直接凭感觉分析。原因是我在 SKILL.md 里把跑脚本写成了可选项语气不够强制。修正方法是把步骤 1 改成“必须”并加粗同时在 Output 里要求“报告必须包含脚本输出的 level_counts 作为依据”。给模型设定强制依赖能有效减少它的“走捷径”行为。5. 高频问题与排查技巧以后别再踩这些坑5.1 五大高频错误我见过的 Skill 翻车现场这半年我看了大量社区里公开的 Skill 文件也帮人 review 过不少发现很多问题非常集中。第一个高频错误是 Skill 没有明确的触发边界。description 写得太大比如“处理与分析日志、监控系统状态、发现系统问题、提供运维建议”Agent 几乎遇到任何运维相关问题都会加载它反而无法聚焦。解法是描述里把适用场景写成具体动作加上“不使用场景”的负向约束。第二个高频错误是正文全是“你是一个专家你要如何如何”。这种写法本质上是角色设定不是工程实现。Skill 的正文应该是一份操作手册而不是人格设定。你写“你是一个资深 SRE”不如写“第一步执行脚本、第二步检查错误率、第三步按模板输出报告”后者的确定性高一个量级。第三个高频错误是脚本与正文脱节。SKILL.md 里写“调用脚本分析”但脚本的实际参数和正文描述不一致模型照着正文调用直接报错。解决方法是每次改完脚本务必同步更新 SKILL.md 里的调用示例最好把脚本的 --help 输出贴在文档里作为参考。第四个高频错误是忽略失败路径。很多 Skill 只描述“正常情况怎么做”完全没写“出错了怎么办”。模型遇到脚本报错、文件不存在、格式不支持这些场景时要么死循环重试要么编造结果。在 QA 段里把常见失败路径写清楚是提升鲁棒性性价比最高的做法。第五个高频错误是试图让 Skill 做所有事。前面说过Skill 是能力单元不是领域全家桶。你做一个“数据分析”Skill不如拆成“数据清洗”“统计摘要”“可视化”三个 Skill你做一个“PPT 排版”Skill不如针对不同页面类型拆成“封面页”“架构图页”“数据表页”三个 Skill。每个 Skill 管一件事Agent 才能按需组合。5.2 排查清单一个 Skill 不好用先按顺序查这几个地方如果你手里的 Skill 表现不稳定我建议按下面的顺序排查而不是一上来就改提示词。第一步查调用确定 Agent 到底加载这个 Skill 没有。很多“Skill 没效果”的问题实际上是 Skill 压根没被触发。查看工具的调用日志确认 description 是否匹配了用户请求。没触发就去改 description 的措辞让它更贴近用户真实表达。第二步查读取确认模型读了 SKILL.md 里的关键步骤没有。这一步比较难直接观察但可以通过输出结果反推如果模型输出里完全没有脚本生成的数据大概率它跳过了步骤 1没有按流程执行。此时需要强化步骤依赖关系把脚本输出设为后续步骤的必要输入。第三步查输出检查脚本本身的输出是否符合预期。单独在终端跑一遍脚本确认没有路径、中文编码、时间格式的问题。脚本日志模块在调试时建议开启 DEBUG 级别能省大量时间。第四步查成本如果一切正常但效果还是慢那就是上下文被无关内容占满了。把 Skill 里所有可裁剪的内容放到 assets 按需读取正文尽量精简到一屏以内。实测下来同样的任务精简后的 Skill 在响应速度和输出质量上往往都有提升。5.3 什么时候坚决不要用 Skill最后说点反直觉的结论。不是所有功能都要做成 Skill。我做技术选型时有几个“一票否决”场景任务本身没有清晰的成功标准时不用任务依赖大量实时外部反馈、不适合固定流程时不用任务只发生一次、未来不会复用时也不用。一句话直接写在 prompt 里比建一个 Skill 文件高效得多。还有一个需要注意的原则是 Skill 数量不宜过多。对一个 Agent 来说挂 3 个高质量 Skill 一定比挂 30 个注水 Skill 效果好。Skill 是给 Agent 的能力做加法但也在给上下文做加法每多一个 SkillAgent 的决策路径就多一分混乱的风险。我的习惯是每个项目最多维护 5 到 6 个真正高频使用的 Skill超出就做合并或重新评估必要性。6. 写在最后像做 API 一样去做 Skill我在实际项目里养成了一个习惯每次想写新 Skill 之前先问自己三个问题这个任务是否高频发生能否定义明确的验收标准步骤中哪些部分必须确定性执行、哪些可以交给模型判断如果三个问题都回答清楚了再动手写目录和文档然后才是具体内容。顺序反过来先写指令后补流程结局大概率是又一个自嗨的垃圾 Skill。Skill 这个概念的流行背后是 Agent 工程化的大趋势。它本质上不是什么新发明而是把软件工程里已经被验证过的模块化、单一职责、接口稳定这些思想迁移到了模型协作这个新场景里。所以别把它当成提示词的高级玩法也别把它当成万能银弹。它是一块砖你得先想好墙砌在哪再用它去砌墙砖才有价值。最后分享一个我踩坑换来的建议做一个 Skill 容易维护一个 Skill 难。每次版本迭代时顺手把变更记录写在 README 里把过时内容及时删掉比不断添加更重要。你看这和写代码是同一个道理——少即是多。
