☰
Agent Skills实战:从大Prompt困局到技能编排架构
2026/10/7 11:39:17 网站建设 项目流程

1. Agent Skills是什么:先从一个翻车的项目说起

去年年中我接手了一个企业内部知识库助手,需求听起来不复杂:员工在飞书里问一句"上季度的报销流程是什么",机器人就得给出准确答复,顺便把相关表单链接甩出来。最开始我走的是当时最流行的路子——把所有业务规则、文档索引、问答逻辑全部塞进一个巨大的System Prompt里,再配上几个function calling接口。初版跑起来确实像那么回事,demo演示的时候管理层还点头了。

但上线第三周就出事了。随着业务文档从二十篇涨到两百篇,Prompt从三千字膨胀到八千字,模型开始频繁出现幻觉,把去年的报销标准答成前年的,甚至会把"休假申请"和"调休规则"混着答。我试着继续往Prompt里加约束,结果就像往沙子里掺水,越多越散。后来我彻底放弃了增量修Prompt的方案,把架构重构成基于Agent Skills的技能编排模式——三个星期后,准确率从76%拉到了94%,维护成本还降了一半。今天这篇就聊聊一个合格的Agent Skills体系应该怎么搭,哪些坑我是真金白银踩出来的。

这篇文章适合谁看?如果你手上正在做AI客服、Copilot工具、知识库机器人,或者任何一个"大模型要执行多步任务"的项目,并且已经感受到大Prompt方案越来越难维护,那这篇文章正好解决你的问题。我会先讲清楚Skill的底层架构逻辑,再给出一套能落地的实现方案,最后把我排查过的几个真实翻车现场摊开给你看。

2. 为什么"大Prompt硬调"这条路必然走死

2.1 上下文窗口再大,也装不下整个业务系统

很多人的第一反应是:模型上下文窗口不是有两百万token了吗,把所有东西都塞进去不就行了?这个想法我在项目里也试过,但实践下来有三个绕不开的硬伤。

第一个硬伤是注意力稀释。模型对Prompt头部和尾部的内容记忆最强,中间部分最容易丢失。当一份业务手册被塞进上下文中间位置时,模型对它的"关注度"会显著下降,表现出来就是——规则明明写在Prompt里,模型却像没看见一样自由发挥。第二个硬伤是更新成本。业务规则每个月都在变,每次修改都是对Prompt的一次大手术,改一行字可能引起系统其他部分行为异常,而且无从排查。第三个硬伤最致命——你没法测试。Prompt越长,输出越不稳定,同样的输入今天能答对明天就答错,回归测试根本没法做。

2.2 推理与执行混在一起,必然互相污染

我在重构前好好想了想,为什么大Prompt方案这么脆弱?根子上是因为它把**推理(该做什么)和执行(怎么做)**搅在了一起。

举个例子。知识库助手的任务是"查报销规则并举一反三给出注意事项"。在旧架构里,模型要先理解这个任务属于哪个业务域,然后在八百行Prompt里定位相关规则,再决定要不要调用接口拿最新数据,最后还要组织语言答给用户。这四层思考全都发生在一次推理里,任何一个环节状态不好,整条链路就歪了。

Agent Skills的思路是把这四层拆开:第一步,模型只负责判断"这问题归哪个Skill管";第二步,Skill内部预先定义好了该怎么办。判断是模型的活,执行是代码的活,两者不再互相干扰。用大白话说,大Prompt模式是让AI当全才实习生——每个任务都要现场想一遍流程;Skill模式是让AI当调度员——它只负责把工单派给合适的老师傅。

2.3 可观测性与可维护性的鸿沟

还有一个经常被忽略的问题是排查故障的效率。大Prompt方案里,用户问一句"报销比例多少",模型答错了,你根本不知道它是在哪一步想错的:是没定位到规则?是规则太长读漏了?还是定位对了但输出时表达错了?这不是玄学,这就是架构缺陷——你拿不到中间过程的日志,能看到的只有输入和输出。

