
做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项目我建议从第一个技能上线时就想清楚这三件事技能描述怎么写才能让模型找得准技能之间怎么避免互相打架出问题之后靠什么数据来定位和修复。这三点想明白了技能体系就算立住了。