首页 / 资讯中心 / 文章详情

Agent Skills实战:从模型能力到技能仓库的工程化设计

Agent Skills实战:从模型能力到技能仓库的工程化设计 ★ FEATURED ARTICLE
业界这两年聊 Agent聊到最后都会落到一个词上agent-skills。模型本身越来越聪明但真正把 Agent 从demo 里很能打推向生产环境真能用的恰恰是这一层平时不太容易被注意到的技能体系。我也踩了不少坑从最初的暴力 prompt 堆砌到后来老老实实做技能仓库的设计中间的过程值得拿出来聊聊。如果你正准备搭一个 AI Agent或者你已经在做了但总觉得效果不稳定、任务一复杂就翻车这篇文章会适合你。我会从为什么光有模型不够讲起拆解技能到底包含哪些层面再给出一套可以直接落地的技能仓库设计思路最后分享几个真实踩坑案例和排查方法。1. agent-skills不是模型能力是模型的手脚先澄清一个容易混淆的概念很多人觉得 Agent 的能力上限取决于背后的 LLM模型强则 Agent 强。这话对了一半。模型负责的是脑但 Agent 要完成真实任务还需要手和脚——而 agent-skills 就是这套手脚。1.1 一个反直觉的实验结果我做过一个对比实验同一个模型当时是 GPT-4 级别的 API配了两套不同的技能方案去处理同一个任务——整理一份竞品分析报告。第一套方案我只在系统提示词里写你是一个资深市场分析师给了大量关于报告结构、分析框架、写作风格的指令。结果模型确实写出了一篇结构完整的报告但里面的数据全部是编的时效性为零行业动态靠的是训练数据里的旧知识。第二套方案我给它注册了四个技能web_search、web_fetch、extract_table、markdown_render。模型在生成报告之前自动调用了搜索技能去拉最新资讯再用抓取技能打开几个具体网页提取表格数据最后整理成报告输出。同一颗大脑产出质量天差地别。这就是 agent-skills 的核心价值它决定了模型能不能把思考变成行动把知识变成结果。1.2 技能的三个层次工具、流程、判断在实际工程里我习惯把 agent-skills 拆成三个层面来理解工具层Tool Layer这是最基础的一层。模型通过外部工具与真实世界交互比如搜索引擎、数据库查询、API 调用、文件读写、代码执行。工具层解决的是模型会想但不会做的问题。流程层Workflow Layer单个工具能做的事情有限真实任务往往需要多个步骤。流程层把若干工具调用编排成一个完整流程比如搜索 → 抓取 → 提取 → 总结就是一个最小的可复用流程。这一层解决的是单步会做但多步不会串的问题。判断层Judgment Layer这一层最容易被忽略却最影响体验。它负责决定什么时候该调用工具、该调用哪个工具、怎么根据工具返回结果调整下一步计划。我见过很多 Agent 死在判断层——明明有计算器这个技能它偏要心算明明有搜索技能它偏要凭记忆瞎编。判断层的本质是决策逻辑它决定了技能能不能被正确使用。理解这三个层次后面的技能仓库设计才有抓手。2. 大规模使用前的关键一步把技能当作产品来设计单个技能能力有限给 Agent 配几十个技能做真实业务的时候技能本身就需要当作产品来设计。这不是简单地把函数注册进列表而是要对技能做拆分、边界定义和体验打磨。我在这块走过弯路最初只是把一堆函数一股脑堆给模型结果效果一塌糊涂。2.1 技能的单一职责与命名规范给 Agent 设计技能一个特别容易忽略的原则是技能要小而专命名要有语义。举个例子我曾经设计过一个大而全的process_data技能把数据清洗、格式转换、统计分析全塞进一个函数参数多达十几个。模型调用的时候经常搞错参数而且因为一个函数干太多事返回结果的格式也很难统一。后来我把它拆成了clean_csv_data、convert_json_to_table、compute_statistics这三个独立技能每个技能参数不超过四个返回格式明确。模型的选择准确率立刻上来了。原因也简单LLM 在工具选择时需要根据技能的名字和描述做语义匹配。大而全的技能名含糊描述含糊模型就拿不准该不该用、怎么用。命名规范这条我现在执行得很严格动词开头 明确对象 可选场景标签例如search_wiki、send_slack_message、deploy_docker_service。描述统一用当用户需要 XXX 时使用该技能完成 YYY返回 ZZZ的句式。2.2 技能之间的边界与上下文传递真实业务中技能之间经常需要协作。比如数据处理流程里fetch_data的结果要传给clean_data再传给analyze_data。这里的关键问题是技能之间的数据通过什么来传递我用过两种方式各有优劣管线式传递每个技能的输出直接作为下一个技能的输入参数在调用链中传递。这种方式效率高但链路长了以后中间某一步出错会导致整个链路断裂且很难排查。黑板式传递Blackboard Pattern每个技能把结果写入一个共享的上下文存储区后续技能按需读取。这种方式灵活但要求设计好存储区的数据格式和访问权限而且模型对数据的理解成本更高。我自己的经验是在 Agent 场景里混合方案最舒服。短链路的固定流程用管线式涉及多技能可选、多路径尝试的复杂流程用黑板式。关键是把中间数据统一成结构化格式比如标准 JSON并明确标注数据来源技能与生产时间方便后续步骤追溯。2.3 技能的可观测性是保底项技能调用不像代码函数调用出了问题你很难一眼看出来。所以设计技能时可观测性必须前置而不是事后补。我现在每个技能在被调用时会自动记录三类信息输入摘要模型传入了什么参数是否涉及敏感数据。执行状态成功、失败、超时失败的具体原因。输出摘要返回结果的长度、类型、关键字段。这些日志会汇总到统一的追踪面板。有一次线上 Agent 频繁报错我看着追踪面板发现web_fetch技能经常超时但 Agent 在超时后重新调用的不是同一个技能而是换了一个不合适的替代技能导致结果质量下降。没有日志面板这种问题很难快速定位。3. 一套可直接上手的技能仓库工程结构技能多了之后就不能再零散地在代码里堆函数了。我推荐用仓库化的方式管理技能类似前端组件仓库的思路。下面这套结构我跑了大半年比较稳定。3.1 技能仓库根目录结构我现在的技能仓库大概是这样的skills/ ├── registry/ # 技能注册中心 │ ├── index.json # 所有技能的注册索引 │ └── categories.yaml # 技能分类及权限标签 ├── core/ # 核心通用技能 │ ├── web_search/ # 一个技能一个目录 │ │ ├── skill.py │ │ ├── schema.json │ │ ├── description.md │ │ └── tests/ │ ├── web_fetch/ │ └── data_extract/ ├── domain/ # 业务领域技能 │ ├── competitive/ │ ├── finance_report/ │ └── customer_service/ └── shared/ # 共享工具、上下文存储、日志模块 ├── context_store.py └── observability.py每个技能目录下必须有schema.json和description.md前者给模型看决定能否正确调用后者给开发者看方便维护与审计。这种双文档分离其实挺重要——给模型看的说明要精简直接给开发者看的说明要详细完整两者不能混在一起。3.2 schema.json 的写法要点schema.json本质上是给 LLM 看的函数签名。这里我吃过不少亏总结几个关键点。第一参数描述必须写人话不能只写类型。比如{ name: web_search, description: 搜索互联网获取最新信息。当用户询问实时数据、最新动态或需要验证事实时使用。, parameters: { type: object, properties: { query: { type: string, description: 搜索关键词建议使用具体名词和短语而非自然语言长句 }, max_results: { type: integer, description: 返回的搜索结果数量默认5最大10 } }, required: [query] } }注意description里的当用户询问实时数据...这句这不是废话。它给模型提供了技能调用的决策依据从语义上告诉模型什么场景用我。很多 Agent 该用工具而不用工具就是因为技能描述里只写了功能没写适用场景。第二参数的取值范围和默认值必须写清楚。模型是概率系统你不限定范围它就自由发挥。上面的max_results我明确写了默认5最大10模型在不确定时就会倾向使用默认值而不是传一个奇怪的数字。第三required 数组要精简。只有真正必须的参数才放进required可选参数全部放到properties里并写明选填与默认行为。这能显著降低模型因参数缺失而报错的概率。3.3 description.md 别写成论文给模型看很多人在写技能描述的时候恨不得把函数的内部实现都写进去。我建议千万别这样。模型消费的描述文本会占用上下文窗口描述越冗长模型做决策时的信噪比越低。我的一贯做法是description.md控制在两百字以内包含四块内容技能一句话定位它能干什么。典型使用场景什么时候该用它。边界声明什么时候不该用它。与相似技能的区别避免模型选错。举个例子# web_search 技能描述 定位通过搜索引擎获取互联网最新信息覆盖新闻、文档、数据页面。 典型场景用户询问最新新闻、行业动态、数据验证、时效性信息。 边界无法访问需要登录的页面无法解析纯 JavaScript 渲染的内容。 区别与 web_fetch 配合使用——search 负责找线索fetch 负责抓内容。这段描述既能让模型快速理解技能定位也能避免它把web_search和web_fetch搞混。3.4 版本的隐性坑技能本身就是程序代码必然面临迭代。但技能版本管理有个独特问题Agent 在运行时是动态选技能的你更新了某个技能正在执行的会话任务可能依然在使用旧版本逻辑。我的解决办法是给技能加版本号并且在调用时显式记录版本。在 index.json 中{ skills: [ { name: web_search, version: 2.3.0, entry: core/web_search/, enabled: true, tags: [search, web, core], permission: standard } ] }当模型调用技能日志会留下版本信息。一旦新版技能引入问题我可以快速回退到上一个稳定版本同时批量回放受影响的会话看哪些任务被污染了。不夸张地说这个设计帮我避免过好几次线上故障。4. 踩坑实录Agent 明明会技能为何就是不用给 Agent 配了一套技能之后你会发现一个让人抓狂的现象模型在有的场景下宁可自己瞎编也不调用你精心准备的工具。我在这个坑里蹲了很长时间最终定位出四个高频根因而且这四个原因每个都有对应的排查手法。4.1 根因一技能名和自然语言习惯不符第一个项目里我管发送邮件的技能叫email_sender_v2_fast。模型在需要发邮件时经常绕开这个技能直接写我已经通过邮件发送了——它根本没找到这个技能。问题出在技能名需要承载语义索引的功能。太长的名字、含版本号的名字、非动宾结构的名字在模型做 tool selection 时都容易失焦。尤其当技能列表里有几十个选项时命名糟糕的技能基本等于不存在。我自己后来的排查手法是把所有的技能从 prompt 里摘出来随机让模型看一段用户对话让它几秒内判断该调用哪个技能。如果模型犹豫了或用了几秒才反应说明这个技能的名字或描述与自然语言习惯不匹配。这个测试方法简单有效强烈建议你在新技能上线时跑一遍。4.2 根因二描述里只有是什么没有什么时候用另一个高频问题是技能的 description 写成了函数注释风格比如Send an email。模型看到之后只知道这个工具存在但不知道什么时候应该激活它——尤其在用户表达比较口语化、迂回的时候比如帮我催一下张工的进度。这句口语里没有出现邮件两个字但如果技能描述里写清楚了当用户需要与同事沟通、催办、同步信息时可以使用 email_sender 技能模型就能完成从意图到工具的映射。描述里只有是什么而没有什么时候用等于只给了一半信息。写技能描述的时候我现在的习惯是用场景化语言明确写当用户需要XXX时使用。仅仅是前面那个例子就让我项目的技能调用率提升了三成以上。4.3 根因三参数 schema 过严导致的报错连锁第三个坑schema 太严格。我曾在某个技能里把date字段设计成严格的YYYY-MM-DD HH:mm:ss格式结果模型在不确定具体时间时要么不敢调用技能要么反复报参数校验错误。在 Agent 系统中一次工具调用失败带来的连锁反应远大于普通程序中一次函数报错。因为模型会把失败的日志读回去重新决策失败越多上下文越混乱后续步骤越容易崩。后来我做了两个重要调整。一是参数校验调整成宽容输入 内部转换模型传什么格式先接收再在函数内部统一解析。比如日期字符串进来先用一个解析器尝试多种常见格式不行再报错。二是给关键参数提供枚举值和建议值模型不确定时靠这些兜底。4.4 根因四模型上下文里的技能选择压力还有一个隐蔽的原因——技能数量过多时模型的选择准确率会断崖式下降。我把技能仓库扩充到五十多个技能后调用准确率不升反降。这可不是玄学。给模型塞了几十个工具后工具选择就变成了一道高难度分类题模型在大量相似选项中犹豫出错率必然上升。解决思路是分层路由——先用一个轻量级路由模型判断当前任务属于哪个领域再把该领域的十几个技能传递给主模型。这个思路跟检索增强生成里用检索缩小信息候选集是一个道理。5. 技能编排的进阶玩法从会调用到会编排技能本身设计好了Agent 的能力上限又从单次调用的准确性转移到多次调用的编排能力上。这一层玩好了Agent 才真正具备解决复杂任务的价值。5.1 流水线编排把固定流程固化为模板很多真实业务任务本质上是有固定流程的。拿竞品分析为例搜索素材 → 抓取详情页 → 抽取关键数据 → 汇总对比 → 生成报告初稿。这段流程每次执行都让模型临场发挥效果不稳定且耗时更好的做法是固化为一个编排模板。我在技能层之上加了一个pipeline概念每个 pipeline 是一组技能的有序调用且中间数据格式已经预先定义好。模型只需要负责各个步骤里的局部决策比如搜索关键词应该用什么而不必操心跳过哪一步、怎么传数据。固化流程之后整个任务的执行时间平均缩短了约百分之四十稳定性提升也很明显。这里也要注意pipeline 的每个步骤之间要保留合规的用户确认节点。尤其涉及发送消息、提交表单、发起支付这类有实际影响的动作必须留出人工确认的窗口不能全自动跑到底。5.2 动态编排给模型一张技能的地图不是所有任务都适合固定流程。复杂、开放的任务还需要模型动态规划。我管这个叫「技能地图」在上下文里给模型的不只是技能列表还包括技能的分类、先后次序建议、组合禁忌。一段精简的技能地图提示可用技能按功能分四类 1. 信息获取类web_search, web_fetch, db_query 2. 数据处理类clean_data, convert_format, compute_stats 3. 内容生成类draft_report, summarize, translate_text 4. 行动执行类send_email, create_ticket, deploy_service 使用优先级遇到问题先走信息获取再处理数据最后生成内容。 行动执行类技能必须经过用户确认后才能调用。 组合禁忌db_query 不能在未认证的环境使用send_email 和 create_ticket 不要同时调用。这张地图的作用是主动引导模型的编排方向又不至于把每一步都锁死。从效果上看模型的规划路径比纯自由发挥时要稳健不少。5.3 技能冲突处理多个技能都能解决同一任务时进阶场景里还会遇到技能冲突——比如web_search和db_query都能回答去年销售额是多少但检索结果可能不一致甚至结论相反。我采取的策略是给技能加上可信度提示{ name: db_query, description: 查询内部数据库获取结构化业务数据权威性高优先于互联网搜索。当用户询问内部经营数据时必须使用本技能而非 web_search。 }同样的场景web_search的描述中明确写上互联网公开信息权威性低于内部数据库。通过描述里的优先级提示让模型在冲突场景下有决策依据。不过话说回来描述里的优先级提示并不能百分之百保证模型选对所以在日志追踪面板里我会把同一问题、不同技能回答不同结果的情况专门标记成异常样例攒多了以后分析到底是哪个环节出了偏差。6. 技能的下一个版本自我进化的空间聊到最后如果你已经有了一套稳定跑通的技能库自然会想一个问题技能能不能像代码一样被持续优化甚至让 Agent 根据运行数据改进自身。我目前在实验的方向有三个一是基于运行日志的自动技能优化。每次技能调用都被记录。如果某些技能长期未被调用它要么是垃圾技能要么是描述有问题导致模型没识别出来。定期分析日志里的调用频次、失败率、纠偏率据此调整技能的描述和参数。二是把沉淀下来的成功调用路径固化为新技能。比如我发现某类任务模型每次都要走 5 步才能完成那就把 5 步固化为一个一步完成的新技能既省 token又减少出错面。这块在电商客服场景里很实用——商品推荐、退换货引导这些高频固定路径完全可以沉淀成复合技能。三是基于合成数据做技能调优的可行性验证。比如用大模型生成问题-期望技能-期望参数的合成样本再拿这些样本去微调轻量路由模型让路由本身更聪明。这个方向我还在验证阶段但初步感觉潜力不小因为它能把对主模型能力的依赖逐步转移到可控的小模型上。每次想到这些我都觉得 agent-skills 工程化的空间其实比模型本身要大。毕竟模型能力是公共资源而技能体系才是你能长期积累、持续沉淀的核心资产。
阅读完成 · 觉得有帮助?
咨询建站