1. 当 Agent 开始“内卷”,Skill 就是它的核心武器
最近两三年,大语言模型的热度从“会聊天”急速转向“能干活”。大家都在做 Agent(智能体),但做着做着就会发现一个尴尬的问题:同一个模型,有的人能让它稳定地完成多步任务,有的人却总觉得它“中看不中用”。差在哪?很大程度就差在agent-skills这个看似不起眼、实际上决定上限的概念上。我自己从最早用纯 Prompt 硬怼,到后来折腾 Function Calling,再到现在系统性地设计 Skill 库,踩过的坑太多了。今天就围绕“agent-skills”这个话题,把我对它的理解、设计思路和落地实操经验一次讲透。
先说清楚它是什么。简单说,agent-skills(智能体技能)就是把 Agent 在某个特定任务上稳定的执行能力,打包成可复用、可组合、可观测的功能单元。它不是单纯的工具函数,也不是一段写在 System Prompt 里的指令,而是介于两者之间的一套“行为范式”。举个例子:让 Agent 查天气,你给它一个get_weather(city)函数,这是工具;但你让它“根据用户的城市和历史偏好,输出一份今天适不适合出门的穿衣建议”,这就不是简单调函数能搞定的,它需要决策链路、知识边界和输出格式的统一约定——这就是 Skill。
我见过很多人把 Agent 做成“大号玩具”,原因就是只堆工具不建技能。模型每次都在自由发挥,同一个问题换个说法结果就不一样,根本没法用在生产环境。而有了 Skills 体系之后,Agent 的行为会越来越像“一个靠谱的员工”,而不是“一个聪明的临时工”。这篇文章适合正在做 Agent 开发的工程师、想系统化设计智能体工作流的 PM,以及被“Agent 落地难”折磨的技术负责人。我会把原理、设计方法和能直接抄作业的代码示例都放出来。
2. Skill 为什么是 Agent 工程的“分层解药”
2.1 从“模型裸奔”到“技能封装”的思路转变
要理解 Skill 为什么管用,得先看一个核心矛盾:大模型的泛化能力和执行稳定性天然冲突。泛化意味着模型同一种能力在不同语境下会变着花样表现,这在开放闲聊里是优点,但在任务执行里就是灾难。比如你要 Agent 每周五下午自动整理项目周报,并且必须包含“上周完成”“本周计划”“风险预警”三个板块。如果全靠 Prompt 约束,模型可能这周记得加板块、下周就忽略了,甚至输出格式歪七扭八。你总不能每次都拿几十条 few-shot 示例去堵枪眼。
Skill 的思路就是把“执行稳定性”从模型身上剥离出来,交给我们自己能控制的代码和流程。一个 Skill 本质上是一层外壳:它内部规定了这个任务的输入输出接口、处理步骤、边界条件和异常兜底。模型在这个外壳的指引下工作,而不是天马行空地自己定规则。这个思路和软件工程里的“高内聚低耦合”高度一致——每个 Skill 只负责一块边界清晰的职责,Skill 之间通过标准接口协作,Agent 核心只做意图识别和路由调度。这样一旦某个技能出问题,你只需要修那一个模块,而不是跟一个“啥都会但啥都不精”的大模型搏斗。
这里我特别想强调:Skill 不是用来限制模型的,而是用来解放模型的。模型不需要再在每一次交互中“回忆”该怎么做某件事,它只需要识别出“这是哪个 Skill 的触发场景”,然后调用即可。就像人类员工,入职培训先建一套 SOP,后面干活按 SOP 走,遇到 SOP 没覆盖的再发挥主观能动性。
2.2 和 Plugin、Tool 的核心差异
很多人会把 Skill 和插件(Plugin)、工具(Tool)混为一谈。我最初也搞混过,直到被生产环境的教训教育了一顿才彻底分清楚。
Tool 是最小粒度的原子操作,通常对应一个函数或一个 API,例如“读取文件”“发送 HTTP 请求”“执行一段 Python 代码”。Tool 没有判断力,给它参数它就执行。
Plugin 是一种宿主级别的扩展机制,往往捆绑了 UI、权限、数据流等系统级能力,例如一个“GitHub 插件”会包含鉴权、事件触发、数据同步一整套。
Skill 则是在 Tool 之上的“编排层”。一个 Skill 可以串起多个 Tool,也可以包含中间决策逻辑、让模型在几个步骤里做选择。更关键的是,Skill 可以维护自己的状态、记忆和默认策略。
我用一个生活化类比说明:Tool 是工具箱里的扳手和螺丝刀,Plugin 是自带抽屉和照明灯的操作台,Skill 则是“修水龙头”这件事本身——它知道先关水阀、再拆旧件、检查密封圈、装新件、通水测试这一整套流程。修水龙头可能要同时用到扳手和螺丝刀,还可能临时决定要不要换个配件。这就是 Skill 的编排能力。
从实现角度来看,Tool 通常是name + description + parameters三段式声明,Skill 则还要多出workflow / policies / fallback这些内容。如果你用 LangChain 或类似框架,Tool 就是@tool装饰器下面的函数,Skill 往往是几个 Tool 加一个状态机、加一套 Prompt 模板,再加一个校验器的结合体。
2.3 Skill 体系设计的四个层次
我在多次重构之后,把 Skill 体系分成四个层次:
第一层是原子技能(Atomic Skill),不可再拆的最基础能力,比如“解析 PDF 文本”“调用搜索 API 获取结果页”。这一层接近 Tool,但多了一层更规范化的输入输出定义。
第二层是组合技能(Compound Skill),把多个原子技能串成一个完整流程,比如“从 PDF 中提取所有联系电话并去重”,内部实际是“解析 PDF -> 正则清洗 -> 去重 -> 格式化输出”四个步骤的组合。
第三层是领域技能(Domain Skill),面向某个垂直场景的整体能力,比如“电商客服工单处理”,它内部可能要调用商品查询、订单状态查询、退款政策判断、话术生成等组合技能。
第四层是元技能(Meta Skill),用来管理和优化其他技能,比如“技能自检”“技能切换条件判断”“学习新技能的流程”。
这个分层的好处是:你可以从下往上逐步积累,而不至于一上来就搞一个巨无霸 Skill 把脑子烧干。早期我自己犯过一个大错误,想用一个 Skill 搞定“市场调研报告生成”,结果内部塞了十几个步骤,参数几十个,最后既不灵活也难维护,模型总是走错路径。拆成四个层次后,每次做一个新品调研,组合一下现有技能就行。
3. 手把手设计一个 Skill:以“竞品分析简报生成器”为例
3.1 明确场景与边界条件
设计 Skill 绝对不要从“我想让 Agent 酷一点”开始,十有八九会翻车。要从具体场景和用户痛点倒推。我拿自己最近在生产环境里维护的一个 Skill 举例:竞品分析简报生成器。需求来自业务团队:每周都要盯几款竞品的动态,手动搜新闻、看更新日志、对比价格,非常浪费时间。
这个 Skill 的目标边界我一开始就写死了:
- 输入:一个竞品名称列表、分析时间范围(默认过去 7 天)、目标用户群描述(可选)。
- 输出:固定结构的 Markdown 报告,包含“本周值得关注的变化”“对目标用户的影响”“我方应对建议”三个板块。
- 不做的事:不给竞品做深度的财务分析,不做大规模用户舆情建模,不做竞品 App 的功能级逆向拆解。这些是其他 Skill 的领域。
界定了边界,后面所有设计就顺了。没边界就会出现模型自由发挥,跑到最后输出一堆让业务方一头雾水的“深度洞察”。
3.2 定义 Skill 的数据模型与运行流程
一个 Skill 的落点最好有一份清晰的运行流程说明,我通常用伪代码、状态流转表和 Prompt 模板三件套来定义它。伪代码用来给开发同事看逻辑,状态流转表用来给调试工具看,Prompt 模板则实际告诉模型在每个环节应该怎么做。
这个竞品分析 Skill 的运行流程如下:
1. 解析输入,明确目标竞品列表和时间范围 2. 并行检索每个竞品的公开信息源(新闻、更新日志、社交媒体官号) 3. 对检索结果做相关性过滤和去重 4. 按“市场动作、产品更新、定价策略、用户反馈”四个维度归类 5. 针对每个维度,结合我方产品定位,生成影响判断 6. 汇总为固定模板的 Markdown 报告 7. 自检:确认每个竞品都有覆盖,缺失的标注出来,不得编造状态流转表就比伪代码更严格。我把状态分成INIT -> RETRIEVING -> FILTERING -> ANALYZING -> GENERATING -> REVIEWING -> DONE。每个状态对应一组允许执行的动作,如果模型想在RETRIEVING阶段直接开始写报告,系统就拦截下来。这一步其实是把 Agent 的“自由意志”关在笼子里,当然实际执行中不是完全机械的,我会在ANALYZING阶段留一个给模型发挥判断力的空间。
Prompt 模板里最重要的一段是:
你是竞品分析报告生成助手。你已经获得以下经过筛选的信息:{context}。请严格按照三个板块输出:本周值得关注的变化、对目标用户的影响、我方应对建议。每个板块不超过 5 条要点。如果某类信息缺失,明确说“暂未获取到可靠信息”,绝对不允许自行编造。输出前请在草稿区进行一致性自检。3.3 绑定工具调用与模型判断力
单个 Skill 要想真正干成事,光靠 Prompt 远远不够,得绑上实际工具。在这个竞品分析 Skill 里,我至少用到以下原子能力:
- 网页搜索(通过搜索 API)
- 目标站点抓取与正文提取
- 文本相似度去重
- 关键词分类匹配
这里有个很关键的工程点:哪些步骤用确定性的代码处理,哪些步骤让大模型判断?我的经验是,重复性高、判断标准明确的用规则,开放性、语义性强的用模型。比如“搜索哪些关键词”这件事,看起来很智能,但固化下来效果反而不错。我给这个 Skill 预设搜索关键词模板:
[{竞品名}] 更新 OR 发布 OR 推出 OR 涨价 OR 降价 OR 收购 OR 合作但“这些搜索结果能不能算作一次值得关注的变动”,就需要模型结合业务语境判断——某个竞品版本号从 2.3 升到 2.4,不一定值得写进周报,但如果这个版本新增了对付费墙的调整,对业务就有影响。这种判断交给模型做,同时我要求它给出判断的一句理由。
从 API 实现角度看,我把这个 Skill 封装成一个异步函数:
async def run_competitive_analysis(competitors: list[str], days: int = 7): state = SkillState.INIT report_context = {} try: state = SkillState.RETRIEVING raw_docs = await retrieve_intelligence(competitors, days) state = SkillState.FILTERING deduped_docs = deduplicate_and_rank(raw_docs) state = SkillState.ANALYZING insights = llm_analyze(deduped_docs, competitors) state = SkillState.GENERATING report = llm_generate_report(insights) state = SkillState.REVIEWING report = validate_report(report) state = SkillState.DONE return report except Exception: log_state_transition(state) raise这段代码展示了 Skill 的标准外壳:有状态跟踪、有阶段划分、有异常日志。异常日志里记录状态非常重要,因为后续调试时你能清楚看到是“检索环节崩了”还是“解析环节崩了”,而不是懵在原地。
3.4 参数设计里最容易踩的三个坑
参数是 Skill 的输入口,设计得好不好直接影响整个链路的鲁棒性。下面三个坑我全部踩过,每一个都花了不止一个通宵来修。
第一坑是“参数过少但描述太抽象”。例如给retrieve_intelligence只传一个query: str,模型可能生成“最近竞品动态有哪些”这种废话,检索出来一堆噪声。后来我加了competitor_name、time_range_days、event_types: list[str]三个字段,每个字段都有枚举约束,搜索质量直线上升。
第二坑是“参数校验形同虚设”。模型是字符串生成器,你定义了target_audience: str,它可能传"我们的目标用户有很多"。所以必须用代码做严格校验,我是用 Pydantic 做结构化校验的,发现非法参数直接触发一次“参数澄清追问”。宁可多问用户一句,也不能让脏参数流进后续链路。
第三坑是“日期参数乱用”。这个我印象极其深刻,模型把相对时间“过去三天”翻译成绝对日期时,经常基于当前系统时间理解,但不同模块的时区默认值不一致,导致检索窗口错位。现在我规范全部用 ISO 格式字符串,并强制指定时区。
4. 把 Skill 组合成 Agent Workflow:从单技能到多技能协作
4.1 用 Router 调度多个 Skill
当一个项目里有十几个 Skill 之后,Agent 核心不再直接执行各个功能,而是变成一个“路由器”。它的主要工作变成:理解用户意图,选择一个合适的 Skill(或多个 Skill 的组合),然后把控制权交出去。
Router 的实现有很多种,我试过用大模型做自由路由(效果不稳定),也试过纯规则匹配(过于僵硬),最终采用的是“规则优先 + 模型兜底”的方案:先通过关键词和正则匹配出候选技能,如果候选置信度低,再调用一次大模型做意图分类。比如用户问“帮我看看竞品本周有什么动作”,关键词匹配到competitive_analysis技能,置信度 0.92,直接走;用户问“分析一下我们和竞品之间的优劣势”,这个表达比较开放,关键词匹配到好几个技能,就交给大模型分类。
Router 设计里有个细节很重要:必须知道每个 Skill 的触发边界和禁入条件。我维护了一份 SVO(Skill-Value-Object)表格,记录每个技能适用的对象类型和场景。例如price_monitor技能只接受“有明确价格模型的 SaaS 或电商产品”,如果用户说“帮我监控房价”,这个技能就不该被触发,否则输出来的价格逻辑完全不适用。
4.2 状态共享与上下文隔离的平衡
多个 Skill 协作,立刻就遇到状态管理问题。每个 Skill 执行时都可能产生中间结果,有时候后续 Skill 需要前置 Skill 的输出做输入。比如“竞品分析简报生成器”需要“信息检索器”的产出去做分析。状态共享是必要的,但共享太多,又会把 Skill 之间的边界模糊掉,导致耦合严重。
我的做法是“通过上下文协议共享,而不是直接塞全局变量”。定义一个TaskContext对象,它只保存指定键值,如retrieved_docs、filtered_insights、report_draft。Skill 通过声明自己“需要哪些键”和“产出哪些键”来读写上下文,框架层保证其他键不会意外暴露。这样既保证了数据传递,又保持了 Skill 内部细节的私密性。
上下文隔离同样重要。用户可能在一次会话里先问了竞品动态,又转头问“帮我写一封给客户的问候邮件”。如果两个任务的数据混在同一个上下文里,模型很可能把竞品内容写进问候邮件里,闹出大笑话。我设置会话级的 TaskContext 快照,每次切换 Skill 时隔离当前快照,让每个 Skill 只能看到它触发时的那份上下文。
4.3 一个真实的多技能协作场景
我给你描述一个我自己线上系统里的实际链路,读完你就知道组合技能威力有多大。用户在对话框里说:“最近竞品调价了,帮我和我们的销售周报合并生成一个简要汇报,重点提醒管理层可能抢走我们的客户。”
这个请求触发了一次 Router 决策,需要三个技能联动:
competitive_analysis:对竞品调价做溯源分析,产出事件摘要和影响面。sales_report_loader:读取销售周报数据,提取近期流失客户原因分布。executive_brief_generator:接收前两者输出,结合客户流失案例做合并,生成有故事线的管理层简报。
执行时,前两个技能并行执行,通过上下文协议把各自的结果注入同一个prepared_insights键里。第三个技能从这个键读取信息,再生成简报。
实现并行执行的时候,我在 Python 里用了asyncio.gather,同时设置超时控制。这里有个惨痛教训:某个搜索 API 偶尔会卡住 30 秒,导致整个请求超时,管理层看不到简报还以为是系统故障。后来我加了熔断逻辑:单个源抓取超过 5 秒就跳过,并标注“部分来源数据缺失”,总超时 20 秒内必须返回结果。真实业务里,部分失败是被允许的,但整条链路失败是不可接受的。
5. 调试 Skill:从效果玄学回归工程科学
5.1 建立可观测性与 Skill 运行日志
很多团队做 Agent 调试,还停留在“给模型换 Prompt,看输出对不对”的玄学阶段。Skill 体系成熟以后,必须把调试场景搬进工程管道。我给每个 Skill 都埋了结构化日志,记录关键节点:
{ "skill_name": "competitive_analysis", "state_transitions": [ {"state": "INIT", "timestamp": "2025-01-20T10:00:01Z"}, {"state": "RETRIEVING", "timestamp": "2025-01-20T10:00:02Z"}, {"state": "FILTERING", "timestamp": "2025-01-20T10:00:07Z"} ], "inputs": { "competitors": ["Acme", "Globex"], "days": 7 }, "output_summary": { "report_length": 823, "sections": ["market_moves", "user_impact", "recommendations"] }, "errors": [ {"stage": "RETRIEVING", "source": "news_api", "message": "timeout_at_5s"} ] }日志能帮你回答几个最关键的问题:模型到底走到哪一步停的?是数据缺失导致输出质量差,还是模型本身理解偏了?是检索阶段就跑了岔路,还是生成阶段没遵循模板?
这里我要强调一个常见误区:很多人看到模型输出不对,第一反应是“改 Prompt”。但如果你有日志,你会发现至少有三分之一的问题是出在检索阶段——根本没拿回有效信息,模型巧妇难为无米之炊。这时候改 Prompt 完全没用,得修搜索词、修排序逻辑、修过滤阈值。可观测性是让你停止“玄学调参”的唯一路径。
5.2 通用排查清单
我整理了一份 Skill 调试时的通用排查清单,每个问题都会在 5 分钟之内定位到大致环节:
- 模型输出格式完全不符合要求:检查生成阶段的 Prompt 模板,确认是否给出足够明确的格式示例;检查后处理解析器是否前后端字段名不一致。
- 输出内容看起来“泛泛而谈”,不像执行了分析:大概率是进入 ANALYZING 阶段的上下文为空,或过滤阶段把有效信息全部剔除了,检查过滤阈值和去重逻辑。
- Skill 永远不触发,Router 总选错技能:检查意图分类规则的打分权重,以及技能触发边界的描述是否足够具体;把用户原话记录下来做回放,看匹配时的关键词命中情况。
- 并发执行时上下文被互相污染:检查 TaskContext 快照逻辑,尽可能使用不可变对象存储上下文,防止多个协程同时修改键值。
- 同一个输入,输出有时稳定有时拉胯:先看是否有 API 超时抖动导致部分数据缺失;再看是否模型采样参数,温度设太高会让生成结果漂移。生产环境建议把温度稳定设在 0.2-0.5 区间,并用 seed 固定随机性(如果你的模型服务商支持的话)。
5.3 两条亲测有效的调优技巧
我分享两个自己亲测效果立竿见影的小技巧。
第一个技巧,给每个 Skill 配上“预期质量自评模块”。在输出最终报告之前,让模型先对自己产出的内容做一次自评,评分标准包括“是否每个输入实体都有对应输出”“是否有未经验证的数据”“输出结构是否与模板一致”。这个自评模块不需要单独训练一个模型,只需要在 Prompt 里多一段引导指令。实测下来,加上自评后,输出漏项和编造数据的概率明显下降,代价是每次多花费几百 token,相比收益完全可以接受。
第二个技巧,做“回归测试集”。我给高频 Skill 维护了一个 50 条输入的离线测试集,包含正常输入、边界输入(比如空列表、极短时间范围)、异常输入(忽然传进来一个不存在的竞品名)。每次调整完某个 Skill 的内部逻辑或 Prompt,我先跑一遍回归集,用脚本把输出和预期做比对,差得太多就直接拦住,不允许上线。这就像软件工程里的单测,虽然不能保证所有线上问题都被拦住,但至少能把“改坏旧功能”的概率降低一个量级。
6. 实战讨论:Skill 的复用、维护与边界思考
6.1 从单个 Skill 到企业级 Skill 库
当你的 Agent 项目从 Demo 走向生产,Skill 的数量会迅速从几个涨到几十上百个,这时代码组织和治理就变成重点课题。我目前采用“Skill 即代码、代码即文档”的策略:每个 Skill 独立目录,包含__init__.py、skill.py、prompt_template.txt、schema.json、tests/五个部分。
skill.py:核心实现,继承同一个基类BaseSkill。prompt_template.txt:模板单独放文件,不硬编码在 Python 字符串里,方便非工程师同事调试。schema.json:输入输出的 JSON Schema,版本化维护。tests/:针对这个技能的回归测试。
目录结构大约这样:
skills/ ├── base.py ├── competitive_analysis/ │ ├── __init__.py │ ├── skill.py │ ├── prompt_template.txt │ ├── schema.json │ └── tests/ │ ├── normal_case.json │ ├── empty_input.json │ └── noisy_source.json ├── sales_report_loader/ └── executive_brief_generator/版本化是我特别想强调的。Skill 的 Prompt 模板或流程逻辑哪怕只改动一句话,都可能对输出产生重大影响。所以每次改动我都打一个 commit,并且在schema.json里保留一个changelog字段。上线前,CI 流水线自动执行回归测试,通过后才能合并。
从组织协作的角度看,让产品经理或运营人员直接编辑prompt_template.txt,你会发现迭代速度快很多。工程团队只需要保证流程、校验、数据管道稳定,具体的业务话术由业务人员掌控。这个“人机分工”的协作模式,是技能库能够持续生长的基础。
6.2 哪些场景不适合做 Skill
讲完该做什么,我也得泼一盆冷水:不是所有任务都适合封装成 Skill。如果场景是极其开放的、非结构化的、且答案天然多样化的任务,例如“陪我头脑风暴一个全新产品概念”,过早地封装 Skill 只会限制模型的发散性,让输出的路数越来越窄。这种场景让模型以自由对话模式工作效果反而更好。
另外,如果需要实时训练数据才能表现的场景(例如让 Agent 实时感知用户情绪并调整对话语气),Skill 体系并不擅长。因为它更依赖于固定的流程设计,而不是动态的情绪建模。此时你可能需要别的手段(比如更细粒度的强化反馈系统)来辅助。
最后,低频场景也不值得做 Skill。我见过团队花两周时间做一个“战略汇报 PPT 风格美化”技能,结果一个月只有几个人用。投入产出比太差,不如让用户直接用现成工具手动处理。
做技能选型时,我会问自己三个问题:这个任务有没有固定路径可循?出现频率够不够高?出问题后的代价是否可承受?三个答案都是“是”,才具备做 Skill 的条件。
6.3 Skill 与 Agent 自主性的长远思考
还有一点值得展开聊聊:Skill 体系会不会阻碍 Agent 的“自主进化”?我一度也担心,把什么路径都设计死了,模型不就成了一个开关按钮了吗?
后来在实践中,我发现正确的 Skill 体系并不会抹杀 Agent 的自主性,反而是在给真正有价值的自主性提供一个稳定地基。就像人类的知识体系一样,基础技能越扎实,创新空间越大。当 Agent 不需要为“怎么调用搜索”“怎么解析 PDF”“怎么保证输出格式符合模板”而烦恼时,它才有余力去做更复杂、更接近人类智能的事:合理怀疑某个信息源的可信度,主动向用户追问缺失信息,甚至跨场景联想“上次处理类似问题时踩过什么坑”。
我目前的架构里,Agent 的自主性集中在两个地方:一个是Router的决策自由度,它可以根据上下文选择一条用户没有预设好的技能组合路径;另一个是模型在技能内部的“边缘判断力”,例如检索结果相关性排序、内容争议识别、输出优先级调整。真正好的自主性,不是让模型随便发挥,而是让它在允许自由的维度上充分自由。
这也就解释了为什么最近整个行业都在强调“可组合、可观测、可治理”的 Agent 架构。Skill 本质上就是为这套架构服务的“乐高积木”,单块积木各有形状,但拼在一起能搭出千变万化的形状。
最后再分享一点我个人的工程习惯
我做了快两年的 Agent 项目,最深刻的体会是:不要迷信“大力出奇迹”的巨型 Prompt,也不要迷信“纯代码写死一切流程”的机械系统。Skill 的价值在于它找到了一个很好的中间点——让模型的聪明才智被用在刀刃上,让工程的确定性覆盖所有可以被工程覆盖的部分。它适合那些想长期迭代、想把 Agent 用在真实业务里而不是停留在 Demo 阶段的团队。
如果你正在从零搭建自己的 agent-skills 体系,我的建议是:不要一上来就模仿大厂那套完整平台,拿一个小任务,做三个 Skill,跑通整条链路,把日志、测试、版本管理这些基础打牢。等你想加第四个、第五个 Skill 时,会发现自己已经拥有了一个能自然生长的系统。这个从“单点技能”到“技能生态”的过程,我自己走得很曲折,希望这篇内容能帮你少踩几个坑。