Skill架构天然具备日志切面。模型判断"这个问题属于Query_LeavePolicy",然后Skill执行体去数据库捞数据——每一步都有明确的日志节点。用户投诉一条答错的消息,我打开链路追踪,一眼就能定位是模型选错了Skill,还是Skill里的数据查询出了问题。这个差异在调试期还不明显,等业务量上来之后,就是天壤之别。

3. 一个可用的Agent Skill到底长什么样

3.1 解剖四个核心组成部分

很多人理解的Skill就是"给模型多一个工具",这个认知太浅了。一个真正可用的Skill,要包含四个层次的内容,缺一个都容易在生产环境出问题。

第一层是元数据(Skill Definition)。这层负责向模型描述:这个Skill叫什么、管什么用、什么情况下该用它、不该用它。这是模型做意图路由的最关键依据,也是整个体系里最需要打磨的部分。我见过太多团队把description写得太笼统,比如"处理请假相关查询",模型根本分不清"我明天想休息一天"和"帮我看看我的年假余额"该走哪个Skill。

第二层是输入Schema。也就是这个Skill接收哪些参数,每个参数的类型、格式、约束条件是什么。这一层决定了模型"猜"你的接口参数时有多容易出错。我实测下来的经验是:参数越少越好,命名越贴合自然语言越好,必填项之外的都尽量给默认值。

第三层是执行逻辑。这是Skill真正干活的代码体——可能是调用内部API,可能是拼装查询语句,可能是触发下游工作流,也可能只是做一次模板化回答。要注意的是,执行逻辑里头还要包含输入校验和结果检查,不能假设模型给的参数永远是合法的。

第四层是失败处理。这是最容易被人忽略、但生产环境最要命的环节。Skill调用了外部API,结果服务挂了,这时候是返回兜底回答、重试三次、还是把错误反馈给模型让它换个思路?没有这层设计,Skill体系会在真实流量下被冲垮。

3.2 一个可直接参考的Skill定义示例

以我项目里的"查假期余额"Skill为例,它的定义是这样写的:

{ "name": "query_leave_balance", "description": "查询员工当前可用的年假、调休、病假余额。当用户询问'我还有多少天假''年假剩几天''能不能休两天调休'等涉及具体假期余额数值的问题时,必须使用本技能。注意:本技能只查余额,不处理请假申请流程。", "parameters": { "type": "object", "properties": { "employee_id": { "type": "string", "description": "员工的工号,可以从对话上下文中提取,若无法确定则默认为当前会话用户" }, "leave_type": { "type": "string", "enum": ["annual", "compensatory", "sick", "all"], "description": "要查询的假期类型,若用户未指明,则设为all" } }, "required": ["employee_id"] } }

注意这个Skill里我写了"注意:本技能只查余额,不处理请假申请流程",这不是废话,这是在模型路由时给它一把尺子——防止用户问"我要请假两天"时,模型误调用这个只读类Skill。在模型能力不够强的阶段,这种正向+反向约束非常管用。

代码运行效果如何?需要加入你自己的真实项目场景来验证。在我实际系统中,Skill执行业务流程图如下:

用户提问 --> [意图路由模型] --> 匹配到query_leave_balance --> [参数抽取] --> employee_id=10023, leave_type=all --> [执行体] --> 调用HR系统API --> 获取结果 --> [模板化组装] --> "您的年假余额为7天,调休余额为2天" --> [失败重试兜底] --> 若API超时则回复"系统繁忙,请稍后再试"

3.3 路由层怎么选型:模型判断还是规则优先

Skill的执行逻辑写好了,接下来要解决的问题是:模型怎么知道该调用哪个Skill?这里有两个主流方案——纯模型路由和规则兜底+模型路由。

