上个月我在给一家客户做内部知识库 Agent 的时候,遇到了一个很典型的问题:系统提示词已经写到三千多字,业务规则、操作步骤、异常处理全塞在里面,模型的表现却越来越不稳定。今天让它查库存,它漏掉一步校验;明天让它生成周报,它把上个月的格式忘得一干二净。排错排了两个晚上,最后发现问题不在模型,而在"能力"的组织方式上——我把所有能力都揉进了提示词,而不是做成独立的 Agent Skill。
那次以后,我把项目里的能力全部按 Agent Skill 的方式重构了一遍:提示词缩到两百字以内,每个 Skill 自带指令、代码、输入输出契约和测试用例,模型稳定性立刻上了一个台阶。这篇文章我就想把这套东西讲透,不是概念复述,而是从"Agent Skill 到底是什么"讲到"运行时架构怎么设计",再到"一个 Skill 从定义到上线的完整开发流程",最后聊几个我在生产环境里踩过的坑。内容适合两类人:一类是正在做 Agent 应用、觉得提示词快撑不住了的开发者;另一类是团队里准备沉淀 Agent 能力平台、想知道 Skill 该怎么抽象和治理的技术负责人。
1. Agent Skill 到底解决什么问题:提示词撑不住的那一刻
1.1 一个真实的失控场景
先说那个让我下决心重构的场景。客户有个工单系统,要求 Agent 每天自动处理三类请求:查库存、改订单状态、生成客户周报。最初我用一个超长系统提示词把所有流程写进去,看起来没什么问题——模型能按提示词做,也能调用几个工具函数。
但跑了两周,问题开始密集出现。改订单状态的流程里,用户地址变更后必须重新校验库存,提示词里明明写了,模型偶尔就是跳过;客户周报的格式要求有七八条,模型经常玩"选择性失忆"。我一开始以为是模型温度参数或者上下文截断的问题,调来调去都不稳定。
后来我换了个角度想:提示词里的每一段"操作流程",本质上是一个独立的能力单元。它有触发条件、有步骤、有前置校验、有输出格式,甚至有自己的失败处理策略。把这些东西全部混在一个超长字符串里,等于让模型每次请求都重新理解一遍"全部业务规则",然后自己决定执行顺序——这其实是在为难模型。它不擅长在几千字里精准检索并正确执行每一条流程规则,模型擅长的是理解意图和生成自然语言。
这就是 Agent Skill 要解决的问题:把"能力"从"提示词"里拿出来,变成一个一个有边界、有版本、可测试、可复用的独立单元。Agent 本身只负责理解用户意图、决定调用哪个 Skill、把结果组织成回答,具体的"怎么做"交给 Skill 去完成。
1.2 为什么现在才火起来
Agent Skill 这个概念其实不是新发明,它更像是把软件工程里的一些成熟思想搬到了 Agent 领域。你细看就会发现,它和微服务架构里的"服务拆分"、分布式架构里的"能力注册与发现"、插件化架构里的"扩展点"是一脉相承的。
早期 Agent 应用少,能力少,一个系统提示词加几个 function call 就够用。但当 Agent 要面对几十个业务动作、十几个外部系统、上百条业务规则时,"把所有东西放在提示词里"这种做法就撑不住了。于是社区开始分化出一层独立的概念层——Anthropic 提出过 Skill 的概念,OpenAI 有自定义 Actions,国内的一些 Agent 平台也在做"技能市场"。大家叫法不同,本质都在做同一件事:把 Agent 的能力原子化、标准化、托管化。
类比一下:单体应用撑不住业务增长时,大家会拆微服务;Agent 的提示词撑不住能力增长时,就需要 Agent Skill。它拆的不是代码,而是"能力"。每个能力都有清晰的输入输出契约、独立的执行逻辑、可观测的调用记录,最终由一个统一的运行时来调度。
1.3 Skill 在 Agent 里的定位
要理解 Skill 的定位,得先看清 Agent 的分层:最上面是 Agent 的核心,包括系统提示词、规划器、上下文管理器;中间是能力层,也就是一系列 Skill;最下面是基础设施,包括模型 API、外部系统接口、工具函数。
Agent 核心负责"做什么"和"什么时候做",Skill 负责"具体怎么做"。举个例子:用户说"帮我把这周的库存报告发到管理层邮箱"。Agent 核心先解析意图,拆出几个子任务:查库存、生成报告、发邮件。然后它选择并调用三个 Skill:库存查询 Skill、报告生成 Skill、邮件发送 Skill。每个 Skill 各自完成自己的部分,把结构化结果返回给 Agent,Agent 最后用自然语言把整个执行过程汇总给用户。
这个分层的好处是:改报告格式只需要改报告生成 Skill,不用动 Agent 核心;加一个"PDF 导出"能力,只要新增一个 Skill 并做好注册,Agent 核心完全不用改。能力可以独立迭代、独立测试、独立灰度,这跟微服务的好处是一样的。
2. Skill、Agent、Tool、Workflow:四个概念的分界与纠缠
2.1 四个概念的准确定义
很多人在 Reddit 和 GitHub 讨论区里会纠结一堆名词:Skill 和 Agent 有什么区别?Skill 和 Tool 有什么区别?Workflow 又是什么?我先把定义理清楚,再讲边界。
Tool(工具)是最底层的能力单元,通常是一次原子操作,比如"调一个 API 查天气""执行一段 SQL 查数据库"。它无状态、无流程、不做复杂决策,输入一个参数返回一个结果。
Workflow(工作流)是固定顺序的步骤编排,步骤之间可能有条件分支,但整体流程是预先定义好的,模型不参与决策。比如"上传文件→格式校验→转码→回写状态"就是一个 Workflow,每一步做什么都是写死的。
Agent(智能体)是一个自治实体,它有观察、规划、决策、行动的能力。它使用模型做推理,决定下一步做什么、调用什么工具、什么时候结束。Agent 的核心是"决策",而不是"执行"。
Skill(技能)是介于 Tool 和 Agent 之间的能力单元。它内部可以包含多步操作、可以带提示词指令、可以有前置条件和后置校验,但它本身不做"要不要做"的决策——决策交给调用它的 Agent。Skill 是"一套完成特定目标的方法论",是一个可复用的、有边界的执行模块。
2.2 Skill 和 Agent 的区别(最容易被绕晕的地方)
Skill 和 Agent 的边界确实模糊,因为一个 Skill 内部完全可以"像一个缩小版 Agent"那样工作:有指令、有步骤、可能还要调用若干 Tool。我区分它们的标准就一句话:谁拥有最终决策权。
Agent 拥有决策权,它可以自己判断用户意图、选择策略、决定什么时候停止。Skill 没有决策权,它只能按照自身定义的流程执行,执行完把结果交回去。哪怕这个 Skill 内部有分支逻辑,分支条件也是预先写死的,不是模型自由发挥的。
再换句话说:Agent 是"指挥官",Skill 是"操作手册"。指挥官看地图决定走哪条路,操作手册在选定的路上告诉你下一步踩哪里。如果把 Skill 写成"会自己做主"的东西,那它实际上已经是一个 Agent 了,这时候你的系统会出现两层决策体,非常容易失控。
2.3 四者边界对照表
我把它们整理成一张表,方便对照:
| 维度 | Tool | Workflow | Skill | Agent |
|---|---|---|---|---|
| 粒度 | 原子操作 | 多步骤流程 | 多步骤+指令 | 完整决策体 |
| 是否有状态 | 无状态 | 可有中间状态 | 可有内部状态 | 有完整上下文 |
| 是否包含指令 | 不含 | 不含 | 包含 | 包含 |
| 谁决定调用 | 调用方 | 固定编排 | 外部 Agent | 自主决策 |
| 是否用模型推理 | 不用 | 不用 | 部分用 | 核心用 |
| 可复用性 | 高 | 低 | 高 | 中 |
| 典型例子 | 查询 API | 订单处理流水线 | 客户周报生成 | 客服机器人 |
这张表我实际用下来觉得最有价值的判断方式不是看复杂度,而是看"决策轴"落在哪:如果一套流程的执行路径完全由外部决定,它是 Skill 或 Tool;如果执行路径由自身基于模型推理决定,它是 Agent。
2.4 Skill 和 Tool 的关系
有人把 Skill 看成是多个 Tool 的封装,这个说法对但不完整。Skill 确实常常内部调用多个 Tool,但 Skill 比 Tool 多出来的东西是"指令"。
Tool 只暴露一个函数签名:参数进、结果出。Skill 除了函数逻辑,还带一小段针对模型的行为指令,比如"这个查询结果如果为空,需要返回一个明确提示而不是报错""生成周报时,若上周无销售数据,请输出'暂无数据'而非空白表格"。这些指令是给模型看的,不是给代码执行的。它们约束模型在拿到结果后如何理解、如何组织输出。
所以一个完整的 Skill 应该包含两部分:给代码执行的程序逻辑,和给模型看的行为指令。两者缺一不可。这也是 Skill 和普通函数封装最大的区别。
3. Agent Skill 的运行时架构:从声明到执行的完整链路
3.1 Skill 的声明格式:Manifest 与实现分离
一个 Skill 要能被 Agent 运行时识别、调度、执行,首先需要一份标准的声明文件,业内通常叫 Manifest。它类似于微服务里的服务注册信息,也类似于 Kubernetes 里的 Deployment YAML。Manifest 决定了"这个 Skill 是什么、能干什么、怎么用"。
我常用的 Manifest 结构包含以下关键字段:
- name:Skill 的唯一标识,比如
inventory_query - description:对能力的自然语言描述,要写得让模型的规划器能看懂,例如"查询商品的实时库存数量,支持按 SKU 或品类过滤。当订单审核需要校验库存时使用。"
- version:语义化版本号,比如
1.2.0 - entry_point:入口函数,指明实现代码里的哪个函数要被调用
- input_schema:入参的 JSON Schema,约束参数类型和必填项
- output_schema:出参的 JSON Schema,方便上层校验结果
- instructions:给模型看的行为指令,约束输出风格和异常处理
- permissions:权限声明,比如需要访问哪个外部系统、需要哪些敏感字段
- dependencies:依赖列表,包括运行时依赖和外部服务依赖
这里我要特别强调 description 字段。很多开发者不重视它,随便写一句"库存查询"。但在实际运行中,Agent 的规划器是通过 description 来做语义匹配的——它要在几十个 Skill 里选出正确的那一个。description 写得太宽泛,模型会选错;写得太啰嗦,模型会忽略。经验是:说明能力范围、典型使用场景、禁用场景。比如"仅用于查询实时库存,不用于修改库存"就比"处理库存相关事务"好得多。
3.2 注册、发现与路由:Skill Registry
有了 Manifest,还需要一个注册中心(Skill Registry)来管理所有 Skill 的元数据。它做的事情和微服务架构里的注册中心一模一样:维护 Skill 列表、支持按名称精确查找和按语义搜索、管理版本和淘汰策略。
Agent 运行时拿到用户请求后,会经历一次"能力发现"过程。目前主流有两种方式:一种是精确路由,Agent 核心根据规则或少量模型推理直接指定 Skill 名;另一种是语义路由,把用户请求向量化,和所有 Skill 的 description 向量做相似度匹配,Top-K 候选再交给模型精排。
我在生产项目里实际用的是混合方案:高频请求走精确路由,规则写在代码里,零延迟且完全可控;低频、开放式的请求走语义路由。比如"查库存"直接映射到inventory_query,而"帮我看看最近仓库有什么异常"这种模糊请求就走向量匹配,从库存 Skill、报表 Skill、告警 Skill 里选一个或组合。
注册中心还会维护 Skill 的存活状态。我在设计时给每个 Skill 加了健康检查接口,运行时定期探活,失联的 Skill 自动摘除,这和分布式架构里的服务健康检查是一样的思路。否则一个从上游系统下线后还挂在注册表里的 Skill,会让 Agent 踩空。
3.3 执行器与上下文管理:Skill Runner
被选中后,Skill 就进入执行器(Skill Runner)。执行器是 Agent 运行时的核心组件,它负责拉起 Skill 的代码、传入参数、注入上下文、回收结果、处理异常。
执行器的第一个任务是参数校验。入参必须按照 Manifest 里的 input_schema 做校验,缺失必填项直接拒绝执行并返回错误信息。这一步不能省,因为 Agent 规划器从自然语言里抽取参数经常出错,比如用户说"查一下上周的库存",模型可能把"上周"当成库存查询的时间参数,但库存查询 Skill 根本不需要时间参数。参数校验可以把错误拦截在执行之前,而不是让 Skill 内部去猜。
第二个任务是上下文注入。Skill 执行时可能需要访问会话历史里的信息,比如用户 ID、当前租户、时区。执行器会维护一个上下文对象,按 Manifest 声明的权限范围把需要的字段注入进去。这里有一条安全原则:默认最小注入。Skill 只能看到它声明要看的上下文,不能默认拿到整个会话的所有数据,否则一个 Skill 的缺陷可能变成整个系统的数据泄漏点。
第三个任务是执行与容错。执行器会管理超时、重试、熔断。我建议每个 Skill 设置独立的超时阈值,不是所有 Skill 都用一个固定值。外部 API 调用类的 Skill 给 10 秒,纯计算类的给 3 秒,超时后执行器统一返回超时错误结构给上层。
执行结束后,结果会按照 output_schema 做一次后校验。结果不符合契约时,执行器会触发一次有限次数的"自我修正"——把结果和 Schema 的差异反馈给模型,让模型重新组织输出。加了这层校验之后,下游 Agent 拿到的数据质量高很多,很少再出现"模型把一个字符串数组输出成了逗号分隔的句子"这种问题。
3.4 权限与安全边界:Skill 不是天然可信的
Skill 本质上是可执行代码,它天然拥有系统访问能力。所以在架构设计里,权限控制必须是一等公民,不是事后补丁。
我的做法是为每个 Skill 声明明确的权限边界,运行时按边界强制约束。权限包括四类:数据访问权限(能读取哪些数据源、哪些字段)、操作权限(能执行哪些变更操作)、网络权限(能访问哪些外部服务)、密钥权限(能使用哪些凭据)。执行器在拉起 Skill 之前先检查权限,不匹配直接拒绝。
另外要特别注意密钥管理。Skill 代码里禁止硬编码任何密钥,所有凭据通过运行时注入。我在实际项目里发现,Skill 一旦可以被 Agent 动态选择,就存在被"诱导执行"的风险——模型在复杂对话里可能被用户带偏,调用了它不该调用的 Skill。所以敏感操作类的 Skill,比如"删除数据""发送邮件""修改订单",我在运行时还加了一道独立于模型的安全策略层,规则在代码里写死,不依赖模型的判断。这个设计我强烈建议所有做 Agent 平台的人都加上。
4. 手把手开发一个 Agent Skill:从定义到上线的完整流程
4.1 选型与目标:先定义边界再动手
开发 Skill 的第一步不是写代码,而是明确"这个能力的边界在哪"。我见过太多人拿到需求就开写,结果 Skill 越写越胖,最后变成一个隐藏的 Agent。
我通常会在动手前用一张小卡片定义清楚四件事:触发场景(什么类型的请求会用到它)、输入范围(它需要哪些参数、明确拒绝哪些参数)、能力范围(它能做什么、明确不做什么)、成功标准(怎么判断这次执行是成功的)。以"客户周报生成 Skill"为例:触发场景是"用户要求生成某客户在指定时间段的周报";输入是客户 ID 和时间范围;能力范围包括查询销售数据、统计关键指标、生成 Markdown 格式报告;明确不做的是发送邮件(那属于另一个 Skill)和生成 PDF。成功标准是输出报告结构和指标口径与业务方确认的模板一致。
边界定完以后,有一半的坑已经提前避开了。
4.2 定义 Skill Manifest:给模型看的说明书
接下来写 Manifest。这是 Skill 的"门面",Agent 规划器靠它来做匹配,执行器靠它来做契约校验。同样以周报生成 Skill 为例,我会这样写:
{ "name": "customer_weekly_report", "version": "1.0.0", "description": "根据指定客户和时间范围生成销售周报,包含销量、销售额、环比变化、TOP 商品、异常提醒。适用于管理层查看客户经营状况的场景。不负责邮件发送。", "entry_point": "main.generate_report", "input_schema": { "type": "object", "required": ["customer_id", "start_date", "end_date"], "properties": { "customer_id": {"type": "string", "description": "客户唯一标识"}, "start_date": {"type": "string", "format": "date", "description": "开始日期 ISO 格式"}, "end_date": {"type": "string", "format": "date", "description": "结束日期 ISO 格式"} } }, "output_schema": { "type": "object", "properties": { "report_markdown": {"type": "string"}, "summary": {"type": "string", "description": "一段话总结经营情况"} }, "required": ["report_markdown", "summary"] }, "instructions": "生成报告时遵守以下规则:1. 环比变化为负值要加警告标记;2. 若时间范围内无销售数据,明确输出'该时间段无数据',不要给出空白模板;3. 指标口径以订单支付时间为准。", "permissions": ["read:sales_orders", "read:customer_profile"] }description 字段我特意写了"不负责邮件发送",这是给模型的一个负向约束,能有效避免 Agent 在需要发邮件时错误地选到这个 Skill 上来。instructions 字段是给模型看的行为约束,它会在执行器把数据返回给 Agent 时发挥作用。
4.3 实现核心逻辑:程序部分与指令部分分离
Manifest 定义好后,开始写实现。一个 Skill 的实现代码有两个部分:正常运行的程序逻辑,和面向模型的指令逻辑。
程序逻辑我用 Python 来演示。入口函数接收标准化参数,从数据源拉数,做聚合计算,最后生成 Markdown 报告:
from datetime import date from typing import TypedDict class ReportInput(TypedDict): customer_id: str start_date: str end_date: str class ReportOutput(TypedDict): report_markdown: str summary: str def generate_report(input_data: ReportInput, ctx) -> ReportOutput: orders = ctx.data_client.query_orders( customer_id=input_data["customer_id"], start=input_data["start_date"], end=input_data["end_date"], paid_only=True # 指标口径:只统计已支付订单 ) if not orders: return { "report_markdown": "该时间段无销售数据。", "summary": "客户在该时间段内无已支付订单。" } total_sales = sum(o["amount"] for o in orders) total_units = sum(o["quantity"] for o in orders) top_products = sorted(orders, key=lambda o: o["amount"], reverse=True)[:5] markdown = _render_report(input_data, total_sales, total_units, top_products) summary = f"本周销售额 {total_sales:.2f},销量 {total_units},TOP 商品为 {top_products[0]['name']}。" return {"report_markdown": markdown, "summary": summary}注意几个实践细节:入口函数没有直接连数据库,而是通过 ctx.data_client 访问数据,这是为了在测试时方便 mock;所有异常都在执行器层面统一捕获,函数内部只抛业务语义错误;Markdown 渲染单独抽成 _render_report,方便后续换模板。
Manifest 里 instructions 那段是针对模型的,它在代码里没有对应逻辑,但它很重要——当执行器把结构化结果返回给上层 Agent 时,Agent 会参考这段指令来组织最终回复。比如"若时间范围内无销售数据,明确输出'该时间段无数据'",就能避免模型自作聪明编造一个空报告。
4.4 接入、测试与发布:别跳过契约测试
Skill 写完不是直接上线,我固定的流程是四步:单元测试、集成测试、影子运行、灰度发布。
单元测试覆盖的是纯逻辑,比如聚合计算、模板渲染、边界值处理。集成测试需要把 Skill 挂到真实 Agent 运行时上,模拟用户请求,验证从规划器选 Skill 到执行器出结果的完整链路。这一步我强烈建议写一组"错误注入"用例:故意给错的参数类型、超时、外部系统返回异常,看整套链路是否按设计兜住。
影子运行是把真实用户请求中的一部分流量复制一份打到新 Skill 上,只记录结果不真正影响业务。我通常跑 2 到 3 天,对比新 Skill 和线上旧版本在同一批请求上的输出差异。这一步能抓出很多测试用例覆盖不到的问题,比如真实对话里的参数抽取经常和测试集的干净参数不一样。
灰度发布时我会按租户维度放量,先给 10% 的流量,稳定后再全量。因为我做的 Agent 是有真实业务影响的,一个周报格式错了客户会投诉,灰度能显著降低这种风险。
5. Skill 和系统提示词工程的关系:不是替代,是分工
5.1 两者本质上的不同
很多人在网上搜"系统提示词工程和 Skill Agent 有什么区别",我直接说结论:它们解决的是不同层面的问题,Skill 不是提示词工程的替代品,而是提示词工程达到天花板之后,下一层的能力组织方式。
系统提示词解决的是"Agent 应该是什么样":它定义 Agent 的角色、语气、价值取向、边界规则。比如"你是一个严谨的客服助手,回答要简洁,不确定时主动说明"属于系统提示词层面。这类内容有全局性,几乎每次交互都要生效,适合放在一个较短的、稳定的、精心维护的系统提示词里。
Agent Skill 解决的是"Agent 应该会什么":它定义具体的操作流程、数据来源、输出规范。这类内容通常只在特定场景下才需要加载。如果把它们也塞进系统提示词,就回到了我开头描述的那个失控场景——系统提示词越长,模型越难在所有上下文里保持一致的遵守度。
5.2 什么场景用提示词,什么场景用 Skill
我的判断标准是三条:
一是全局性 vs 局部性。角色定位、边界伦理、通用回复风格,全局生效的用提示词;特定业务流程、特定数据操作,只有特定场景触发的用 Skill。
二是静态性 vs 动态性。半年不变的内容可以留提示词;经常迭代、业务方隔三差五改规则的内容必须拆成 Skill,否则每改一次提示词都要做一次全量回归。
三是知识性 vs 操作性。告诉模型"公司产品有哪些优势"是知识,用提示词注入没问题;要求模型"按口径算销售额并生成报告"是操作,需要和代码结合,必须用 Skill。
实际项目里,我见过最好的架构是这样一个组合:系统提示词控制在 300 字以内,包含角色、语气、安全红线;业务规则和操作流程全部拆成 Skill;两者通过模型规划器协作。这样改业务规则只改对应 Skill,改完独立测试、独立发布,完全不碰 Agent 核心。
5.3 Skill 之间也需要提示词工程
有趣的是,Skill 本身的 instructions 字段也需要提示词工程。这部分工作在项目里占比不低,我甚至有一张专门的 instructions 优化记录表,记录每次修改对输出质量的影响。
比如"生成报告时遵守以下规则:1. 环比变化为负值要加警告标记",这个写法比"把环比负值标记出来"更有效,因为前者指明了规则的适用位置和触发条件。还有一点:instructions 不要写"不要做什么"的抽象描述,要写"遇到什么情况做什么具体处理"。比如把"不要生成空报告"改成"若时间范围内无销售数据,明确输出'该时间段无数据'",模型的表现会显著更好。
6. 生产环境里做 Skill 开发,我踩过的坑和现在的习惯
6.1 坑一:Skill 越做越像 Agent,最后变成两层决策体
这是我犯过的最大错误。早期我开发一个"异常订单处理 Skill",为了让它足够"聪明",在 Skill 内部加了很多模型推理分支:如果订单金额大于阈值就自动审核、如果客户信誉等级高就跳过验证……结果 Skill 内部自己会做决策,外部 Agent 也在做决策,两个决策体互相不知道对方的存在,项目后期出现大量"Agent 以为 Skill 做了、Skill 以为 Agent 会决定"的漏洞。
现在我在设计 Skill 时有一条硬性规定:Skill 内部的 Model 调用只能用于"理解和生成",不能用于"决策"。比如周报 Skill 可以调用模型来润色措辞,但"要不要发给客户"这个决策必须留给上层 Agent。这条规定执行之后,双层决策导致的混乱基本绝迹。
6.2 坑二:追求"万能 Skill",一个 Skill 恨不得覆盖所有场景
有段时间我试图做一个"数据分析 Skill",既能查库存、又能算销售额、还能做人效分析。结果就是 description 写得又长又泛,模型经常选错,输入参数也设计得极其复杂。后来我把它拆成三个独立的 Skill,每个只管一件事,参数各自简单清晰,准确率明显提升。
原则很简单:Skill 的边界越小越好,宁可多几个 Skill,也不要一个万能 Skill。判断标准是——如果这个 Skill 的 description 需要用 200 个字才能说清楚,它大概率该拆了。
6.3 可观测性和灰度发布:Skill 一定要能"被看到"
Agent 应用天然带不确定性,同一个 Skill 在不同上下文里表现可能完全不同。这让可观测性从"应该做"变成了"必须做"。我在每个 Skill 的入口和出口都埋了结构化日志,记录了请求 ID、入参、出参、耗时、异常栈。更重要的是,我会把 Agent 规划器选 Skill 那一刻的"决策依据"也记录下来,比如是精确路由还是语义匹配、匹配分数多少。这样出了问题,能快速判断是"选错了 Skill"还是"Skill 执行出错了"。
另外在灰度发布上,我对 Skill 的版本管理用了语义化版本加契约测试的组合:主版本号变化表示输入输出契约变了,必须走全量回归;次版本变化表示内部逻辑增强但契约不变,可以灰度放量;补丁版本就是小修小补直接发布。这个规则让整个团队的 Skill 迭代有了节奏,不会出现"改了一个小细节把下游搞挂"的事故。
6.4 我现在沉淀下来的工程习惯
做了半年多 Agent Skill 开发,有几条习惯我希望每个入行的人都能早点养成。
第一,每个 Skill 必须有 owner。Skill 不是写完就结束的一次性代码,它是需要持续维护的能力资产,没有负责人的 Skill 会在三个月内变成谁都不敢碰的定时炸弹。
第二,Skill 的数量要做定期审计。我会每两周让系统统计哪些 Skill 被高频调用、哪些三十天无人调用。无人调用的 Skill 要么是场景已消失,要么是 description 写得太差导致规划器永远选不中它。前者下架,后者改描述重试。
第三,永远给 Skill 一个"退化路径"。外部系统不可能永远稳定,Skill 必须定义自己在依赖不可用时的行为:是降级返回旧数据,还是明确告知用户"当前不可用"。我遇到过最糟的情况是 Skill 依赖的报表服务挂了,但 Skill 还在傻傻重试,把下游拖垮。现在所有 Skill 的容错策略都写进 Manifest,由执行器统一执行。
第四,先让一个 Skill 跑通,再讨论平台。很多团队一上来就做"Skill 平台""技能市场",实际上连一个 Skill 都没跑顺。我的建议是先手工写三到五个核心 Skill,把流程跑通、把坑踩完,再抽象出公共底座。否则你做的平台只会是空中楼阁,看起来很美,实际用不起来。
最后分享一个小技巧:我在本地维护了一套"Skill 沙盒",用假的上下文对象和 mock 数据源把所有 Skill 串起来跑,每改一行代码就全量跑一遍。这套沙盒的维护成本不高,但帮我挡掉了大量"上线了才发现回归问题"的尴尬。Agent 应用的不确定性已经够多了,能通过工程手段控制住的部分,尽量在开发阶段就控制住,别带到生产环境里去赌。