做AI Agent有个特别隐蔽的分水岭:团队前期讨论的都是模型能力——选哪个大模型、上下文开多长、幻觉怎么压。等系统跑到一定规模,讨论重心就会变成“这个技能该不该拆成一个独立技能”“两个技能的边界到底在哪”。如果你也走到了这一步,说明你已经撞上了Agent工程化里最绕不开的主题——agent-skills,也就是智能体的技能体系建设。
我今年大部分时间都在折腾这块。从最初在系统提示词里堆技能描述,到后来重构成一套完整的技能注册、编排、评估闭环,中间踩了不少坑。这篇就把整个思路和实操记录整理出来,给正在做Agent应用、或者准备往这个方向投入的同学一个参考。
1. 为什么Agent欠的是一套技能体系,而不是更多提示词
1.1 技能和提示词是两种完全不同的东西
很多团队一开始的做法是:往系统提示词里猛堆能力描述。“你可以调用查询订单接口”“你可以调用退款接口”“当用户提到发票时,你需要先验证身份”。这种做法的好处是启动快,坏处是扩张完全失控。
提示词本质上是一次性交互约定,它只在“这一次对话”里生效。技能则不同,它是可复用、可注册、可参数化的能力单元,有自己的名称、描述、入参规范、出参结构和版本号。我习惯打个比方:提示词像你给临时工写的一张便条,技能则像一个有标准接口的插座,任何Agent流程需要这个能力时,按规范插上就能用。
项目从“提示词时代”进入“技能时代”,通常有几个标志:
- 同一个能力被三四个不同场景重复描述,改一处漏一处;
- 新增一个能力时要小心翼翼,因为不确定会不会影响其他能力;
- QA同学开始问“这个能力到底在哪些场景生效、权限边界是什么”,没人能答清楚。
如果你已经出现以上任何一个信号,说明该从“堆提示词”切换到“建技能体系”了。
1.2 技能体系带来的三个核心价值
第一是可发现性。技能注册之后,Agent在决策时能通过技能清单检索到“现在有什么能力可以用”,而不是靠提示词里零散的文字描述猜。这里有个关键点:技能的描述写得好不好,直接决定模型召不召得准,后面我会专门展开。
第二是可复用性。同一个技能可以被多个Agent、多个流程共用,能力底座只需要维护一份。比如“获取用户实名信息”这个技能,客服Agent能用,交易风控Agent也能用,不需要各写一套。
第三是可治理性。权限、配额、灰度、回滚,都能通过技能层统一做。以前这些逻辑散落在应用代码里,有了技能体系之后,它们变成了基础设施的一部分。
我见过不少项目在早期觉得“技能体系太重了,先跑起来再说”,结果跑到两三个月就卡住,返工成本远大于一开始就建体系。这个取舍要提前想清楚。
2. Agent技能系统的落地架构:从注册到调用的全链路
2.1 技能注册中心:先定义清楚“一个技能长什么样”
我们实践下来,技能注册中心是整个体系的基石。每个技能在注册时,建议至少包含以下字段:
| 字段 | 说明 | 示例 |
|---|---|---|
| 技能名称 | 全局唯一,语义清晰 | query_order_status |
| 技能描述 | 给模型看的触发说明 | 查询订单的最新物流和状态信息,适合用户询问“我的订单到哪了”时调用 |
| 入参Schema | 结构化参数定义 | order_id, user_id |
| 出参Schema | 返回值结构定义 | status, estimated_time, tracking_list |
| 版本号 | 支持灰度与回滚 | v1.2.0 |
| 权限标签 | 调用前置条件 | 需要用户已登录且订单归属校验 |
这里最容易犯的错误是把技能描述写成“给开发者看的技术文档”,大段大段讲实现细节、数据结构、鉴权方式。站在模型的角度,它需要一个“什么时候该用这个技能”的触发型描述。我在某项目里见过一个技能描述写了三百字,从底层存储讲起,结果模型在绝大多数场景里根本不触发它。
后来我们把描述格式收敛成了三个固定部分:这个技能做什么、适合在什么用户请求下触发、不适合在什么场景下触发。加上了“不适合触发”的负向约束之后,误召回率明显下降。
2.2 技能调用链路上的关键环节
技能注册好了,接下来是调用链路。按照实际发生顺序,链路包括模型决策、参数提取、技能执行、结果回写四个环节。
模型决策环节考验的是技能描述的“可被检索性”。一些团队会把几十个技能一次性塞给模型,结果模型在长列表里经常选错。我们后来采用的是分层技能路由:先用一组粗粒度的“技能分组”让模型判断进入哪个域(比如交易域、售后域、账号域),再在域内做细粒度匹配。这样做的好处是把每次决策的候选项从30个降到了5个以内,准确率提升非常明显。
参数提取环节是幻觉重灾区。用户说“帮我看看上周买的那个键盘到哪了”,模型需要从自然语言里抽出订单ID。这里必须做两件事:一是用入参Schema约束模型输出JSON,避免自由文本;二是在技能执行前做参数校验,必填项缺失就立刻反问用户,而不是带着空参去调接口。
结果回写环节很多人会忽略。技能执行完之后,返回值要不要写回记忆、要不要触发后续技能,都应该有明确的规则。我们用的是一套“事件通知”机制:技能返回结果后,登记为一条领域事件,编排层决定是结束、追问、还是继续调用下一个技能。
2.3 技能中间件与网关的放置策略
技能体系跑起来之后,一定会面临横切关注点:认证、限流、审计、配额。
我的建议是统一放在技能网关里做,不要在单个技能实现里各自处理。理由很简单:如果每个技能开发者都自己写一遍鉴权,大概率写到后面五花八门,出一次安全事故根本没法追溯。
网关层我们主要做四件事:
- 识别调用方身份和权限标签,做前置校验;
- 按技能维度和用户维度双重限流,防止单个技能被打爆;
- 全量记录调用日志,包括入参、出参、耗时、模型决策依据;
- 配置热更新,技能上下线不需要重启服务。
网关的存在也带来了一个额外收益:技能A和技能B如果都依赖同一个底层服务,流量可以在网关层做聚合和转发,给下游服务减少了连接压力。这块属于架构上的长期回报,初期不觉得,等规模上来之后才体会到。
3. 技能编排实战:从单技能到复合任务的关键机制
3.1 谁来编排:模型自主、预设流程、还是两者混用
单技能调用相对简单,一旦用户请求需要多个技能配合完成,就进入编排问题。比如“帮我申请退货并把运费险也一起退了”,至少涉及订单查询、退货单创建、运费险理赔三个技能。
编排的第一层选择是:让模型自己决定技能顺序,还是用预设流程模板。
模型自主编排的优点是灵活,能应对长尾场景;缺点是行为不可控,尤其在多步骤任务里容易漏步骤、跳步骤。预设流程模板的优点是稳定可控;缺点是覆盖面有限,用户请求稍微绕一点就匹配不上。
我们最终用的是“策略层+执行层”两层结构。策略层还是一张大模型,负责把用户请求翻译成一个“技能调用序列草案”;执行层则有一个轻量的规则引擎,负责校验这个序列是否满足预定义的业务约束。
举个例子,退货流程里业务约束是“必须先冻结原订单状态,才能创建退货单”。模型可能会先创建退货单,规则引擎就会拦住并调整顺序。这样做的好处是既保留了模型的灵活性,又守住了业务底线。
3.2 复合任务的错误恢复与部分成功处理
编排的多技能任务里,最麻烦的是“做到一半失败了”。比如查询订单成功、冻结状态成功、创建退货单时下游服务超时了。这时候用户那边看到的是“我已经发起退货了”,而系统里退货单其实没建出来。这就是典型的部分成功问题。
我们当时的处理方案是三步:
- 记录中间状态:每个技能执行完成后,立即把结果写入任务状态表,而不是等整个编排流程走完再落库;
- 断点续跑:失败后不要求从头开始,而是从失败的技能重试,前提是重试逻辑要做幂等;
- 用户可见降级:如果重试两次仍然失败,就转入人工处理队列,同时给用户一个可理解的提示。
这里特别想强调幂等性的设计。技能只要是可重试的,一定要支持“同一请求重复提交不产生副作用”。比如创建退货单这种操作,我们在入参里加了一个client_request_id,下游用这个字段做去重。没有幂等保护的重试,就是在拿线上数据开玩笑。
3.3 跨技能状态传递与上下文压缩
多技能编排意味着一个技能的输出经常是另一个技能的输入。比如查询订单技能的返回值里带着商家ID,后续的“联系商家”技能就需要这个ID。
有两种传递方式:显式传参和隐式上下文。显式传参是指在编排序列里把一个技能的输出字段映射到下一个技能的入参字段;隐式上下文则是把前序技能的结果摘要写入Agent记忆,由模型决策时自行提取。
我们的经验是核心业务参数尽量用显式传参,保证链路闭环;辅助参考信息用隐式上下文。如果全部依赖隐式上下文,模型在长链路里很容易把关键参数记错,等到执行层才发现参数不对,返工成本很高。
上下文压缩也是必须考虑的问题。一个涉及六个技能的任务,每个技能返回1000字的结果,累计就是6000字,再叠加历史对话,很容易撑爆上下文窗口。我们的做法是在每个技能返回后即时做结构化摘要,只保留对后续步骤有意义的字段,原始结果存到外部存储,需要细节时再按需查询。
4. 实测中躲不开的坑:技能冲突、权限边界与并发安全
4.1 技能误召与互斥:描述相似惹的祸
技能数量超过20个之后,会遇到一个非常有趣的问题:模型开始“点错技能”。最常见的场景是两个技能在功能上相近,描述又写得模棱两可。
比如我们同时有“查询订单物流”和“查询订单预计送达时间”两个技能,在回归测试中,模型经常在用户问“我什么时候能收到”时去调用第一个技能,返回一堆物流轨迹节点,而不是直接给出预计时间。
这里不是模型不聪明,而是技能设计本身有问题。功能相近的技能根本不应该同时存在,正确做法是先做技能合并,实在合并不了再通过触发条件做区分。
我们后来定了一条规则:如果两个技能的功能重叠度超过一定阈值,禁止同时上架。新技能在注册时走一次“冲突检测”,把名称和描述与存量技能做向量相似度比对,相似度高的会被打回,要求说明与存量技能的差异点。这个机制上线之后,技能误召率降了大约四成。
4.2 权限模型:粗粒度可用,细粒度可控
技能体系的权限设计比微服务时代更麻烦,因为调用方不是某个确定的服务,而是模型生成的随机意图。
我们的权限模型按三层来设计:
- 用户级权限:决定“这个人能不能用这个域的功能”,比如未登录用户不能触达任何交易技能;
- 角色级权限:决定“这类人能不能用这个技能”,比如普通用户能用退货,只有客服专员能直接用“强制拦截订单”;
- 技能级权限:决定“这个调用请求背后的业务场景允不允许”,比如深夜时段禁止触发批量操作类技能。
在实现上,用户级和角色级由网关前置校验完成,技能级则靠技能内部的策略表达式。这里有一个实操细节:权限判定的结果必须回写进调用日志,方便事后审计时还原“为什么这个调用被放行/被拦截”。
还有一个容易踩坑的点是“高危技能的确认机制”。比如“批量修改价格”这种技能,如果用户一句话触发了,直接执行风险太大。我们给这类技能配置了二次确认动作:先调用“发送确认卡片”,等用户明确确认后再继续。刚开始觉得这个设计会降低体验,实际上用户并没有觉得麻烦,反而对系统的安全性更有信任感。
4.3 并发调用下的状态污染与资源隔离
技能体系一旦上线,同一个Agent会并行处理多个用户会话。如果技能实现里有共享的可变状态,就会出现A用户的请求改掉B用户的数据这种严重事故。
我们遇到过真实案例:某个技能内部用了一个模块级缓存变量保存“当前请求的上下文”,在低并发测试时一切正常,压力上来之后,请求互相覆盖,用户看到的信息开始串号。查了整整两天,最后定位到是共享变量问题。
修复方案很简单:把所有技能改成无状态实现,请求级数据全部通过参数传递,绝不写入类成员变量。凡是无法做到无状态的场景(比如复用长连接池),就把连接池按用户维度做隔离或用上下文对象显式绑定。
另外,并发上限也要按技能做隔离。高消耗技能(比如调用外部AI服务做文本分析的技能)如果和低消耗技能共享同一个线程池,一旦前者被打爆,后者也会跟着阻塞。我们把技能分成了几档资源池,核心交易技能独享资源,普通查询技能共享配额,这样某个技能出问题不会拖垮整条链路。
5. 技能质量评估与持续迭代:没数据就谈不上优化
5.1 衡量技能体系的四个核心指标
建技能体系不难,难的是持续优化。优化必须建立在数据反馈之上,我们每周固定看四个指标:
| 指标 | 含义 | 我们的目标 |
|---|---|---|
| 命中率 | 技能被召回的条数占应召回总数的比例 | 不低于90% |
| 完成率 | 技能被调用且成功返回的比例 | 不低于95% |
| 回退率 | 技能执行失败后触发人工兜底的比例 | 不高于10% |
| 修复率 | 一次失败后通过重试或替代技能成功恢复的比例 | 不低于70% |
这几个指标不是平行的。命中率低说明技能定义和描述有问题,回到注册环节去优化;完成率低说明技能实现不稳定,回到研发链路去排查;回退率高说明技能的边界覆盖不够,需要考虑补技能或改交互策略;修复率低说明失败处理链路薄弱,要重点搞重试和降级。
5.2 基于失败日志的技能改进闭环
指标只负责发现问题,真正的改进靠日志分析。
我们每两周做一次技能失败日志的归类复盘。具体做法是把所有失败调用按“失败阶段”分组:是模型没召对技能,还是参数提取缺失,还是技能执行超时,还是权限校验拒绝。分组之后你会发现真正的问题往往集中在某几个技能上,而不是均匀分布。
举个例子,有一段时间“查询订单”技能的完成率一直徘徊在90%左右,看日志发现大量失败原因是“用户ID缺失”。进一步排查发现是上游在某种登录态下拿不到用户ID。这不是技能本身的问题,而是身份传递链路的漏洞。我们修复了上游后,完成率直接拉升到98%。
还有一个典型优化是技能描述的A/B测试。同一个技能可以准备两套描述,分别部署到不同流量桶,观察命中率和误召率差异。这个做法成本很低,但收益非常直观。我们有一版描述把触发条件的顺序调整了一下,把高频场景放到前面,命中率提升了6个百分点。
5.3 技能的灰度、上架与下架机制
技能体系的迭代节奏要稳定,不能今天上线明天下架。
我们的做法是:每个技能按v1.0、v1.1这样的版本独立发布,发布时只放5%的流量灰度,观察完成率和错误率,达到阈值后再逐步放大。灰度期间发现异常随时摘流量,不要等用户投诉了再处理。
下架机制同样重要。长期没被命中的技能要定期清理,因为技能列表越长,模型做决策时干扰越大。我们定了一个规则:连续30天命中率为0的技能,进入待下架清单,由技能负责人确认后下线归档。这听起来很基础,但实际执行起来能有效防止技能体系越来越臃肿。
在我个人看来,agent-skills最容易被低估的其实是“治理成本”。技能从几个涨到几十个的时候,研发还撑得住;涨到上百个的时候,没有一套注册、路由、观测、迭代机制根本没法维护。早期咬咬牙把体系建起来,后面省下的是几十倍的时间。如果你正在做Agent项目,我建议从第一个技能上线时就想清楚这三件事:技能描述怎么写才能让模型找得准,技能之间怎么避免互相打架,出问题之后靠什么数据来定位和修复。这三点想明白了,技能体系就算立住了。