Skill 在项目中的实践 —— 从 Agent 困境到 Skill 工程化

大家好,今天想和大家聊一个最近在项目里特别有感触的话题:怎么让大模型 Agent 从"看起来很聪明"真正变成"能干活、干得稳"。

这篇文章我会沿着 Agent痛点 → Skill 是什么 → 怎么写好一个 Skill → 一个真实案例 → 怎么让 Skill 更可靠 的路径,把我们踩过的坑和总结的方法讲清楚。

一、先说说 Agent 在落地时到底卡在哪

在动手做 Agent 的时候,开发者和用户经常会被几个问题反复折磨。具体表现主要有三个。

  • Prompt 极度脆弱且难以复用:将所有逻辑塞入 System Prompt 会导致指令过载。一旦需要调整某个微小行为,往往引发"牵一发而动全身"的灾难性后果,且不同场景间的 Prompt 几乎无法复用。
  • 工具调用(Tool Calling)的不稳定性:当 Agent 挂载的工具过多时,模型容易出现"幻觉",调用错误的工具、遗漏必填参数或陷入死循环。
  • 上下文窗口溢出与注意力分散:复杂任务需要极长的上下文,不仅增加 Token 成本,还会导致大模型在执行多步推理时"遗忘"最初的目标或中间状态。

这三个痛点其实指向同一件事:我们一直在用"对话"的方式驱动 Agent,但真实工程需要的是"规格"。

二、Skill 机制:从"对话"到"工程"的一次范式跃迁

Skill 到底是个啥

一句话讲清楚:Skill 不是一段 Prompt,而是一个自包含的、结构化的能力封装单元。

你可以把它理解成 Agent 的"专业培训模块"。打个比方:

  • 没加载 Skill 的 Agent,像一个什么都懂一点、但从没真正干过活的实习生,让他做事全靠你一句一句教。
  • 加载了 Skill 的 Agent,更像一个揣着"标准作业流程(SOP)"的资深专家,活儿怎么干、什么时候该停、出问题怎么处理,都写在册子上。

差别就在于:前者靠灵感,后者靠规格。

一个完整的 Skill 长什么样

一个像样的 Skill,通常由七个核心要素组成。

  • 元信息 --- Skill 的"身份证":名称见名知意(如 page-generator),描述说清它解决什么问题,标签用于分类检索(如 React、Ant Design)。
  • 触发语义 --- 定义"什么时候该用我"。触发可以是显式的(用户说 /generate-form),也可以是隐式的(检测到用户打开了组件目录且在描述表单需求)。好的触发语义做到该触发时不漏,不该触发时沉默。
  • 上下文与前置条件 --- 声明运行前需要什么。比如 page-generator 的前置条件:已安装 Ant Design、路由配置已存在。不满足直接终止,避免跑到一半才发现环境不对。
  • 执行流程 --- Skill 的核心,将任务拆解为原子步骤序列,每步有明确的输入、输出和工具白名单。好的流程让 Agent 按蓝图执行,不需要"发挥创意"。
  • 工具约束 --- 遵循最小权限原则。比如 component-builder 只需要读组件库文档和写组件文件,不需要调接口、改路由、跑部署。
  • 验证与护栏 --- Skill 质量的生命线。每步完成后有断言:组件生成 → 跑 ESLint + type check;页面完成 → 验证 loading/空态/错误态的覆盖。验证失败时护栏接管:可自动修复的重试,需人工判断的升级,不可恢复的终止并清理。
  • 领域知识 --- 将组织的隐性知识显性化:编码规范、架构约束、历史事故教训。随项目演进持续更新,是 Skill 区别于一次性 Prompt 的根本所在。

Skill 和 Prompt 到底差在哪

直接上对比:

维度 Prompt Skill
组织方式 一段线性文本 结构化模块
复用性 靠复制粘贴 声明式引用
激活方式 始终生效 按需触发
可测试性 很难验证 可单测、可回归
组合能力 拼一起就打架 能编排组合
版本管理 几乎没有 可追溯、可回滚