纯模型路由最省事,让大模型读一遍所有Skill的description,然后输出自己该调用哪个。缺点是模型会有概率性抽风,Skills一多还会互相抢生意。我目前的线上方案是混合式:先用非常轻量的规则层做前置过滤(比如会话中检测到"余额""剩几天"这类强关键词,直接把候选Skill范围缩小到两三个),再把缩小后的候选列表交给模型裁决。这么做的实测效果是路由准确率从88%提升到96%,而且规则层逻辑简单,出了问题容易调。

4. 从零构建一个技能库:五步落地实操

4.1 第一步:盘点高频任务,划清边界

不要一上来就写代码,先用一个周末的时间把业务方的历史提问记录翻一遍。我当时的做法是把三个月内所有用户问句拉出来,按主题聚类,统计频率,然后给每个主题写一句话定义。这里有个关键经验:Skill的粒度宁可粗一点,也不要切太碎。我刚开始把"查年假""查调休""查病假"拆成了三个Skill,结果模型频繁把问题路由错。后来合并成"query_leave_balance"一个Skill、内部用参数区分假期类型,准确率立刻上来了。

Skill粒度怎么判断?我给你一个标准:如果你发现自己要写超过三句话来解释这个Skill的边界,那它大概率太碎了;反过来,如果这个Skill里要写一排switch case分五六种逻辑,那它太胖了,应该拆。一铲子下去,边界清清楚楚的才是好Skill。

4.2 第二步:把description写成"给陌生人看的产品说明"

这回是Skill工程里最考验功力的环节。很多技术出身的朋友容易把description写成技术文档风格——"调用此接口可获取员工假期余额数据",但关键问题是:模型不是人,它要用这段描述来判断意图,描述写得好不好直接决定路由质量。

我总结了一个description写作公式:触发场景 + 具体例句 + 反向排除。触发场景是"当用户询问XX时",具体例句是"例如'我还有多少天假''年假剩几天'"——例句宁可多写,因为模型对具体句子的理解力远比对抽象规则的理解力强。反向排除是"注意:本技能只查余额,不处理XX情况",这个能显著降低模型把相似意图误判到这个Skill的概率。

4.3 第三步:参数Schema设计要克制

参数设计的核心思想就两个字:克制。我踩过的坑是给Skill设计了七八个参数,期望模型一次性把信息全抽出来。结果模型在抽取时频繁出错,缺这个漏那个。后来我把参数缩减到只留关键的两三个,其余信息交给执行体内部从上下文或数据库补全。

比如"查假期余额"这个Skill,其实只有一个必填参数——employee_id。leave_type给了默认值all。这样的话模型就算没听清用户要查哪种假,也不会调用失败。记住:每个必填参数都是一次潜在的意图误判点,能省则省。

4.4 第四步:执行体要带"防呆设计"

执行体是Skill真正干活的代码,很多人都写得很"直给"——拿到参数直接请求API,拿到结果直接返回。这在真实生产环境是大忌。我在重构后给所有Skill的执行体加了三个标准件:

第一个是输入预校验。模型抽出来的employee_id可能带空格、可能是中文数字,甚至可能跟用户提的不是同一个人的工号。我在校验层做了格式清洗、白名单校验,不合法就进失败分支。第二个是结果结构校验。外部API返回的数据未必是预期的格式,特别是HTTP 200但body里是个错误码这种情况,需要显式判断。第三个是降级分支。API超时、限流、异常,都应该有对应的温和回复,不能直接把堆栈拋给用户。

这里用Python写一个执行体骨架,你可以直接参考:

def execute(employee_id: str, leave_type: str) -> Result: # 输入预校验 employee_id = validate_employee_id(employee_id) if not employee_id: return Result( status="failed", reply="抱歉,我没有识别到您的工号,请确认是否已绑定账户。" ) # 核心调用 res = hr_api_client.get_leave_balance(employee_id, leave_type) # 结果结构校验 if res.code != 200 or res.data is None: log.warning(f"HR API返回异常: {res}") if res.code == 504: return Result(status="failed", reply="系统查询超时了,请稍后重试") return Result(status="failed", reply="暂时无法获取假期余额,请稍后再试") # 模板化组装 balance = res.data reply = build_reply_message(balance, leave_type) return Result(status="ok", reply=reply)

