Agent技能这个词最近在我身边的工程圈子里出现频率明显变高了。从最早大家争论Agent该不该有长期记忆到后来讨论怎么规划任务、怎么调用工具再到现在所有人的关注点都落在了同一个问题上怎么让Agent在处理真实任务时稳定地拿出正确的处理能力。这个正确的处理能力落到工程上就是一套体系化的技能库skill library。你可以把它理解成Agent的手和脚模型负责想技能负责做。模型判断当下该解决什么问题技能就是它从工具包里抽出来的那张解题卡。没有技能的Agent模型再聪明也只能空转技能定义混乱、描述含糊模型再强也会被带偏。这篇文章我把Agent技能从定义、设计、实现到维护完整拆开讲一遍适合正在做Agent应用开发、或者打算把业务能力往Agent框架里接入的开发者参考。不需要你提前掌握什么复杂框架只需要写过一点脚本、看得懂配置文件就能跟着搭出一套可用的技能模块。1. 先对齐一个概念Agent技能到底是什么先说结论Agent技能是一个自洽的、可被Agent按语义触发并执行的功能单元。它由四部分组成描述信息、接口定义、实现代码、依赖声明。这句话看起来简单但很多人一开始都会把技能和工具插件混为一谈导致后面设计出来的东西既不像工具也不像技能Agent调用的时候全是问题。我见过不少团队把Agent技能简单理解成给模型加了一个函数。这个方向不能算错但漏了最关键的一层技能的核心是面向任务场景的匹配而不是面向功能的暴露。同一个功能如果用传统接口暴露调用方需要明确知道函数签名和参数格式如果用技能的方式暴露Agent只需要理解这个场景该用这个能力剩下的参数组装、调用、结果处理都由技能模块自己负责。这种差异直接影响了设计方式。技能里的描述、触发条件、参数默认值、错误处理策略权重都比普通函数要高得多。说得夸张一点技能设计得好不好有时候不取决于代码写得多漂亮而取决于描述文案写得是否让Agent看得懂、选得对。1.1 它和工具、插件的区别在哪很多框架里工具插件技能这三个词经常混用。但如果细看它们在工程上的设计焦点还是有明显差异维度工具 / 插件Agent技能调用的驱动方式偏功能入口模型按需调用偏任务场景模型按语义匹配后调用粒度通常较粗一个接口对应一组操作更细面向一个完整的小任务闭环描述的重要性中等有函数签名就能用极高描述决定召回率与触发准确度参数处理多为显式传参调用方负责组装内置参数校验、默认值与组装逻辑生命周期模块级维护技能库级维护可组合、可评测、可回滚我这么说不是要制造概念对立。实际框架中可以完全不做区分模块照样能跑。但设计思路不同后期维护成本会很不一样。如果你把技能当工具来做就会忽略触发条件、负面样例、失败回退这些关键设计点如果你把工具当技能来做又会陷入一个能力拆成几十个技能的过度设计搞得Agent每次决策都在一堆相似描述里纠结。1.2 技能在Agent执行循环里的位置一个典型的Agent工作循环分成四段感知、规划、执行、观察。感知负责理解用户输入和环境信息规划负责把任务拆解成步骤执行负责调用具体能力观察负责评估执行结果并决定下一步。技能在规划和执行之间扮演了关键桥梁。没有技能体系的时候Agent的执行就是固定写好的几个函数模型只能挑着用有了技能体系之后Agent可以先检索技能库选出最匹配的技能再传入上下文参数执行。而且技能的调用结果还会带着结构化的元信息返回给模型比如执行状态、耗时、错误码这些信息会直接影响模型下一步的决策。我在实际项目里的感受是技能模块做得扎实的Agent明显更听话。表现在两个地方一是模型能准确判断什么时候该用哪个能力而不是频繁误调用或者反复试探二是执行出问题的时候模型知道怎么上报、怎么回退而不至于硬着头皮往下走最后给用户一个莫名其妙的答案。2. 拆解Agent技能的核心构成与分类理解技能是什么之后接下来要搞清楚它的内部结构。很多开发者觉得写个函数加段描述就行了结果技能一多整个库马上就乱了。一个可维护的技能一定不是一份代码加一段话那么简单。2.1 一份技能的四层组成我一般建议把技能拆成四个层次来看每一层都有明确责任描述层技能叫什么、干什么用、什么场景触发、什么场景不要触发。这一层是给Agent的规划模块看的决定了它会不会被正确选中。接口层输入参数有哪些、必填还是选填、默认值是什么、输出结构是什么、失败时怎么返回。这一层是给调用链路的契约决定了技能能否被稳定执行。实现层实际的逻辑代码、脚本、数据处理模板。这一层是执行体可以很简单也可以很复杂。依赖层运行环境依赖、外部服务连接、权限要求。这一层决定了技能在什么条件下能跑起来部署时最容易出问题。这四层缺一不可。只写描述不写接口模型选中了也不知道怎么传参只写接口不写依赖本地跑得好好的换台机器就崩。我在帮某个团队梳理历史技能时发现大量僵尸技能都是死在依赖层——描述和接口都还正常但底层依赖库的版本环境已经没人能还原了。2.2 从基础能力到元技能技能不是越细越好也不是越粗越好而是要有一个层次结构。按我常用的分类方法技能大概可以分成四类基础能力技能文件读写、网络请求、数据清洗、格式转换这类通用能力。它们不依赖具体业务可以被上层技能反复组合。领域业务技能电商订单处理、客服工单流转、财务报表生成这类特定业务场景的完整流程。它们往往由多个基础技能组合而来。复合编排技能把多个步骤编排成一个稳定流程。比如日常舆情分析就是抓取、清洗、关键词提取、生成报告四个动作的组合。元技能用来管理和优化Agent自身行为的技能比如任务拆解、自我反思、上下文压缩、技能调用失败后的补救策略。基础能力和领域业务之间的界限需要根据团队现状判断。技能粒度太粗模型调用不灵活粒度太细模型每次都要做大量组合决策既慢又容易出错。实用经验是先从一个完整的业务闭环切入跑通之后再往下拆不要一开始就建两三层的技能树。2.3 技能设计的三条红线第一单一职责。一个技能只负责一件完整的事。有些开发者为图省事把搜索、抓取、存档写成一个大技能结果模型只是想做一次普通搜索也被迫走了全套流程又慢又占资源。正确做法是拆成三个技能再写一个复合技能把它们串起来。第二最小依赖。技能能少依赖外部服务就少依赖尽量把基础设施型操作封装在内部。依赖越少部署越容易故障面也越小。一个技能如果对数据库、文件系统和外部API都有硬依赖那它就是个随时可能爆炸的雷。第三边界明确。技能必须清楚定义自己管什么、不管什么尤其要写清不该触发的情况。比如一个订单查询技能描述里需要明确只处理订单状态查询不处理退款申请不然用户说帮我退掉这笔订单时模型可能误选这个技能然后一脸茫然地返回一个功能不支持的错误。3. 从零手写一个Agent技能理论讲多了容易飘下面直接落地上手。我带大家写一个常见的技能网页正文提取。它接收一个URL抓取网页并清理噪声返回干净的标题和正文。这是一个典型的基础能力技能足够说明全流程。3.1 先用一页纸把接口定清楚写代码之前先别急着敲键盘。拿一张纸把这些问题列清楚这个技能解决什么问题给定网页地址提取正文内容。输入是什么URL字符串。输出是什么网页标题、清理后的正文、抓取时间。可选参数是什么最大返回长度、是否保留图片链接。哪些情况不该触发用户只是闲聊提到一个链接不要求提取内容用户想要的是截图而不是正文。依赖条件需要网络请求库和HTML解析库。这个过程看起来简单但我见过太多人跳过了。直接写代码会导致参数设计得很随意后面模型调用时各种传参错误。接口定清楚后面每一步都有据可依。举个例子我常看到有人把最大长度参数设计成必填项用户每次请求都得多带一个数字一旦不带模型就会自己随机编一个。正确做法是给默认值而且默认值要贴合大多数使用场景。3.2 描述文案写得好不好直接决定召回率技能描述是给模型读的不是给人读的。写描述的核心目标是让模型在正确的场景选中它在错误的场景忽略它。我见过最典型的错误写法是此技能用于网页内容页面内容的信息获取和内容抽取。这句描述表面上没毛病但信息量太低。模型只能判断出这技能和网页有关至于什么情况下用、参数带什么、输出长什么样全都得靠猜。我用的是场景加边界的写法一份可用的描述长这样name: web_content_extractor version: 1.2.0 description: | 从指定URL抓取网页正文自动去除导航、广告、页脚等噪声 返回结构化标题和正文文本。 适用于用户要求抓一下这个页面总结这篇文章把这个链接的内容存档等场景。 注意本技能只负责正文提取不处理正文摘要不做内容翻译 也不适用于截图、登录后才能访问的页面。 tags: [web, crawl, extraction]注意看最后一句注意本技能只负责……不处理……这行不是废话而是给模型划定的决策边界。实测下来带有明确负例的描述触发误判率明显低于只有正面描述的版本。我甚至见过只靠一句负例描述就解决了一整周误召回的案例。3.3 目录结构、注册加载与初次验证在技能库中一个技能推荐使用独立目录存放。目录结构可以这么组织skills/ └── web_content_extractor/ ├── config.yaml # 技能元数据、参数定义、依赖声明 ├── skill_main.py # 核心实现代码 ├── test_cases.json # 回归测试用例 └── README.md # 面向维护者的说明config.yaml 里的内容除了描述之外还要把接口层和依赖层写清楚inputs: url: type: string required: true description: 目标网页的完整URL包含协议头 max_length: type: int default: 10000 description: 返回正文的最大字符数超出部分截断 outputs: title: type: string content: type: string fetched_at: type: string depends_on: - requests2.31 - beautifulsoup44.12 permissions: network: true filesystem: false注册加载时我用两种方式验证一是直接让Agent处理一段自然语言指令观察是否会选中这个技能二是构造几个负例指令比如帮我截个图翻译这篇文章确认模型不会误选。这里有个实操技巧验证时不要每次都重新编场景而是把触发测试的指令沉淀成测试集。第一次可能就五六条跑通后慢慢补充到几十条。这些指令以后就是技能回归测试的基础资产。4. 调用执行链路实战拆解当技能设计和注册完成后真正决定体验的是Agent执行技能的全过程。这块最容易出问题也最值得提前想清楚。4.1 触发与匹配Agent是怎么选中它的模型选中一个技能的过程整体上分为四步意图解析、候选检索、打分排序、选定执行。意图解析是把用户的自然语言转换成结构化意图比如帮我把这个网页存下来会被解析成网页内容存档意图。候选检索是在技能库中找出可能与这个意图相关的技能这一步通常依赖语义匹配或关键词匹配。打分排序是根据技能描述与当前意图的匹配度进行排序选出最优项。最后模型执行选定技能的调用。了解了这条链路你就会明白描述文案为什么那么重要。如果描述里关键词覆盖不到存档这类表达候选检索阶段就把它筛掉了如果描述里没有边界说明打分排序阶段可能把不相关技能排到前面。我曾经遇到过一个文本摘要技能被模型用来处理图片描述生成就是因为描述里写了文本内容信息处理这种过于宽泛的表述导致边界完全失效。4.2 参数组装、执行与结果标准化选定技能后模型需要把用户请求中提取的信息填入技能参数。这一步的最大坑是模型填入的参数经常不够完整或者格式不符合预期。解决思路是接口层给足兜底。我习惯把所有参数都设计成要么有默认值要么有校验规则。比如URL参数如果模型没传或传了非URL格式技能内部要能直接报错并返回友好提示而不是等到发起网络请求时才暴露问题。参数校验放到最前端可以省掉大量难排查的运行时错误。执行成功后输出要做标准化。拿我们的网页提取技能来说输出统一成包含title、content、fetched_at的结构。这样做的好处是上层复合技能和推理模型都能按固定格式读取结果不用每次去猜返回字段。我在一个项目里踩过坑两个技能的返回结构不一致导致上层Agent在组合使用时不得不做大量二次解析还经常解析失败最后统一结构后才彻底稳定。4.3 失败链路重试、回退与如实上报技能不是每次都能执行成功。网络超时、页面结构异常、编码识别错误各种意外都可能发生。失败链路设计得好不好对用户体验影响非常大。我的策略是快速失败加一级回退。快速失败是指技能在执行初期就要做好参数校验和环境检查哪怕多花几十毫秒也远好过执行过半了才发现问题。一级回退是指当技能内部处理失败后不直接把原始报错抛给用户而是先尝试一次简化处理比如网页正文提取时CSS选择器主规则失败了就尝试按段落标准标签提取。回退也失败时一定要如实上报。我见过某些项目为了让Agent看起来更智能失败了还硬编一个结果回来这比直接报错更要命因为用户会拿着错误结果往下走后面的决策全被带偏。失败上报至少应该包含哪个技能失败了、错误类型是什么、重试了几次、最后采用了什么回退方案。5. 技能库的长期维护与安全边界单个技能能用起来之后真正考验工程能力的是技能库的整体治理。技能一多命名、管理、版本、安全这些问题会接踵而至。5.1 从单技能到技能库的组织策略技能库不是把一堆技能目录堆在一起就完事了它需要层级和命名规范。我常用的组织方式是分三层目录skills/ ├── core/ # 基础能力技能跨业务复用 │ ├── file_ops/ │ ├── http_ops/ │ └── data_process/ ├── biz/ # 领域业务技能按业务域隔离 │ ├── order_svc/ │ ├── content_svc/ │ └── report_svc/ └── meta/ # 元技能管理Agent自身行为 ├── task_decompose/ └── context_compress/命名方面我倾向用业务域_动作的格式比如order_query、content_summarize。这样命名空间清晰也方便写检索阶段的关键词匹配规则。版本控制上每个技能目录用独立版本号推荐语义化版本主版本号在接口不兼容时递增、次版本号在增加可选参数时递增、补丁号在修复内部逻辑时递增。技能库整体用代码仓库管理配置文件和实现代码一起入库。这个习惯能在技能行为变化时快速回溯是谁、在什么时候、动了什么一目了然。5.2 用测试集守住技能召回率技能和普通代码一样会随迭代而腐化。描述被改了一次之后触发准确率可能悄悄下降也许测试时没发现上了生产才暴露。所以回归测试不是可选项而是必备项。我建议每个技能都维护一个测试集包含三类用例正向触发用例用户意图匹配时技能应该被选中。负向触发用例意图不匹配时技能不应该被选中。参数执行用例给定输入技能执行结果与预期是否一致。评估指标可以用这张表来盯指标含义建议值召回率应该触发时正确触发的比例越高越好至少大于0.9精确率触发结果中正确触发的比例越高越好至少大于0.85任务完成率执行成功返回有效结果的比例依据技能场景而定平均耗时从调用到返回的耗时视场景越短越好不用追求每次都是100%因为用户语言表达本身有模糊性但至少要在可控范围内。每次技能描述或实现变更后跑一遍测试集指标掉得厉害的一律打回重改。这个过程很枯燥但它是技能库长期稳定性的基石。5.3 权限、沙箱与输入校验技能一旦被Agent自主调用就涉及权限边界问题。我的原则是最小权限技能默认没有文件系统写入权限、不读取环境变量中的敏感信息、非必要不访问内网服务。每次申请权限时都要问一句这个权限真的需要吗执行环境尽量隔离。哪怕本地开发机也建议把技能执行放在临时目录或容器化的沙箱里。这样做的好处是即使技能代码有缺陷也不会把宿主环境搞乱。输入校验这块核心是防注入。技能参数里如果有URL、文件路径、查询语句这类内容先做格式校验和偏差处理再交给底层执行模块。举个小例子文件读取技能拿到路径参数时不能直接拼接给文件系统要先检查路径是否在允许访问的根目录内。这些细节看着琐碎但安全风险往往就藏在这种地方。6. 常见问题与排查速查表技能开发过程中踩过的坑我整理成一份速查表方便大家遇到问题快速定位。6.1 常见的四类故障与修复方向现象可能原因排查方向技能偶尔不触发但有时又正常描述文案覆盖不全缺少某些表达方式的触发词分析用户实际表达补充同义表达和示例模型总在无关场景选错技能描述缺乏负例边界或两个技能描述过于相似给技能增加不适用说明拉开技能描述差异度参数经常传递不完整接口定义不清晰缺少默认值和校验规则用默认值兜底增加参数格式校验技能运行一段时间后准确率下降上游页面结构或依赖服务发生变化检查外部依赖、更新测试集、对比历史版本输出本地执行正常部署后异常依赖层不一致缺系统级库或权限不足核对依赖清单、检查沙箱权限、复现运行日志排查时我通常按先描述层、后接口层、再环境层的顺序进行。因为触发类问题九成出在描述和接口只有最后的运行异常才需要往环境和依赖里钻。6.2 我踩过的三个实操坑第一个坑是技能误触发的惨痛教训。曾经给一个销售数据查询技能写了很常规的描述结果用户问帮我查下明天天气怎么样时模型居然调用了这个技能因为描述里有一句可以查询各种实时数据。后来我花了一天时间把所有技能的描述都重新梳理了一遍统一加上了不适用场景这一节误触发才彻底消失。自那以后我再也不写各种所有这类无边界词。第二个坑是参数默认值设计不合理。有个技能把返回条数默认值设成了20用户每次让模型查询时结果都只有20条而业务上需要最多200条。后来模型为了要到200条只能在请求里额外强调要全部数据但有时候强调不到位又回落到20条。改成默认值200并按需传参后问题立刻解决。设计默认值一定要按大多数场景下最合理的值来定而不是按技术实现方便来定。第三个坑是版本迭代导致历史请求异常。有一次改了技能内部的数据解析逻辑没升主版本号结果线上Agent还在按旧规则组装参数新老版本混跑了好几天部分请求返回了完全错误的结构。那次之后我强制要求所有技能变更必须走同一套代码评审和版本发布流程这个问题就再没出现过。最后分享一点个人习惯做了这么多技能设计的工作我个人最深的一点体会是技能的本质不是代码封装而是把模型能理解的任务意图和系统能执行的具体能力之间那条桥修得尽量宽、尽量稳。描述写得精模型才不会选错接口定得好执行才不会卡壳边界划得清用户才不会被误导。我还有一个小的习惯就是在每个技能里加一个变更备忘录文件记录这个技能从诞生到每次迭代的关键决策。这个备忘录看起来不起眼但在几个月后回头排查各种奇怪问题时它往往是还原现场和判断得失的最有力依据。如果你正准备做或者已经在做Agent技能相关的开发建议先别急着堆技能数量。从一两个真正高频的场景起步把描述、接口、回退、评测这四件事做扎实然后慢慢扩展。技能库的魅力不在于大而全而在于每个技能都可靠、可预期、可维护。