这不是量变,是质变。当能力被封装成 Skill,Agent 就从"一段对话"升级成了"一个可编排的执行引擎"。

Skill 的完整调用流程

一个 Skill 从被触发到完成,会走完如下五个阶段的生命周期。

阶段一:识别与匹配。 根据触发语义做意图路由。结果可能有三种------零个 Skill 匹配(那就走通用对话)、刚好一个匹配(直接激活)、多个候选(按置信度排序,或者让用户选)。

阶段二:上下文装配。 按需加载领域知识、校验前置条件、绑定工具权限。装配完成后,Agent 就从"通用大脑"切换成"专才模式",眼里只剩下当前任务需要的信息,不再被无关上下文干扰。

阶段三:逐步执行。 按 SOP 以"执行---验证"的双拍节奏推进。断言通过就走下一步,失败就触发护栏。

阶段四:终态验证。 整体跑完后再做一次总检查。比如页面生成完,验证路由能不能跳转、样式一致不一致、表单校验和联动规则是不是都生效了。

阶段五:反馈闭环。 结果给到用户,执行轨迹写进日志。生成的组件符不符合编码风格?用户改了几次才满意?这些数据反过来驱动 Skill 持续迭代。

三、怎么写好一个 Skill:实操五步法

写 Skill 之前,推荐先用 Ant Skill Creator 这个脚手架工具来辅助。它会用结构化对话引导你完成 Skill 的初始设计。

打开 Creator,它会先抛给你四个问题:做什么、何时触发、产出什么、怎么验证。这四个问题正好对应下面五步法的前三步------界定边界、拆解流程、定义验证规则。

Step 1:界定边界(先想清楚再动笔)

动笔前先回答三个问题:

  • 这个 Skill 做什么? 一句话说清输入和输出。比如:"给定一个页面需求描述和接口文档,生成一个符合 Ant Design 规范的中后台 CRUD 页面,包含搜索表单、分页表格、新增/编辑弹窗。"
  • 这个 Skill 不做什么? 把不处理的场景明确排除掉。比如:"不处理路由注册、不做接口联调验证、不处理复杂的流程图类交互。"
  • 谁来用这个 Skill? 是 React 中后台开发?还是小程序开发?不同受众决定了术语体系和粒度。

边界一旦清晰,你就不会写出那种"听起来无所不能、实际啥都干不好"的 Skill。

Step 2:拆解流程(把活儿切成原子步骤)

把"完成这件事"拆成 3 到 7 个原子步骤。每一步都要满足三个条件:

  • 单一职责:一步只干一件事
  • 可独立验证:做完就能判断对错
  • 有明确交付物:输出是能检查的东西,不是"我感觉做完了"这种模糊状态

每一步都很具体,Agent 照着干就行。

Step 3:定义验证规则(这一步决定了 Skill 是不是摆设)

为每一步、每个最终结果定义验证条件。

验证层级 时机 例子
前置条件 执行前 是否已安装 antd?项目是否有全局路由配置?
步骤断言 每步后 Step 3 生成的表单,是不是包含了所有接口字段对应的表单项?
终态核对 全流程后 页面是否通过 TypeScript 编译?表单校验是不是覆盖了必填、格式、边界?表格操作列按钮有没有 loading / disabled 态?

写验证条件的关键是"具体、可机械化判断"。 千万别写"页面功能完整"这种 Agent 自己读完都心虚的东西,要写"ESLint 零 error、TypeScript strict mode 编译通过"------这种能直接跑出结果的。

Step 4:注入领域知识(把团队的经验喂进去)

把团队沉淀下来的东西写进 Skill:

  • 编码规范:你们用 CSS Modules 还是 styled-components?状态管理用 Zustand 还是 Redux Toolkit?
  • 模板示例:放一些代码模板、参考片段进去。
  • 历史教训:上次那个审批页面上线后各种白屏,就因为没处理接口异常时的 ErrorBoundary------那就把"每个页面必须有错误边界"加成护栏。

