1. Agent Skills把大模型从“聊天框”里解放出来的关键一环做 AI Agent 开发的人最近应该都绕不开一个词Agent Skills。不管是研究 OpenAI 的 AgentKit还是在 LangChain、CrewAI 这类框架里写工具函数本质上都在做同一件事——给大模型配上“手脚”让它能真正完成具体任务而不只是生成一段看着像样的文字。我最早接触这个概念时最大的困惑是大模型本身已经这么强了什么代码、文案、分析都能生成为什么还需要那么复杂的工具系统后来实际做项目踩了几次坑才明白模型能“说”不代表能“做”。它天然缺少三个东西对外部世界的感知能力、对实时数据的获取能力、以及对计算机资源的直接操纵能力。Agent Skills 就是解决这三件事的标准化方案。这套东西更适合哪些人学如果你是正在做 Agent 应用开发的工程师或者准备把大模型能力接入业务系统的架构师又或者只是对 AI 自动化感兴趣、想自己搞点项目练手的技术爱好者这篇文章都值得花几分钟看完。我会把 Agent Skills 背后的设计思路、核心实现、以及实际开发中那些文档里不会写清楚的坑全部摊开来聊一遍。2. 为什么要有 Agent Skills大模型的能力边界到底在哪2.1 大模型的“先天缺陷”它活在静态世界里聊 Skills 之前必须先把问题定义清楚大模型到底缺什么我用一个特别直观的类比来说明。你把 ChatGPT 当成一个知识量极其渊博、但被锁在图书馆里不许出去的学者。你问他量子力学、请他写诗、让他分析一篇文章的结构他都能侃侃而谈。但你让他查一下现在东京的天气他做不到因为他的知识只截止到训练数据的最后一天。你让他帮你把本地一个文件夹里的图片批量压缩他也做不到因为他连你电脑里有什么文件夹都不知道。这是大模型与生俱来的三堵墙知识墙所有信息都来自训练数据无法感知实时动态新闻、股价、天气、库存这类信息天然缺失。交互墙不能主动调用外部 API、不能读写本地文件、不能操作数据库、不能执行命令。行动墙模型不会主动“做事”它的输出永远只是 token 序列想要落地到真实世界必须有人或者工具把这些 token 翻译成可执行的指令。这三堵墙不拆掉大模型再聪明也只能是一个“高级版的文本生成器”永远无法成为真正意义上的智能体。Agent Skills 就是来拆墙的。2.2 从 Tools 到 Skills概念的演进与关系这里要先把一个容易混淆的概念区分清楚。很多文章把 Agent Skills 简单等同于 function calling 或者 tools这其实不够准确。最原始的一代方案是 function calling。OpenAI 在 2023 年推出这个能力时核心逻辑是开发者写好一堆 JSON Schema 描述的函数注入到消息里模型根据用户意图挑一个合适的函数把参数填好返回一个结构化的调用请求然后你的代码拿到这个请求去真正执行函数再把结果喂回给模型。这就是最朴素的“工具调用”。到了 Agent Skills 这个概念时代事情变得更工程化了。Skills 不再只是单个函数而是围绕某个具体能力域组织起来的“技能包”。它通常包含一个或多个工具/函数的定义与实现必要的提示词或使用说明告诉模型在什么场景下该调用这个技能输入输出的规范化描述可能还包含技能依赖的配置、权限声明、错误处理逻辑打个比方function calling 是给工人递一把螺丝刀Agent Skills 是递给他一个装满工具、图纸、操作手册的“工具箱”并把“如何正确使用这套工具完成某个环节”的方法一并讲清楚。从工程架构上看Skills 也不是孤立的。它上面有一层 Agent 的“调度大脑”负责理解用户目标、拆解任务、规划执行序列下面有一层模型能力LLM和运行环境Runtime做支撑。Skills 处在中间偏下的位置是“大脑”和“世界”之间的传导层。2.3 为什么要“技能化”而不是“功能化”我在做项目时经常被问到一个问题我直接写一堆函数让模型调用不就行了干嘛非得搞什么 Skills答案是组织方式决定扩展效率。当你只有三五个函数时怎么写都行。但当你的系统里有几十个、上百个能力接口时如果没有标准化封装维护成本会指数级上升。Skills 把能力按领域切成一个个独立的模块每个模块内部自有逻辑对外只暴露清晰的调用接口。这样做的好处是可复用性高。一个写好并验证过的 Skill换个项目也能直接引用不用重写逻辑。可维护性强。某个技能出问题了只动那个模块不会炸掉整个 Agent。可扩展性好。要加新能力只需新增一个 Skill不需要动 Agent 的核心逻辑。可测试性好。每个 Skill 是独立实体可以单独写测试用例验证正确性而不是等整个 Agent 跑起来才发现问题。这些优势在真实项目中体现得很明显。我后来维持的一个项目里有约四十个技能如果没有这套标准化封装靠堆函数的方式迟早把自己堆死。3. Skill 的不同类型与典型应用场景3.1 按交互对象分类从实际开发角度我习惯把 Agent Skills 分成四类。这种分类方式不是纯理论而是影响你怎么设计技能的内部结构。第一类是信息获取类。这类技能负责向外部的数据源取数据比如调用天气 API、查询数据库、抓取网页、搜索文档。核心特点是“只进不出”技能执行后只是把数据带回来不改变外部世界状态。这类技能是最好写的因为边界很清楚风险也最低。但要特别注意数据的有效性和安全问题模型拿回的数据是否可信、是否过期都需要后置校验。第二类是系统操作类。负责影响或改变系统状态比如写文件、发邮件、调用支付接口、修改数据库记录。这类技能必须做权限控制和操作确认因为一旦模型判断失误产生的影响是真实且可能不可逆的。第三类是决策分析类。这类技能不直接和外界交互而是辅助模型完成复杂的推理、计算、结构化输出。比如写代码脚本并执行、做数据统计、生成报表。它们更像是模型的“外挂大脑”把一部分计算能力从模型本身拆出来交给确定性系统完成避免模型自己算错。第四类是协作类。负责 Agent 与人的交互比如向用户发起澄清问题、请求提供缺失参数、展示进度状态。很多初学 Agent 开发的人会轻视这个类别但实际上人机协作是 Agent 能落地的关键。一个只知道闷头干活、做错了才来找人的 Agent商用价值是很低的。3.2 典型应用场景盘点结合这四个类型Agent Skills 在真实场景里能做的事非常多。我这里列几个做项目时接触过的高频场景帮助理解它到底能产生什么实际价值。数据分析场景Agent 通过技能连接数据库接受自然语言提问转换成 SQL 查询语句执行再把结果用自然语言解释给用户。这是目前落地程度很高的场景之一。自动化办公场景技能封装了读写表格、生成文档、发送纪要邮件、汇总周报等能力。 Agent 可以定时或按指令执行一整套办公流程。运营监控场景Agent 定时调用监控接口拉取系统指标数据判断是否异常必要时通过 IM 工具通知值班人员。代码辅助场景Agent 不仅能聊代码还能通过技能在本地仓库中搜索代码、执行测试、查日志甚至提交 PR。个人助理场景管理日历、提醒事项、自动整理通讯录信息等都是典型的轻量级技能包。这些场景有个共同点都要求 Agent 有能力与某个外部系统或数据源发生真实交互。没有 Skills 的模型是做不到的。4. 手把手开发一个 Agent Skill从设计到落地4.1 Skill 的目录结构与基本组成理论说完了直接上手。我以写一个“查询订单状态”的 Skill 为例完整过一遍开发流程。在常见的 Agent 开发框架中一个 Skill 通常就是一个目录。我用过比较顺手的组织方式是order_query_skill/ ├── SKILL.md # 技能说明文档给模型看的 ├── functions/ │ ├── query_order.py # 技能实现主逻辑 │ └── format_order.py # 辅助格式化 ├── schemas/ │ └── query_order.schema.json # 函数入参schema └── tests/ ├── test_query_order.py └── fixtures/ └── sample_orders.jsonSKILL.md 是这个技能包的“说明书”也是系统里最重要的文件之一。它的读者不是人而是大模型。你在这个文件里写清楚这个技能是干什么的、什么时候用、怎么用、有哪些注意事项模型在运行时会参考它来决定要不要调用技能、怎么正确调用。SKILL.md 的内容不能写得天马行空。理想情况下应该包含技能名称与一句话职责描述适用的场景和不适用的场景这部分尤其重要能有效防止模型错误调用使用前置条件比如需要在什么文档中找到 order ID调用步骤的提示关键边界与失败后的处理建议写这个文档的核心原则是把它当成给一个聪明但没有常识的新同事写的操作手册事无巨细都要交代清楚。如果你的文档写得含糊模型大概率会在实际调用时给你整出各种花活。4.2 核心函数与参数 design 的实操细节函数定义是 Skill 的心脏也是和模型交互的协议层。不管你底层用什么语言最终对外呈现的都是 JSON Schema。先看一个实际例子{ name: query_order, description: 根据订单编号查询订单的实时状态、物流信息和金额明细, parameters: { type: object, properties: { order_id: { type: string, description: 用户的订单编号格式形如 ORD20250114001, minLength: 10, maxLength: 20 }, include_logistics: { type: boolean, description: 是否返回物流轨迹默认 false。只有用户明确询问物流信息时才传 true, default: false } }, required: [order_id] } }写 schema 有幾個特别容易踩的坑。description 一定要写清楚触发场景和默认行为。许多初学的人喜欢在 description 里直接写“查询订单”结果模型根本不知道什么时候该用这个函数用户一提“我的快递到哪了”它也可能不会联想到。正确写法应该把用户在自然语言中的常见表述也包含进去比如“查询订单/查快递/跟踪物流/看看我买的xx发货没”。参数设计上要尽力减少模型自由发挥的空间。模型填参数时本质是在做一次概率预测你的 schema 约束得越具体它预测准确率就越高。那些 enum 枚举、pattern 正则、format 格式声明全都别偷懒。有些开发者在 schema 里写的东西极其宽泛比如不管什么参数都是 string也没约束格式最后模型乱传参拿到结果又是一堆解析失败问题根本不在模型身上是你把门开得太大了。4.3 一个完整实现示例函数内部的实现逻辑因业务而异但有一个通用建议函数要足够“原子”一次调用只做一件事别搞大而全的万能函数。我用一个简化版示例说明这里用 Python 写个查询函数示意风格实际项目会接入真实的订单服务 APIimport requests def query_order(order_id: str, include_logistics: bool False) - dict: # 1. 参数基础校验千万别省 if not order_id or len(order_id) 10: return { success: False, error: order_id 格式不正确请确认订单编号后重试 } # 2. 请求内部订单服务这里换成你的真实端点 try: resp requests.get( fhttps://api.internal.example.com/v1/orders/{order_id}, params{with_logistics: include_logistics}, timeout5 ) except requests.Timeout: return {success: False, error: 订单服务响应超时请稍后重试} # 3. HTTP 状态码处理必须有不然模型看到的全是干瘪的 500 if resp.status_code 404: return {success: False, error: 未找到该订单请检查订单编号是否输错} if resp.status_code 500: return {success: False, error: 订单服务暂时不可用请稍后再试} resp.raise_for_status() # 4. 返回结果做结构化打包附上业务错误码 data resp.json() return { success: True, result: { order_id: data[order_no], status: data[status_desc], amount: data[total_amount], created_at: data[created_at], logistics: data.get(logistics_traces, []) } }这段代码里有几个值得注意的设计点。第一个值得注意的地方是函数返回了精确的错误描述而不是只返回个 error code。原因很简单错误信息是给模型看的让模型能在下一轮对话中向用户解释发生了什么、给出合理建议。如果模型只拿到一串数字码它没法组织回复只能瞎编。第二个值得注意的地方是超时和 HTTP 状态码的处理既保证模型能拿到明确状态也避免它在某个不可用的服务上反复空转。接口的返回设计也有人有不同习惯。早期的做法是直接返回原始 JSON让模型去总结。更工程化的经验是过滤掉无用字段只返回模型需要的信息。模型上下文窗口是有限的里面塞满无关字段不仅浪费 token还会干扰判断。4.4 写清楚 SKILL.md 是决胜关键网上大部分 Agent Skill 教程把精力放在写代码上其实代码是最容易的部分难的是代码之外的那份文档。我写一份 SKILL.md 的浓缩版本# Skill: 订单查询 ## Description 根据用户提供的订单编号查询订单最新状态、金额与物流信息。 适用场景用户询问“订单什么时候发货”、“我的快递到哪里了”、“查一下订单状态” 不适用场景用户提出退货、退款、修改地址等变更类操作需转人工或跳转售后技能。 ## When to use 必须满足以下条件才可调用 1. 用户提供了有效的订单编号以 ORD 开头后接数字 2. 或用户直接表达的查询意图明确如“查一下我昨天买的手机发货没” 3. 用户在追问“为什么没发货”之类的原因分析时也需要先查一次最新状态再结合业务规则解释。 ## Workflow 1. 从用户消息中提取订单号若缺失主动向用户索要同时给出订单号的位置提示 2. 调用 query_order 函数 3. 根据返回结果组织自然语言回复要求包含订单当前状态、关键时间节点若有 ## Key Rules - 未拿到订单号前禁止调用函数 - 查询结果中不要虚构任何物流轨迹字段缺失就明确说缺失 - 对于已发货订单如果用户没问物流明细默认只回复状态和时间节点 - 当服务异常时回复要引导用户稍后再试不要反复调用超过 2 次这份文档直接决定了技能运行时的表现。好多时候 Agent 傻傻分不清调用时机或者参数填得千奇百怪追根溯源都是 SKILL.md 写得不到位。你写得越清晰模型表现越接近预期。你写得越含蓄模型表现你就只能“随缘”。5. Skill 开发避坑指南实测总结出来的血泪教训5.1 描述模糊是万恶之源我在多个项目里反复遇到同一个问题同一个函数描述的措辞不同模型调用的准确率能差出一大截。举个例子。你写“查询订单”作为 description和写“当用户询问订单的发货进度、物流轨迹、是否已签收、当前状态时调用此函数注意用户提供订单号是必要前提”两种写法在实测中触发准确率差距巨大。后者模型几乎每次都能选对前者却频繁出现该调不调、不该调乱调的情况。原理不难理解。模型选择的依据就是你的描述和用户输入之间的语义相关性。描述越具体越贴近用户可能说的话匹配就越准。写 description 时要站在模型视角问自己如果用户用一百种方式表达同一个意图我这段描述能覆盖多少种我建议拿真实用户话术去测。哪怕收集不到真实用户的也要自己模拟几十条不同说法去调 description。这是一件很磨人但收益极高的事。5.2 参数校验比模型能力更值得信任很多人的代码拿到模型传进来的参数直接丢进后面的服务不做任何校验。这是很危险的习惯。模型补全参数时虽然整体表现不错但仍然会生成非法值。尤其是日期格式、数字范围、枚举值这些字段错得五花八门。你在函数入口做一层校验和标准化转换防的不是别人就是防这类偶发错误。这不是对模型的不信任而是系统设计的基本素养。所有外部输入都有可能不合法模型生成的参数也是“外部输入”。类别之一常见问题是模型把用户原话里的信息错误映射。比如用户说“帮我把周二那个订单取消掉”但系统里有多个周二订单模型大概率会随机挑一个订单号填进去。这种场景下你需要设计必要的确认机制让模型向用户澄清后再调用函数。5.3 上下文爆炸Skill 结果太大怎么办如果查询结果有几千条数据你全部塞进上下文让模型总结token 消耗会很惊人而且模型在“大海捞针”式的长文本里表现并不稳定。更合理的做法是先让函数做一层摘要。比如返回统计量而不是明细“共找到 128 条记录其中未发货 12 条已签收 95 条运输中 18 条问题订单 3 条”然后让模型基于摘要回复用户再按需拉明细。这类控制在业务代码里做比在模型层做要便宜且可控得多。还有一个相关点是重复调用的问题。有些场景模型会陷入循环调用同一个函数不做控制的话一次任务可能打几十次 API。我在函数层加了简单的调用频率保护同样一个参数盾牌连续出现三次就直接中止并提示模型换策略。5.4 权限与安全边界Skill 一旦能操作系统或数据安全问题就不是“以后再说”了而是上线前必须想清楚。我见过最吓人的一次事故是一个测试 Agent 有了“删除本地文件”的能力结果在某个测试环节中真的把一台开发机上的临时目录清空了。幸好是临时目录影响有限但这件事给我敲了警钟。自此之后我给所有具有危险操作的 Skill 设置了两道闸门写操作必须二次确认当模型判定需要执行删除、覆盖、发送等操作时必须先调用一个“请求用户确认”的技能拿到明确同意后再执行。运行环境沙箱化能力代码都跑在受限容器里文件系统、网络、系统权限均按最小化原则配置。这两道闸门在商用场景中是底线要求省了这一步等待你的可能就是一次事故通报。6. 常见问题与排查技巧从日志到行为的完整链路6.1 模型就是不调用 Skill怎么办这是初学 Agent Skills 时遇到频率最高的问题。模型面对用户指令毫无反应既不调用函数也不报错就硬生生回了一段话。排查路径是有套路的先确认用户意图是否真的在你的技能覆盖范围。如果不在模型不调用是正常且正确的行为别强求。检查描述中是否包含触发边界。很多时候不是模型不想调是它根本不知道该调。用更明确的“当用户说...时调用此函数”句式。调整模型参数。temperature 调低到 0.2 以下让模型行为更确定减少随机性。在 prompt 里显式列出可用技能列表。有些框架会做自动注入但如果你没启用模型可能根本不知道有哪些工具存在。查看底层日志看看模型到底收到了什么消息。很多时候是框架层过滤掉了你的 skill而不是模型的问题。排查这一类问题的核心思路是先确定问题出在“信息不足”还是“判断错误”再针对性解决。盲目调 prompt 是没有出路的。6.2 参数一遍遍传错怎么办如果模型明确调用了函数但参数经常不对基本是 schema 设计的问题。检查这几个地方参数 description 是否包含用户常见表达方式。用户口中的“单号”在你的 schema 里可能叫“tracking_no”请说明清楚映射关系。是否缺少 enum 或 format 约束。取值范围明确的参数果断用 enum日期格式加 format: date时间戳加 pattern。required 是否只包含真正必须的。多传比少传好因为模型会猜宁可要求得严格一些也别让它自由发挥。是否有相同语义的两个参数。如果 schema 里既有“order_id”又有“order_no”模型会彻底懵掉。同一个事物全库只能叫一个名字。这四项逐一排查下来参数准确率往往能提升不少。6.3 技能执行结果不稳定当同一个输入技能跑出来的结果有时好有时坏处理思路和症状是有关的。具体现象要具体看。如果表现差异巨大基本是模型输出随机性导致的降低 temperature 是有效手段。如果提示词太复杂模型在长上下文里容易“迷失重点”把重要指令往前放用更清晰的结构拆分步骤。如果一次调用要处理多个子任务模型会顾此失彼拆成多个技能串行调用更可靠。另外多轮对话中的歧义累积也是个隐患前期聊的内容会污染后续调用决策必要时在关键步骤做一次“重新聚焦”让 agent 忽略不相关的历史信息。6.4 日志与可观测性设计最后说一个容易被忽略但特别重要的点Skill 的可观测性。Agent 系统的调试难度远超传统应用因为它的行为是概率性的同一个输入不同轮次的输出可能不同。如果你没有一套完整的日志系统出了问题基本只能靠猜。我在每个关键节点都打了结构化日志记录模型收到的用户消息原文模型选择调用的技能名称与参数函数执行的结果摘要与耗时模型基于函数结果生成的最终回复是否有异常分支、重试、拒绝调用等情况这些日志不仅用于事后排查更重要的是用于训练前的数据复盘。我会定期翻日志找出那些模型判断错误的案例用来优化 SKILL.md 和 schema。日志就是技能优化的燃料没有数据的迭代都是盲目迭代。另外建议做一个简单的 dashboard展示各个技能的调用频率、失败率、平均耗时。这个数据的价值远超你的预期它会明明白白告诉你哪些技能是高频刚需、哪些技能已经形同虚设、哪些技能经常出错需要重构。7. 我的一点额外体会Agent Skills 这套模式发展得很快但核心逻辑一直没有变用结构化、模块化、可组合的方式给大模型赋予和真实世界协作的能力。许多人刚接触时追求花哨想办法让模型能调各种复杂的 API但我在实战中发现真正让 Agent 好用的往往是那些基础技能做得足够扎实描述准确、校验严谨、日志完整、边界清晰。一个技能包从能跑到跑得稳中间差的是几十次真实用户的反馈是反复打磨的 description 文案是一层又一层防御性的参数检查。这些工作不性感甚至有些枯燥但它们是 Agent 系统稳定的根基。就好比盖楼幕墙再好看地基不牢一阵风就能让你回到原点。如果你正准备开始写自己的第一个 Skill我的建议是别贪多从一个真实高频的需求出发做一个、测透一个、用日志和用户反馈打磨一个再扩展下一个。把基础的模式和套路吃透等到组件多了、场景复杂了你再回头看就会发现那些当初觉得很玄妙的 Agent 能力原来都是这些细碎而扎实的技能模块拼出来的。
