这几年做 AI Agent 相关的事,有个感受特别明显:真正拉开项目上限的,往往不是模型选得多强,而是你给 Agent 配了多少高质量的“skills”。前阵子我围绕agent-skills这个思路做了一轮系统梳理和实践,从结构设计、开发流程到安全边界都过了一遍。今天把这段时间折腾出来的经验,连同踩过的坑一起写出来。
这篇文章不聊脱离实际的概念,重点放在能直接落地的内容:技能到底是什么、和 tools/子 agent 怎么区分、一个技能的内部结构该怎么搭、从零开发一个技能要走完哪些步骤、以及部署和调试时最容易翻车的几个点。无论你是刚接触 agent 开发,还是已经在调多 agent 系统,这篇文章应该都能给你一些参考。
1. Skills 的本质:Agent 能力模块化的关键拆解
1.1 先搞清楚:Skills 到底解决什么问题
用大白话说,skills 就是给 Agent 装上的“可复用技能包”。你不需要每次对话都重复把一整套专业流程塞进提示词,而是把它封装成一个独立模块,Agent 在合适的场景下自动调用。
这件事为什么重要?因为大模型的上下文窗口再大,也是有限的。如果你把所有专业知识、操作步骤和约束条件都堆在 system prompt 里,有几个实际后果:
- 长上下文会让推理速度明显下降,token 成本也跟着涨。
- 提示词中的关键指令容易被大量无关信息稀释,Agent 在关键环节的表现反而变差。
- 更新一套业务规则,得去改全局提示词,改动风险非常大。
Skills 把这些内容挪到独立文件里,按需加载。Agent 判断当前任务需要某项能力时,再读取对应技能包。这个模式和人类的工作方式很像:你上手术台时会调用外科手术技能,写代码时会调用工程技能,日常聊天时那些技能都处于休眠状态,不会占用你的“思考带宽”。
1.2 Skills、Tools、子 Agent 的功能边界
很多人第一个问题是:skills 和 function calling(工具调用)有什么区别?我最初也把这两个概念混在一起,直到踩了坑才彻底想清楚。
Function calling 是模型与外部系统交互的通信协议,核心是让模型输出结构化的调用参数。它更接近“接口层”,解决的是“模型如何请求某个动作”的问题。
Skills 则是一个完整的“能力单元”,一个技能可以包含多个工具调用,也可以不调用任何工具而只做知识检索或文本处理。比如“代码审查技能”就可能调用静态分析工具、运行测试脚本、检索项目规范文档,最后把结果汇总。这是一整套流程,不是一个孤立的函数。
子 agent 则是由主 Agent 动态创建的运行实例,有自己的上下文和提示词。通常在任务需要独立的工作空间或上下文隔离时使用。
举一个直观的例子来说明三者的关系:你让 Agent 写一份市场分析报告。function calling 负责“查数据”“生成图表”这些单一动作;一个“市场分析”技能负责把这套流程串起来:数据搜集、指标计算、行业对比、生成结论;如果任务是“同时分析海外和国内两个独立市场”,那可能拆成两个子 agent 并行处理,各自维护独立的上下文。
1.3 为什么 Agent Skills 越来越受重视
从我的实践观察来看,这波趋势的驱动力很实际。
过去 Agent 项目做完一个 demo 容易,但生产环境落地很难。原因在于:Agent 的表现不稳定性、调试成本高、效果不可控。
Skills 的模块化特性,恰好从几个方向缓解了这些问题:
- 稳定性:把专业流程固化成可预期的步骤,而不是让模型临场发挥。
- 可维护性:技能独立更新,不影响系统其他部分。
- 可复用性:一次封装,多处使用,甚至能在不同 Agent 之间共享。
- 逐步构建生态:技能包可以跨项目、跨团队、甚至跨组织共享,类似 GitHub 上的开源代码库。
这也是为什么像 Claude 的 Agent Skills 规范、Codex 的技能体系、各种 skills 平台会接连出现,本质上大家都在推动 Agent 能力的“组件化”和“标准化”。
2. 一个优质 Skills 的结构拆解:核心细节与设计规范
2.1 技能文件的基本组成
一个规范的 Agent Skill 通常是一个独立目录,里面包含核心三件套:技能描述文档(SKILL.md)、脚本或代码文件、辅助资源。
我平时常用的目录结构是这样的:
skill-name/ ├── SKILL.md ├── main.py(或 main.js) ├── assets/ │ ├── templates/ │ └── reference/ ├── requirements.txt(或 package.json) └── config.json各文件的作用:
SKILL.md是整个技能的“说明书”,包含触发条件、使用场景、执行流程、注意事项。Agent 会根据这份文档判断何时调用该技能、怎么调用。- 脚本文件是具体逻辑的实现。
assets存放辅助材料,比如报告模板、参考数据、图片素材。- 依赖文件声明技能运行需要的第三方库。
2.2 SKILL.md 的写法:决定技能使用效果的关键
SKILL.md 写得好不好,直接影响 Agent 是否会在正确时机调用技能、能否正确执行完整流程。好的 SKILL.md 有四个层次的内容:
身份定义:技能叫什么名字、核心功能、适用场景。要写得具体,不要模糊。比如“本技能用于生成月度营销数据报告,包含渠道数据抓取、趋势分析、异常告警”,就比“本技能用于处理营销数据”好用得多。
触发条件:明确告诉 Agent 什么时候调用。这十分关键,因为模型需要判断当前用户请求是否落在技能能力范围内。我一般会写 both positive triggers(什么情况下应该用)和 negative triggers(什么情况下不要用)。比如一个“合同审查技能”,就要写明“当用户提供合租文本且要求审查风险时使用”,同时写明“当用户仅询问合同范本时请勿使用”。
执行流程:用清晰的步骤序列描述技能的完整工作流,要具体到每一步的输入输出、处理逻辑、判断标准。
注意事项:包括边界条件、已知限制、需要避免的误操作。
写 SKILL.md 时最容易犯的错误是写得过于简略。有些人觉得模型能“理解意图”,随便写两句话就行。但在实际测试中,含糊的描述会导致 Agent 在关键决策点上的表现随机,整个技能的效果大打折扣。
2.3 技能代码的设计原则:做小而专的执行器
技能的核心代码应当遵循一个原则:必要时才自己动手算,判断交给描述文档,执行交给脚本。
这意味着脚本文件要尽量做“确定性”的工作:数据读取、格式转换、API 调用、文件操作、计算汇总。复杂的决策逻辑尽量显式地写在代码分支里,降低模型的决策自由度。
举个例子,一个“日志分析技能”的脚本里,核心流程应该是:
- 接收日志文件路径或文本。
- 按预定义规则解析日志格式。
- 统计错误码频率、延迟分位数、异常模式。
- 生成结构化输出。
这些步骤一旦写成代码,行为就是确定的。这样做的好处是:Agent 的输出质量不再依赖模型临场发挥,而是依赖代码正确性。
2.4 依赖与资源配置:技能可移植的关键
为了让技能可以被共享和复用,依赖管理要重视。我的做法是:
- 用
requirements.txt或package.json明确声明依赖库及版本。 - 在 SKILL.md 中写明运行环境要求(Python 版本、Node 版本、系统依赖)。
- 将静态资源放在
assets目录中,避免硬编码外部路径。 - 所有外部服务的密钥从环境变量读取,不要写进代码或配置文件。
还有一个容易被忽略的点:技能要设计成“可重入的”。也就是说同一个技能在一次任务中可能被多次调用,脚本不能依赖全局状态,每次运行都应当是独立的。我在开发中遇到过一次事故:某个技能使用了一个全局配置文件来保存中间态,结果并发调用时数据互相污染,排查了很久才发现问题。
3. 实操全流程:从零开发一个可复用的 Agent Skill
3.1 阶段一:需求分析与边界划定
正式动手前先想清楚两个问题:这个技能解决什么问题、什么时候不需要用它。
以我最近做的一个“Redis 异常诊断技能”为例。项目背景是我们有一套自建的 Redis 集群,经常出现内存突刺和延迟抖动,每次排查都要人工看几十项指标。最开始想训练一个专门的模型来做诊断,后来想想太重了。最终方案是把经验固化成一个技能,配合监控数据,让 Agent 自动完成诊断流程。
需求分析阶段,我把“诊断流程”拆成了四个子模块:
- 基础健康检查:连接状态、CPU 使用率、内存占用。
- 慢查询分析:找出耗时最长的命令。
- 内存分析:查看 key 过期策略、大 key 分布、内存碎片率。
- 延迟归因:梳理可能导致延迟升高的因素。
同时也定好了边界条件:这个技能不做变更操作,只做诊断和建议。因为一旦放宽边界去执行写操作,风险就完全不同了。
3.2 阶段二:细拆执行步骤定义输入输出
需求明确后,接下来把整个执行流程落成文档。我在 SKILL.md 里把流程写成了这样:
- 接收目标 Redis 实例的连接参数(host、port、password)。
- 执行基础指标采集脚本。
- 针对发现的异常指标,执行对应的专项诊断。
- 汇总所有数据,按模板生成诊断报告。
- 输出报告,包含异常结论、详细证据、可行的修复建议。
每一步的输入输出都定义清楚。比如“基础指标采集”这一步,输出的是序列化的 JSON 数据,后续步骤从 JSON 里读取字段做判断,这样的数据契约让整体流程非常明确,Agent 也能把注意力放在步骤编排上,避免临场发挥。
3.3 阶段三:编码实现的关键细节
编码阶段最大的挑战不是写业务逻辑,而是设计可靠的执行环境。我遇到了一个实际问题:技能脚本运行在一个沙箱环境,很多系统命令不可用,直接运行 redis-cli 会失败。解决方案是用 Python 的redis库代替命令行工具来采集指标。
核心代码的逻辑直接又实用:
import redis import json def collect_base_metrics(conn_params): client = redis.Redis(**conn_params) info = client.info() metrics = { "connected_clients": info.get("connected_clients"), "used_memory_human": info.get("used_memory_human"), "used_memory_peak_human": info.get("used_memory_peak_human"), "mem_fragmentation_ratio": info.get("mem_fragmentation_ratio"), "total_commands_processed": info.get("total_commands_processed"), "instantaneous_ops_per_sec": info.get("instantaneous_ops_per_sec"), "rejected_connections": info.get("rejected_connections"), } return metrics这只是基础采集部分。完整的技能还包含慢日志读取、大 key 扫描、命令统计等模块。值得注意的是,大 key 扫描必须使用SCAN命令而非KEYS,否则在生产环境下会导致 Redis 阻塞,这个细节在开发时就得想清楚。
3.4 阶段四:测试与效果验证
技能开发完成后,测试是必做的一步。我的测试策略分三层:
单元测试:直接对脚本做单元测试,验证输入输出是否符合预期。
全流程模拟:编写模拟的“对话剧本”,按用户在不同场景下的提问方式,测试 Agent 能否正确调用技能并得到合理结果。这一步能发现 SKILL.md 中触发条件描述不清晰的问题。
生产灰度:经过前两步验证后,放在真实场景小范围试用。
测试中我发现一个很有意思的问题:SKILL.md 里写的“当用户提到内存突增或内存占用过高时使用本技能”,但用户实际描述问题的用词可能很随意。后来我把触发条件写得更宽泛,加入了“Redis 性能下降”“查慢的情况”“诊断 Redis”等多种描述,效果好了很多。
我把这样的前后版本测试对比记录在一个速查表中:
| 测试场景 | 原版触发词 | 优化后触发词 | 命中率变化 |
|---|---|---|---|
| 直接命令式 | “诊断 Redis” | “诊断 Redis” | 原始已命中 |
| 症状描述式 | “帮我看看 Redis 为什么慢” | 补充“Redis 变慢、延迟高” | 从 62% 升到 95% |
| 间接提问式 | “我们线上缓存频繁出问题” | 补充“缓存抖动、内存爆炸” | 明显改善 |
3.5 阶段五:发布与迭代
技能测试通过后,将它收进技能库,并在配置文件中注册。我的做法是在主 Agent 的配置里维护一份技能索引,标明该技能的关键触发描述和关联场景。这样主 Agent 在思考时能快速扫描候选技能集,不用加载所有技能。
发布之后仍要持续观察。技能上线后,我会定期从 Agent 日志中抽取出调用记录,分析哪些请求激活了技能、哪些请求本应触发但没有触发、有哪些误用。每个迭代周期都根据日志反馈调整 SKILL.md 的描述词语和触发条件。这个环节,说实话,花的时间比开发本身还要多,但效果提升是最显著的。
4. 从架构视角看 Skills:定位、协同与安全边界
4.1 Skills 在 Agent 系统中的定位
在我的架构视图里,一个完整的 Agent 系统可以抽象成几层:
- 记忆层:保存长期信息和状态。
- 工具层:封装原子操作,对外提供接口。
- 技能层:组合工具与知识,形成半自动化的任务执行能力。
- 编排层:思考、计划、决策,调度技能和工具。
Skills 正好处在工具之上、编排之下的位置。它既不像裸工具那样单一,也不像编排层那样需要完全动态的智能。它相当于是“半成品”的活动模块:执行路径大体固定,但在具体参数、分支选择、汇总方式上保留灵活性。
这个分层的价值在于:把稳定的工作流固化成技能,把不确定的创造性任务留给编排层。这样整个系统的行为是可预期的,同时又保留了智能调度的弹性。
4.2 多 Agent 协作与 Skills 的协同关系
在多 Agent 场景下,我对 skills 的使用有一个重要原则:每个 Agent 应当有自己擅长的技能集,同时可以按需共享通用技能。
比如我曾经构建过一个数据处理项目,包含三个 agent:
- 数据采集 Agent:负责从多个数据源获取数据,拥有各数据源的抓取技能。
- 数据分析 Agent:负责数据清洗和统计建模,拥有数据处理技能。
- 报告生成 Agent:负责生成业务报告,拥有报告撰写技能。
每个 Agent 的知识库中装配相应的技能,避免把所有技能都塞给一个 Agent。这样做的好处很实际:
- 各 Agent 的上下文更精简,决策质量提高。
- 技能的安全边界更清晰,不会出现“分析数据时误触发了写操作”的情况。
- 技能库可以独立演进,某类技能更新不会影响其他 Agent。
4.3 安全边界:Skills 可能带来哪些风险
这是必须认真讨论的问题。技能给了 Agent 更强的执行能力,但同时也放大了潜在危险。我在实战中总结了几个高风险点:
权限过度:技能以 Agent 的身份执行数据库操作、文件写入、外部 API 调用。如果技能代码里包含高危操作(删除表、批量写文件),而触发条件又过于宽泛,就有误操作的可能。务必要在技能代码里做二次确认。
上下文注入:技能处理外部输入的数据时,可能被恶意内容引导偏离预期行为。比如一个网页抓取技能,如果抓取到的页面内容里嵌入了“忽略之前的指令”这类引导文案,模型可能中招。解决方案是:尽量把数据处理逻辑代码化,减少模型直接接触原始文本的环节;严格隔离指令和数据。
资源滥用:过于复杂的技能可能消耗大量 token 或执行时间。一个常见的问题是 Agent 在没有必要的情况下反复调用技能。我在配置技能时通常会加入一层调用频率的约束,并在 SKILL.md 中提醒 Agent“只有在确实需要时才调用”。
信息泄露:技能可能接触到用户的敏感信息(API 密钥、业务数据)。在技能输出阶段要加入脱敏策略,日志系统里也不能打印完整的敏感字段。
总结一句我认为比较重要的话:技能给 Agent 的“手”,但“手”能做什么,取决于你在代码和描述文档里设定的边界。
5. 常见问题与排查技巧实录
5.1 触发时机不准
这是技能开发中最常见的问题。表现为:用户明明需要某种能力,Agent 却不调用对应的技能;或者不需要时频繁误调用。
排查思路也比较统一:
- 检查 SKILL.md 中的触发条件是否过于严苛或过于宽泛,尽量使用贴近真实用户语言的描述。
- 观察 Agent 的“思考过程”,看它当时是否已经识别到了技能但没有调用,可能是技能的描述不足以让它产生足够信心。
- 简化技能描述的开头部分,让模型快速理解核心功能。
5.2 技能内部报错
技能代码在沙箱环境中运行时,经常会遇到环境依赖不完整、权限不足、路径错误等问题。
经验是:在技能目录中放一个自检脚本,验证依赖是否完整、系统命令是否可用、关键路径是否存在。这样 Agent 在启用技能前可以先运行自检,把问题提前暴露出来,避免中途才报错。
5.3 输出格式不稳定
技能返回的结果有时候被模型重新加工,导致格式漂移。我用的方法是:让技能返回严格结构的 Markdown 或 JSON,并在 SKILL.md 里强调“直接使用技能输出结果作为最终回答的主体,不要随意删改”。同时在输出层做一个格式校验,如果不符合预期就重新调用技能。
5.4 上下文过长和 token 消耗偏高
如果你的技能在每次调用时都要塞入大量参考文档,token 消耗自然会上去。选用方案是:
- 通过索引只加载相关片段,而不是全量大文件。
- 设置缓存的机制,同一会话内多次调用同一技能时复用初次加载的中间结果。
- 把大段辅助资料放进 assets,按需引用,不要塞进主上下文。
5.5 常见问题排查速查表
| 症状 | 排查方向 | 处理建议 |
|---|---|---|
| 技能完全不触发 | SKILL.md 触发条件描述不匹配 | 用用户高频话术重写触发条件 |
| 技能触发后中途卡住 | 代码依赖缺失或运行环境受限 | 添加自检脚本预先检查依赖 |
| 技能结果被模型乱改 | 输出契约约束不够强 | 返回结构化数据并强约束使用方式 |
| 同一技能会话内重复执行 | 缺少调用状态记录 | 以会话 ID 维度维护调用的幂等标志 |
| 多个技能互相干扰 | 全局状态污染或描述混淆 | 提高技能隔离性,独立上下文和配置 |
写在最后的一点个人体会
这几轮 agent-skills 的实践做下来,我最大的体会是:技能开发与其说是写代码,不如说是在想办法把含糊的需求“讲清楚”。SKILL.md 写得越清楚,Agent 的行为就越稳定。任何一个模糊的词,都可能成为生产环境里的一个意外。
如果你准备在自己的项目里引入 skills,我的建议是起步时不要贪多。先把手头最频繁、最容易出错的三五个任务抽出来,做成专门的技能,运行一段时间,观察效果,再迭代扩展。技能不是越多越好,而是越准确越好。开始阶段慢一点,后面系统的运行会越来越顺畅。
后续如果有机会,我还会继续分享技能库的跨项目复用方案、更复杂的多技能协同编排,以及更细致的测试方法论。希望这波内容对正在做 Agent 开发的朋友有实际帮助。