
如果一位做Agent研发的朋友问我当前最值得投入的方向是什么我会说是“技能”。模型本身会越来越强但真正决定Agent能不能落地、能不能稳定完成任务的往往是底层有没有一套足够扎实的技能体系。这里的“agent-skills”指的就是把大模型从“会聊天”推向“会干活”的那一层工程化封装。我这两年带团队做智能体平台最大的体会是技能不是写几个函数那么简单它涉及到意图识别、参数抽取、工具编排、错误恢复、人机协同等一系列设计问题。这篇文章不聊泛泛的架构概念直接拆解我在“agent-skills”这个方向上的项目实践包括技能设计的核心思路、落地过程中的具体实现、以及那些文档里不会写但实战中一定会踩的坑。内容偏工程向适合已经在做Agent研发的工程师也适合正准备从原型走向产品的技术负责人。1. 整体设计与思路拆解把“技能”当作系统建设的核心单元1.1 为什么技能值得单独成为一套体系先明确一个前提我们说的“技能”不是模型能力也不是工具函数而是介于两者之间的一套可复用组件。模型负责理解语言、推理意图技能负责把推理结果变成可执行的动作再把动作的执行结果组织成模型可以理解的信息。没有这层封装Agent只能停留在“对话式问答”阶段有了它Agent才可能真正操作业务系统、处理复杂任务。我在项目里把技能定义为四元组意图描述什么时候该调用、输入Schema需要哪些参数、执行逻辑具体怎么跑、输出规范结果怎么返回给模型。这四部分缺一不可。只写执行逻辑不定义输入Schema模型就不知道该传什么参数只写意图描述没有输出规范模型拿到的结果就没法判断成功与否。早期我们团队做过一个实验同样的任务让Agent直接调用函数结果成功率只有六成左右很多失败都是因为参数传错、结果解析不出来。后来把同样的能力封装成规范化的技能组件成功率提升到九成上下。差异不在模型能力而在于信息结构的确定性。这个发现直接推动了整套技能的体系建设。1.2 技能的粒度应该如何划分技能粒度的选择是个反复权衡的过程。粒度太粗比如把“处理整个订单流程”作为一个技能可复用性极差换个业务场景就得重写粒度太细比如把“打开数据库连接”都做成技能会让模型面对成百上千个候选技能意图路由的精度必然下降。我的实践经验是技能粒度应当控制在“能够独立完成一个业务动作且具备明确判定标准”这个级别。拿电商订单场景来说“创建退货申请”是一个技能“更新库存数量”是另一个技能而“执行售后全流程”就不应该是一个技能它是由多个技能编排出来的工作流。判断标准很简单如果某个技能在其他场景中大概率用不到或者它内部实际上是多个可以独立调用的动作串起来的就要考虑拆细。还有一种情况需要留意就是技能之间存在依赖关系时的粒度划分。比如“确认库存”和“创建订单”有先后依赖但我们没有把它们合并成一个技能而是保留了两个独立技能通过编排层来控制执行顺序。原因是库存确认可能在售前场景独立使用合并起来反而降低了灵活性。实践中保持技能的单职责原则把组合语义交给上层编排体系解决系统会更干净。1.3 确定性逻辑与生成式逻辑的分工Agent系统里往往有两种执行路径一种是完全确定性的程序逻辑一种是依赖模型推理的生成式逻辑。设计技能体系时必须想清楚哪些环节用确定性逻辑哪些环节交给模型。我的原则是凡是可枚举、可规则化的逻辑一律走确定性实现凡是需要理解、判断、归纳的逻辑才交给模型。拿参数抽取举例当技能输入Schema定义得足够清晰时用模型做参数抽取是合理的因为自然语言到结构化参数的映射确实需要语义理解。但一旦参数值可以枚举比如订单状态就不要让模型自由发挥而是通过Schema里的枚举约束加上代码侧校验来确定。相比之下如果技能内部包含复杂的业务规则比如优惠计算、库存分配这些逻辑必须用代码写死绝不能期待模型自行推导。这个分工背后有一个很实际的原因可测试性。确定性逻辑可以写单元测试可以持续集成可以保证同一输入永远有同一输出。生成式逻辑做不到这一点。技能体系的核心目标就是把模型的不确定性关在笼子里让整个系统的大部分模块具备传统软件工程的可靠性只在必要的地方暴露模型能力。1.4 一个可参考的整体架构按照上面的思路我在项目中落地了一套分层架构。最底层是技术组件层包括各类API封装、数据库操作、消息服务等基础设施往上叠加一层是技能层每个技能组件完成一次可复用的业务动作并统一暴露为标准化接口再往上是编排与规划层负责将复杂任务分解成技能调用序列最上层是对话与交互层承接用户请求管理多轮上下文。这里要特别强调的是技能层与编排层一定要解耦技能组件不应该感知自己处在哪个工作流里这样才能让技能保持可复用性。关于技能与工具的区别也值得多说一句。工具是技术能力的最小暴露单位比如“调用订单接口”技能则带有业务语义比如“查询订单状态”。同一个工具可以被多个技能复用同一个技能也可以调用多个工具。我在设计阶段就严格区分了这两层避免直接用工具充当技能导致业务语义缺失。如果技能层只是工具的扁平映射Agent就理解不了“什么时候用、为什么用”意图路由的准确性会大打折扣。2. 核心细节解析与实操要点技能描述、参数Schema与最终执行逻辑2.1 技能描述怎么写才容易被模型理解技能描述本质上是写给模型看的使用说明。这部分的细节处理会直接影响模型能否在恰当的时机选中这个技能。我在实践中总结了一套可复用的描述规范。技能描述应该包含四个关键要素技能的整体用途一句话说清楚、典型触发场景列举用户可能怎么说、关键限制条件什么情况下禁止使用、与其他技能的边界什么时候不该选它。举一个具体例子一个“查询实时天气”的技能描述可以写成“根据用户提供的地理位置查询指定城市当前天气状况及未来预报。当用户询问天气、温度、降水概率、出行是否适宜时使用。仅支持国内主要城市查询若用户未提供城市名请先询问确认后再调用。与‘查询历史天气’的区别在于本技能只返回实时和短期预报数据。”这里有一个很关键的原则示例比抽象描述有效。同样描述一个筛选条件的边界写“当用户表示要查看交易记录、订单列表、消费明细时使用”比写“本技能用于查询用户相关的交易信息”更好。模型是概率推理具体示例能把匹配概率显著拉高。但这个做法也有代价描述太长会占用上下文窗口所以每个技能描述需要精炼建议整体控制在150个字符以内把信息密度拉满。另一个值得关注的细节是负面描述的重要性。大多数技能设计者只写了“什么时候该用”忽略了“什么时候不该用”。实际测试中我发现有时候模型会把两个语义相似但边界不同的技能搞混这时候负面描述反而比正面描述更有效。比如一个“取消订单”的技能明确加上“仅当订单状态为待发货或待付款时才允许取消已发货订单请引导用户走退货流程”模型的误调用率会下降一半以上。2.2 参数Schema设计中的边界情况参数Schema是技能组件的接口契约也是模型做参数抽取的依据。这部分设计的质量直接决定了技能能否被正确调用。我在实践中总结了一套参数Schema设计的检查清单。核心参数必须与非核心参数区分。核心参数缺失时技能应该直接终止执行并返回缺参提示非核心参数缺失时可以用默认值或置空技能照常执行。如果Schema里不区分这两类参数模型往往会因为纠结某个次要参数而影响整体抽取效率。一个经验是在参数定义里显式标注“required”和“optional”之外还要在参数语义描述里给出默认值逻辑比如“如果用户未指定排序方式默认按时间倒序”。参数之间的依赖关系也要在Schema里表达清楚。比如“城市”和“城市编码”两个参数前者是人可读的后者是下游接口需要的模型抽取出城市名之后通常无法直接转化为城市编码这时要在技能内部完成映射而不是期望模型一步到位。设计原则是凡是模型掺和进来会导致更多不确定性的环节就把它放在确定性逻辑里做掉Schema里的参数应该是模型能够可靠抽取的语义单元而不是下游系统的字段副本。更隐蔽的问题是参数冲突。用户说“给我看本月所有的消费记录金额大于500的就行”这里其实有两个约束时间段和金额阈值。如果Schema只有“时间范围”和“金额范围”两个参数模型抽取时可能把“500”放进时间范围里吗这种错误确实会在实践中出现。应对方式是给每个参数编写描述时附上典型值的格式说明和抽取示例同时加强对抽取结果的规则校验。该拒绝的时候就返回参数解析异常让模型重新询问用户而不是带着错误参数去执行。2.3 执行逻辑的封装规范技能的执行逻辑承担着把参数变成真实结果的重任也是出错率最高的部分。我在项目中总结出一套统一的执行逻辑代码框架每个技能都遵循相同的过程模式参数校验、权限判断、核心执行、结果封装、异常映射。参数校验在技能入口处完成不要试图在中间过程里反复检查。校验逻辑包括必填项检查、类型检查、值域检查同时处理参数间的一致性。这里我通常会做一层额外的防护即使用户通过模型传入的参数已经经过Schema定义也不能完全信任技能内部必须自己再校验一遍。原因是模型抽取时可能输出违反Schema定义的意外值虽然概率低但一旦发生就可能导致严重问题在涉及订单金额、库存数量等敏感操作时尤其要严查。权限判断环节容易被忽略。同一个技能普通用户和管理员能够执行的范围往往不同。我建议把权限判断放在公共框架里统一处理而不是让每个技能自己实现。权限失败时返回的统一错误信息应当明确告知模型“没有权限”而不是“该操作失败”否则模型会把权限问题误判为执行问题从而重试同一操作制造大量无意义的调用。核心执行逻辑要遵循单一职责原则。一个技能内部的代码量建议控制在200到300行以内超过这个规模就考虑拆分。执行过程中涉及外部调用的部分必须设置超时时间并做好降级处理这样整体Agent才能在某个下游系统故障时优雅降级而不是整个会话卡死。2.4 输出规范的重要性技能的输出是模型下一步决策的依据输出结构混乱会让模型“听不懂”。我的经验是技能输出应该统一为三个部分执行状态、结构化结果、人类可读摘要。执行状态包括success、executed_with_warning和failed三种不能只返回true和false因为部分成功的情况在实际业务中非常常见。结构化结果是程序可以直接处理的数据用JSON格式返回。人类可读摘要则用于对话回复时直接展示给用户或者供模型转述。这里有一个容易被忽视的细节返回给模型的结构化结果不能原样返回数据库里的全量字段应该做剪裁和汇总。数据库订单表可能有二十多个字段但用户当前关心的可能只有订单号、商品、金额、状态这几个。如果技能把所有字段都塞给模型不仅浪费上下文还可能让模型在生成回复时捡了芝麻丢西瓜把关键信息淹没在无用字段里。我在技能开发规范里明确要求结构化结果字段不超过六个超出部分必须做聚合描述。3. 实操过程与核心环节实现从零到一搭建一个可用技能3.1 开发一个技能的标准流程技能开发看起来简单但把它做规范还是需要一套流程支撑。我在团队推广的标准流程分为六步需求定义、设备选型、Schema设计、执行逻辑编码、测试验证、注册发布。第一步是需求定义。需要明确这个技能的服务场景、用户诉求、输入输出的边界。我通常会要求用“用户故事”的格式写清楚谁在什么场景下遇到了什么问题希望通过这个技能解决什么。定义不清晰的时候不要急着动手写代码宁可花一两天时间把需求聊透。第二步是设备选型。确定哪些能力由代码实现哪些环节需要模型参与。比如前文提到的参数抽取通常需要模型但执行逻辑一定用代码。判断标准是前文提到的确定性原则凡是结果可枚举、逻辑可穷尽的就不用模型。除此之外还要考虑开发效率有些临时性的分析诉求没必要做成技能可以先让模型直接处理观察出现频率再考虑固化。第三步是Schema设计。前文已经详细讲了设计要点这里补充一点Schema完成后要第一时间拉上算法同事和业务方一起评审。算法同事能指出哪些参数模型可能抽不准业务方则能验证参数语义是否符合实际业务表达习惯两边都对得上这个Schema才算合格。第四步是执行逻辑编码。严格按照前文的框架实现注意内部解码注释要齐全。技能代码是高度标准化的但业务逻辑千变万化注释详细程度对后续维护起着决定性作用。第五步是测试验证。准备一批标注好的测试用例覆盖正常调用、参数缺失、参数冲突、边界条件、权限状态等场景。每条用例明确标注期望行为保证回归正确性。第六步是注册发布。把技能元信息名称、描述、参数Schema、版本号注册到技能中心接入统一的监控和日志体系。发布完成后要持续观察调用情况和失败模式作为下一轮优化的依据。3.2 一个具体技能的实现过程演示接下来用一个“为团队创建一个内部会议室预订技能”的例子把完整的实现过程串起来。这个技能的业务语义非常明确给定时间范围和参会人数查询可用会议室并提交预订。首先梳理需求用户的典型请求包括“明天下午三点到五点有没有可以容纳十人的会议室”、“帮我订一个后天上午的会议室”。技能边界可以设定为只负责查询和预订不做冲突协调不处理取消操作。权限要求是必须是团队成员。Schema这样设计必填参数有start_time、end_time、capacity可选参数有location、need_projector。start_time和end_time用ISO 8601字符串格式并加注示例capacity是整数描述里注明“若用户说大约十人则按十人处理”location是可选项用于限定办公区need_projector是布尔值默认false。执行逻辑上模型抽取出参数后技能先校验时间合法性结束时间必须晚于开始时间且跨度不超过四小时。然后查询会议室按容量匹配并把上限放宽20%也就是请求十人时查询容量至少为八人的房间这是为了给真实使用留一点冗余。找到可用房间后直接调用会议室系统API提交预订。在返回结果里说明预订成功、返回会议室编号和使用时间同时附上一条取消说明提醒用户如需改期走另一个技能——这就在技能层面实现了衔接。这个技能开发完成后测试用例覆盖了这么几种情况数据完整时正常预订、时间重叠时返回冲突提示、容量不足时推荐最近可用的替代时段、用户角色无权限时返回权限错误。每一条用例都验证了输出中的execution_status字段是不是符合预期。3.3 技能注册中心与版本管理当技能数量超过十来个之后靠手工管理已经不现实。我在项目中做了一个轻量级的技能注册中心每个技能在注册时提交结构化配置信息然后由统一的调度层负责向模型动态展示。这里的一个关键决策是不要把所有技能的完整描述一次性全塞给模型而是做分层路由。先用一个粗粒度的分类器或模型快速判断锁定相关的技能子集再把子集的详细技能描述发送给模型做精细选择。分层路由背后的原因很直接。模型能有效处理的候选技能数量有限当技能数量达到两位数时一次性展示全部技能描述会让模型陷入选择困难。分层路由能缩小候选集降低误选率。实践中这个决策的收益很大当技能数量从二十个增长到八十个时精确匹配率没有明显下降。版本管理同样值得认真设计。技能迭代过程中最常见的坑是新版本覆盖旧版本导致依赖旧版本技能的工作流悄悄用上了未验证的新逻辑。我在项目中给每个技能建立不可变的版本ID编排层显式指定使用的版本范围。变更生效前必须经过灰度流程首先把流量切到新版本观察一段时间确认没有异常后再全量放开。在这套机制下回滚操作从应急事故变成了常规操作整个系统的稳定性有了明显改观。3.4 效果测试与观察指标围绕技能效果我看重四类指标检出率、准确率、任务完成率、平均交互轮数。检出率衡量正确时机下技能是否被调用准确率衡量调用时机是否正确任务完成率衡量技能执行后用户诉求是否真实解决平均交互轮数衡量完成一个任务需要多少次对话轮数越少越说明技能表达清晰、能一次把事情办好。这里需要特别说一下任务完成率不能只看技能内部执行是否成功。有时候技能执行成功但用户并不满意比如订了会议室但时间不是用户原本想要的这说明参数抽取环节可能出了问题或者缺少二次确认逻辑。所以我会把技能执行成功率the code succeeded和任务完成率user problem solved分开统计。一个完善的Agent系统两类指标之间的差值应该逐步缩小如果差距迟迟没有变化说明技能的执行语义与用户真实诉求之间存在错位需要回到需求定义阶段排查。我建议在项目早期就搭建技能的监控看板以技能为维度展示调用次数、执行成功率、平均耗时、失败原因分布、参数分布等。没有这些数据支撑很多优化只能靠感觉进行这在大规模Agent场景下几乎无法保证质量。4. 常见问题与排查技巧实录那些轮子上滚出来的真经验4.1 模型始终不调用某个技能实际部署中经常遇到一种情况技能层明明已经接入模型却总是无视它的存在宁可自己编答案也不调技能或者只在极少数场景里碰巧用对。这个问题排第一位的原因往往是技能描述与用户真实表达之间存在语义代沟。排查思路是这样的查看日志中该技能的曝光量即候选集中是否包含它与调用量的比值。如果曝光量很低说明分层路由阶段就把它过滤掉了检查分类器的路由规则是否有遗漏如果曝光量正常但就是不调用问题出在描述本身。处理办法是用真实用户语料重新整理描述找十到二十条最贴合技能使用场景的用户说法把它们浓缩成描述里的典型触发场景并在负面描述里写清与相近技能的边界。还有一种情况容易被忽略就是技能的输入Schema约束过严模型担心调用后参数抽取失败所以宁可绕过去。比如某个技能要求地理坐标参数但用户通常只说城市名模型可能因为缺乏经纬度信息而不敢调用。解决方法是在Schema里把城市名作为可选输入内部做地理编码转换而不是要求模型直接抽取一个它根本算不出来的参数。4.2 参数反复抽取错误参数抽取错误的表现形式五花八门但根源往往集中在几个地方。最常见的是日期时间解析错误用户说“下周二”模型输出一个错误的绝对日期。这类问题的根源在于模型的时间指代消解能力不稳定与技能本身的Schema描述无关。应对办法是不在模型层解决绝对时间计算而是让模型把“下周二”原样保存在参数里放到技能内部执行逻辑中做解析和偏移计算这样时间解析就落到了确定性代码里。数字单位混淆同样频繁出现。用户说“给我订五六个房间”正确理解是拍一个数或者按6处理但模型可能把这个意思理解成从5到6的区间。Schema描述里要明确规定模糊数量的处理策略——比如“当用户给出模糊数量时向上取整后执行”同时代码侧对抽取结果做一次范围校验杜绝超出合理区间的参数值进入执行环节。在参数抽取方面尽力提高准确率的同时也要接受一个现实就是纯靠模型这一步无法做到100%准确。有一次我们的客服Agent连续出现多起金额参数错误排查后发现是因为涉及“折扣价”“到手价”“标签价格”三个语义相近的字段模型经常抽错对象。最终方案是在用户表达里先通过一个反问轮次确认关键金额数值再进入执行环节把错误拦截在入口处。这类“人机确认”机制就属于技能编排层面Buff在关键决策点上设置确认环节是提升整体稳定性的必杀技。4.3 技能执行成功但用户满意度不高这个问题的信号是会话结束后用户打了低分或者用户很快又重复了类似的诉求而日志显示Agent确实调用了技能且返回了成功状态。这种情况下最典型的根因是技能满足了字面诉求但忽略了真实意图的完整性。举个例子“查询订单状态”的技能成功执行了返回了“已发货”用户却给了低分。看看对话记录用户其实真正想问的是什么时候收到货。被满足的只是一部分信息真实需求并没有被完整覆盖。解决方案有两种思路第一扩展现有技能的输出在返回状态的基础上补一个预计送达时间的推算第二在编排层增加后续技能建议查询状态之后主动追加物流进度的查询。我通常优先选择前者因为一个技能的语义边界变宽要比引入一次额外的技能编排更经济但也提醒要警觉范围的膨胀失控。另一个常被忽略的干扰因素是回复语气和可读性。技能返回结构化结果后模型负责生成面向用户的回复如果模型只把结构化字段机械念了一遍用户的体验会相当生硬即便数据完全正确也难以让人满意。实践中我会在输出规范里明确增加“人类可读摘要”字段让技能自带一段通顺的话术模板模型在这段内容基础上做修饰出来的结果会比从一堆字段里自由发挥稳定得多。4.4 技能之间的边界模糊导致乱调业务场景扩到一定程度后技能数量上来了技能间的边界模糊成了主要矛盾。类似场景出现多个技能都截获同一个用户诉求的情况Agent调用的那个往往不是最优解。针对这个问题的关键手段是编写清晰的“技能边界说明”在描述里用大量精力标注“不要做什么”。用我们实际遇到的案例来说明。团队同时有“查快递”和“查订单”两个技能。一开始的“查订单”描述里写着“查看用户的订单信息”结果用户问“我买的手机到哪了”时模型优先调用了“查订单”查出来的是订单状态已发货却答不上来物流轨迹到哪个城市了。后来在“查订单”技能的描述里加入了“若用户询问配送位置、物流轨迹、包裹到哪请调用查快递技能而不是查订单技能”情况立刻好转误调度的比例明显下降。当边界模糊问题多发于某一组技能时还要考虑一个更彻底的解法——合并技能或引入父技能。把“查订单”和“查快递”两个技能合并为“查询订单配送状态”一个技能内部根据参数与上下文分流。这样模型只需要选一次降低路由出错的概率。合并带来的代价是技能内部逻辑变复杂所以是否合并的决策标准是如果一组技能的调用经常需要互相补充或先后协作合并的收益大于成本就合并如果它们各自面向差异明显的独立场景则保留独立并做好边界的限定。4.5 问题速查表把上述问题和排查思路整理成一张速查表方便遇到问题时快速对照。常见问题可能原因快速处理方案模型从不调用某技能描述与用户表达语义有代沟路由阶段被过滤用真实语句重写触发场景检查分层路由规则调用率正常但效果差参数抽取不准不合适的Schema定义降低参数抽取难度把复杂计算移入代码加入确认轮次技能执行成功但用户不满意字面诉求满足但真实意图缺漏回复话术生硬扩展技能输出增加人类可读摘要相似技能互相干扰边界描述不清增加除非和负面描述考虑合并父技能时间、金额类参数总出错模型处理数值/日期表达不稳在代码侧做解析不在模型层做计算技能内部报错频繁外部依赖不稳定缺超时处理为外部调用增加超时和降级方案4.6 效果优化节奏与个人体会最后还想聊一点非技术层面的体会。技能体系建设本质上是一个持续迭代的过程不存在“做完”的状态。早期跑通一个可用的技能可能只需要一两天但后续面向真实数据打磨优化的周期会是前期的数倍。我在实际项目中通常会在第一批技能上线后留出两周专门做迭代通过监控把调用率低的技能一份份调优把报错集中的场景一个个打补丁这段时间投入产出比是最高的。关于给技能数量设上限的问题我也不担心多。当技能数量从几十个涨到上百个以后纯靠模型在候选技能里做路由已经不够用了需要引入更精细的分类索引和检索机制。我们目前的做法是给技能打标签按领域、对象、操作类型三个维度建立索引模型先通过标签快速缩小候选范围再做最终决策。这个机制运作得很好。只要底层的注册中心、版本管理、监控体系做得足够扎实技能规模的扩张并不会带来同步的维护复杂度增长反而会因为复用率提高让整体研发效率越来越高。