Agent Skills实战:从提示词工程到可复用技能包
发布时间:2026/9/12 18:01:44 锦皓数字建站

Andrej Karpathy 关于 skills 的分享最近被翻来覆去地讨论我一开始觉得无非又是给大模型加几段 prompt的变体直到自己动手把一个技能包拆开又组装回去才意识到这东西的思路和传统提示词工程完全是两个维度。Karpathy 想解决的问题很直接为什么我们每开一个新对话模型就要从零开始理解任务为什么一个团队积累的领域经验不能像代码库一样被复用、分发、版本管理这篇文章我会从 skills 的动机讲起拆解一个技能包的目录结构、SKILL.md 的写法、Agent Harness 到底怎么调度它然后带大家手写一个能实际落地的代码库体检技能最后聊聊我踩过的几个坑。无论你是在用 Claude Code、Codex、OpenCode还是自己撸 Agent 框架这套思路都能直接搬过去。1. Karpathy 抛出 Skills 那天戳中的是 Agent 的命门1.1 每次对话都从白纸开始是 Agent 最大的浪费我以前做 Agent 应用时有个很头疼的体验精心设计的一套 system prompt在这个会话里跑得好好的换个新会话就全废了。模型还是那个模型但对话上下文一旦清空它连你昨天教过它的最佳实践都记不住。这就像你雇了一个天赋很高的实习生但每天早上他都会失忆你必须重新把公司规章、项目背景、代码规范念一遍然后他干一天活晚上又忘了。Karpathy 在公开分享中反复表达过类似的不满。他觉得我们不应该每次都用 prompt 去描述一个任务而应该直接给模型一个能力包——这个包里装着做这件事需要的步骤、脚本、参考案例模型拿到就能干活。他管这个东西叫 skills大意是让模型像人一样积累手艺而不是每次都临时抱佛脚。这个想法听起来温和但实际上是在挑战整个 Agent 应用层的设计范式从告诉模型怎么做变成给模型准备好做的工具和方法让它自己决定怎么用。1.2 为什么传统提示词工程解决不了这个问题有人可能会问我用 prompt 把步骤写详细点不行吗我用 few-shot 多给几个例子不行吗我在实际项目中试过效果都不理想原因有三个Prompt 是一次性的。它存在于上下文窗口里会话结束就没有了没法沉淀、没法复用。Prompt 会占用宝贵的上下文。你写一个复杂流程的详细步骤加上几个示例可能几千 token 就没了。上下文窗口就那么大留给真正任务处理的额度自然变少。Prompt 难以工程化。代码可以写单测、可以 review、可以版本管理但 prompt 很难做这些事。改一个字可能影响全局却又没有合适的测试手段。Skills 的优雅之处在于它把过程知识从上下文里搬到了文件系统。模型不需要在上下文中背下全部操作步骤它只需要知道有这么一个技能它的说明书在某个文件里需要的时候去读就行。打个比方prompt 是你在前台口头交代给实习生的话skill 是项目组共享的那本 SOP 手册后者明显更可靠。1.3 Superpower Skills 这个词背后的含义热词里出现的 superpower skills其实是在强调同一个概念一个好的技能包应该让模型获得超能力级别的能力跃迁而不是简单地补充一两句提示。比如给模型一个写 SQL 的技能包里面包含数据库 schema 探查脚本、SQL 规范文档、常见查询模板那模型写 SQL 的能力会比普通 prompt 提升一个档次因为它是在工具 规范 示例的完整支撑下工作。我在自己的项目里也验证过这个效果。单纯在 prompt 里写请分析这个代码库是模糊的模型可能会泛泛而谈。但如果模型加载了一个分析技能它就会知道应该先跑一遍结构扫描脚本、再统计模块依赖、最后输出报告格式效果完全不一样。2. 一个 Skill 的解剖学从 SKILL.md 到脚本武器的完整结构2.1 SKILL.md 是技能包的说明书也是模型的第一入口社区里已经形成了基本共识一个技能包通常是一个独立目录核心是SKILL.md文件。这个文件起着说明书的作用模型决定是否调用某个技能时首先读的就是它。一个标准的SKILL.md长这样--- name: codebase_analysis description: 分析一个代码库的模块结构、依赖关系和潜在风险。当用户要求审查项目、了解代码结构、评估技术债务时使用。 --- # Codebase Analysis 分析代码库时你需要按以下步骤执行 1. 使用 scripts/scan_structure.py 获取目录树和模块清单 2. 读取关键模块的入口文件理解核心流程 3. 使用 scripts/analyze_deps.py 生成模块依赖关系 4. 根据依赖分析结果识别循环依赖和过度耦合 5. 输出分析报告包含模块清单、依赖图描述、风险点注意前面的 YAML frontmatter 部分name是这个技能的唯一标识description则是触发这个技能的关键词索引。模型在收到用户请求后会先看所有已挂载技能的 description判断哪个技能最匹配当前任务。所以 description 写得越准确、越能覆盖用户的表达方式这个技能被正确调用的概率就越高。这是一个很多人忽略的细节。我见过有人把 description 写得很抽象比如这个技能用于分析代码结果模型在用户问这个项目的架构怎么样时根本想不起调用它。后来把 description 改成分析项目结构、模块关系、依赖情况、代码质量问题适用于代码审查、项目梳理、技术债务评估等场景召回率明显提升。2.2 光有文档不够脚本才是技能的肌肉Karpathy 强调的另一个点是skill 不能只是文档它应该包含可执行的脚本和辅助文件。文档负责给模型指路脚本负责帮模型干那些文本模型不擅长的事情。一个完整的技能包目录结构通常是这样my-skill/ ├── SKILL.md ├── scripts/ │ ├── scan_structure.py │ └── analyze_deps.py ├── references/ │ ├── project_style_guide.md │ └── common_patterns.md └── examples/ ├── example_input.json └── example_output.jsonSKILL.md使用说明书scripts/可执行脚本模型可以调用它们获取数据、执行操作references/参考文档模型在需要时翻阅examples/示例输入输出让模型了解标准答案长什么样这里的关键点是模型本身不擅长精确计算和大量数据处理但它很擅长理解脚本输出的含义。所以技能包的正确逻辑是让脚本负责读数让模型负责解读。比如分析代码依赖你可以写一个 Python 脚本用 AST 去静态解析整个仓库的 import 关系输出一份 JSON 结构然后让模型基于这份 JSON 写分析报告。这比让模型自己读几百个源文件高效得多准确性也高得多。2.3 为什么文件系统是比数据库更合适的技能载体我第一次接触这个设计时有个疑问为什么不用向量数据库存技能为什么不用 JSON 配置后来想明白了。文件系统天然具备几个数据库很难复制的优势透明可审计任何编辑器都能打开查看团队 review 没有门槛Git 友好技能包的变更可以 diff、可以回滚、可以多人协同权限可控可以利用操作系统权限控制谁能改谁的技能离线可用目录拷到另一台机器上就能用不需要同步中心化服务更重要的是文件系统对模型来说是低门槛的。模型本质上是文本处理器文件就是文本的集合它可以毫不费力地读目录、读文件、执行脚本不需要额外的工具链支持。所以社区里出现的几个主流实现比如 Claude Code 的 skills、Codex 的 agent skills都不约而同地选择了目录加 Markdown 的结构因为这是最朴素也最可靠的方案。3. Skills 为什么能跨工具流行Agent Harness 的调度逻辑3.1 Harness 是什么模型、工具和文件之间的调度层单纯有技能包还不够还得有一个调度框架让模型知道怎么加载技能、什么时候调用技能、调用后怎么处理输出。这就是热词里反复出现的 Agent Harness。Harness 可以理解为模型外面的那层壳它负责管理模型的上下文、调用外部工具、运行脚本、处理文件读写。Skills 正是在 Harness 层面被加载和执行的。Karpathy 的观点是模型本身是一台没有外设的计算机Harness 才是那台完整的主机——skills 就是插在主机上的各种硬件。一个典型的技能调用流程是这样的用户向 Agent 提出请求Harness 收集所有已挂载技能的 description 列表Harness 把这个列表作为可选能力注入模型的上下文窗口模型判断当前请求需要用哪个技能返回一个调用技能 X的决策Harness 读取该技能的 SKILL.md将内容注入上下文模型按 SKILL.md 中的步骤执行需要时通过 Harness 运行脚本脚本输出返回给模型模型继续执行直到任务完成所以你会发现模型的上下文窗口就像一块工作台Harness 会根据任务需要把对应的说明书和工具放到台面上而不是把所有东西都堆上去。这就是 skills 方案节约上下文的原理。3.2 一个技能包如何被多个 Agent 共用热词里出现了大量工具名opencode skills、codex skills、claude code skills还有 npx skills 这种安装命令。这说明社区已经出现了一个重要趋势——技能包正在被设计成跨工具可移植的。我自己实际测试过同一个 SKILL.md 目录在 Claude Code 里能加载在 Codex 的 agent 模式下也能用只要 Harness 支持按目录加载技能格式上基本是通用的。这有点像 Java 的一次编写到处运行——当然还没那么完美但大方向是对的。npx 那个命令很有意思npx skills add sandai-org/vidmuse-skills --agent claude-code -g -y它做的事情就是把某个技能仓库克隆到本地的技能目录里然后告诉当前 Agent 有新的技能可以用了。这种npx skills add的模式本质上是在把技能包变成一个像 npm 包一样的东西可以被搜索、安装、升级、卸载。我甚至见过有人把团队的技能包放在 Git 私有仓库里新成员入职后一条命令拉下来就获得了一个积累了半年的团队最佳实践库。这个体验挺震撼的。3.3 Skills 与 MCP 的定位差异很多人会把 skills 和 MCPModel Context Protocol搞混我在项目里也经常被问到这个问题。我的理解是MCP 解决的是模型怎么连接外部工具和数据源是一个通信协议skills 解决的是模型怎么按照完整流程完成一类任务是一种能力封装。两者的关系更像是互补的一个 skill 的脚本里完全可以调用 MCP 暴露的工具来获取数据。你是一个 MCP 服务器暴露一堆原子操作而 skill 是教模型如何把这些原子操作编排成一个完整的业务流程。所以不要把它们放在对立面看实际项目中经常是共存的关系。4. 手搓一个 代码库体检 Skill 的完整过程4.1 先想清楚输入、输出和边界空谈理论没意思我直接分享一下自己从零搭一个技能包的过程。选代码库体检这个场景是因为它足够典型既需要脚本大规模扫描又需要模型做语义分析和报告生成能完整体现 skills 的协作优势。在设计之前我先想清楚了这个技能的边界输入用户想分析哪个目录、关注什么维度结构、依赖、安全、规范输出一份结构化报告包含模块清单、依赖关系、风险点、改进建议边界只做静态分析不做运行时测试只输出建议不直接改代码想清楚边界很重要不然技能就会失控——模型可能过度发挥直接帮你去改代码那就危险了。4.2 编写扫描脚本我写了一个结构扫描脚本用 Python 标准库里的ast模块做依赖分析不需要安装任何第三方依赖容易在各种环境下跑起来。#!/usr/bin/env python3 Scan repository structure and analyze module dependencies. import ast import json import os import sys from collections import defaultdict def scan_directory(root: str) - dict: Walk the directory and parse Python files, extract imports. modules {} dependency_graph defaultdict(set) for dirpath, _, filenames in os.walk(root): # Skip common noise directories skip_dirs {.git, node_modules, __pycache__, .venv, venv, dist, build} if any(part in skip_dirs for part in dirpath.split(os.sep)): continue for filename in filenames: if not filename.endswith(.py): continue filepath os.path.join(dirpath, filename) rel_path os.path.relpath(filepath, root) try: with open(filepath, r, encodingutf-8) as f: tree ast.parse(f.read()) imports [] for node in ast.walk(tree): if isinstance(node, ast.Import): imports.extend(alias.name for alias in node.names) elif isinstance(node, ast.ImportFrom) and node.module: imports.append(node.module) modules[rel_path] imports for imp in imports: if imp.startswith(.): # Relative import, resolve to the modules package base base rel_path.replace(/, .).rsplit(., 1)[0] dependency_graph[base].add(base.rsplit(., 1)[0] imp[1:] if imp.startswith(.) else imp) else: dependency_graph[rel_path.replace(/, .).rsplit(., 1)[0]].add(imp) except SyntaxError: modules[rel_path] [SyntaxError] # Convert sets to sorted lists for JSON serialization deps {k: sorted(v) for k, v in dependency_graph.items()} return {modules: modules, dependencies: deps} if __name__ __main__: target sys.argv[1] if len(sys.argv) 1 else . result scan_directory(target) print(json.dumps(result, indent2, ensure_asciiFalse))这个脚本做的事不复杂遍历目录树解析每个 Python 文件的 import 语句输出模块清单和依赖关系。但它解决了一个实际问题——让模型不用逐个打开几百个文件去自己读代码。脚本的输出是结构化的 JSON模型读起来非常方便。SKILL.md里的步骤就明确写了先运行这个脚本再基于脚本输出做分析。这样模型不会盲目地乱翻代码。4.3 写 SKILL.md 并注册到 Agent接下来是SKILL.md。我在写的时候特别注意两个地方一是步骤要足够具体二是要明确告诉模型每个阶段该看什么、产出什么。--- name: codebase_health_check description: 对代码库进行结构性体检。适用于要求分析项目架构、模块依赖、代码组织方式、潜在维护风险的场景。当用户说分析这个项目看下代码结构这个项目哪里有问题时使用。 --- # Codebase Health Check 你是一个资深的代码架构顾问。收到用户的分析请求后按照以下流程操作。 ## 第一步扫描项目结构 运行以下命令获取项目的模块与依赖清单 bash python3 scripts/scan_structure.py 项目路径如果项目不是 Python 项目扫描脚本效果有限请先用find命令列出目录树再人工分析文件组织方式。第二步分析模块组织基于扫描结果统计顶层模块数量和文件总数找出单文件超过 500 行的上帝文件标注职责不清的目录命名第三步分析依赖关系从脚本输出的 dependencies 字段中找出循环依赖A 依赖 BB 同时又依赖 A标出被大量模块依赖的核心模块这些模块的稳定性至关重要标出仅有自身依赖、从未被别的模块引用的孤儿模块第四步输出报告报告格式如下## 项目概况 ## 模块组织评价 ## 依赖关系分析 ## 风险点清单 ## 改进建议报告需要分级标注问题严重程度高影响稳定性的结构问题、中影响可维护性的问题、低风格和规范类问题。注册到 Agent 的方式取决于你用的工具。如果是 Claude Code把整个目录放到 ~/.claude/skills/ 或项目级的 .claude/skills/ 下即可如果是 Codex放到 ~/.codex/skills/ 下如果你用的是自研 Agent那你需要在 Harness 层实现一个扫描技能目录 - 读取 description - 按需注入的加载器逻辑并不复杂。 ### 4.4 实测效果有 Skill 和没 Skill 的差距 我拿一个中型项目做了对比测试同一个模型一组只管接提示词一组加载了 codebase_health_check 技能。 没加载技能时模型的回答停留在项目看起来结构清晰这种空话层面它不确定应该看哪些文件也不会主动跑脚本分析停留在表面。加载技能后模型会先跑扫描脚本拿到真实的模块和依赖数据然后基于数据指出utils 模块被 32 个文件依赖但其中没有单元测试、存在一个 1200 行的数据库访问文件需要拆分这类具体结论。 差距的本质在于没技能时模型是个没有工具的专家只能凭感觉说有技能后模型是个拿着体检设备的大夫能给你出具真正可执行的数据。这就是 Karpathy 说的给模型技能而不是给模型提示的实践含义。 ## 5. 我在实际使用中踩过的坑路径、权限与技能幻觉 ### 5.1 模型把技能里的路径当成绝对路径 第一次跑通时我就发现一个问题SKILL.md 里写的脚本路径 scripts/scan_structure.py模型有时候会拿项目里另一个同名文件来执行或者把相对路径解析错。 后来排查清楚模型在长任务中会丢失当前工作目录这一上下文它可能分不清这个脚本是技能包里的还是目标项目里的。这也是为什么很多技能包在被大型 Agent 执行时偶尔会出现找不到文件的错误。 我的解决思路是在 SKILL.md 里做一个强制约定所有技能包脚本必须通过环境变量或固定前缀引用比如 bash python3 ${SKILL_DIR}/scripts/scan_structure.py 目标项目路径SKILL_DIR由 Harness 注入指向技能包所在位置。这样一来无论模型当前工作目录是什么都不会跑错脚本。类似的坑我也在其他技能包里遇到比如参考文档的路径、示例文件的路径统一套这个约定就好。5.2 权限膨胀与脚本任意执行技能包里放着脚本本质上是让模型获得在本地执行代码的能力。这个能力一旦被滥用后果非常严重。我见过社区里有人撸了一个技能包里面有个脚本写了rm -rf清理逻辑结果模型在删除临时文件的语境下竟然试图去执行它幸好用了沙箱环境才没有酿成灾难。我的建议是三条技能包里的脚本只做读和报告不做删除或修改。如果确实需要改代码让模型输出 patch 内容而不是直接执行。在 Harness 层面对脚本执行做白名单。可以运行的脚本要满足路径在技能目录内等条件。所有技能包脚本先人工 review 再上架。把技能包当成代码依赖来管理别人写的技能包不能盲目信任先看一遍里面的脚本再装。5.3 技能描述重叠导致的选错技能当系统里挂载了几十个技能时新的问题出现了模型选技能的准确率下降。比如你有一个codebase_health_check技能还有一个dependency_audit技能两者的 description 都提到依赖分析模型就会犹豫甚至选错。这个问题在初期技能不多时几乎不会出现但技能库膨胀后一定会遇到。我的处理方法是每个技能的 description 力求单一职责不要试图覆盖太多场景关键触发词要明确使用用户真实会说的表达方式定期给技能库做瘦身把重叠的技能合并或拆掉我还给自己定了一个规则一个技能包只解决一类问题。如果我发现某个技能包里的步骤涉及代码分析和自动修复两件大事就果断拆成两个技能。单一职责在技能包设计里同样成立。5.4 技能包的版本管理升级可能引发的连锁反应最后再提一个很多人忽视的问题技能包升级了但之前的分析结果可能就不再可复现。因为技能包里的脚本可能改了输出格式SKILL.md 可能改了步骤顺序这些都会影响模型的行为。我现在会把技能包做成带版本的目录比如codebase_health_check_v1、codebase_health_check_v2并在 SKILL.md 的 frontmatter 里加一个version字段。分析报告里也可以让模型记录本报告基于技能包 v2 生成。这样将来复盘时至少能知道当时的分析是在什么技能指导下产出的。这个习惯在团队协作时特别有价值——否则你根本搞不清楚某次分析结果是基于哪版流程得到的。Skills 这套玩法说到底是在给 Agent 建一座可以不断积累的手艺库。我现在会把自己做过的高质量分析、常用的操作流程、团队沉淀的规范都逐渐打包成技能放进一个 Git 仓库统一管理。新项目需要时一条命令拉下来就能用。这个习惯形成之后你会明显感觉到 Agent 项目的开发效率上了一个台阶——毕竟模型资源不变变的只是它身边多了哪些趁手的工具。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。