AI Agent 工程化落地:从 Tools 到 Agent Skills 的完整实践指南 最近半年一直在折腾 AI Agent 的工程化落地agent-skills这个词几乎每天都会出现在我的工作清单里。GitHub 上拿这个名字命名的仓库越开越多行业讨论里也频繁把“技能库”“技能编排”挂在嘴边。这个词本身不算新概念但它背后指向一个实打实的变化AI Agent 的能力不再是一次性的提示词也不是临时接一个函数而是逐步沉淀成一套可复用、可测试、可组合、可版本化的能力模块。这篇文章算是我自己在这条路上的一份工程笔记聊聊我理解的 Agent Skills 到底是什么、怎么设计、踩过哪些坑。适合正准备做 Agent 能力封装或者已经在维护 agent 工具链的工程同学参考。1. 先搞清楚 Agent Skills 到底在解决什么问题1.1 从 Tools 到 Skills中间隔了一整个工程化思路agent-skills这类项目之所以能被单独拎出来讨论是因为它把“Agent 会干什么”这件事从一个运行时问题变成了一个研发管理问题。最早大家用 Function Calling给模型挂几个函数比如查天气、算个算术。这个阶段本质是把“模型不会的、或者容易算错的事情”外包给确定性代码。后来发现函数越挂越多模型选择函数的准确率开始下降而且函数只是单一动作没法表达“先做 A再校验 B失败就降级到 C”这种完整流程。Skills 就是把这一层补齐了。一个 Skill 不再是“单个动作”而是一个“完整的做事方法”它包含使用说明、输入输出约定、执行脚本、校验规则、异常回退甚至还有配套的测试用例。模型不再是简单地调用一个工具而是像拿到一份带 SOP 的工单按步骤把事情办完。我理解的核心差异在于Tools 解决的是“Agent 能调用外部能力”Skills 解决的是“Agent 能稳定地把一件事做好”。前者解决有没有后者解决好不好、稳不稳、能不能复用。1.2 为什么这个时间点突然需要单独搞一套 Skill 体系说白了是因为 Agent 应用开始从 Demo 往生产环境走了。Demo 阶段一段提示词写死就行生产环境不行生产环境要回答几个非常现实的问题同一个任务十个不同会话里做出来的结果能不能基本一致这个任务消耗多少 Token能不能把成本压下来Agent 学过的能力怎么沉淀换一个人接手还能不能用某个能力改坏了怎么发现怎么回滚Skills 就是把这些问题变成可管理对象的载体。一个 Skill 是一个文件夹里面有说明、有代码、有测试、有版本号。它跟普通代码库的管理方式几乎一模一样只是运行的时候由模型来调度。我见过不少团队把 skills 目录放在独立仓库里用常规的 Code Review 流程去审每一个 skill 的改动效果非常好。把 Agent 的能力当成代码来治理这才是 agent-skills 类项目真正带来的价值。2. Skills 和 Tools 的区别以及一个 Skill 的最小可用结构2.1 打个比方Tools 是刀Skills 是菜谱把 Tools 和 Skills 的关系想清楚后面的设计才不会跑偏。刀具是工具它能切菜、能剁骨但它不知道怎么配菜、不知道火候。菜谱是完整的方法论它规定了你需要哪些刀具、按什么顺序处理食材、出现什么情况用什么替代方案。模型本身是厨师它读菜谱、拿刀具、最终出菜。这个比方能解释很多实际问题。比如为什么有的团队函数一堆但 Agent 还是干不好活因为只有刀没有菜谱厨师只能靠灵感自由发挥。为什么 Skills 体系里的单个脚本往往都很简单因为复杂度被流程和说明吃掉了脚本只需要把确定性的那部分算对。我在实际项目里踩过一个大坑一开始把所有能力都往 tool 里塞结果模型的选择准确率越来越差因为工具数量超过二三十个之后光靠 description 区分彼此的边界就变得很困难。后来改成 Skills 体系把多个工具调用、校验、降级打包进一个 skill模型只需要在“十几个 skill”里做选择而不是在“几十个函数”里做选择准确率一下子就上来了。2.2 一个 Skill 的标准解剖结构参考社区里常见的 agent-skills 项目布局一个标准 skill 通常是这样的目录结构skills/ ├── changelog_generator/ │ ├── SKILL.md │ ├── scripts/ │ │ ├── parse_diff.py │ │ └── classify_changes.py │ ├── requirements.txt │ └── tests/ │ ├── golden_001.json │ └── golden_002.json ├── document_summarizer/ │ └── SKILL.md └── registry.yaml核心文件是SKILL.md它决定了模型能不能理解、会不会在正确时机调用这个 skill。我常用的结构是 YAML frontmatter 加正文--- name: changelog_generator description: 根据 git diff 生成结构化变更说明适用于 commit message、release note 场景 version: 1.0.0 allowed-tools: [run_command, read_file, write_file] tags: [git, changelog, docs] ---正文部分才是灵魂它告诉模型什么时候用、怎么用、不能用在哪适用场景拿到 git diff、需要产出变更说明时使用。不适用场景用户只是想看 diff 原始内容时不要使用。执行步骤先校验输入是否为空再统计变更文件和增删行数按文件类型做归类最后按指定风格生成输出。输出格式严格输出 JSON 对象包含 summary、categories、risk_items 三个字段。失败处理diff 超过 5000 行时先输出文件级摘要任何步骤失败都返回文件列表而不是报错退出。scripts 目录里放的是确定性逻辑比如解析 diff 文本、统计行数、按文件后缀归类。这些部分绝对不能交给模型现算不然 token 成本和出错率都压不住。tests 目录放 golden test每个测试包含输入样本和期望输出改完 skill 跑一遍就知道有没有破坏旧行为。2.3 什么样的 Skill 才算合格跟代码一样能跑的 skill 和合格的 skill 之间差距很大。我在评审 skill 的时候会按这几条标准过描述是“给模型看的”不是“给人看的”。不要写“强大的变更说明生成器提升团队协作效率”要写“当输入包含 git diff 文本、且需要生成提交信息或发布说明时使用”。判断标准是把 description 随机塞给一个没看过代码的工程师他也能判断什么时候该调。输入输出边界清晰。每个字段有没有类型、有没有必填、有没有取值范围都要明确。最忌讳的是“你看着办”式的字段。步骤是正面指令。少用“不要做什么”多用“应该做什么”。模型对否定指令的执行效果远差于肯定指令这个在 skill 提示词里体现得特别明显。有兜底路径。任何 skill 都必须回答一个问题输入不符合预期时怎么办。是报错、降级、还是给默认结果必须写死。有测试用例。没有测试的 skill 一旦改坏你往往要等到生产环境被坑了才发现。3. 实操一个能落地的 Skill代码变更说明生成器光讲概念没意思我拿最近实际整理的一个 skill 当例子完整走一遍设计到验收的流程。选它的原因很朴素频率高、输入清晰、确定性和大模型能力各占一半非常适合当模板。3.1 需求拆解哪些交给代码哪些交给模型需求一句话就能说清给一段 git diff产出一份人话版本的结构化变更说明。但拆开看里面混着三种不同类型的任务纯计算统计变更文件数、增删行数、涉及哪些目录。这类必须交给脚本模型算不准还费 token。分类判断这次变更是修 bug、加功能、还是改文档。这类可以用启发式规则按文件后缀和 diff 关键词粗分再用模型做二次确认。文案生成把变更内容组织成 commit message 或 release note。这类是模型的强项交给模型。拆完边界设计原则就出来了脚本负责“算得准”模型负责“写得好”谁也不越界。3.2 接口合约先把输入输出钉死设计 skill 的第一件事不是写提示词而是定接口。我当时的合约长这样{ input: { diff_text: {type: string, required: true}, style: {type: string, enum: [concise, detailed, conventional], default: concise}, locale: {type: string, enum: [zh, en], default: zh} }, output: { summary: string, categories: [feat, fix, docs, refactor, test], risk_items: [string], stats: {files_changed: int, insertions: int, deletions: int} }, errors: [EMPTY_DIFF, DIFF_TOO_LARGE, PARSE_FAILED] }这里有几个容易被忽略的设计点第一错误码一定要单独定义。不要返回“处理失败”这种笼统信息EMPTY_DIFF和PARSE_FAILED的后续处理策略完全不同。前者说明调用方给错了输入后者说明脚本解析逻辑有 bug 或者 diff 格式太怪。第二enum 字段能省掉大量解释成本。模型不用猜“style”该填什么值三选一出错概率直线下降。第三stats 字段放在 output 里很有用。模型生成文案时可以直接引用“变更了 23 个文件、新增 180 行、删除 42 行”比自己数准确得多而且这些数字天然适合放在 commit message 的正文里。3.3 实现骨架SKILL.md 加脚本怎么配合SKILL.md 的执行步骤我写得很直白尽量避免让模型做任何计算类工作检查 diff_text 是否为空为空直接返回EMPTY_DIFF。用parse_diff.py解析出文件列表、增删行数、目录分布产出中间 JSON。用classify_changes.py按“文件后缀 diff 内关键词”做一次粗分类产出候选类别列表。模型基于中间结果按 style 和 locale 生成 summary 和 risk_items。校验输出是否符合合约不符合就再生成一次最多重试两次。如果 diff 超过 5000 行跳过细节解析只输出文件级统计和目录分布。第 4 步是唯一需要模型发挥的地方。我在这步的提示词里刻意告诉模型“summary 要基于 stats 字段写不要自己从 diff 里重新数数字。”这样就把模型的幻觉空间压缩到最小。脚本部分没什么花活核心逻辑就是正则加统计。一个小提示diff 文本里二进制文件、 renaming重命名、纯文档改动这三类在解析时最容易出错。二进制文件没有统一的文本 diffrename 在 git 里默认可能是 delete 加 add文档改动则容易误判成代码重构。我的脚本里专门处理这三种情况分类时不会把.md文件的修改归到feat里。调用方式上接入 agent 框架之后大概是这种感觉result agent.execute_skill( changelog_generator, input{ diff_text: git_diff_string, style: conventional, locale: zh, }, )3.4 测试与验收没有 golden test 等于裸奔这个 skill 的测试集我按场景分成了几类每个都对应一个真实踩过的坑测试场景输入特征期望行为空输入diff_text 为空字符串返回EMPTY_DIFF错误码超大 diff超过 5000 行的合并请求降级为文件级摘要不卡死纯文档改动只有 .md 文件变更categories 只含 docs二进制文件diff 中出现Binary files differ跳过解析计入文件数统计重命名操作文件被 git mv识别为 rename不计入新增删除乱码输入非 UTF-8 编码文本返回PARSE_FAILED不抛异常验收标准我也定得很量化golden 集上成功率不低于 95%单次调用模型 token 消耗不超过 2500全流程耗时不超过 15 秒。这三个指标分别对应质量、成本、体验任何一个不达标都说明设计有问题得回去拆步骤。4. 常见问题与排查技巧实录4.1 模型死活不调用 Skill问题十有八九出在 description 上这类问题最多。模型就像个实习生它只会根据 skill 描述判断“这个活归不归我管”。描述写得太宽泛比如“生成高质量变更说明为团队提供支持”模型找个场景就硬套写得太窄比如“处理 git diff”模型遇到复杂点的需求就不敢调。我的排查习惯是先把 description 改成下面这种句式触发条件 输入形式 输出内容 明确的反例。比如当输入包含 git diff 文本、且需要生成 commit message、发布说明或变更摘要时使用。输入必须为完整 diff 字符串输出为结构化 JSON。如果用户只想查看 diff 原始内容不要使用此 skill。改完这句话调用率直接翻倍的情况我见过不止一次。改完之后一定要抽样测试拿历史对话记录问一遍“这里应该调用哪个 skill”统计一下命中率再上线。4.2 技能跑飞了先检查确定性边界是不是被模型占了有次我做一个统计类 skill发现每次输出数字都对不上。查了半天发现问题出在提示词上我让模型直接“分析 diff 中的变更规模”模型就真的开始自由发挥估算完全没用脚本统计的结果。这类问题的根源都一样把可以写成代码的部分交给了模型。排查思路很直接——把输出里所有带数字、带状态的字段列出来问自己一句“这个值能用脚本算出来吗”。能就坚决写在 scripts 里模型只能引用结果不能参与计算。这是 skill 设计里性价比最高的一条规则。4.3 改一行提示词炸了一片旧用例skills 最容易被忽视的坑是没有回归测试。上个月我调了 summary 风格的提示词测试里没覆盖这个字段结果线上输出的 JSON 结构变了下游解析直接挂了一批任务。后来我把那次教训固化成流程每次改SKILL.md必须跑一遍tests/目录下的 golden tests如果行为是有意变更的先更新测试再改提示词顺序不能反。4.4 技能环境脏了装个隔离层才省心skill 的脚本依赖环境跟主 agent 环境混在一起是最容易埋雷的地方。有的 skill 要 pandas有的要网络请求库装在一起出现版本冲突跑起来就是各种 ImportError。我现在每个 skill 都自带requirements.txt重依赖的 skill 单独建虚拟环境或者容器执行。轻量原则也很重要能用标准库解决的绝对不引第三方库。很多 skill 的核心逻辑就是字符串处理一个re模块就够硬上框架纯属给自己找麻烦。顺手整理了一张问题速查表日常排查基本够用现象可能原因排查方向模型频繁调用错误 skilldescription 边界不清重写描述为“触发条件输入输出反例”输出数字不准模型参与了计算把计算逻辑搬到脚本里改一次旧能力就坏缺少 golden test补测试先改测试后改实现脚本 ImportError依赖冲突独立虚拟环境按锁文件安装skill 静默失败错误处理是空响应定义错误码明确降级路径调用很慢步骤里有多余的模型往返精简提示词能一步算完的别分两步5. 一些个人的实操体会做agent-skills这类项目我最大的感受是它看起来是在写提示词、写脚本本质上是在做产品的流程梳理。最开始我犯过贪多的毛病一口气设计了二十多个 skill结果大部分躺在仓库里吃灰。后来我改成只围绕高频任务做一周出现三次以上的操作才值得封装成 skill。一个 skill 从设计到测试成本不低如果一周用不上几次性价比根本划不来。第二个体会是SKILL.md的文本质量直接决定整个体系的成败。我会先用模型帮我起草一版 description 和执行步骤然后逐字手工改。模型起草能把常见路径写全但边界条件、反例、异常处理这些必须人肉补。倒不是模型想不到而是它不会站在“这个 skill 会被不同人、不同上下文反复调用”的角度去写这部分只能靠经验补。第三个体会是把 skill 当代码管而不是当文档管。版本号、changelog、code review、测试用例一样都不能少。我现在的做法是每个 skill 文件夹里自带一份简短 changelog记录行为变更配合 git 历史谁改了什么一目了然上线前算三个指标调用次数、成功率、每次成功调用的 token 成本。指标不达标就不放量这比靠感觉判断靠得住得多。另外一个很实用的细节skill 命名和文件命名我也定了统一的规范。目录名用蛇形命名name 字段跟目录保持一致description 首句控制在 60 字以内。这些看着是小规矩但当 skill 数量过了十个模型描述检索的准确率就跟这些细节强相关命名乱的体系命中的表现一定差。如果你正准备搭自己的 skills 能力库我最后的建议是从两三个最高频的轻量任务开始跑通全部流程再把经验复制到更多技能上。流程通了后面加技能只是复制粘贴加调整的事流程没通技能越多维护越痛苦。