AI Agent技能体系设计:从模糊意图到可执行动作的工程实践

发布时间:2026/9/26 8:17:35
AI Agent技能体系设计:从模糊意图到可执行动作的工程实践 我印象很深的一次经历团队花了两周时间把Agent助手从能聊天调到了能干活结果一上线就翻车——用户说帮我把上周的销售数据整理成周报系统愣是把整理数据理解成了生成一份PPT模板用户当场就炸了。问题不在模型在技能。我后来把整套agent-skills体系推翻重写了一遍稳定性和用户满意度才真正拉上来。过去一年里AI Agent从多轮对话进化到自主完成任务核心分水岭就是技能体系的成熟度。所谓技能不是模型本身的能力而是我们给Agent装配的一组可复用、可编排、可评估的动作单元——查数据库、调API、发消息、生成文档、操作浏览器每一样都是一项技能。会不会设计技能直接决定了Agent的上限也决定了它到底是个靠谱的同事还是个嘴上王者。这篇文章面向两类读者一是正在做Agent应用、但被时好时坏折磨到失眠的开发者二是想从原理上理解为什么有的Agent真的好用、有的Agent像个智障的产品负责人和技术管理者。我会把技能的定义、注册、调度、编排、评估整个链路拆开讲穿插我这几个月真实踩过的坑和最后沉淀下来的方案。先声明一点下面所有代码和字段设计都是我在生产环境验证过的不是Demo级的玩具。1. Agent技能体系的本质把模糊意图变成可执行动作1.1 一张会说话的能力清单胜过十页提示词很多团队做Agent第一步就是把系统提示词越写越长——你是一个智能助手你可以查询数据库数据库中有用户表、订单表……查的时候注意……如果用户问天气你该怎么办……。结果呢提示词越长模型越记不住越容易在关键节点上自由发挥。技能体系的核心思路完全不同不要教模型怎么做只给它一张能力清单让它知道有什么可以用、什么时候用、用完能得到什么。剩下的推理过程交给模型但动作边界由技能体系卡死。打个比方提示词像一份岗位职责说明书写得再详细员工也会有自己的理解偏差而技能体系像一套标准化的工具接口——螺丝刀就是拧螺丝的你不用担心它被拿去当锤子用。我最后的实际感受是把原来那份2000多字的系统提示词压缩成了不到300字的助手人设策略说明再把所有工具能力搬进技能注册表之后任务的完成率反而提升了三成。原因很简单模型不需要在大量互相矛盾的指令里猜用户意图了它只需要做选择。1.2 技能是LLM与工具之间的契约不是一段代码我在团队内部经常强调一个观点技能不是函数本身而是LLM与真实世界之间的一份契约。函数是给程序员看的技能是给模型看的。同一个工具如果它的技能描述写得不好模型就不知道怎么调用、什么时候调用、传什么参数。举个例子你有一个get_weather(city, date)函数。如果技能描述只写获取天气模型面对明天去上海开会有必要带伞吗这种问题时可能根本不会联想到这个技能。但如果技能描述写成根据城市和日期查询天气预报适用于出行规划、活动安排、穿衣建议等场景返回包含降水概率、温度、风速的天气信息模型就会非常明确地在合适时机调用它。所以我把技能定义拆成四个层次来看意图层这个技能解决什么问题什么场景下该被触发接口层参数是什么、类型是什么、返回值长什么样约束层什么时候不该用、有什么副作用、需要什么权限示例层给一两个典型的调用示例帮模型建立这个技能长这样的直观认知一个成熟技能库的维护工作大部分时间不是在写业务代码而是在打磨这四层定义。代码写错了能编译报错技能定义写错了只会让Agent在用户面前抽风。2. 技能清单设计一次定义处处调用的元数据规范2.1 技能定义的最小字段集照着抄就行我先给出一份我在生产环境里沉淀下来的技能定义模板这是经历过多个项目考验的版本不是网上那种花架子{ name: sales_report_query, version: 2.3.1, description: 按日期范围、区域、产品线等条件查询销售明细支持聚合统计。适用于周报月报生成、业绩复盘、异常波动排查等场景。不适用于预测未来数据。, parameters: { type: object, properties: { start_date: {type: string, format: date, description: 起始日期格式YYYY-MM-DD不能晚于end_date}, end_date: {type: string, format: date, description: 结束日期格式YYYY-MM-DD}, region: {type: string, enum: [华东, 华北, 华南, 西南], description: 区域筛选条件不传则返回全部区域}, dimension: {type: string, enum: [daily, weekly, monthly], description: 聚合粒度默认daily} }, required: [start_date, end_date] }, returns: { type: array, items: { type: object, properties: { date: {type: string}, region: {type: string}, revenue: {type: number}, order_count: {type: integer} } }, summary: 返回按时间排序的销售记录数组字段包含日期、区域、营收、订单数 }, examples: [ { request: 查询上个月华东区每天的销售额, arguments: {start_date: 2025-01-01, end_date: 2025-01-31, region: 华东, dimension: daily} } ], capability: read, timeout_ms: 10000, scopes: [data_analyst, admin] }有几个字段是一开始容易漏、后来发现必须加的version技能是会迭代的没有版本号你就没法做灰度回滚。我见过太多团队改了技能描述之后效果反而变差结果连改了什么都不知道。capability这个技能是读还是写操作。读操作可以放心让Agent自动执行写操作发消息、改数据、下订单必须走额外的确认流程。examples别看它小小一段这是帮助模型理解技能的最有效手段。模型对示例的模仿能力远比对抽象描述的理解能力要强。2.2 技能描述的质量决定了模型调用的准确率这是整个技能体系里性价比最高的优化点。我做过一个控制变量实验同一个技能只改描述文案不碰任何代码逻辑模型的调用准确率从61%涨到了89%。代价只是花了半小时重写描述。怎么写好技能描述我总结了三条硬规则第一描述里必须包含什么时候该用和什么时候不该用。模型踩坑最多的场景不是不知道该调哪个工具而是把A工具用在B场景。比如一个生成图表的技能如果描述里不写不适用于表格数据展示表格请用export_csv技能模型就可能在用户要数据表的时候给画个柱状图。第二用词要具体避免抽象形容词。高效查询是废话按时间范围过滤才是有效信息。描述里出现的每一个概念都应该是参数表里出现过的字段形成闭环。第三用动词宾语的结构开头。我对比过很多描述写法查询销售明细比该技能用于销售数据的查询和分析工作更容易被模型命中。可能因为动词开头的表达更接近模型在训练语料里见过的函数注释风格。2.3 权限与副作用安全底线必须在技能层卡死很多团队把权限控制放在对话层让模型去判断这个操作能不能做。这是非常危险的——模型天生倾向于满足用户请求尤其是当用户用请务必快点处理这样带有催促性的语言时。正确的做法是把权限下沉到技能层跟业务逻辑彻底解耦。在一个Agent技能注册表里每一项技能都带有明确的能力标签只读、可写、需要二次确认、仅限特定角色。调度器在把技能放行给模型之前先根据当前对话的安全等级做一次过滤。我踩过的一个真实教训早期版本里删除客户数据这个操作跟查询客户数据放在同一个技能里由模型自己判断参数。结果测试人员在一次模拟对话里说了句把测试账号的客户记录清理一下我们准备切换环境了Agent二话不说就开始删数据了。虽然当时连的是测试库但那次之后我把所有写操作全部分裂成独立技能并且强制要求带confirm_operation的前置技能。代价是多了一次人机交互换来的却是敢把Agent放到生产环境的底气。3. 技能调度实战上下文感知的选择逻辑与失败回退3.1 静态清单还是动态发现取决于技能库的规模技能怎么暴露给模型两种主流思路一种是静态注册表把全部技能塞进上下文另一种是动态发现按需检索之后注入。我两个方案都试过给你一个清晰的取舍标准维度静态注册表动态发现技能数量50个可接受50个必须用上下文开销每个技能约200-500 token50个技能轻松吃掉1万 token只注入Top K个开销可控调用延迟低多一次检索增加约100-300ms实现复杂度简单需要额外维护检索索引误召回风险无全部可见有召回不准就调不到如果你的Agent技能少于30个老老实实用静态注册表就行。上下文窗口现在虽然大了但token就是钱而且技能描述越多模型的选择注意力越分散。超过50个技能之后我强烈建议上动态检索——把技能描述向量化根据用户当前的消息和已有的对话摘要做相似度搜索取出最相关的8-10个技能注入上下文。我这边最终是混合方案高频核心技能常驻长尾技能进向量库。常驻技能控制在15个以内其余全部走检索。线上效果很稳上下文开销压缩了一大半。3.2 参数抽取从自然语言到函数签名的翻译官技能调用的另一大难题是参数抽取。用户说帮我看看上个月华东的销售怎么样你得把它翻译成{start_date: 2025-01-01, end_date: 2025-01-31, region: 华东}。模型干这活没问题但有几个隐藏的坑日期理解是重灾区。下个月最近三天今年Q3这种相对时间不同模型的理解差异很大甚至同一个模型在不同会话里都会给出不同结果。我的做法是在参数描述里强制要求模型输出具体的日期范围另外在技能内部加一个日期解析器对模型给出的参数做二次校验发现日期异常就主动追问用户。枚举值的兜底匹配也非常关键。参数表里定义了enum: [华东, 华北, 华南, 西南]但用户可能说江浙沪北方区域除了北京以外的地区。如果模型直接把江浙沪塞进region字段技能就崩了。我在技能框架里加了同义词映射表把常见的口语化表达映射到标准枚举值上映射不上就触发澄清反问绝不猜测。这里要给所有做Agent的同行一个忠告参数的默认值设计一定要谨慎。一个技能如果所有参数都可选模型就倾向于少传参数最后执行出来的结果和用户预期相差十万八千里。我的原则是核心参数必须必填拿不到就反问次要参数设置一个显式的默认值并且在返回值里标注该结果基于默认参数XX生成让用户知道权重在哪。3.3 失败回退设计让Agent学会承认做不到技能调用不可能永远成功。网络超时、参数校验失败、下游依赖返回异常这些在真实环境里天天发生。最可怕的不是失败而是失败之后Agent开始编造成功。我遇到过最离谱的一次一个查询订单状态的技能超时了Agent居然跟用户说您的订单已发货预计明天到达。后来一查那段话是模型根据上下文自己脑补的跟订单状态毫无关系。这事的根源在于技能框架没有告诉模型调用失败后该怎么办模型只能用自己最擅长的方式——编——来维持对话的连贯性。我的解决方案是给每个技能配置显式的失败处理策略重试策略哪些错误码可以自动重试最多重试几次重试间隔多少我一般只对超时和5xx做1-2次重试其他错误一律不重试避免在错误方向上浪费时间和token。降级路径主技能挂了有没有次选技能顶上比如查询天气失败可以用获取未来24小时降水预报顶上但要明确告诉用户当前展示的是预报数据。诚实上报所有技能调用必须在返回结构里带上status和error_message字段。模型发现status: failed时只允许做一件事——如实地告诉用户这个操作目前没成功原因是……建议换个方式或稍后再试。我把这个逻辑做成了框架层的硬约束不依赖模型自觉。在代码里拦截模型生成的结果一旦检测到技能调用失败后模型还在编造成功就强制改写回复内容。这是个笨办法但非常有效生产环境的幻觉率直接降了一个数量级。4. 复合技能编排原子技能如何组装成高阶工作流4.1 单技能能解决80%的简单请求剩下20%靠编排用户的真实需求很少是单次技能调用能搞定的。就拿开头那个例子来说把上周的销售数据整理成周报一次完整的执行链路应该是查询销售明细query_sales按周维度做聚合统计aggregate_sales识别数据波动和异常detect_anomalies生成周报文案generate_report_text排版成文档create_document每一步都是一个独立的原子技能而把它们串起来的逻辑就是编排。这里有个原则性问题编排逻辑应该由代码控制还是让模型自由发挥我的答案是业务链路稳定的部分交给代码编排模型只负责链路易变的部分。举个具体的分界线查数据—聚合—生成图表—输出报告这四步顺序是固定的每一步的输入输出关系也是明确的完全可以用代码写死成一个复合技能但用户中途说把图表改成饼图或把上一个季度的数据也加上这种动态需求就必须由模型来判断该插入哪个步骤。4.2 状态管理编排最容易翻车的地方复合技能编排里翻车率最高的不是步骤选择而是状态管理。一个多步骤任务执行到第三步崩了怎么恢复Agent记不记得自己已经执行到哪一步了如果用户中途改需求之前执行的中间结果怎么处理我现在的做法是在编排器里维护一个显式的任务状态对象包含三个部分当前步骤、已完成步骤的中间产物、待执行步骤的队列。每一步技能执行完毕都把结果写入中间产物缓存一旦任务中断用户可以说继续Agent从断点接着干不需要重新跑一遍。还有一个细节每一步技能调用的结果都附带一个summary字段比如已成功获取华东区1月1日至1月31日的销售明细共返回247条记录总营收1523万元。这个summary不是给系统看的是给下一步的技能和给用户看的。它让整条链路里的每个环节都能快速理解前一步发生了什么也让用户在中途可以随时打断提问——等一下这个数字包含退款订单吗Agent可以基于summary快速回答而不是重新翻原始数据。4.3 人机协同的检查点设计我给所有涉及写操作的复合技能加入了一个通用机制关键动作前置确认。流程执行到某个节点比如删除客户记录发送全员邮件提交订单系统会自动插入一个人工确认步骤把即将执行的操作、涉及的参数、可能的影响范围列给用户确认。这一步既是为了安全也是为了用户体验。用户对Agent的信任是逐渐建立的前期给足确认环节反而会让他们更放心地放手让Agent去干后面的活。等线上跑了一段时间、确认率稳定在90%以上之后再考虑对低风险操作放开确认。5. 技能评估与迭代没有测试集支撑的技能库是空中楼阁5.1 给技能建一个体检中心很多团队做Agent上线之后的效果全靠用户反馈感觉还行偶尔抽风。这种状态没法持续优化。如果把技能体系当成一个软件系统来对待那它必须有自己的单元测试和回归测试。我给技能库建了一套评估基线核心是四类指标触发准确率该调的技能调对了没有不该调的时候有没有乱调参数正确率调用时机对了参数填得对不对日期、姓名、编号这些关键参数有没有传错结果满足率技能执行成功了返回的结果是不是用户真正想要的失败恢复率失败之后是否正确上报和降级还是开始编造成功每一类指标都对应一批预置的测试用例至少几百条覆盖正常场景、边界场景和故意刁难的场景。每次技能定义有改动先跑一遍基线看各项指标是涨是跌。没有这套流程你根本不知道一次优化是进步还是倒退。5.2 最常见的三种失败模式以及怎么快速定位我跑了半年的基线测试把失败案例归纳成三类典型模式描述歧义型。技能描述写得太宽泛模型理解出了偏差。定位方法把失败案例里的模型调用日志打出来看它选的技能和实际该用的技能差在哪。解决办法在描述里加不适用于条款或者调整技能名的表述。参数幻觉型。模型把用户根本没提到的信息填充进了参数。最典型的就是日期——用户说看一下销售数据模型就默认成今天其实用户想知道的是整个季度。定位方法看调用日志里的参数和用户原话的对应关系。解决办法收紧必填项没有明确依据的参数必须触发反问。编排断裂型。复合技能执行到中间某一步上一步的输出没接住下一步就没法执行。定位方法看任务状态对象卡在哪个步骤。解决办法为每对相邻技能定义清晰的输出-输入映射并增加中间产物的格式校验。5.3 迭代节奏小步快跑每版可回滚技能是活的必须持续迭代但迭代最怕失控。我的经验是两条一是每次只改一个变量。不要同时改三个技能的描述再一起上线出了状况你根本不知道是哪个改动导致的。二是版本灰度。新版本技能定义先在小流量上跑一天对比评估基线的各项指标稳定了再全量。我这边现在保持着一个很实用的节奏每周固定花半天处理技能维护——新增需求带来的新技能、用户反馈暴露的描述问题、基线测试里不合格的存量技能。这个节奏看着慢但三个月下来技能库的调用成功率和用户满意度都是稳定向上的曲线。说到这我想起来一个收尾的小技巧也是我最近才沉淀下来的在技能库的维护文档里给每个技能都记一条血泪史记录它在线上出过的最大一次事故和当时的修复过程。这看起来像是自我检讨实际上特别有用——新人接手Agent项目的时候读一遍这些记录比看十遍设计文档都管用。我团队的实习生靠这个文档两周就能独立维护技能库了。这个做法算是踩过这么多坑之后留给自己的一个还算体面的纪念品。