前阵子和几个做智能体Agent的朋友交流发现大家的困惑高度一致搭个能聊天的机器人早就不是难事了难的是让Agent真正会干活——查数据、写报告、操作表格、调接口每换一个任务场景就得重新调Prompt、写工具函数、补异常处理累得不行而且完全没有积累。我最近大半年一直在折腾Agent相关的东西从最早写死的一堆if-else工具函数到后来尝试把技能拆开做模块化再到现在把整套能力组织成专业技能树踩了不少坑也终于跑通了一条比较顺的路线。这篇是上篇先把最基础也是最重要的部分讲透Agent Skills智能体技能到底是个什么东西为什么值得把它组织成一棵技能树以及怎么搭出第一个能用的技能。这篇文章适合谁看如果你是刚开始做Agent、还处在每次重写工具函数阶段的开发者这篇文章能帮你换一套思路如果你已经在用技能机制但技能一多就乱成一团不知道什么该拆、什么该合那这篇里的分层设计和颗粒度经验应该能派上用场。1. 先搞清楚Agent Skills到底是什么1.1 从一次真实的翻车经历说起讲个我自己的真实经历。几个月前接了个内部数据汇总的需求听起来很简单每天凌晨把昨天各渠道的运营数据抓下来汇总成一张表再配一段文字总结发到工作群里。做起来才发现全是坑。数据源有六七个每个接口返回格式都不一样有的接口要登录token时不时过期汇总结论还要按固定的措辞模板写不能太口语也不能太书面。更麻烦的是需求方隔三差五就要加新指标每次加指标我都要动主逻辑改完还要担心老功能被改坏。现在回头看这个问题的根源不是我代码写得差而是我把整个流程当成了一次性任务来处理——抓数据、清洗、汇总、写总结全都耦合在一堆函数里任何一处变动都会波及全局。后来我读到业内关于Agent Skills的思路一下子想通了应该把这类能力拆成一个个独立的、可被模型自动发现和调用的技能一个技能只管一件事比如抓取某个数据源清洗并汇总表格按模板生成周报。Agent不需要知道你的代码怎么组织的它只需要看每个技能的使用说明就能在需要的时候自己选择合适的技能、填对参数、串联调用。这种模式跑通之后我再加新指标基本只改清洗汇总这一个技能其他技能碰都不用碰。这就是Skills带来的第一波红利职责边界清楚了改动才能落在局部。1.2 Skills不是Tools的换皮很多人第一次听说Agent Skills的反应跟我一样这不就是工具函数Tools吗不就是把函数注册给模型去调吗我刚接触时也这么想但真刀真枪用一阵子之后发现两者定位差别很大。我用一张表总结下自己理解的差异维度传统Tools工具函数Agent Skills技能载体形式通常是一个函数、一段可执行代码强调说明文档代码示例数据的整体通常是一个目录可复用性经常为某次任务现写跟业务逻辑强耦合独立打包跨任务、跨Agent复用发现机制需要在Prompt里预先声明有哪些函数可用通过技能清单和说明文档让模型自己判断何时调用组合能力弱工具之间很少互相配合强技能可以调用技能形成技能树上下文适配基本靠人把逻辑写死技能自带上下文和使用范例模型按需调整参数维护方式散落在各个项目里换个项目就失效集中管理天然适合版本化和共享沉淀我记得有个比喻特别贴切Tools就像工具箱里的单个扳手你知道它干什么用但得你自己去拿Skills则像一份岗位说明书配套工具不仅告诉模型有什么工具还告诉它什么时候该用、怎么用、用的时候注意什么。所以Skills和Tools不是替代关系而是后者是前者的组成部分。一个完整的技能至少包含能力描述、执行代码、使用示例这三块缺一块模型都很难可靠地调用它。1.3 行业风向和生态为什么说现在是入场的好时机2025年这股技能化的风向非常明显。多家头部AI实验室和开源社区不约而同地推了类似概念虽然命名不太一样——有的叫Skill有的叫技能包有的叫能力模块——但底层方向高度一致让Agent从只会对话进化到会干活而会干活的关键就是把干活能力标准化、模块化、可组合化。其中一个很关键的落地动作是行业内逐渐形成了一个技能一个目录目录里放一份SKILL.md说明文档的通用约定。你可以把它理解成技能的自述文件模型靠它来判断该不该调用这个技能、参数怎么填。这种事实标准一旦形成价值是网络效应式的技能库越大Agent能干的活就越多能干的领域越宽愿意往库里贡献技能的人就越多。所以现在去学Agent Skills的构建思路不是追热点而是在给智能体开发打地基。2. 为什么要把技能组织成树专业技能树的三大价值2.1 用游戏技能树理解智能体的能力分层玩过RPG游戏的人都熟悉技能树长什么样底层是一堆基础被动技能往上解锁主动技能再往上是职业大招每个高级技能通常依赖几个低级技能做前置。其实Agent的技能树就是这个逻辑只不过解锁不靠打怪攒经验靠我们手动设计和注册。最初我的技能都是平铺的写一个技能丢进目录一个写了几十个之后发现一个问题数量多起来以后模型根本不知道该优先看哪个我维护起来也头疼。后来我按照依赖关系重新整理了技能树把底层能力抽出来让上层技能显式声明我依赖哪个基础技能整个逻辑才变得清爽。做技能分层这件事最大的价值是降低认知负担。无论是人维护还是模型检索都不用在几十个技能里大海捞针。你只需要顺着树的层级逐层往下找就行。2.2 一次注册处处调用技能树带来的第二个价值非常直接复用。举个最简单的例子。把天气查询做成了一个稳定注册的技能之后任何一个Agent只要它的技能列表里有这一项就可以直接调用它。它不需要知道你底层是用requests还是httpx不需要关心你接的是哪个天气服务商、token怎么刷新只需要知道查天气入参城市名这一件事。这种复用的意义在团队协作里体现得更明显。我们团队里有个同学专门维护内部数据源访问技能其他人做Agent的时候根本不用关心那些接口的鉴权细节只要调这个技能就行。这相当于把之前散落在各处的基建代码收敛成了一个统一的技能入口改鉴权逻辑只改一处其他人无感知。2.3 组合基础技能拼出复杂能力技能树最妙的地方在于真正复杂的业务能力往往是几个基础技能排列组合出来的。举个例子每周产品动态报告这个技能看起来挺复杂但它本质上就是检索舆情信息 查询业务数据库 按模板生成周报 推送消息到群这四个基础技能的串联。你只需要写一个编排层把每个技能的输入输出串起来就行不需要把整个报告流程写成一个巨大的脚本。这样做还有个额外好处替换成本低。今天觉得某个舆情源不好用只需要替换检索舆情信息这一个技能的实现编排层和其余技能完全不用动。这种可替换性在业务需求频繁变动的场景里太重要了。3. 拆解技能树的关键设计从根节点到叶子节点3.1 一个技能长什么样SKILL.md基础结构前面提到业界逐渐形成了一种技能定义方式把每个技能做成一个独立目录目录的核心是一份SKILL.md文件里面写清楚这个技能是干什么的、怎么用、参数是什么、有什么注意事项。它决定了模型能不能正确理解和调用这个技能。一个标准的SKILL.md通常包含三块YAML元信息区技能名称、一句话描述、参数定义、版本号用法说明区触发条件、调用方式、输入输出示例注意事项区边界条件、错误处理、特殊约定我自己的技能目录结构一般长这样skills/ weather_query/ SKILL.md query.py requirements.txt examples/ example_input.json example_output.json对应的SKILL.md长这样--- name: weather_query description: 查询指定城市的实时天气。当用户提供城市名并询问天气情况时使用。 version: 1.0.0 parameters: city: type: string description: 城市中文名如杭州 date: type: string description: 日期格式YYYY-MM-DD默认当天 --- ## 用法说明 1. 将city和date作为参数传入query.py 2. 输出包含温度、湿度、风力、天气状况 ## 示例 python query.py --city 杭州 --date 2025-03-20 ## 注意事项 - 城市名必须是中文常见写法 - 历史天气仅支持近30天 - 接口限流每分钟10次超出时报错注意description最能影响模型是否调用这个技能。写查询天气和写当用户需要了解某地当天或未来几天的天气情况时使用支持温度、湿度、风力差异非常明显。前者模型经常犹豫后者模型基本能精准判断。3.2 技能粒度怎么定拆多细才算合适这是被问得最多的问题也是我前期踩坑最狠的地方。技能拆得太多太碎索引膨胀、检索变慢模型决策负担也大拆得太粗技能内部塞了一堆分支逻辑又退回了一锅烩的老路子。我自己沉淀下来三个判断原则第一一个技能要能用一句话说明白它解决了什么问题。说不明白要么太碎要么太杂。第二技能要做的是单职责但这个职责本身要具备被复用的可能。如果一个技能只能在一个场景里用到那它大概率不是技能而是那个场景的一次性代码。第三看调用门槛。如果一个技能你需要传十个参数才能跑通它很可能拆得不够细。我自己的经验是参数超过四五个模型出错率会明显上升这时候就该考虑拆分了。我举两个踩过的例子。最开始我把处理Excel和生成报表拆成了两个技能后来发现几乎所有报表场景都要先处理Excel这两个技能几乎总是被一起调用。于是我保留处理Excel为基础技能把生成报表改成一个组合型技能内部显式依赖前面的基础技能。这个调整让调用链清晰了很多。3.3 三层技能树结构基础层、领域层、编排层为了不让技能树管理变得混乱我建议按三层来做结构设计层级名称职责举例L1基础设施层跨领域通用能力文件读写、网络请求、数据清洗、消息推送L2领域能力层某个业务域的专用能力订单统计、舆情检索、库存查询、指标计算L3组合编排层跨多个领域技能的任务编排周报生成、竞品监控、经营分析L1层的技能是全公司所有Agent都能用的这部分最值得投入做标准化。L2层技能跟业务强相关通常由各业务团队自己维护但接口要遵循统一的约定。L3层技能其实是一个脚本它自己几乎不写具体实现而是把L1和L2的技能按一定顺序编排起来。设计这套结构的时候有个心得层与层之间不能反向依赖。L2的技能可以依赖L1的技能但L1的技能绝对不能去依赖L2的业务技能否则上层逻辑一变动底层就会跟着崩。3.4 命名和描述规范让模型真正读懂你的技能技能树搭得再好看如果命名和描述写得含糊模型调用的时候照样抓瞎。这块我吃过亏认真总结过几个改进点。命名要用动词开头让人和模型一看就大概知道这个技能干什么。比如query_weather就比weather好generate_weekly_report就比report好。不要用抽象名词堆砌数据处理这种谁都会迷惑。描述要给出触发条件而不是只给功能概括。看这两个描述的区别差的数据分析功能。好的当用户提供CSV或Excel格式的交易明细表且需要按渠道或按日期维度汇总数据、计算环比变化时使用。对JSON格式的支持正在开发中。后面这句话相当于告诉模型你该不该调我。模型每天要决策好多次描述里没有触发条件它就越倾向于不调或乱调。参数定义要覆盖类型、格式和边界。参数描述最少要包含类型string/number/array等、枚举值如果有、默认值、示例值。特别推荐给出一个入参和出参的示例模型对示例的模仿能力比对规则理解能力强得多。4. 从零实现第一个技能完整实操记录4.1 我选的场景月度汇总报告技能理论讲再多不如自己动手写一个。我选了一个很有代表性的场景月度销售数据汇总。为什么选它因为它足够简单又包含了一个技能最常见的基本要素读文件、做计算、格式化输出。需求是这样输入是一个销售记录的CSV文件每一行包括日期、渠道、销售额三列。技能要完成的事是按渠道汇总月度销售额计算环比变化并生成一段适合汇报的文字总结。我把技能命名为monthly_sales_summary放在skills/monthly_sales_summary目录下。4.2 编写SKILL.md和Python实现先写SKILL.md这份说明是整个技能的门面必须交代清楚入参和出参--- name: monthly_sales_summary description: 当用户提供销售明细CSV文件需要按渠道统计月度销售额、计算环比变化并生成汇总报告时使用。输入文件必须包含date、channel、amount三列。 version: 1.0.0 parameters: file_path: type: string description: 销售明细CSV文件的路径 month: type: string description: 要汇总的月份格式YYYY-MM --- ## 用法说明 基于输入的CSV和月份按channel聚合total_amount并计算较上月的环比变化率。 输出JSON包含每个渠道的汇总数据和一段总结文本。 ## 示例 python summarize.py --file_path ./sales.csv --month 2025-03 ## 注意事项 - 文件编码必须为UTF-8 - amount为空的行按0处理 - 要求至少有一种渠道有当月数据否则返回错误然后是核心的summarize.py我会保持它短小、直接不掺多余的功能#!/usr/bin/env python3 月度销售汇总技能实现。 import argparse import csv import json from collections import defaultdict from datetime import datetime def load_sales(file_path): 读取CSV按(月份, 渠道)聚合销售额。 monthly defaultdict(float) with open(file_path, encodingutf-8) as f: reader csv.DictReader(f) for row in reader: month row[date][:7] amount float(row[amount] or 0) monthly[(month, row[channel])] amount return monthly def summarize(file_path, target_month): 生成指定月份的渠道汇总和环比。 monthly load_sales(file_path) current {ch: v for (m, ch), v in monthly.items() if m target_month} if not current: raise ValueError(f{target_month} 没有数据) # 计算上一月环比 dt datetime.strptime(target_month, %Y-%m) if dt.month 1: prev_month f{dt.year - 1}-12 else: prev_month f{dt.year}-{dt.month - 1:02d} prev {ch: v for (m, ch), v in monthly.items() if m prev_month} totals sorted(current.items(), keylambda x: x[1], reverseTrue) total sum(v for _, v in totals) lines [f{target_month} 共销售 {total:.0f} 元总计 {len(totals)} 个渠道] for ch, amt in totals: ratio if ch in prev and prev[ch] 0: ratio f环比 {(amt - prev[ch]) / prev[ch] * 100:.1f}% lines.append(f- {ch}: {amt:.0f} 元{ratio}) return {summary: \n.join(lines), details: dict(totals)} if __name__ __main__: parser argparse.ArgumentParser() parser.add_argument(--file_path, requiredTrue) parser.add_argument(--month, requiredTrue) args parser.parse_args() print(json.dumps(summarize(args.file_path, args.month), ensure_asciiFalse))这个脚本没什么高深技巧但足够说明一个技能实现的基本要素明确的命令行入口、可读的返回结构、清晰的错误处理。模型调用技能时它看到的其实是这个脚本的说明书所以脚本本身的实现可控可测就够了。4.3 技能索引如何让Agent发现技能光有技能文件还不够Agent得知道去哪找这些技能。我的做法是在技能树根目录放一个技能索引文件集中登记所有技能的名称、位置、简介、依赖关系和版本号。{ skills: [ { name: monthly_sales_summary, description: 按渠道统计月度销售额并生成汇总报告, path: skills/monthly_sales_summary, version: 1.0.0 } ] }模型在实际决策时流程大致是先读技能索引的description列表判断当前任务可能用得到哪些技能再打开对应技能的SKILL.md细看用法最后决定调用。所以索引里的description是技能第一次被筛选时的关键。这里写不好技能做得再好也可能被埋没。这也是为什么我一直强调描述不要省字触发条件、适用对象、典型场景都是值得写进去的信息。4.4 端到端验证别在关键时刻掉链子技能写完必须验证而且不能只验证正常路径。我通常会跑三组用例正常输入、边界输入、非法输入。正常输入就是我给的第例子一条CSV带三个渠道跑出来结果要符合手工计算。边界输入就试只有一个渠道的数据、当月的所有amount都是0、跨年场景1月算上一年的12月环比。非法输入就试文件路径不存在、CSV列名拼错、amount列有非数字文字。每种情况都要明确行为——是报错还是给默认值这直接影响Agent能不能自己从错误中恢复。我的经验是一个小技巧是在SKILL.md的注意事项里写清楚常见的错误场景和对应处理方式。比如amount为空按0处理文件编码必须是UTF-8否则返回明确错误提示。因为模型虽然不能真的看Python报错信息但它能根据说明预判边界情况在调用前就把文件处理好。5. 技能树搭建中的常见坑与排查指南5.1 坑一描述写得太短模型根本不调用最常见的坑就是SKILL.md的description写得过于简略。早期我写过一堆只有一个句子的描述处理Excel表格发送邮件查询订单。结果就是模型面对一个发送对账单邮件的任务时完全没意识到发送邮件技能可以用反而自己去现场生成了一堆邮件发送代码。排查方法很简单看Agent的调用日志如果某个技能从来没被调用过先别怀疑功能看看描述里有没有说清楚触发条件。写描述有个技巧每次写之前问自己假如我是一个从来没看过这个技能的模型我看到这句话会不会想到要用它。如果答案是不会就得加触发场景。5.2 坑二技能职责重叠调用逻辑混乱技能数量超过二十个之后很容易出现两个技能描述相似的情况。比如query_sales和query_order都能查销售数据区别只是一个按渠道聚合、一个返回订单明细。模型经常选错。这个问题的根源是技能边界没有划清楚。我最终的解决办法是在两份SKILL.md里都交叉声明了异同本技能与query_order的区别在于本技能只返回聚合结果不返回明细如需明细请用query_order。这种交叉引用极大减少了误选。5.3 坑三过度拆分导致技能库膨胀我犯过一次很极端的错误为了追求纯技能化把一个小小的业务逻辑拆成了六七个超细技能。结果模型调一次任务要看六七份SKILL.md上下文长度捉襟见肘决策准确率反而下降。后来我给自己定了个上限一个技能的实现如果少于20行就先别拆放一起。技能粒度不是越细越好而是刚好能被复用最好。宁可先粗一点等某段逻辑确实出现第二个使用场景时再把它抽出来也不迟。5.4 坑四改了技能忘了管版本技能是有版本的生命体。我吃过一次大亏某天改了基础数据清洗技能的规则却没有更新SKILL.md里的version结果上层十几个Agent还在按旧规则调用统计口径全对不上排查了一个晚上。从那以后我强制把版本号管理写进流程凡是修改了技能的输入输出或行为逻辑版本必须升一位并且在SKILL.md的变更记录里写上改动说明。虽然多花一分钟但能让整个技能树的使用者都清楚这个技能变了什么。5.5 坑五忽略了多模态数据和外部依赖很多技能不是单纯的输入文本输出文本。有的技能要读用户上传的PDF或截图有的技能要调用外部API有的技能本身就依赖其他技能的输出。这些情况在设计技能目录时就要考虑到。比如我的技能目录凡是依赖外部API的都会在requirements.txt里声明好依赖库并且把服务不可用时的降级方案写进SKILL.md的注意事项里。凡是需要读文件的都会明确支持的文件格式和大小上限。不然模型在真实场景里遇到一个PDF输入完全不知道该让哪个技能处理。6. 一点个人体会以及下篇准备聊什么技能树的搭建不是一次性的工程更像是在跟项目需求一起生长的过程。我的建议是不要追求一步到位先挑两三个高频场景把技能做出来跑通再根据实际调用日志去调整粒度和边界。就我个人经验来说最值得花时间的永远是SKILL.md里的description和注意事项代码反而是最简单的那部分。模型是一个读说明书的执行者说明书写得不清楚实现再漂亮也白搭。这篇上篇聊了Agent Skills是什么、为什么值得做成技能树、以及怎么落地第一个技能。下一篇我计划讲讲更进阶的部分如何让Agent在某些场景下自己生成新技能、怎么评估一个技能被调用得够不够好、以及多Agent之间技能共享的模式选择。这些东西我也是在实际项目里一点点摸索出来的等写出来了再来和你一起拆解。