ARTICLE DETAIL

资讯详情

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

Agent Skills:给大模型装上一双会干活的手

Agent Skills:给大模型装上一双会干活的手 1. 为什么 agent-skills 值得单独拿出来做先说结论如果你正在做 AI Agent不管是基于大模型的聊天机器人、自动化工作流还是内部知识助手你迟早会遇到一个问题——模型“知道”很多但“做”得很差。我一开始做 Agent 的时候脑子里只有一条逻辑把大模型接上工具函数让它可以调 API、查数据库、读文件。跑了一轮 demo 发现模型确实会调工具但调得极其不稳定。同样一句话上午它能正确判断该调用“天气查询”工具下午它就自己编了一个工具名。更离谱的是它经常把工具的参数填错把“城市”字段填成“日期”。后来我才意识到问题根本不在“能不能调工具”而在“模型有没有一套稳定、可复用、可组合的执行方案”。这就是 agent-skills 真正要解决的问题给 Agent 装上“会干活的手”而不是只给它一堆“孤立的按钮”。你完全可以把它理解成大模型是大脑工具是四肢的关节而 Skill 是大脑里已经固化下来的“动作记忆”。你不需要每次走路都重新学习怎么抬腿——你一旦学会了“走路”这个技能后续所有需要移动的场景都会自动调用它。Agent 也一样一旦把某个高频任务沉淀为 Skill后续每次执行都省去大量重新思考和试错的成本。这篇文章会围绕 agent-skills 这个方向从设计思路、实操步骤、运行效率、常见问题四个维度展开。适合两类人看一类是刚接触 Agent 开发、正在搭第一个原型的新手另一类是已经跑通 demo、但被“模型不稳定”折磨得焦头烂额的同学。我尽量不写教科书内容全部是实际项目中踩过的坑和验证过的方案。1.1 把“会聊天”和“会做事”彻底分开我踩过的第一个大坑是以为大模型天然就“会做事”。实际上大模型唯一天然擅长的是“生成看起来合理的文本”。当你让它直接完成一个多步骤任务时它很容易漏掉中间步骤直接从第 1 步跳到第 4 步误解任务范围把“统计这个月的数据”执行成“随便看看数据格式”生成的结果格式漂移上次返回 JSON这次返回 Markdown 表格下次直接写一段散文。这背后的原因不是模型不够聪明而是任务复杂度越高模型在每一步上能分到的“注意力预算”就越少。一个大模型同时要做理解用户意图、规划步骤、选择工具、生成内容、检查结果五个任务挤在同一个上下文窗口里必然互相抢资源。agent-skills 的核心理念就是把“规划”和“执行”拆开。Skill 负责把某类任务固化成一套标准流程模型只负责“判断当前该用哪个 Skill”和“把参数填准确”剩下的事情交给 Skill 内部已经验证过的步骤去执行。这样一来模型的负担大幅降低稳定性和可预测性自然就上来了。我给你一个直觉类比让一个厨师临时发挥做一桌菜和让他按照已经写好的标准食谱做菜哪个更稳定当然是后者。Skill 就是那张标准食谱。1.2 我最初的 Agent 架构遇到的瓶颈我在没有 Skills 概念之前搭过一个内部文档问答 Agent。架构很简单用户提问 → 检索相关文档 → 把文档片段塞进上下文 → 大模型生成回答。跑起来之后第一个版本效果还行。但一旦问题变复杂比如“帮我看看这周的销售数据对比上周找出异常项并生成一封发给管理层的邮件”它就崩了。原因很直接这根本不是一个问题而是四个问题——查数据、算对比、找异常、写邮件。它需要四个不同的能力模块协作而我没有在架构层面给它们分工。正确做法应该是把“查数据”“算对比”“找异常”“写邮件”分别做成独立的 Skill再由一个更高层的路由逻辑决定串并行顺序。这相当于把一个复杂项目拆成多个可复用的子任务每个子任务都有自己的一套验证标准。这既是软件工程里的模块化思想也是 agent-skills 的实操内核。2. 拆解 agent-skills 的核心结构2.1 Skill、Tool、Workflow 到底什么关系先说清楚三个概念很多人搞混。Tool最底层的原子能力比如“查询天气 API”“读取本地文件”“执行 SQL”“发送邮件”。它一般是一次性动作不涉及复杂决策。Skill对一类任务的可复用流程封装它内部会调用一个或多个 Tool也可能包含简单的判断逻辑。比如“生成销售周报”这个 Skill内部可能需要调用“查询数据库”“计算变化率”“调用文本生成模型”三个 Tool。Workflow更上层的编排它负责把多个 Skill 按顺序或条件组合起来完成一个端到端的业务目标。用代码的粒度来类比Tool 是函数Skill 是模块Workflow 是完整的应用。没有这层区分Agent 开发会迅速变得像意大利面条一样纠缠不清。我建议你在项目初期就明确这三层边界。我自己的做法是在代码仓库里建三个目录tools/、skills/、workflows/每个目录里面再按业务域分子目录。这个成本很低但对后续维护帮助巨大。你绝对不会想在一个六个月大的项目里翻遍全部代码才能搞清楚一个技能到底依赖哪些工具。2.2 设计一份 Skill 文件的关键要素一个标准化的 Skill 定义文件应该包含以下要素技能名称机器可读的唯一标识比如generate_sales_report不要用模糊的中文描述。技能描述这是最关键的字段。它决定了大模型能不能在正确的时候想起这个 Skill。描述里要写清楚“这个技能在什么场景下用、输入需要什么、输出是什么”。输入参数 Schema明确每个参数的类型、是否必填、取值范围。这能有效防止大模型乱填参数。执行逻辑技能被调用后要做什么可以是代码、提示词模板或两者的组合。输出格式约束规定返回值结构方便上层 Workflow 继续处理。依赖项该 Skill 需要哪些 Tool、哪些外部数据源、哪些配置环境变量。我后来在把 Skills 模块化之后最大的体感是Debug 的难度断崖式下降。以前模型答错了你根本不知道在哪一步错的现在你只需要按顺序检查模型有没有选对 Skill、参数有没有填对、Skill 内部执行是否成功。每一步都可以单独看日志、单独加测试。3. 实操从一个常用的 Agent Skill 开始3.1 明确目标与输入输出拿“销售周报生成”这个场景举个例子这是几乎每个业务型 Agent 都会遇到的需求。首先明确目标输入一个时间段比如上周一到上周五输出一份结构化的周报。结构包括总销售额、环比变化、TOP3 增长产品、TOP3 下降产品、异常提示。这个目标听起来简单但第一次做的时候你很容易犯一个错一上来就让大模型直接写周报。正确做法是先拆步骤从数据库统计周期内的订单总额对比上一个周期的订单总额计算变化率按产品维度聚合找出增长和下降的前三名判断是否存在异常比如某产品销量突然下降超过 30%将以上数据交给大模型让它生成一段自然语言的周报内容。这五步中前三步是纯计算逻辑不需要大模型参与第四步是规则判断也不需要大模型只有第五步需要大模型。让大模型只干它最擅长的事情其他事情全部用确定性逻辑搞定。这就是 agent-skills 和“直接在提示词里要求模型干所有事”的本质区别。我见过太多人写 Prompt 时上去就是“你是一个资深数据分析师请帮我分析销售数据并生成周报”然后给模型塞一堆数据。结果模型分析得云里雾里甚至会把数字都加错。原因很简单统计计算不是大模型的强项哪怕 GPT-4 级别的模型在做多位数的精确计算时也会出错。它适合做的是把计算结果转写成语气自然的报告文本。3.2 写技能描述Skill Description这一步直接决定模型的调用命中率。我来展示一个我试过多次后觉得靠谱的描述模板。name: generate_sales_report description: 生成指定时间段的销售周报。 当用户提到周报销售报告业务分析等关键词时使用。 输入需要提供起止日期如果用户没有明确说明日期范围 默认使用上周一到上周五。 输出为包含总销售额、环比变化率、TOP产品排名和异常提示的结构化报告。 parameters: start_date: type: string required: true description: 统计起始日期格式 YYYY-MM-DD end_date: type: string required: true description: 统计结束日期格式 YYYY-MM-DD很多人写描述时会犯两个极端错误要么太短就写“生成报告”要么太长把内部执行逻辑全部写进去。前者会让模型误用后者会让模型选择时“挑花了眼”。我的经验是描述里只写三件事什么时候应该用这个技能、输入怎么给、输出大概长什么样。至于内部怎么实现不是模型需要关心的。另外参数 Schema 一定要严格。我见过不少项目在参数缺失时直接让 Agent 编一个参数填进去。解决办法是在描述里明确写如果用户没有提供必要参数先向用户提问确认不要擅自设定默认值业务含义重要的参数尤其如此。这一步会让你的 Agent 显得“更懂规矩”而不是“自作聪明”。3.3 写可执行的内部逻辑这里我用一个简化版的 Python 片段作为示意。实际使用中你可以用任何后端语言核心逻辑是通用的。# tools/query_sales.py 简化版 def query_sales(start_date, end_date, group_by_productFalse): 从业务数据库查询销售数据 sql SELECT DATE(order_time) as sale_date, product_name, SUM(amount) as total_amount FROM orders WHERE order_time BETWEEN %(start_date)s AND %(end_date)s GROUP BY DATE(order_time), product_name # 这里省略数据库连接与执行细节 return execute_sql(sql, {start_date: start_date, end_date: end_date}) # skills/generate_sales_report.py 简化版 def execute(start_date, end_date): # Step 1: 统计本期总销售额 current_period query_sales(start_date, end_date) current_total sum(row[total_amount] for row in current_period) # Step 2: 统计上期总销售额用于环比 last_period_total calculate_last_period_total(start_date, end_date) # Step 3: 按产品聚合计算排名 product_ranking aggregate_by_product(current_period) # Step 4: 异常检测规则 anomalies detect_anomalies(current_period, last_period_total) # Step 5: 交给大模型生成自然语言报告传入已经算好的数据 report_text llm_generate( system_promptREPORT_PROMPT_TEMPLATE, data{ current_total: current_total, last_period_total: last_period_total, change_rate: (current_total - last_period_total) / last_period_total, product_ranking: product_ranking, anomalies: anomalies, } ) return {report: report_text, data: {...}}这里第 1 步到第 4 步全部是确定性计算第 5 步才交给大模型。你注意到没有我在第 5 步把已经算好的数据整理成结构化的data传给大模型而不是让它自己去查。这样做的结果是模型永远不需要做算数它只负责把数字说得像人话。3.4 本地测试与调试技巧Skill 写完不是直接上线的必须先做本地验证。我个人会做三件事第一单独跑 Skill 的输入输出测试。把预期输入和预期输出写成一个测试用例用真实数据跑一遍确认计算结果准确。这一步只验证代码逻辑不涉及大模型。第二用不同的日期范围跑几次确认边界情况不崩溃。比如用户只给了开始日期不给结束日期系统应该报参数错误还是默认补全这个要提前定规则。第三把 Skill 接到 Agent 的提示词系统里做一次端到端测试。这次测试核心是模型能不能在正确场景下选中这个 Skill参数填得对不对。我强烈建议你把“模型选错 Skill”和“Skill 内部执行失败”分开记录日志。前者说明描述写得不够清楚后者说明代码逻辑有 Bug。两者混在一起排查效率极低。一次偶然看到某内部项目把所有 Agent 日志都塞进同一张表排查问题得像考古一样翻半天后来我坚持让每个 Skill 都有独立的 trace ID这种做法已经被我奉为标准。4. 技能运行效率与上下文管理4.1 上下文是最贵的资源Agent 运行成本的大头几乎都在大模型接口的 token 消耗上。你每往上下文里塞一段不必要的内容都是在烧钱。很多人在做 Agent 时有个坏习惯为了让模型“考虑周全”把所有相关数据一股脑全塞进去。比如生成销售周报时不仅放了汇总数据还放了原始订单表甚至把上个季度的报告模板也放了进去。结果模型输出质量没有提升账单翻了三倍。Skill 的设计恰好能缓解这个问题。因为 Skill 把大部分计算逻辑挪到了代码层只有最终的结果摘要会进入大模型的上下文。在一个真实案例里我把一个 Agent 的上下文从平均 8000 token 降到 2000 token 左右响应速度提升了一倍多成本下降了将近六成。这个收益纯粹来自“让代码做计算让模型做表达”这一条原则。但这里又有一个新问题当你的 Agent 拥有几十个甚至上百个 Skill 时模型怎么知道当前该用哪个你不可能把所有 Skill 的描述全部塞进上下文那样上下文会爆炸。4.2 正确使用“即插即用”模式解决上述问题业界有一种比较流行的做法把 Skill 列表做成一个Skill Registry然后在运行时只把与当前用户意图相关的几个 Skill 描述注入上下文。这有点像一个索引系统先把所有的技能卡片拿出来用一个轻量级分类器判断用户意图可能属于哪几个域再把对应的 Skill 详情交给大模型选择。举个具体例子。我的 Agent 里同时有“生成销售周报”“生成用户画像”“推荐营销文案”“查询物流信息”等多个 Skill。当用户说“帮我写一封给高价值用户的营销邮件”时系统不会把所有技能说明都展示给大模型而是通过关键词和意图分类先缩小到“推荐营销文案”“生成用户画像”两个候选再让大模型在这两个之间做最终选择。这套“先粗筛后精选”的双层结构让大模型每次只需在 2 到 5 个候选技能里做选择而不是从几十个里面硬挑。选择准确率会高很多因为“选择范围越小误判率越低”这个规律在大模型身上特别明显。4.3 技能组合与路由单个 Skill 能解决单个问题但真实业务很少只涉及一个技能。比如“给高价值用户写营销邮件”这个任务拆开来看涉及两个 Skill先用“用户画像分析”筛选出高价值用户及偏好再用“营销文案生成”基于画像产出内容。这两个 Skill 存在前后依赖关系需要上层 Workflow 做路由。我的经验是不要把所有组合逻辑都写死。完全写死的 Workflow 虽然稳定但缺少灵活性。最好是让 Workflow 层做“顺序编排”和“条件判断”至于每一步具体用哪个 Skill仍然让大模型根据上一步的输出动态决定。这就像一场接力赛你规划好了每一棒是跑 100 米还是 200 米但每一棒交接后由当时的状态决定具体怎么跑。反过来我也提醒你别把路由完全交给大模型。大模型在复杂分支判断上并不可靠。如果某个流程有严格的前置条件你必须在代码层做校验。我见过一个惨痛的教训某 Agent 在处理“退款申请”和“取消订单”两个操作时因为把权限校验完全交给了大模型模型误判用户身份把一个普通用户当作管理员放行了一个本不该通过的退款操作。最终他们紧急在代码层加了身份校验才止住了风险。凡涉及权限、金额、删除等高风险动作一定不能依赖大模型自行判断必须在代码层做强约束。5. 常见的问题与排查经验5.1 Agent 答非所问最常见的是用户问了一个问题Agent 却调用了一个完全无关的 Skill。比如用户问“这个月公司加班情况怎么样”Agent 却调用了“天气查询”的 Skill。你说离谱不离谱但我的实际观察里这种错乱往往就出现在 Skill 描述写得太模糊、或者候选技能列表里有两个描述高度相似的时候。排查手段很简单。先看日志里模型实际选择了哪个 Skill、置信度多少、有没有可供调用的候选列表。如果模型在选错的同时还选了正确的候选那问题多半出在候选描述有歧义。解决方法是改描述让每个 Skill 的使用边界更清晰。比如“天气查询”的 desc 明确写“用于获取某城市未来几天的天气情况不用于工作考勤”“加班分析”的 desc 明确写“用于统计员工加班时长、加班费及部门分布”。另一个容易踩坑的点是大模型有“惯性”。如果你把某个 Skill 的权重排在最前面它即便不适用也有偏差倾向去选它。所以我会在运行时把候选 Skill 顺序打乱或者给每个候选的优先级加随机扰动。这听起来很玄学但在减少“偏好固定第一个选项”这个问题上效果很明显。5.2 技能之间互相干扰当你给 Agent 加了很多 Skill 之后还会遇到一种隐蔽的问题Skill A 执行完给 Skill B 传了错误格式的中间结果。比如“做用户画像”的 Skill 输出的是一个 JSON 对象字段叫customer_level取值范围是 “high/mid/low”而“营销文案生成”的 Skill 期望输入字段叫user_tier取值范围是 “gold/silver/bronze”。两边字段命名和取值都不一样对接自然出问题。这个问题根治的办法是每个 Skill 都定义一份严格的输入输出契约并且最好有专门的 schema 校验函数。在 Skill 入口处做validate_input()在出口处做validate_output()任何一方不符合契约就立刻抛错。宁可让流程失败也绝不让脏数据往后流。这和软件工程里的接口防劣化是一个道理。我最初建设 Skill 库时因为怕加校验太麻烦想偷懒结果一个字段名不一致的问题让我排查了整整一下午。后来我把“所有跨 Skill 传递的数据必须用 schema 校验”写进了项目规范这类问题几乎绝迹。5.3 同样的问题这次答了下次不答这是大模型 Agent 项目里最让人崩溃的现象同一个输入上午运行正常下午就翻车。这种“时好时坏”的情况一般有三个来源第一个来源是大模型采样温度太高。Agent 在做关键决策时如果 temperature 设置在 0.7 甚至更高模型的输出随机性会非常大。我的做法是给“技能选择”和“函数调用”类的任务使用 temperature0 或 0.1给“文案生成”类的任务才允许较高的 temperature。简单来说风险敏感的操作要尽可能“冷”创意内容才允许“热”。第二个来源是上下文污染。如果 Agent 在多轮对话中前面轮次的历史消息一直堆在上下文里后面模型就很容易受前面的用词影响导致同样的当前轮意图被不同地解读。解决方法是定期压缩历史消息只保留最新的几轮对话或者用提取摘要的方式替代原始历史。第三个来源是依赖的外部服务不稳定。比如某个第三方 API 在高峰期的返回延迟超过 5 秒Agent 的重试机制处理不当就会直接报错。这个排查起来要结合链路追踪去看前端看起来是“模型答不出”实际上是“工具调用超时”。6. 我从 agent-skills 这条路上总结的几条经验最后分享几点个人体会不算什么惊天动地的结论但都是真金白银踩出来的。第一越早给 Agent 建立 Skill 边界后面越轻松。不要等到功能越堆越多、代码越来越乱时才考虑架构。这不是前期“过度设计”而是给后续所有功能预留一条清晰的分层高速公路。第二让大模型做表达让代码做计算让规则做校验。大模型适合生成自然语言和融合上下文信息确定性计算交给代码安全和权限校验交给硬规则。三者各司其职Agent 才不会变成“什么都能说但什么都不靠谱”的玩具。第三每个 Skill 都要有独立的 trace ID。这句话我要重复一遍因为它救过我很多次。Agent 系统不像传统单体应用一个任务会跨多个模块执行没有 trace ID 几乎没法定位问题。设立这个习惯省下的排查时间是实实在在的。第四别贪心一次只加一个 Skill。我在项目初期总想同时上十几个 Skill结果一出问题新旧功能全混在一起根本分不清。后来改成小步迭代每新增一个 Skill先单独验证再接入主流程观察一段时间稳定了再加下一个。整个系统反而上线得更快。如果你也在做 Agent并且遇到“模型表现不稳定”“加需求就崩”“代码维护成本爆炸”这类问题我的建议很直接早点引入 agent-skills 的设计思路把大模型从“全能选手”的位置上拉下来让它只做自己擅长的那一部分。你会发现系统的稳定性和可维护性会出现质的提升。这是我在自己的项目里验证过很多次的一条路径希望这篇分享能给你的 Agent 开发之路省下一些不必要的坑。
返回列表