领域知识不是一次写完的,而是每次翻车后补充进去的。每一次翻车,都是让 Skill 变聪明的机会。

Step 5:迭代打磨(上线只是开始)

Skill 上线不是终点,持续打磨才是它从"能用"走到"好用"的关键。除了被动从执行数据里发现问题,还有三种主动打磨方式。

  1. 失败反哺机制

每次失败都自带打磨素材------失败场景 + 人工最终是怎么修好的。

  • 记录:这次 Agent 卡在哪一步?人工最后是怎么救回来的?
  • 归类:按周聚合一下,找高频失败模式。比如"生成的表格总是缺少操作列的权限控制逻辑"。
  • 回写:把模式沉淀进 Skill------更新前置条件校验、补充领域知识、加新的护栏。

核心心法是:别浪费任何一次失败。每次翻车都是免费的质量测试用例。

  1. 同行互审(Skill Review)

代码要 code review,Skill 也一样。可以问几个问题:

  • 别人能不能光看你的 Skill 定义就懂它要干嘛?
  • 触发条件有没有漏洞,会不会在不该触发的时候触发?
  • 护栏够不够严,某一步挂了恢复策略合不合理?
  • 领域知识有没有过时,废弃的规范现在还在用吗?

形式可以很轻:每两周花 20 分钟,一个人讲、两个人挑刺,比任何自动化检查都更能发现"你没想到的情况"。

  1. 对抗性测试

主动用刁钻的输入去试探 Skill 的边界:

  • 故意输入不完整的信息("帮我生成个表单",但不说有哪些字段)
  • 故意制造矛盾(同时要求"最简化实现"和"覆盖所有交互态")
  • 故意输入领域外的任务(让一个写医疗前端的 Skill 去配 Webpack)

对抗性测试的目的不是让 Skill 什么都能干,而是确认它在不该干的时候能干净利落地拒绝。一个好的 Skill,会优雅地说:"这需求我不太清楚,能再确认一下吗?"

打磨不是一次性动作,是个习惯。推荐节奏:每周花 30 分钟,做一轮用例回归 + 翻最近的失败记录 + 挑一个边界场景做对抗测试。 时间不长,但坚持三个月后,成熟度差距会非常明显。

四、扩展:融合 Harness Engineering --- 让 Agent 从"能跑"到"可运营"

Harness Engineering 是什么

Harness Engineering(Harness 工程)是围绕 AI Agent 构建脚手架系统的一门工程学科。这个概念由 Vivek Trivedi(LangChain)首次明确提出,其核心公式为:

Agent = Model + Harness

你不是模型,那你就是 Harness。

Harness 是模型之外的一切------系统提示词、工具调用、文件系统、沙箱环境、编排逻辑、钩子中间件、反馈回路、约束机制。模型本身只是能力的来源,只有通过 Harness 把状态、工具、反馈和约束串起来,它才真正变成一个 Agent。

六层架构体系

一个成熟的 Harness 体系分为六层,从"定义边界"到"兜底恢复"形成完整闭环:

层级 名称 解决什么问题 关键设计 类比(新员工)
L1 信息边界层 Agent 该知道什么 定义角色与目标,裁剪无关信息 岗位说明书
L2 工具系统层 怎么跟外部世界交互 工具选拔、调用时机、结果提炼 办公工具
L3 执行编排层 多步骤任务怎么串 理解→判断→分析→生成→检查 标准操作流程
L4 记忆与状态层 长任务中间结果怎么管 独立管理状态、中间产物、长期记忆 项目管理系统和笔记本
L5 评估与观测层 怎么知道做对了 独立于生成的验证机制 质检流程
L6 约束校验与恢复层 出错了怎么办 预设规则拦截错误,重试或回滚 红线规则和应急预案