加的每一层看起来都"多余",但生产环境的数据辣不辣眼睛,只有上线后才知道。没有这层防呆,一个API偶发超时就能让整个Agent体系被用户骂上热搜。

4.5 第五步:建立Skill回归测试集

Skill体系的优势之一就是可以做回归测试。我建了一个"意图金标集":从历史真实用户问句里挑出200条,人工标注好每条应该路由到哪个Skill。每次改完一个Skill的描述或路由规则,就跑一遍这批测试数据,看看路由准确率是升了还是降了。没有这套机制,你就永远不知道改完一个Skill是不是把另一个Skill搞挂了。

这里有个实操细节:金标集需要每隔一两周补充新问句。用户总会用你意想不到的话术提问,把带回来的新case加入测试集,路由准确率才会持续提升。维护这个测试集是我认为整个Skill体系里性价比最高的一件事。

5. 实测中的翻车现场与排查链路

5.1 事故一:两个Skill怎么也分不清

上线第一周就出现了一个诡异现象:用户问"报销打完款了吗",系统一会儿走"查报销进度"Skill,一会儿走"查报销规则"Skill。我在日志里追踪发现,路由模型在两个Skill的description之间反复横跳,相同的问题今天走A明天走B。

排查链路是这样的:首先打开链路日志,确认问题确实出在路由环节而不是执行环节——两个Skill都执行成功了,但选错的那个答案牛头不对马嘴。然后我把两个description拿出来并排看,问题立刻暴露了——"查报销进度"的description里写的是"处理报销相关查询",而"查报销规则"写的是"回答报销标准相关问题"。模型会认为"报销打完款了吗"既跟进度有关,又跟规则沾边,于是两边都在候选区。

修复方案很简单:把description改成互斥的强烈触发词。"查报销进度"只回答"钱到哪了、款有没有打、审批走到哪一步了";"查报销规则"只回答"能报多少、范围是什么、流程怎么走"。改完之后,相同问题的路由准确率从82%升到了99%。教训:描述Skill时,痛痛快快地把这个Skill的场景边界锁死,不要用模糊的领域词汇。

5.2 事故二:模型抽参数时"过拟合"到了错误语义

有一次用户问"我想用调休抵半天,你看看我这周还能不能请假?"——这个问句其实包含两个诉求:查调休余额、顺便确认能不能请假。我的路由模型正确选到了query_leave_balance,但是参数抽取环节把"leave_type"抽成了"sick"(因为"请假"两个字触发了病假联想)。结果系统一本正经地回答"您的病假余额为5天",用户当场无语。

这个问题的根因在于:参数抽取的提示词里给的枚举含义不够清晰。我给enum值写了"annual年假、compensatory调休、sick病假",模型确实没有理由把"请假"理解成"调休"。修复办法是在description里把每个枚举值的语义都写了跨场景例句——"当用户提到'调休''换休'时使用compensatory"——效果立竿见影。参数枚举命名的偏差,是Agent体系里最容易忽略但实际频繁翻车的地方。

5.3 事故三:Skill内部的循环请求烧穿预算

还有一个让我后怕的事故。某个Skill的执行体在调用外部搜索API时,如果第一次结果为空,代码里写了一个for循环自动换关键词重试三次。结果某个高峰期,搜索API响应变慢,一批请求全部触发了循环重试,单次用户请求最多发了12次外部API,直接导致合同额度超支。

排查链路从成本监控告警开始,拉出计费日志,定位到一批"search_skill"的调用链,看到每个链路里都挂了七八次API记录。修复方案是给所有外部重试加上总次数上限、单次超时和熔断开关,同时在Skill执行体入口加了一层高峰期限流。这里要提醒一句:给执行体写重试逻辑之前,一定先做一次"极端情况推演"——如果外部系统延迟百分之五十,我的重试策略会把流量放大几倍?

