
开头先聊个现象最近 Agent 类项目满天飞但绝大多数团队卡在同一个坎上——模型能力明明够任务就是交付不了。你问它能不能做它什么都能做你真让它做它要么中途跑偏要么把简单事搞复杂。问题的根源往往不在模型而在技能体系。agent-skills这个话题说白了就是研究怎么把模型会变成Agent 能交付今天这篇文章我把这套东西从设计思路到落地实现完整拆一遍分享给正在做 Agent 应用开发或者准备入场的团队做个参考。1. 为什么技能成了 Agent 项目的新瓶颈1.1 从模型能力到任务交付中间缺的正是技能层先理清一个概念大模型本身具备的是知识和推理能力但它不具备稳定完成某类任务的能力。你可以让模型写一段 Python 代码但你不能指望它稳定地爬取某网站并结构化存储数据——后者涉及目标解析、反爬策略、数据清洗、异常恢复、结果校验这一整条链路由一个稳定流程串起来才是技能。传统做法是把这套流程写死在代码里或者塞进一段超长 System Prompt。前者的问题是每换一个场景就要改代码Agent 的灵活性全没了。后者的问题是Prompt 一长模型注意力被稀释表现反而变差。技能体系要解决的就是在这两者之间找一个中间态——把完成某类任务的方法论沉淀成可复用、可组合的模块Agent 运行时动态调用既保留灵活性又保证稳定性。1.2 agent-skills 与普通提示词的本质区别很多人误以为技能就是写得好一点的提示词这个理解差了十万八千里。提示词是静态的它描述的是你应该怎么做但不管执行结果。技能是动态的它包含触发条件、执行步骤、验收标准、异常处理策略甚至会根据执行反馈自动调整。举个例子一个网页信息抽取技能提示词里你只能说提取页面的主要内容但当页面结构异常、需要调整选择器、需要处理动态加载内容时提示词就无能为力了而技能会内置多级降级策略页面渲染不出来就换无头浏览器动态内容加载慢就调大等待阈值每一步都有对应的条件和动作。再打个更生活化的比方提示词是菜谱技能是厨师。菜谱告诉你红烧肉怎么做但火候大了该关小火、糖色不够该补老抽这些临场判断菜谱不会写厨师会。2. 技能体系的核心设计思路2.1 技能的粒度划分不是越细越好设计技能体系第一步是决定技能的颗粒度。颗粒度太粗技能变成一个什么都能干但什么都干不精的万金油复用性差颗粒度太细技能库膨胀到几千个Agent 光选技能就选半天上下文消耗也受不了。我自己的实践体会是按任务类型划分是比较好用的标准而不是按业务场景。比如网页内容提取是一个技能而不是提取新闻标题提取商品价格提取招聘信息各建一个技能。业务差异通过参数传入技能本身保持通用。这样做的原因是底层方法高度一致差异只在选择器和解析规则上完全可以参数化。一个技能内部的步骤数我建议控制在 3 到 8 步。少于 3 步说明这个技能拆得太碎多于 8 步说明它应该继续往下拆否则可维护性会急剧下降。2.2 技能描述规范让 Agent 一眼看懂技能除了要被人类维护更重要的是被 Agent 理解和使用。Agent 靠什么理解技能靠技能的元数据描述。这部分写得不好技能写得再漂亮也是废的。我总结了一套比较成熟的技能描述模板分为四块第一块是触发条件明确什么情况下 Agent 应该调用这个技能。第二块是输入输出契约定义入参的格式、类型、必填项以及输出数据的结构。第三块是执行流程用简短的自然语言描述核心步骤。第四块是限制条件说明哪些情况技能不适用、哪些场景需要降级。这里有个细节描述一定要用Agent 视角而不是人类视角。人类看爬取页面并提取正文就理解了Agent 需要的是当用户请求包含 URL 且意图为获取页面内容时使用本技能。入参 url 为完整的 HTTP/HTTPS 地址输出 markdown 格式正文。页面加载失败时等待 5 秒后重试一次。——这种精确到触发条件和异常处理路径的描述Agent 才能真正用起来。2.3 技能与工具调用的边界划分技能和工具Tool/Function是 Agent 体系里最容易混淆的两个概念。我的理解是工具是原子操作技能是对原子操作的编排。读文件、发请求、调 API、执行 SQL这些是工具。而分析某份数据报告并生成摘要是技能它内部会依次调用文件读取工具、数据解析工具、文本生成能力并且每一步之间有逻辑依赖和容错处理。这个区分决定了你的代码结构工具层保持薄和通用技能层才是业务逻辑的主要载体。实际开发中我看到不少团队把工具写得特别厚一个工具函数里塞了十几步业务逻辑Agent 调一次就好几分钟调试时根本分不清是哪一步出了问题。正确的做法是工具只做一件事且做到极致——判断复杂度的时候你可以试一下用一个反问来检验这个工具输出能不能被另一个技能直接复用如果不能它可能就太厚了。3. 从零搭建 Agent 技能库的完整步骤3.1 第一步定义技能描述文件的结构在实践里我推荐用 JSON 或 YAML 格式给技能定义一个集中的描述文件这样人类可读Agent 解析起来也方便。下面给出一个实际在用的 schema 做参考name: web_content_extractor version: 1.2.0 description: 当用户提供 URL 并要求获取页面内容或提炼信息时使用此技能。 支持静态页面和动态渲染页面输出为结构化 Markdown。 triggers: - 用户请求中包含 URL且意图为获取页面内容 - 用户说帮我看看这个链接里有什么/‘总结一下这个页面’ - 批量任务中需要对一组 URL 做内容提取 input: url: type: string required: true description: 完整的 HTTP/HTTPS 链接地址 output_format: type: string required: false default: markdown enum: [markdown, json] output: content: type: string description: 提取到的页面正文内容 title: type: string description: 页面标题 meta: type: object description: 页面元信息包括作者、发布时间等 steps: - 用 requests 库请求目标 URL设置 10 秒超时 - 若返回状态码非 200等待 5 秒后重试最多重试 2 次 - 用 BeautifulSoup 解析 HTML定位 article 或 main 标签 - 若目标标签不存在降级为提取 body 内最大文本块 - 将提取结果转换为指定输出格式 error_handling: - 请求超时: 返回错误信息页面加载超时请确认 URL 可达 - 内容为空: 提示页面可能为动态渲染尝试使用 js 渲染模式 - URL 无效: 返回提示链接格式有误请检查地址别嫌写描述文件麻烦这恰恰是整个技能体系里投入产出比最高的一步。描述文件写清楚了Agent 才能正确决策写不清楚技能库再大也是给 Agent 增加噪声。3.2 第二步搭建技能注册与检索机制技能描述写好之后需要一个注册中心来管理所有的技能。这个注册中心在 Agent 内部主要负责两件事一是技能索引让 Agent 能快速知道当前环境里有哪些技能可用二是输入校验Agent 调用技能之前在这里统一检查参数合法性。技能索引的做法有一个比较关键的细节把技能的核心功能和典型用法做成 embedding 向量存起来。Agent 拿到用户请求后先把请求做向量化。计算向量相似度选 top-k 个技能返回再结合描述文件做最终决策。这样做的好处是技能多了以后能保持检索效率。如果技能总数少于 20 个直接用关键词匹配或干脆全量丢给模型选都可以但超过 50 个就必须上向量检索了——不用把它想得多复杂类似一个搜索的过程以用户请求为搜索词在索引里找最匹配的技能说明文本。我自己的经验是这个检索模块不要做得太重基于一个轻量的向量库服务就足够了。真正的重头戏是技能描述本身描述写得好简单检索就能命中描述写得烂再先进的检索算法也救不回来。3.3 第三步写好技能的状态机技能执行不能是一段从头到尾的线性代码。现实任务里随时可能出意外网页结构变了、第三方接口限流、数据处理到一半内存爆了。技能必须有状态管理。我习惯把技能执行拆成四个状态idle待命、running执行中、degraded降级中、completed/exception完成或异常。每个状态转移都要有明确的条件。比如从running转移到degraded条件是某一步执行失败且该步骤有降级方案转移到exception条件是降级方案也失败。这样做有两个实实在在的好处。第一可观测性大幅提升——每个技能的执行进度、当前处于哪个环节、有没有降级一目了然。第二异常恢复有迹可循——用户说刚才跑失败了你能明确知道是在哪一步失败的而不是看到一堆日志干瞪眼。4. 技能调优与真实场景避坑指南4.1 上下文消耗控制别让技能库吃掉你大半的上下文窗口所有做过 Agent 应用的人都会遇到同一个问题技能描述文件一多请求还没发出去上下文先被技能说明占满了。以gpt-4o这类模型的上下文窗口来算技能说明所占用的 token 很可能直接挤占掉真正对话内容的空间。解决方法有两个方向。一是激进裁剪只保留技能描述里 Agent 做决策必需的部分触发条件 输入输出契约把执行细节从描述文件里挪到技能内部代码里。二是在调用时按需拼接检索命中哪些技能就只把哪些技能的描述注入上下文而不是一股脑全塞进去。我们测过单技能描述文件控制在 500 token 以内比较合适。超出这个量Agent 对这个技能的理解准确率会有肉眼可见的下降——它开始频繁忽略描述里的限制条件凭自己的直觉乱来。这里补充一个实验心得我试过把描述压到 300 token 以内准确率最优。原因是描述越短模型越容易抓住核心描述一长它反而会从中抽取出自己觉得重要的那部分信息而你精心设计的条款它反而注意不到。你把这个想成给一个人快速介绍另一个人的性格——你不可能一句不落地说完对方的所有细节挑最关键的说反而更容易让对方在关键时刻做出符合预期的判断。4.2 技能冲突问题多个技能同时觉得自己该上场技能库上规模以后一定会遇到技能间的抢活问题。比如你既有网页信息抽取又有新闻聚合用户给了一个新闻网站的 URL这俩技能都触发了Agent 到底该用哪个这里需要给技能设计一个优先级概念并且要在描述文件里明确写出来。我一般会在技能描述里加一个priority字段数值范围 1 到 10。数值越高表示当多个技能同时匹配时Agent 越应该优先考虑它。同时在检索阶段做一个简单的冲突消解如果 top-k 里有两个技能的triggers高度重叠就只返回优先级高的那个。但靠优先级只是治标更根本的解法是在技能设计阶段就规避重叠。怎么规避简单让技能的职责边界尽可能清晰。IP 归属查询的技能就只管查归属地新闻聚合的技能就只管多源信息集合汇总。如果一个技能能拆成两个更聚焦的技能不要犹豫拆。4.3 可观测性技能执行过程要能被看见技能是一个黑盒还是白盒直接决定了上线后你的头发能保住多少。我强烈建议从第一天起就做好技能执行的日志埋点。每个技能执行的关键节点都要记录技能名称、版本号、入参、出参、执行耗时、每一步的中间结果、降级/异常路径。很多同学会问这有什么难的打印日志不就行了吗但实际做起来会发现Agent 的调用链路比传统程序复杂得多。一个任务可能先后调用了四五个技能每个技能内部又有多个工具调用日志分散在不同模块里。你需要的是一个集中的链路追踪视图把所有技能调用按时间线和调用关系串联起来。简单实现可以用一个trace_id贯穿整个任务生命周期每个技能执行时带上这个trace_id把日志打到统一收集服务里再按trace_id聚合展示。做完了这个你会发现调试 Agent 的效率翻倍不止。4.4 常见问题速查表最后把这些年见过的高频问题整理成一张速查表直接对照排查问题现象根因分析排查方案Agent 总是忽略某个技能描述不清晰或检索埋没检查触发条件描述看和用户请求的语义距离是否过远降低描述文件复杂度技能执行到一半就停了状态机缺失或异常处理不完整检查步骤间是否有状态转移为每步加 超时/失败 处理分支Agent 反复调用同一个技能没有做结果缓存引入结果缓存相同入参直接返回上次结果技能输出格式不稳定输出契约定义太粗在描述文件里给出输出示例要求 Agent 严格按示例结构输出技能越用越不准技能版本未管理引入版本号管理定期回归测试不同版本的技能表现留出降级路径5. 关于技能复用与长期积累的个人体会最后分享一点我的真实感受。技能体系的搭建本质上是一个资产积累的过程和写代码不太一样。写代码是一次性的投入产出技能是一次投入、长期收益——只要你的技能描述文件写得够好每一次 Agent 调用它都在以比较低边际成本复用它背后那套沉淀过的执行逻辑和踩坑经历。我的建议是如果团队还在 Agent 应用的早期完全可以先不追求技能体系的完备度用两三个核心技能快速跑通业务流程验证价值。等跑通了再逐步把业务和数据积累的流程沉淀成新的技能。关键是要形成每次踩坑都反哺技能的机制——线上出了问题不要只修代码把修复经验写回技能描述和异常处理分支里。这样技能会越用越聪明Agent 的表现也会越来越接近一个资深工程师的水平而不是永远停留在应届生层面。这个过程没法一蹴而就但只要走对方向后面的复利效应会很明显。