ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

告别提示词膨胀:为Agent构建可插拔技能库的实战方法论

告别提示词膨胀:为Agent构建可插拔技能库的实战方法论 做 Agent 项目做久了你多半会撞上同一堵墙模型越强你越想把所有本事都塞给它结果提示词越写越长长到模型开始选择性失忆长到你都不敢随便改一个标点。我自己是在第三次把二十多条技能说明硬塞进 system prompt 之后彻底放弃这个做法的转而搭了一套独立的 agent-skills 技能库——把能力拆成一格一格可插拔的技能单元让模型按需调用、按需挂载。这篇文章就把我踩过的坑、最后的落地结构和完整方法论一次讲清楚适合正在做智能体应用、被工具管理和提示词膨胀搞到头大的开发者和产品经理。1. 为什么 Agent 需要一套独立技能库从提示词膨胀说起1.1 我最初的做法把技能写成行为准则塞进提示词做第一个 Agent 的时候我并没有技能库这个概念。当时的需求很朴素让模型根据关键词调用一个内部检索接口。于是我在 system prompt 里写了一段说明大意是当用户提到某某关键词时调用某某接口然后把返回结果整理成列表。第一个版本挺好用第二个、第三个功能也照这个思路加进去。三个月后提示词已经攒到四千多字。模型开始出现两个很典型的毛病一是把较早写进去的技能规则忘掉用户问得直白它都反应不过来二是遇到两个语义相近的技能说明时开始随机抽风今天调用 A明天调用 B完全不可控。更糟糕的是每轮对话都要把所有技能说明完整发一遍单轮 token 成本肉眼可见地上涨。后来我意识到这本质上不是一个提示词怎么写的问题而是架构问题。大模型每一轮对话都像是一个全新的人接手工作他唯一的任务依据就是当前上下文。如果我让他同时记住几十条行为准则他一定会按照自己的偏好挑选一部分、忽略一部分。而技能库要做的就是把这部分记忆从提示词里搬出来让模型只在他真正需要某项能力的时候才看到对应的完整说明。1.2 技能、工具、知识与提示词的边界到底怎么切在动手搭技能库之前我先把概念理了一遍。因为如果连什么是技能都说不清后面写出来的规范一定很混乱。我的划分逻辑是这样的提示词是常驻记忆负责设定角色的基本立场、输出风格、默认行为边界知识是被动记忆比如文档、FAQ、数据模型在需要时查询没有查询动作就不会主动使用工具是外部动作接口比如发请求、执行代码、写文件模型通过调用工具改变外部世界技能是可复用的操作程序它是一段结构化的指令告诉模型在特定场景下按什么步骤执行任务并且可以组合工具完成目标。举个例子让模型调用一个发邮件的接口这是工具让模型知道当用户要求批量发送周报邮件时先读取收件人列表再逐封生成正文、调用发信接口、最后汇总失败项这是技能。技能的特别之处在于它包含决策逻辑但又是独立、可命名、可版本化的单元。| 维度 | 提示词 | 知识库 | 工具 | 技能 | | 作用 | 设定行为立场 | 提供事实 | 执行外部动作 | 编排操作流程 | | 是否常驻上下文 | 是 | 按需检索 | 按需调用 | 按需加载 | | 能否单独评测 | 难 | 可 | 可 | 可 | | 典型故障 | 上下文膨胀 | 检索不准 | 调用失败 | 命中不准 |1.3 技能库真正要解决的三件事复用、评测、降级想明白边界之后我给自己定了三个目标后来发现这三个目标其实就是技能库存在的全部理由。第一是复用。同一套生成结构化会议纪要的技能既可以在客服 Agent 里用也可以在内部分析 Agent 里用不需要复制粘贴几十遍 prompt。只要技能文件是可挂载的任何 Agent 都能在需要时加载它。我在实际项目里建立了两个技能库一个是通用库放每个人都可能用到的技能另一个是领域库按业务线拆分各团队自己维护。第二是可评测。提示词没法单独跑一遍测试但技能可以。我给每个技能都配了独立的任务样本分布在触发正确性、步骤完整性、输出格式三个维度上。每当有人修改技能我就跑一遍回归看有没有把别的场景带崩。这个做法听起来简单但确实帮我拦住了绝大多数悄悄劣化。第三是可降级。真实环境里没有哪个能力是永远可靠的。技能库里必须有一条明确的降级路径A 技能失败时是尝试 B 技能还是退回主 Agent 的基础回复。我在技能字段里专门配置了 fallback目的就是让失败的时候系统有一个体面的出口而不是让模型硬着头皮胡编。2. 技能单元的接口设计把能力变成可被调用的规格2.1 一份技能清单应该包含哪些字段确定要搭技能库之后我参考了不少开源 Agent 项目和内部工具的经验最终把技能单元收敛成下面这套字段。核心思路是让技能既能让模型读懂也能让人维护。name: weekly_report version: 1.3.0 description: 根据群聊/邮件/任务系统记录生成周报。适用于用户要求总结本周工作生成周报周报帮我写一下等场景。 trigger: - user_says: [周报, weekly report, 本周工作] - explicit_mention: true instruction: | 1. 先调用 message_finder 拉取最近 7 天与当前用户相关的记录 2. 将记录按【项目】【已完成】【进行中】【阻塞】四类分组 3. 每组输出不超过三条每条保留关键结果数字 4. 使用 markdown 表格呈现最终周报。 parameters: since: type: string default: 7d description: 统计起始时间偏移。 audience: type: enum values: [leader, team, self] default: leader output: format: markdown_table required_fields: [summary, progress, blockers] fallback: basic_agent_reply这套字段里name 和 description 是模型能否正确认识这个技能的关键instruction 是执行层面trigger 和 fallback 是路由和容错。我在实际运行中还加过不少字段比如 tags、author、last_validated但最终归档时保留的就是这份精简集合因为字段越多越容易在维护时互相打架。2.2 描述文本的写法先让它能被检索到再谈执行我踩过最直接的坑就是 description 的写作方式。一开始我习惯把技能完整步骤都写进 description结果发现模型反而抓不住重点。因为技能加载的机制是先由路由层或模型根据用户输入检索技能检索本质上是语义匹配一段几百字的 description 会让相似度计算变得非常模糊。后来我把 description 压缩成两句话第一句说明技能做什么第二句说明在什么场景下激活最多再补一个不要使用的场景。例如上面这个例子description 就明确写了适用于用户要求总结本周工作、生成周报。那些完整的操作步骤全部放进 instruction 字段只在技能被命中之后才被加载进上下文。改完这个之后我的技能命中率明显上升上下文开销也小了很多。2.3 目录结构、版本号与挂载规则维护者友好也很重要技能库不是一堆文件的随便堆叠目录规范决定了一个团队能否长期协作。我的习惯是在仓库里用这样一个结构skills/ common/ meeting_minutes/ SKILL.md assets/ tests/ weekly_report/ SKILL.md tests/ internal_tools/ ...每个技能一个目录目录名是全小写、下划线分割SKILL.md 是唯一的主文件tests 目录放评测用例assets 放技能执行时可能需要的模板文件。版本号我放在 SKILL.md 头部不单独建文件因为改技能逻辑时几乎总是要同步改文档放在一起才能避免文档和代码分家。挂载规则上我倾向于给每个 Agent 配一个 manifest 文件显式声明它需要加载哪些技能而不是让系统自动扫描全部技能。自动扫描的优点是省事坏处是模型可选的技能太多路由噪音变大。经过实测单 Agent 挂载 15 到 25 个技能是比较舒服的范围再往上命中准确率会出现可察觉的下降。3. 技能编排的实战拆解单技能命中与多技能协作3.1 触发策略显式调用、隐式路由还是混合技能库搭好后另一个问题摆在面前技能什么时候被触发。我先后试过三种策略最终选择了混合方案。显式调用是指用户在对话里直接说出技能名比如调用 weekly_report 技能。这种方式最可靠但把负担推给了用户普通用户几乎不会这么说话只能作为调试手段。隐式路由是指模型根据用户输入自行判断从技能库里挑一个合适的技能来执行。这是最自然的体验但也是误命中风险最高的路径。我见过最典型的翻车是用户问这个月的数据怎么下载系统匹配到了数据导出技能结果把一份汇总报表发出去而不是原始文件。问题出在 description 没有写清楚技能的边界。混合策略是我最后用的方案系统先做一轮轻量规则匹配如果在用户输入里检测到明确的技能关键词就直接触发对应技能如果没有检测到关键词再由模型从技能列表中选择。这样既保留了自然对话的体验又给了系统一个确定的兜底。规则匹配层不需要很复杂一个关键词表加少量正则就行。3.2 一个多技能协作的例子自动周报管线技能真正发挥威力是在多技能协作的场景里。我拿周报来说单独一个 weekly_report 技能做不了什么因为它的输入来自好几个数据源。我把整个流程拆成三个技能每个技能负责一个阶段。message_finder拉取用户最近七天的群聊、邮件和任务系统记录输出结构化 JSON字段包括来源、时间、内容摘要session_digest把上一步的 JSON 记录按主题聚类生成摘要和关键结果输出分组数据weekly_report接收 digest 的输出结合用户希望的周报受众渲染成最终 Markdown 周报。这样做的好处是每一步都可以单独调试。比如 message_finder 返回的数据格式变了只需要改动第一个技能后面两个完全不用碰。我在真实项目中经常这样拆每个技能最好只做一件事超过三件事就直接考虑拆分。3.3 状态与上下文的传递技能之间不要互相扒上下文多技能协作最容易忽略的是数据传递。很多人误以为技能 A 跑完之后技能 B 自然能看到 A 的所有过程这在纯对话模型里可能是因为同一份上下文延续了但一旦技能执行涉及外部工具调用、或者上下文被裁剪它就不成立了。我的做法是每个技能输出一个明确的结构化结果最常见的就是 JSON。下一个技能只依赖这个结果不依赖上一技能的内部日志。如果一个中间结果太大比如拉回来的记录有几十条我会让技能先压缩成摘要再往下传把上下文占用控制住。这有点像是工厂流水线每个工位只接收上一工位交验的半成品不直接翻上一工位的垃圾箱。提示所有技能之间的数据契约最好集中定义在一个 schema 文件里改任何一个技能的输入输出格式时先改契约再改实现避免A 改了输出 B 悄悄坏了这类回归问题。4. 落地踩坑记录命中率下降、资源膨胀与降级策略4.1 坑description 越来越长命中率反而下降这个坑我在前文提过但值得单独复盘一遍完整链路。当时有个数据导出技能description 写得很详细包含了导出格式、导入系统、权限说明、失败重试步骤一共四百多字。上线第一周效果不错第二周开始用户反馈明明在说导出Agent 却去执行了别的技能。我的排查过程是这样的先看路由层日志发现模型确实看到过这个技能但它被排在候选列表中间偏后的位置再看模型候选排序发现数据导出与报表生成的语义非常接近最后定位到根因——过长的 description 让排序器难以抓住它最核心的辨识特征。修复方案是重写 description只保留做什么和何时触发把细节全部移入 instruction并在末尾补了一句如果用户只是要查看数据而不是生成文件请不要使用本技能。4.2 坑技能互相干扰——语义拥挤与命名冲突多个技能放在一起模型不一定能分清它们的边界。我遇到过一次很典型的冲突一个技能叫 data_query负责查询业务数据库另一个叫 data_export负责把查询结果导出成文件。表面看边界很清楚但在实际对话中用户说帮我把这个数据拉出来模型在两秒钟内犹豫不决最后随机选中一个。排查方法也很直接把两个技能的 description 并排放在一起读看它们是否共享了太多关键词。果然两个 description 都写了数据查询结果区分度严重不足。我最后不只是在文字上做了区分还调整了名称语义data_query 改成 query_raw_recordsdata_export 改成 export_records_to_file让模型看到名字就能猜到边界。这个改动看起来不起眼但效果非常明显。我后来总结出一条经验技能的命名和 description 一样都是给模型做的导航标识千万不要只图人类读起来顺口。一个内部叫了十年的历史名词在模型眼里可能和另一个技能完全撞车。4.3 坑技能失败后模型开始胡编或死循环技能执行不是百分之百成功的尤其涉及外部接口时。最让我头疼的是模型在技能失败后的行为有些模型会在报错之后假装成功有些会陷入同一技能的反复调用。前者产生虚假结果后者烧掉大量 token。我的修复是在技能调用层加了一个统一的失败协议技能返回结构化错误码同时给出下一步建议如果错误码被判定为可重试最多重试两次如果仍然失败就直接走 fallback不再让模型继续折腾。这里给所有做同类系统的同行一个建议不要在技能执行成功路径上写太多逻辑把失败处理写清楚比把成功写漂亮更重要。4.4 我踩完坑之后固定的资源管理清单复盘完所有坑之后我把技能库的资源管理收敛成下面这张清单每次新项目接入时照着检查一遍。单个 Agent 挂载技能数控制在 15 到 25 个超出就拆分 Agent 或用子 Agent每个技能 description 不超过 120 字原则是两句话一个负向场景每个技能必须有独立 tests 目录改动后必须跑回归所有技能间数据传递使用统一 schema禁止直接引用对方内部状态失败处理必须有 fallback 字段不允许无兜底挂载。5. 给技能库做体检评测集建设与迭代节奏5.1 技能评测的四个维度触发、可用、副作用、耗时技能和算法一样必须能量化验证。我在实践中把评测收缩为四个维度覆盖了技能从被选中到执行结束的全过程。触发准确率在正确场景下技能被正确命中的比例结果可用率技能执行完输出能被下一步消费的比例副作用率技能在执行过程中产生不该有的副作用的比例比如误调了权限较大的接口耗时与 token 开销一次完整技能调用的大致成本用于决定是否需要拆分或精简。这四个维度不是平级的触发和可用是基础的生死线副作用是风险线耗时是成本线。我一般先保证前两项稳定再谈优化后两项。项目早期如果直接去抠 token 成本往往会捡了芝麻丢西瓜因为技能能不能被正确选中才是所有收益的前提。5.2 最小评测集怎么建从真实日志里抽而不是自己编很多人在这一步习惯自己写测试用例我强烈建议不要。自己写的问题往往是想法中的用户而不是真实的用户。我建评测集是从线上日志里抽真实对话样本按类型去重后最少留 30 到 50 条每个技能至少覆盖五种变体。| 用例类型 | 数量 | 判断标准 | | 直接触发 | 10 | 应当命中目标技能 | | 语义等价 | 10 | 换种说法仍应该命中 | | 歧义场景 | 5 | 应当命中更合适的技能 | | 负向场景 | 5 | 不应当命中任何技能 | | 乱序混合 | 5 | 多技能场景下调用顺序正确 |这个表格是我每次给新技能建评测集时的模板。负向场景尤其容易被忽略但它其实是防止误伤最重要的部分。没有负向样本你会发现自己改完 description 之后命中率数字反而变好看了因为模型开始什么都往这个技能上靠。5.3 迭代节奏一次只改一个变量避免批量翻车技能库最大的维护风险是并行变更。团队里两个人同时改两个技能如果评测集不够强根本不知道谁的责任。我的流程是一次只改一个技能改动前先跑一遍旧评测集确认基线改完再跑一遍对比两个版本的触发准确率。任何指标下降超过两个百分点直接回滚。就这样循环几轮之后我的技能库才变得真正敢改。后来我又在 CI 里接了一个自动跑评测的脚本每次 push 技能目录的改动就会自动出这份对比报告。回归成本从原来的手动一两个小时降到了几分钟项目的可维护性彻底上了一个台阶。写到最后想分享一个体会技能库的建设没有终点每次模型版本升级、每个新业务接入都会暴露新的边界问题。别试图一次设计成完美答案先让 10 个技能跑稳再逐步扩张比一开始就铺几百个技能文件扎实得多。如果你正在被 Agent 项目的提示词膨胀和工具调用混乱折磨从一套 10 个左右的技能库开始三个月后你会回来感谢当时的决定。
返回列表