5.4 一套通用的排查方法:链路日志 + 回放测试

踩了这么多坑之后,我沉淀了一套自己的排查流程,分享一下:

  1. 先看路由日志,确认模型选的是不是对的Skill。不是的话,问题在意图路由层。
  2. 再看参数日志,确认抽出来的参数合不合理。参数错了,问题在抽取层。
  3. 看执行日志,确认执行体内部的每一步是否按预期走。
  4. 用金标集回放,把出错的用户问句喂给同一个系统,确认修复是否生效、有没有副作用。

这四步走一遍,大部分问题都能精准定位。如果你发现始终定位不到问题,那就要回头检查是不是Skill的description写得太模糊,导致模型行为本身不稳定——这种情况改了日志也查不出花来。

6. 从"能用"到"好用":技能调优与体系演进

6.1 多轮对话里的状态与记忆,是Skill体系的隐藏难点

单个Skill调好了还不够,多轮对话场景才是真正的分水岭。用户说"帮我查一下张三年假",接着又补了一句"顺便看看他的调休"——这句话在系统里怎么处理?最自然的设计是让上一轮的Skill状态延续下来,把第二轮的两个参数合并进第一轮的查询上下文里。我的实现思路是引入一个轻量级的会话状态槽位:第一轮Skill执行完后,我把参数和结果摘要写回会话槽位;第二轮路由模型先看槽位里有啥,再决定是新建Skill调用还是基于槽位信息增量执行。

说实话这个模块目前还没有标准答案,各家都有自己的实现方式。我的建议是,第一阶段别追求通用记忆机制,先把"业务强相关的状态槽位"做好就够了——比如刚才那种查询类场景,一个skill_result_slot就能覆盖绝大多数需求。

6.2 Skill版本管理与团队协作

当你的技能库超过二十个Skill,并且是三四个人一起维护时,版本问题就浮出水面了。我们今天上午刚改完A Skill的路由描述,下午发现B Skill的准确率掉了——这种狗血剧情我经历了不止一次。

落地方案非常朴素但好用:每个Skill的变更必须走Pull Request,路由金标集测试必须过90%才能合并。Skill的description变更看似是个小改动,但影响的可能是所有路由的决策分布。把"改了Skill描述必须附带金标集通过率"写进代码评审清单,能挡掉至少一半的踩坑。另外,执行体代码和Skill描述文件一定要放在同一个仓库同一次发布里,分开管理会出现"描述已经写好了,代码还没部署"的中间态,触发各种离奇错误。

6.3 跨Agent的技能共享——下一步的扩展方向

目前我正在尝试把Skill抽象成团队级的共享资产。思路是建一个内部的技能注册中心,每个Skill定义成一个带契约的声明式文件,任何Agent实例都可以通过注册中心拉取。这样新开一个Agent项目,不用从零维护一套技能库,直接注册需要的现有Skill即可,更多时间去沉淀新的业务技能。

这里做一个小预告:声明式Skill定义如果能标准化,未来的Agent协作会变得更像"乐高拼装"——不同团队维护不同的技能模块,上层Agent通过编排把它们组合成完整工作流。到这个阶段,Agent不再是单个系统的附属品,而是一个可以跨系统复用的业务能力层。这中间的技术选型(比如用JSON Schema还是YAML做契约、要不要引入语义注册表)还在探索中,有进展了我再写一篇继续分享。

最后再补一句个人体会:我在这个项目里最大的收获不是技术方案的升级,而是重新理解了"架构"的意义——把"需要猜的部分"和"不需要猜的部分"干净利落地分开,永远好过在大Prompt里用更多的文本去掩盖不确定性。Agent Skills的本质就是这条原则的工程化落地。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询