💡 落地建议:不要试图一开始就搭齐六层。从 L1(信息边界)和 L6(约束与恢复)入手,这两层投入产出比最高。

一个真实案例

lifecycle 是一个工作流编排器,解决的是前端项目启动前那条漫长的前置链路:理解需求 → 写系分文档 → 设计设计稿 → 搭建项目 → 连接 API。用户只需说一句话(比如"帮我生成一个xxx系统的前端系分并开始开发"),Agent 自动走完 几 个阶段,最终产出一个能直接跑起来的项目脚手架。

设计思想

这个 Skill 的设计思路其实很简单,核心就四句话:

编排不执行。 编排器自己不写代码、不生成文档、不画设计稿。它只做一件事------按顺序调度子 skill,把它们的产物串起来。就像工地上的项目经理,不搬砖不砌墙,但知道什么时候该叫谁来干什么。

只认产物,不靠记忆。 阶段之间怎么衔接?不靠 Agent 的"记忆力",靠文件。上游产出了 SDD 文件,下游读取 SDD 路径才能启动。上游产出了设计稿,下游拿着设计稿列表继续。每一个阶段的"入场券"是上一阶段的产物,不是 Agent 脑子里记得"我好像干到第三步了"。

多入口归一。 用户给的可能是语雀 PRD 链接、一段文字需求、或者现成的 SDD 文件。编排器在入口处识别类型、分拣路由,不管哪种输入,最终都汇入同一条管道,产出统一的东西。

中断了能继续。 几 个阶段的流程,不可能每次一口气跑完。编排器把当前进度、中间产物路径持久化下来,用户下次说"继续",直接从断点恢复。

它踩中 Harness 六层的地方

回头看,这些设计思路恰好落在了 Harness 六层框架上:

  • L1 信息边界:入口路由识别输入类型,让 Agent 在最开始就知道这次任务的视野范围------是从 PRD 起步还是直接复用 SDD?不用在所有信息里大海捞针。
  • L2 工具系统:每个阶段有明确的工具组合。翻译层用 yuque + Read,可视化层用 SVG 生成,工程化层用模板 + Bash。工具按需装配,不乱调。
  • L3 执行编排:最显性的一层。决策树判断输入类型,状态机管理阶段推进,几 个阶段拆成标准化的步骤序列,Agent 照着跑就行。
  • L4 记忆与状态:持久化 JSON 记录当前阶段、SDD 路径、设计稿列表、服务层路径。阶段间靠这些产物显式传递,不靠 Agent 的上下文窗口"回忆"。
  • L5 评估与观测:设计稿生成后和 SDD 页面列表逐项比对,项目启动后验证编译通过。每步都有可检查的基准,不是"看着差不多就行"。
  • L6 约束校验与恢复:OneAPI 权限不足 → 提示用户处理,子 skill 缺失 → 提示安装后重试,Axure 链接不可访问 → 降级为纯文字需求理解。每种失败都有对应的出口。

具体怎么做 - 用 Harness 思想来写 Skill

五步法 + 六层架构怎么配合

上面讲的写好skill的五步法是实操手册:界定边界 → 拆解流程 → 定义验证 → 注入知识 → 迭代打磨,告诉你"怎么写一个 Skill"。六层 Harness 体系是验收清单,告诉你"写到什么程度 Agent 才能可靠执行"。两者不是替代关系,是操作手册和质检标准的互补。

五步法已经覆盖的,六层让它更严谨:

  • Step 1 界定边界 ↔ L1 信息边界层。 五步法问了三个好问题:做什么、不做什么、谁来用。Harness 补一条:
  • 边界定义放在 SKILL.md 最显眼的位置,让 Agent 一进来就看清视野。视野太大注意力涣散,太小信息又不够。
  • Step 2 拆解流程 ↔ L3 执行编排层。 五步法要求每步单一职责、可独立验证、有明确交付物。Harness 再补两条:
  • 工具显式化(L2):每步开头标注工具清单,比如"本步工具: Glob, Read, Grep"。Agent 不用从段落里推测该用什么工具,天然形成最小权限约束。
  • 状态归一化(L4):多步骤 Skill 的中间产物(文件路径、用户选择、配置项)统一收敛到一份结构化的 Context JSON。所有步骤的产出写进去、入口条件从中读出。
  • Step 3 定义验证规则 ↔ L5 评估与观测层。 五步法已经强调"验证要具体、可机械化"。Harness 把它推到极致------验证必须是 Agent 实际执行的操作,不能是 Agent 读完就能说"我确认了"的文字。对比一下:

