资讯动态

AI Agent 技能工程化实践:Skill 拆分、测试与回归全指南

发布时间:2026/10/14 10:43:30 来源:尧图企业网站定制
做 AI Agent 的团队应该都有体会写一个能跑通 Demo 的 Agent 很容易但把它放进真实的工程环境、让它可以被维护、被测试、被团队其他人接手完全是另一回事。这个Skills项目的初衷就是想把 Agent 里的各种“能力单元”从散落的提示词和函数调用里抽出来变成一套有目录、有规范、有验证、能迭代的工程资产。这篇文章会拆解我在项目里踩过的坑和沉淀下来的打法包括 Skill 的目录设计、输入输出约束、测试与回归、版本控制以及最常见的失败模式。适合正在做 Agent 应用、或者想把原型系统往生产环境推的工程师参考不是理论科普是能直接拿去用的经验。1. 先说清楚Agent 里的 Skill 到底是什么1.1 为什么不能继续用“一个大提示词 一堆函数”早期做 Agent 应用大家都习惯把所有指令塞进一个 system prompt再把工具列表一股脑丢给模型。这个方案在最开始确实跑得通因为场景单一、工具数量少模型就算偶尔犯糊涂人工兜底也能接受。但项目业务一复杂问题就接踵而至prompt 超过一定长度后模型对指令的执行准确率明显下降尤其是靠后的指令。工具函数之间没有清晰的语义边界模型经常会选错工具或者在同一个场景里反复调用不同工具。出了问题很难排查——你不知道模型是在哪一步做错了决定因为整个调用链没有任何中间产物。Skill 本质上是把“模型做某件事的能力”封装成独立单元。每个 Skill 有自己的说明文档、输入输出格式、执行逻辑和验证条件。模型不再直接面对一个巨大的工具列表而是面对一组有清晰边界的技能模块按需要激活和调用。拿我自己的例子来说之前做一个客服工单分类系统老方案是一个超长 prompt 6 个分类函数。分类准确率在测试集上看着还行但一上真实数据就频繁出现离谱的分类结果。后来我把分类逻辑拆成独立的 Skill每个 Skill 独立维护一份分类规则和边界条件的说明准确率立马上了一个台阶。原因很简单模型在单一上下文里处理的信息越聚焦决策就越稳。1.2 Skill 与普通工具函数的区别很多人会把 Skill 理解成“带说明文档的工具函数”但它们在工程上的差别非常大普通工具函数是确定性的输入合法就返回结果不合法就报错。Skill 内部可能包含多次模型调用可能包含多个工具函数的组合甚至可能包含对其他 Skill 的调用。也就是说Skill 不再是“一个函数”而是一条可以自包含的工作流单元。它自己有输入、有输出、有内部状态、有终止条件。举个实际的例子。在我最近做的一个数据报表 Agent 项目里我需要 Agent 能够“按自然语言查询生成图表”。这个能力如果做成普通工具函数你需要把自然语言转 SQL、SQL 执行、结果转图表配置全塞进一个函数里代码极度耦合。但如果拆成三个 Skill语义解析 Skill把自然语言转成结构化查询意图。数据查询 Skill执行查询返回统一格式的数据集。图表生成 Skill根据数据集和用户偏好生成图表配置。每个 Skill 可以单独测试、单独替换、单独调优。这就是工程工作流的意义——不是让 Agent 变得更强更聪明而是让它的能力变得可拆解、可演进。1.3 Skills 项目要解决的四个工程问题这个项目在实际落地过程中我一直围绕四个问题在转第一可发现性。Agent 怎么知道在什么场景下该用哪个 Skill如果全靠模型从长列表里猜那必然会出现选择困难。我们需要一段精简的说明索引让模型在极短时间内判断是否激活某一个 Skill。第二可测试性。Skill 内部的每一条路径都要能被单独触发和验证。不能等到整个 Agent 跑挂了之后才去排查。第三可观测性。每次 Skill 调用的输入、输出、中间决策路径都要留痕否则后续想优化都不知道改哪里。第四可演进性。改了一个 Skill不能影响其他 Skill 的行为。换句话说Skill 之间要尽量解耦最好能支持独立的上线回滚。这四个问题本质上就是工程化改造的标准命题。只不过这一次改造对象从传统服务变成了一个充满不可预测性的 AI 系统。2. Skill 的目录结构与设计规范2.1 推荐目录结构在实际项目里我们最后采用的目录结构长这样skills/ ├── manifest.yaml ├── query_understand/ │ ├── SKILL.md │ ├── schema.json │ ├── examples/ │ │ ├── valid_cases.md │ │ └── edge_cases.md │ ├── validator.py │ └── prompt.py ├── chart_render/ │ ├── SKILL.md │ ├── schema.json │ ├── examples/ │ └── validator.py └── share/ ├── common_types.py └── utils.py这个结构看起来简单但每一层都有讲究。manifest.yaml是全局索引声明所有可用的 Skill 以及它们的依赖关系。模型在启动时会先读这个文件来建立全局认知。它的核心作用是把 Skill 列表压缩成一份轻量的路由表避免模型把每个 Skill 的详细文档都塞进上下文。每个 Skill 目录里SKILL.md是给模型看的不是给人看的。它写清楚这个 Skill 擅长什么、不擅长什么、在什么情况下应该切换到其他 Skill。这部分的语气和格式要反复打磨到“一读就能懂”。schema.json定义这个 Skill 的输入输出格式。它承担的是工程约束在下游代码里会被拿来直接做校验。validator.py是我们自己的校验逻辑。模型输出经常不严格遵循 JSON schema光靠声明式校验不够还得有代码层兜底。examples/目录放正反案例。正面案例让模型知道正确路径长什么样负面案例让它知道怎么避免踩坑。2.2 SKILL.md 的写作规范SKILL.md 的写法决定了 Skill 的使用率。我见过很多把 SKILL.md 写成产品需求文档的团队写了几百行模型根本读不进去。一份好用的 SKILL.md 应该控制在 80 到 120 行包含四个模块What这个 Skill 做什么一句话讲清楚。When什么时候该用它什么场景不该用它。这个模块最重要能极大降低模型选错 Skill 的概率。How操作步骤但只写关键步骤不写详细逻辑。Output输出格式和典型的返回值例子。我举个例子在报表数据查询这个 Skill 里when 部分的写法是## When to use - 用户请求中包含明确的数据字段比如销售额用户数订单量。 - 用户指定了时间范围、地域、渠道等筛选条件。 - 问题答案需要通过数据库查询获得不能通过常识推理得到。 ## When NOT to use - 用户只是在询问图表样式没有涉及具体数据。 - 数据来源是用户上传的临时文件而不是数据仓库。 - 查询逻辑涉及跨多个数据源的复杂 join此时应交给 data_service_skill。写清楚 When NOT to use 比写 When to use 更能提升准确性。模型是概率决策明确的负面边界能有效抑制乱用。2.3 Schema 设计的三大原则Skill 的输入输出 Schema 和普通 API 的 Schema 看起来差不多但设计逻辑有差异。第一个原则是输入必须有冗余信息。模型对隐式信息的理解不稳定所以我们在输入里会同时放“用户原始描述”和“结构化解析结果”。一旦结构化结果解析错了模型还有机会从原始描述里发现异常。第二个原则是输出必须分隔人与机器路径。有些值是需要直接返回给用户的有些是给下游 Skill 用的。混在一起的话上游 Skill 的输出格式改了下游直接崩。我们会在 Schema 里用summary字段存给用户看的内容用data字段存结构化数据。第三个原则是必须提供默认值或空值策略。模型不会每次都给齐所有字段。比如查询销售数据时用户没说时间范围Schema 里就要定义默认是最近一个月还是全量。没有明确默认值的 Schema 设计是 Agent 系统里大量运行异常的源头。Schema 设计完了之后要让 validator 在每一层跑一遍。模型输出必须经过校验才能传递到下一层凡是校验不过的宁可让这个 Skill 返回一个“能力不可用”的结果也不能把脏数据往下游传。2.4 原子 Skill 与复合 Skill把 Skill 分为原子和复合两层是我在这个项目里做得最正确的一个决定。原子 Skill 做单一一件事比如“解析自然语言日期”“计算两个时间段重叠天数”“把金额单位统一换算”。它们内部不调用其他 Skill模型直接面向它们做最终决策。复合 Skill 负责编排内部会调用多个原子 Skill并按一定顺序组织流程。比如“生成月度销售报表”这个复合 Skill 内部会依次调用数据查询、缺失值补全、图表生成三个原子 Skill。这个分层最大的好处是测试非常干净。原子 Skill 出问题范围一定很小复合 Skill 出问题大概率是编排逻辑不对而不是底层的某一个能力失效。在工程落地中我强烈建议团队把 80% 的精力花在打磨原子 Skill 上。复合 Skill 的流程可以靠改配置调整原子 Skill 的质量却决定了一个流程的天花板。3. 实操环节把一个工作流拆成可编排的 Skill 集合3.1 从业务流到 Skill 图的拆解方法这里分享一个比较好用的拆解法我一般叫它“输入输出追踪法”。第一步写出当前的业务流程把每一步的输入输出都列出来哪怕这一步目前是人做的。比如做一个工单摘要工作流流程就是接收工单原始文本提取关键信息包括客户、产品、问题类型、紧急程度检索历史工单生成摘要第二步对每一步问三个问题这一步有几种输入类型输出会被谁消费如果这一步暂时做不了有没有替代方案第三步把依赖关系画成一张无环图找到可以被独立封装的节点。凡是输入输出边界清晰、可以被其他流程复用的节点就拆成原子 Skill。以工单摘要为例“提取关键信息”和“检索历史工单”都没有依赖前续步骤边界又非常明确就应该独立成 Skill。而“生成摘要”依赖前两个步骤的输出但它的输出格式又非常稳定所以适合作为复合 Skill 对外暴露。3.2 一个完整的 Skill 实现样例拿数据查询这个 Skill 举例它的SKILL.md核心部分长这样--- name: data_query description: 根据结构化查询条件从数据仓库读取数据并返回统一格式的数据集 input: table: string metrics: array dimensions: array filters: object time_range: object output: columns: array rows: array row_count: integer --- ## When to use - 收到来自 query_understand_skill 的结构化查询意图。 - 用户直接提供了完整的表名和字段名。 ## When NOT to use - 查询目标不是数据仓库而是外部 API。 - 需要对多个表做复杂 join应交给 data_service_skill。 ## Steps 1. 根据 input.table 检查表是否存在不存在则返回 TABLE_NOT_FOUND。 2. 根据 input.metrics 和 input.dimensions 拼接查询字段注意 metrics 必须配合聚合函数。 3. 将 input.filters 的对象结构转换成 SQL WHERE 子句注意空值字段。 4. 执行查询并限制最大返回行数默认 1000。 5. 返回统一格式的数据集。对应的validator.py里会做一个重要检查确保查询结果里的字段和采集时的字段完全一致。这个检查能在第一时间发现模型拼接查询字段时的幻觉。实际跑主干流程的时候我发现一个反复出现的问题模型在拼接metrics和dimensions时经常混淆比如把本应做聚合计算的sum(sales_amount)写成一个普通字段。为了堵住这个坑我调整了 schema在输入里明确标注每个字段的语义类型并且给每个 metrics 字段强制配一个聚合函数模板。改完之后错误率直接从原来的百分之二三十降到了个位数。3.3 如何设置 Skill 的调用边界与退出条件很多人会忽略Skill 内部也必须有状态机和退出条件否则 Agent 很容易在一个 Skill 内部死循环。我在validator.py里总会写一个最大重试次数比如同一个查询动作重试三次仍返回异常就放弃本轮调用返回一个降级结果而不是无限循环。另一个边界是上下文隔离。复合 Skill 内部调用原子 Skill 时要避免把所有中间结果都堆在上下文里继续传。中间结果该落盘就落盘传给下一步的只保留 Schema 里声明的字段。这个操作能大幅减少模型“读到太多无关内容”造成的上下文污染。做一个形象的类比Skill 调用链如果设计得不好就像一个没有任何人在中途把关的工厂流水线上一环节的废料直接流到下一环节的加工台到最后一环时废料已经堆满了产线。合理的退出条件和上下文隔离实际上是在每个环节设置了质检点。3.4 本地调试工具用回放代替真实调用工程工作流里面一个非常提升效能的工具是回放日志。我们把每次生产环境中的 Skill 调用日志完整记录下来存到本地。调试新版本 Skill 时直接拿历史真实输入跑新的代码对比输出差异。这个方案比你自己编造测试用例强得多。因为真实用户的输入分布很难靠手工模拟出来。回放既有正常场景也有各种出人意料的边界情况。我自己的切身感受是回放调试大概能帮你项目上线时少花至少一半的时间在做测试数据上而且测试质量还更高。它不是用来替代测试集而是作为回归测试的一个子集存在。4. 常见问题与排查技巧实录4.1 模型不按 Schema 输出怎么办基本上每个人都会遇到这个问题。我的经验是分两层治理。第一层是声明层也就是把输出格式要求写进 prompt这是基础但远远不够。第二层是代码层validator 对所有输出做防御性解析。不能只依赖模型遵守 prompt要能识别偏离并自动修复。自动修复的策略通常有三步尝试把输出强转成目标类型比如字符串数字转 int、时间字符串转日期对象。尝试从输出里做字段映射比如模型把user_name写成了username用常见别名表兜底。尝试裁剪。当模型在前面的总结文字里混入了 JSON就提取 json 块的那一段再解析。如果在代码层修复不了就让这个 Skill 返回一个固定的OUTPUT_VALIDATION_FAILED状态由上层流程决定是重试还是降级。4.2 Skill 之间的冲突怎么处理多个 Skill 之间出现边界重叠是很正常的。比如“图表生成”和“表格导出”都有可能处理用户“把数据给我”的诉求。冲突处理的核心是优先级规则。我会在 manifest.yaml 里声明 Skill 的优先级并且要求模型优先选择描述更精确、匹配度更高的 Skill。同时会在 SKILL.md 的 when 部分增加反例来降低误命中率。比如图表生成 Skill 里明确写一句“当用户要求导出原始数据表格时不要使用本 Skill应切换到数据导出 Skill”。如果两个 Skill 经常冲突除了调整说明之外还可以考虑检查模型路由逻辑里是不是缺少一个前置的意图分类。有时候问题不在 Skill 内部而在 Agent 的调度层。4.3 Schema 变更导致历史 Skill 失效这是一个隐蔽的坑。线上 Skill 的 schema 变更会导致旧版本 Agent 实例调出的数据和新版本校验器不匹配从而返回大量异常。我们项目里的解法是把 schema 也纳入版本管理并在 manifest.yaml 里记录每个 Skill 版本依赖的 schema 版本号。部署上线时做自动比对不一致就不允许滚动。从工程角度看schema 版本管理可以复用传统 API 兼容性的成熟经验。任何对外暴露的 Skill 变更 schema 时必须先考虑向下兼容无法兼容的就必须走新版本名比如data_query_v2而不是原地修改。4.4 模型在 Skill 调用链中迷路一个非常典型的错误行为是Agent 在复合 Skill 内部已经拿到了正确的结果但因为最后的回答阶段缺少明确的指令又去额外调用了一个无关的 Skill生成了一个错误的补充说明。排查这种问题时靠日志是最好的方式。我会在关键节点插入 log记录每个 Skill 的激活时间、输入来源和输出去向再用日志回溯现场看模型的决策链是从哪一步开始跑偏的。治本的方案通常是优化 prompt 的结束条件。明确告诉模型当达到目标状态时直接停止调用任何 Skill进入最终回答。表达能力强的模型一旦把这个条件内化迷路率会明显下降。4.5 性能与成本的平衡Skill 拆细了之后调用链路变长模型调用次数变多延迟和成本都会上升。这里面有一个非常值得重视的取舍问题。我的经验是不是所有步骤都需要大模型。原子 Skill 当中很多步骤其实是规则逻辑比如单位换算、时间归一化这些完全可以用传统代码实现不必过模型。另一个值得考虑的方向是常见路径可以走缓存。比如相同用户、相同查询意图的请求短时间内可以复用上一次的结果。对于查询类 Skill缓存命中率往往能到百分之三四十能省下一大笔模型调用成本。如果某个复合 Skill 频繁出现在高延迟链路里可以考虑把这个链路整体固化成一个专用策略跳过部分中间 Skill 的模型调用。核心思路始终是模型调用是资产也是成本精确地用在必要的地方才划算。5. 工作流的演进后续5.1 从 Skill 到可观测评估体系当 Skill 数量到了两位数你就需要一套自动评估机制而不是每次新版本上线都靠人工抽查。我们在项目里搭建了一个简易评估池把每个 Skill 的历史真实输入和期望输出沉淀成用例集每次更新 Skill 之后自动跑一遍全量用例看准确率指标是否有回退。这个评估池目前在行业内有一定共识但底层逻辑并不复杂你需要一组足够贴近线上分布的测试样本它们最好是真实流量的采样而不是人工编造的理想样本。评估的不只是模型输出的准确率还要评估路径选择正确率。也就是说Agent 有没有在正确的位置激活了正确的 Skill。这一点往往比最终输出结果更容易隐藏问题。5.2 团队协作与 Code Review当多个工程师一起维护 Skill 集时最怕的是互相之间改了彼此的边界。我们后来约定了一个规则每个 Skill 都有唯一的负责人改动 Skill 说明文档和 schema 都必须通过 review。Review 时关注的顺序是边界描述是否清晰、Schema 是否向后兼容、validator 是否覆盖了足够多的异常路径、示例用例是否反映了真实线上分布。这个顺序和我们平时做代码 review 关注点很不一样但更贴合 AI 系统的特殊性。5.3 经验的沉淀与固化做 Skill 体系的时间越长越能体会到它带来的一个隐藏收益经验本身开始变得可沉淀、可复用。以前做一个新的 Agent 应用总要从头开始写 prompt、调函数、试错。现在不同基于已有的 Skill 资产池新应用只需要做两件事从资产池里挑选可复用的 Skill重置参数。针对新业务场景补齐缺失的原子 Skill。这其实是把 AI 开发从“艺术家模式”推向“工程模式”的关键一步。它还远不完美但至少团队不必每次都从零开始。6. 最后的一些真心话在这个项目上踩了大半年的坑之后我最大的体会是给 AI Agent 配工程工作流真正的难点不是智能而是确定性。你没办法让模型每次输出完全一样但你可以通过 Skill 的边界、校验、验证器和回滚机制把不确定性控制在一个可控的范围里。做这套体系从初期到成熟经历过很长一段时间的怀疑——投入了大量时间在编排设计上却觉得 Agent 的“智能感”反而不如一个乱写大 prompt 的版本来的灵敏。后来我才想明白那种灵敏不过是脆弱的表象场景稍一扩展就塌了。真正能在生产环境里稳定服役的 Agent靠的就是这种看似保守的工程化约束。如果你也正在做 Agent 应用我的建议很简单先把 Skill 拆分做薄让每个 Skill 的意图边界尽可能精确给每个 Skill 配好 validator 和日志然后建立一套基于真实流量的回归机制。这会让你后续的所有优化都变得有的放矢。这个 Skill 工作流的方向还有很大的扩展空间包括 Skill 自动生成、Skill 组合的自动优化、以及跨 Agent 共享的 Skill 仓库。我相信未来这会成为 Agent 基建里一个核心的基础单元。当下如果你正好在面临同样的困惑希望这篇分享能给你一些方向和信心。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