Agent技能化重构:告别提示词膨胀,打造可插拔能力单元

发布时间:2026/10/14 9:31:22
Agent技能化重构:告别提示词膨胀,打造可插拔能力单元 最近接手了一个Agent项目核心痛点一句话就能讲清楚模型一直在“变笨”。不是模型本身退化而是随着功能越加越多系统提示词越来越长每次对话都要把几十个工具描述、规则、示例塞进上下文里token开销暴涨模型反而频繁选错工具、漏掉步骤。后来我们整个团队把架构换成了技能化skills的思路把Agent的能力从“一段人设提示词”重构成“一组可插拔的技能”问题才真正缓解。这篇文章就把这个过程、设计取舍和踩过的坑完整写出来给正在做Agent应用的同学一个参考。所谓agent-skills简单说就是不再把Agent当做一个巨大的提示词模板而是把它拆成一个个可以独立描述、独立加载、独立验证的“能力单元”。每个技能包含自己的触发条件、输入输出契约、实现逻辑甚至自带回归用例。Agent在运行时按需发现、加载和编排技能而不是把所有能力一次性灌进上下文。这套思路对做工具型Agent、客服型Agent、办公自动化Agent的人都有参考价值无论你是用开源框架还是自研编排底层逻辑都是相通的。1. 为什么Agent不能再靠“堆Prompt”过日子——技能化的底层动机1.1 “提示词膨胀”是怎么毁掉Agent的我先说一个真实的现场。项目早期我们把所有功能都写在系统提示词里工具列表、使用规则、格式要求、历史对话摘要、当前任务状态……每个功能上线就往里加一段。大概加到第四五个能力域的时候问题开始密集出现。第一个问题模型的注意力被稀释。模型不是无限上下文的内存prompt越长关键指令的权重就越低。明明工具描述写得很清楚模型就是“看不到”尤其是当关键说明被埋在第2000行以后触发率直线下降。我们实测过一段包含15个工具描述的prompt模型正确选工具的成功率从95%掉到80%以下而且不稳定。第二个问题没法单独迭代。某个工具逻辑要改意味着整段系统提示词要重新测试一遍因为改动会影响其他工具的描述语境。你改一个工具可能让另外三个工具的调用率骤降。这种耦合让每一次发布都像拆炸弹。第三个问题上下文记账失控。所有工具的完整描述都要常驻上下文而真正被某次任务用到的工具可能只有两三个。典型场景下80%的上下文窗口被“潜在的、可能用不上”的能力描述白白占掉留给真实对话和任务状态的token反而紧张。1.2 技能化的本质把“能力”从“人设”中拆出来我当时意识到问题的根源是把“Agent是谁”和“Agent能干什么”混在了一起。人设是稳定的、居中的、影响所有交互的元信息而能力是离散的、条件依赖的、只在特定任务中出现的信息。这两类信息放在同一个prompt里互相污染是必然的。技能化做的事情就是把这个捆绑拆开中央prompt只保留Agent的身份、基础行为准则、通用输出规范其他全部下沉为独立的技能单元。每个技能单元自带完整的触发条件、参数说明、实现代码、结果校验逻辑。模型的任务不再是“记住所有工具”而是“判断当前该调用哪个技能然后让技能自己处理”。这个转变很像从“让人工客服背全公司产品手册”变成“给客服一个能实时查到对应产品资料的内部知识库”。手册越背越乱知识库可以随时更新。1.3 从任务型玩法到通用Agent技能是复用的基本单元早期很多人做Agent是“一个场景一把梭”比如做个翻译Agent就只写翻译工具做个订餐Agent就只写订餐工具。这时候堆prompt没毛病因为能力面窄。但一旦你想做一个能同时处理邮件、日程、数据分析、客户信息查询的通用Agent能力种类和数量必然爆炸堆prompt的模型就扛不住了。技能化提供了一个非常自然的复用单元每个技能独立开发、独立测试、独立发布然后像乐高积木一样组装到不同Agent里。一个“日历读写”技能可以被日程Agent用也可以被邮件Agent用还可以被报表Agent用。技能库积累到一定程度新Agent的开发成本会显著下降——很多时候只是重新组合已有技能而不是重新设计prompt。技能化的另一个隐含好处是每个技能可以独立做质量评估。你不需要等整个Agent系统上线再把所有能力混在一起测而是可以针对单个技能跑回归用例确认这个技能的输入输出、边界行为都还在预期内。这对生产环境的稳定性价值极大后面我会专门展开。2. skill和function call的区别在哪里一个“能复用”一个“到处贴”2.1 function call解决的是“让模型会调用”但没解决“让能力可管理”很多同学会问我们不是早就有function calling了把函数定义成JSON schema喂给模型模型按格式调用不就已经实现技能化了吗这里必须分清楚。function call解决的是一个底层交互问题怎么让模型稳定地发起一次外部调用。它只是把函数的输入参数描述给模型听模型决定要不要调、传什么参数。但function call本身不承担“组织能力单元”的职责。实际工程里用function call直接用出一堆问题函数定义和业务逻辑散落各处模型能调用的函数越来越多之后只能靠prompt去告诉模型“这些函数里哪些相关”最终又回到prompt膨胀的老路上。而且function call没有生命周期概念——它不负责技能的更新、废弃、灰度、回滚也没有办法对“某个函数是否被调用成功、调用结果质量如何”做独立评估。2.2 skill的结构一段声明描述模型怎么选一组实现模型怎么调一个真正的skill单元至少包含四层信息第一层是技能元信息包括技能ID、名称、版本、作者、依赖关系。这层是给“Agent框架”和“开发者工具”看的方便检索、注册、更新和权限控制。第二层是技能描述包括触发条件、适用场景、使用限制、示例。这一层是给“模型”看的模型通过它判断当前任务是否命中这个技能。描述写得好不好直接决定了技能被选中的准确率。第三层是输入输出契约包括参数定义、返回格式、错误码、超时设置。这层是给“运行时”看的相当于技能和Agent之间签了一份协议谁也不能越界。第四层是实现逻辑也就是真正的代码可以是一个函数、一个API调用、一段外部脚本或者一个子Agent。这层是给“执行器”看的。把这四层放在一起一个技能就是一个独立发展的单元描述变了说明技能的适用边界变了输入输出变了说明接口契约变了实现变了说明内部逻辑变了。每一层都可以单独做版本管理也可以单独测试。这个结构比裸function call多出来的关键能力是“可发现性”和“可管理性”。裸function call里模型看到的是一堆平铺的函数签名而skill模式下模型看到的是一个“按需展开的目录”只有被判定为相关的技能才被加载进上下文。2.3 边界划分什么应该做成skill什么不该做技能化不是银弹也不是所有能力都适合拆成技能。我踩过一轮之后总结了一个粗略的判断标准做成技能的能力需要同时满足“高复用”“边界清晰”“有稳定契约”三个条件。高复用是指这个能力会被多个场景、多个Agent重复使用。比如文本摘要、日程查询、数据格式化这种能力做成技能是划算的。反过来那种只属于某一个Agent、只为某一次活动写的临时逻辑做成一等技能反而是负担。边界清晰是说能力能用一个明确的输入输出契约包围起来。比如“翻译文本”输入是文本和语言输出是译文边界极清晰。但比如“理解用户意图”这种能力输入输出模糊做成技能会导致模型不知道怎么选还不如留在中央逻辑里。有稳定契约是指频繁调用、对外接口相对固定。如果某个能力接口天天变做成技能后每次改动都要重新做回归成本反而不如做成普通代码模块。还有一种情况不建议做技能那种“只有一行逻辑、模型看一眼描述就能做”的能力。比如“把字符串转大写”这种做成技能纯属给系统增加噪音让模型多一次决策。3. 技能库的落地设计元信息、信号格式与动态加载3.1 技能清单文件用YAML把能力暴露出来我们在落地时第一个做的东西是一个技能清单文件。每个技能目录下有一个YAML声明文件类似这样id: calendar.list_events name: 日程列表查询 version: 2.3.0 description: | 用于查询用户在指定时间范围内的日程事件。当用户询问“我明天有什么安排” “本周会议有哪些”时使用。不能在未指定时间范围时猜测先向用户确认。 trigger: intents: [schedule.query, calendar.list] entities: [date_range, user] input: date_start: type: string description: 开始日期ISO 8601格式如 2025-06-01 required: true date_end: type: string description: 结束日期含当天 required: false user_id: type: string description: 要查询的用户标识默认为当前会话用户 required: false output: schema: calendar_event_list error_codes: [EVENT_FETCH_FAILED, INVALID_DATE_RANGE] examples: - input: {date_start: 2025-06-01, date_end: 2025-06-07} output_summary: 返回该用户本周内的所有日程按开始时间升序排列这个文件是整个技能库的地基。每次Agent启动或者技能库更新框架会扫描所有技能清单建立一张“技能索引表”。这个索引表只保留技能ID、名称、一句话摘要、版本号、依赖关系等轻量信息常驻于框架内部不会全部塞进模型上下文。只有模型判定某个任务可能命中某个技能时框架才会把那个技能完整的描述和实现加载进去。3.2 输入/输出信号给模型一个确定的“接口契约”技能描述里面最容易忽略的是输入输出契约的严谨程度。很多初版技能只写了“查询日程”没有明确参数格式结果模型自由发挥传参格式五花八门技能实现里要做一堆防御性解析。我的建议是把输入输出契约当作前后端接口来设计而且要有明确的错误码。每个技能都定义清楚哪些参数必填哪些可选时间格式统一到什么标准列表类型的返回结果最多多少条上游API失败时抛什么错误码。这些契约写清楚之后模型的参数生成成功率会明显提升因为模型看到的不是一句笼统的功能描述而是一份清晰的填写说明。契约还要考虑“无法满足条件时的行为”。比如用户问“下周有什么安排”但我能拿到的日程数据只覆盖了工作日那技能应该返回一个明确的提示还是补上周末的空数据这类边界规则必须在技能描述中写清楚否则模型会自己发挥。把边界行为写进技能是对模型决策空间的收束也是稳定性的来源。3.3 动态发现与加载不要让Agent把技能库整个塞进上下文技能库的规模上来之后一个核心工程问题就出现了运行时到底怎么让模型知道该用哪个技能这里最忌讳的做法是把所有技能描述平铺到系统提示词里。我们早期试过技能数量到40个以上时描述本身就吃掉大量token而且模型选技能的准确率急剧下降。技能描述之间有语义重叠时尤其明显——比如“查天气”和“查空气质量”两个技能仅看名称好像有点像模型就经常选错。我们最终的方案是两阶段加载。第一阶段是粗筛框架根据当前对话的意图、实体、关键词先从技能索引里筛出候选技能一般控制在3到5个。第二阶段才把这些候选技能的精简描述描述会被压缩成一两句话注入模型上下文让模型做最终选择。两阶段的好处是大部分不相关内容根本不会进入上下文模型的选择空间被限制在一个很小的范围内准确率自然稳定。这里要特别提醒粗筛器本身也是一段逻辑它的质量直接决定技能库的效果。我们最开始用规则匹配效果一般后来改成轻量分类模型准确率和召回率都提升明显。粗筛器做得好模型的选择压力会变得很小粗筛器做得烂再好的技能描述也白搭。4. 多技能编排与运行时隔离从demo到可上线的关键一跃4.1 同一个Agent一次该用多少技能注意力预算技能化解决了“能力描述过长”的问题但引入了另一个问题一次任务可能涉及多个技能这些技能之间怎么编排开始的时候我们试着让模型“自己决定调用顺序”结果很酸爽。比如一个任务需要“先查日程再定会议室再发通知”模型经常跳步或者把三个技能的调用参数一次性猜完然后前一步的结果还没拿到就开始传后一步的参数。类似这种“串行依赖任务”模型很难凭空推理出正确的数据流顺序。后来我们引入了一个概念注意力预算。每次任务允许模型最多感知N个技能我们是N5每个技能也限制只能向模型暴露必要的信息。超出预算的能力必须通过“技能编排图”来提前定义而不是靠模型自由发挥。换句话说复杂任务不是让模型去一步步探索技能而是先有一个编排图把技能之间的依赖关系和数据流定义好模型在编排图内做选择。4.2 技能间的冲突与路由同名动作、依赖顺序技能多了以后冲突问题谁也逃不掉。最常见的冲突有两种语义重叠和同名动作。语义重叠就像我前面说的查天气和查空气质量两个技能都匹配“今天空气怎么样”这句话但用户真正想要的只有一个。我们的处理方法是给技能描述加负向条件明确写清楚“什么情况下不要使用本技能”。比如查空气质量技能要写明“仅关注温度、降水、风力时不使用本技能”。负向条件能显著减少模型错选。同名动作则更隐蔽。比如“创建日程”这个动作日历技能有任务管理技能可能也有两个技能同时被粗筛选中模型到底调哪个我们的做法是引入技能路由规则根据实体类型和上下文优先级来决策如果用户提到“会议”优先走日历技能如果提到“待办”走任务管理技能两者都提到了就生成一个组合任务而不是让模型挑。依赖顺序问题也很典型。有些技能必须先于另一些技能执行比如“查天气”必须在“安排户外活动”之前完成因为后者要根据天气决定活动建议。我们为此给技能定义前置依赖框架在编排时检查技能是否具备执行条件不满足就回退给模型去补充信息或调整顺序。4.3 运行时安全与隔离技能证明、崩溃回退与审计技能不只是代码它还可能是一个外部API调用、一个脚本甚至一个子Agent。这意味着它带来的风险等级比纯prompt时代高得多。我们在生产环境强制了三件事。第一件事是技能权限边界。每个技能都声明自己需要什么权限访问哪个数据域、调哪个外部接口、能否写操作、能否读取用户隐私字段。运行时会按最小权限原则给技能分配令牌技能拿不到超出权限范围的资源。比如“查日程”技能可以读日历但不能改写可以读用户名但不能读手机号。这种隔离让单个技能的安全问题不会扩散到整个系统。第二件事是崩溃回退。技能执行失败时不能把原始堆栈直接抛给模型否则模型会被错误信息带偏。我们的做法是每个技能都有标准化的错误输出框架统一截获异常并转换成错误码再决定是重试、换技能还是询问用户。有些技能还内置了降级策略比如主API超时了自动切换备用数据源再试一次两次都失败才把错误码抛出来。第三件事是审计日志。每个技能调用都记录调用时间、输入参数、输出摘要、耗时、token消耗。这是上线后调优的基础。比如我们发现某个技能调用成功率突然下降通过审计日志能定位到是输入参数分布变了还是上游API变更了。没有审计日志做技能调优等于闭眼开车。5. 实测中的意外与调优技能拆分、命名冲突与回归验证5.1 技能拆错了要么太粗要么太细技能粒度是我在整个项目里花时间最多的地方也是最容易返工的部分。拆太粗一个技能内部被塞进了太多分支逻辑描述很难写清楚模型经常因为描述歧义选不对。拆太细技能数量爆炸粗筛器的压力变大系统复杂度也上去不少技能还属于无效复用。我总结了一个比较实用的粒度判断方法把技能描述写出来后自己站在模型的角度只看描述能不能在一句话内说完“什么时候用”和“返回什么”。如果这句话需要加两三个转折说明粒度太粗应该继续拆。反过来如果这个技能的使用场景和另一个技能高度重叠描述很难写出区分度说明可能拆得太细了。我们后来对“日程”这块做过一次重构最初是“日程管理”一个技能内部带增删改查全部逻辑模型经常搞混。后来拆成“日程查询”“日程创建”“日程修改”“日程删除”四个动作性技能外加一个“订会议室”技能描述清晰度大幅提升模型选技能的成功率从82%升到96%。5.2 描述写不好模型永远选不中技能描述是模型选择的第一依据但很多人把精力都花在内部实现上描述随手一写。这是一个非常大的误区。我们的经验是技能描述要分三个部分来写触发条件、使用限制、典型示例。触发条件要具体必须包含用户可能说出的真实句式和场景而不是抽象的功能描述。使用限制要写边界尤其是“不要何时用”。典型示例要真实把一两个输入输出样例写进去模型对样例的敏感度远高于抽象的规格说明。另外一个重要经验是描述要面向模型而不是面向开发者。我们早期写技能描述用的是开发规范语言“本模块用于处理符合RFC 5545规范的日程数据解析”。模型看到这种描述基本等于没看到因为触发它决策的是“用户问明天几点开会”这种自然语言。后来全部改成用户视角的描述“用户询问日程安排、会议时间、空闲时间段时使用”。效果立竿见影。描述还需要定期维护。用户话术会变新功能会引入新场景一个技能如果描述里的示例已经过时模型的选择准确率就会下降。我们把技能描述纳入了和代码一样的评审流程每次改动都触发一次回归测试而不是“描述随便改改就行”。5.3 回归用例每个技能都要有“金样例”技能库上线之后我强烈建议给每个技能维护一组回归用例——我们内部叫“金样例”。每个技能至少准备10条以上覆盖不同分支的输入输出样例其中要包括正常请求、参数缺失、参数越界、超时、权限不足等场景。回归用例最大的价值是让重构成为可能。没有金样例之前改动一个技能描述我们只能靠肉眼观察几个对话看看效果不科学也不可重复。有金样例之后每次技能改动都自动跑一遍全量样例准确率和覆盖率一目了然。哪个技能退化、哪个场景回归立刻就能发现。我们还做了一个补充操作从真实对话日志中挖掘失败样例定期补充进回归集。生产环境里模型选错技能、参数传错格式的真实case比人为构造的样例更能暴露问题。这种“线上反哺线下”的闭环是技能库质量持续提升的关键。最后说点个人体会这套agent-skills的思路真正跑通我们大概花了两个多月期间推翻了两版设计。最大的体会是技能化不是一个“框架问题”而是一个“工程问题”。框架给你提供的是技能的壳但技能的颗粒度、描述质量、契约设计、回归机制每一件都是需要自己动手磨的。不要指望某个现成工具能替你解决这些工具只是基础设施真正决定Agent上限的还是能力单元本身组织得好不好。如果你正在做的Agent开始出现提示词越加越长、工具选择越来越飘的情况不妨试试往技能化的方向重构先拆一个高复用域做实验跑通后再逐步铺开。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询