不产生工具调用痕迹的验证不算验证。 五步法给了方向,Harness 给了及格线。

  • 弱验证:"确保移动端适配"------Agent 读完点头说好的,过了
  • 强验证:"用 Playwright 分别在 375px 和 414px 视口截图,和设计稿叠图对比偏移量"------Agent 必须调工具、拿真实结果
  • Step 4 注入知识 / Step 5 迭代打磨,在六层里没有直接对应。这不是 Harness 漏掉了------六层管的是"结构质量"(骨架结不结实),Step 4-5 管的是"内容质量"(血肉鲜不鲜活)。两者关系是:先用六层搭好框架,再通过持续打磨往里填经验。

五步法漏掉的,六层补上:

五步法讲了怎么写正常流程、怎么验证、怎么打磨,但没有系统性回答一个问题:某一步出错了怎么办? 这正是 L6 约束校验与恢复层要解决的。

不需要额外的工作量------在 Step 2 拆解流程时,给每个关键步骤多加三行:

  • 可自动恢复:子 Skill 缺失 → 提示安装后重试
  • 可降级处理:某 MCP 不可用 → 从已有文件提取信息
  • 需人工介入:接口权限不足 → 暂停流程,提示用户处理后再继续

多问自己一句"这一步如果挂了 Agent 该做什么",Skill 就从'演示环境专用'变成了'生产环境可用'。

写在最后

六层架构的意义,不在于把 Skill 拆成六个独立模块,而在于提醒 Skill 作者三件事:

  1. 你的 Skill 是一张地图,不是一个仓库(L1-L3)
  2. 你的验证标准应该是一串操作,不是一段建议(L4-L5)
  3. 你的失败处理不是附录,是正文(L6)

做到这三条,Skill 就从"一个人写给自己看的操作手册",变成了"一份 Agent 能机械执行的可靠规格"。不需要平台支持,不需要新工具------只改变写作习惯。

从 Prompt 到 Skill,是第一次质变------能力从对话中"结晶"成了结构化模块。用 Harness 思想让 Skill 的可靠性持续进化,是第二次质变的前奏------当平台支撑就绪时,这份结构化规格可以直接被读成可编排的执行计划。

希望今天分享的这些东西,能帮大家在写 Skill 的时候少踩点坑、多积累点真正能用的资产。

相关推荐
大龄秃头程序员1 小时前
RN 0.86 新架构实战:TurboModule + Fabric 组件 + iOS 原生容器,完整代码开源
前端·react native
这是个栗子1 小时前
前端开发中的常用工具函数(八)
开发语言·前端·javascript
Hilaku1 小时前
Vue 和 React 真正的差距,不在语法,而在团队犯错成本
前端·javascript·程序员
研☆香2 小时前
为什么变量声明在赋值前也能使用?—— 深入理解 JavaScript 变量提升与作用域
开发语言·前端·javascript
Liora_Yvonne2 小时前
目录结构到底怎么设计?别再把 components 和 utils 当垃圾桶了
前端
柯克七七2 小时前
我把 bug 修得太干净了,测试组以为这个模块本来就没问题
前端·javascript·typescript
Null1552 小时前
浏览器唤起桌面端应用(终极篇)
前端·javascript
Quz2 小时前
QML Label 完整用法与 Text 组件选型
前端·qt
妙码生花2 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(三十四):后台初始化请求实现
前端·javascript·go