ARTICLE DETAIL

资讯详情

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

从散装提示词到结构化技能库:Agent稳定落地的关键实践

从散装提示词到结构化技能库:Agent稳定落地的关键实践 上个月我做内部客服与销售场景的智能体联调碰见的头号问题不是模型答不上来而是它明明知道该干什么动作却总不到位说好了要调用CRM拉客户记录它随手编了一个工单号说好了按销售SOP发跟进邮件它把邮件写得既不符合公司口径也漏了关键附件。我一度以为是提示词写得不够长后来反复试了几轮才发现问题根本不在“说得多不多”而在“能力被组织成了什么形态”。这篇想聊的 agent-skills就是我后来把散装提示词收拾成结构化技能库的一套实践总结技能该怎么定义、怎么写、怎么注册、怎么路由以及上线后必然会碰见的三个坑和完整排查过程。如果你正在搭工具型Agent、多智能体协作系统或者给Agent做产品落地这篇文章里的大部分内容可以拿来直接用。就算你只是写过一个带function calling的Demo看完也会明白为什么有些Agent项目“演示很惊艳、上线就翻车”——绝大多数翻车点并不在模型推理而在技能层的边界没划干净。1. agent-skills到底在解决什么问题从“会聊天”到“能干活”的最后一公里1.1 大模型原生能力与“可执行能力”之间隔着一条沟先说一个很多人没意识到的前提模型本身有很强的语言理解和生成能力但“能力”这个词在Agent语境里必须被重新定义。对客服机器人来说能回答“我们的退货政策是什么”和能执行“查订单、判断是否超期、生成退货单、发送确认短信”是两件完全不同的事。前者靠模型参数就能完成后者需要一套代码、接口、权限、状态管理共同配合。模型只是这套执行链路的语言中枢真正干活的是技能。我当时把团队里所有Agent相关的代码和提示词清点了一遍发现大家写Agent的方式高度雷同在系统提示词里长篇大论描述角色和任务然后在用户对话里塞几个工具返回的结果指望模型自己看着办。这种做法的最大问题是——没有边界。模型每次都在“自由发挥”中试图猜测该调用什么、不该调用什么一旦上下文里出现相似信息它就会产生非常自然的误判。而 agent-skills 要做的就是把“边界”变成显式结构哪项能力是一门独立技能技能接收什么参数、触发条件是什么、内部按什么流程执行、输出格式长什么样全部预先定义清楚。1.2 为什么碎片提示词不够用必须技能化我在项目里经常用一个类比让一个实习生直接看公司规章制度干活和你先给他一份权限清单、每项任务配上标准操作手册效果是完全不同的。碎片提示词相当于前者技能化相当于后者。Agent在真实业务里要处理的请求是高度重复的高频动作就那么几十个查订单、改地址、生成报价、跟进客户、提交工单、审批报销。每个动作都必须具备稳定的输入输出契约否则根本没法做测试、没法做回归、没法多人协作。更有意思的是技能化的收益会随Agent数量增加而放大。我见过不少团队同时维护三四个Agent每个Agent的提示词里都写了一坨“你会调用订单接口”之类的描述结果接口一改所有Agent集体失灵排查起来只能逐个人工翻提示词。把技能抽出来做成独立模块后接口变更只需要更新对应的技能实现所有Agent通过技能注册表自动获取新行为。这其实就是agent-skills这套思路在工程上最核心的价值让能力可以被单独维护、单独测试、单独升级。1.3 agent-skills的最小可执行单元一个技能该长什么样我在自己的项目里划定过最小单元的边界供参考一个技能至少要包含五样东西少了任何一样都不算“可执行技能”。第一是技能名称必须短且无歧义第二是描述说明这个技能在什么条件下被触发、核心职责是什么第三是输入参数声明逐个列清楚字段名、类型、是否必填、含义第四是执行逻辑可以是提示词加代码的组合也可以是一段固定的处理流程第五是输出格式明确定义返回给Agent的内容长什么样。这五样东西看起来简单真正做下去就会发现难在平衡写得太粗模型不知道该在什么时候调用写得太细维护成本暴涨而且描述里任何一个词都可能微妙地改变模型的选择。我在下面几节会分别展开讲这套定义怎么落地重点是给出可以直接抄的schema和踩出来的经验。2. 一套能落地的技能schema目录结构、字段定义与参数约束2.1 技能仓库的基本目录结构我先说我在项目中实际采用的目录结构它不一定适合所有团队但对中大型Agent项目很有参考价值。每个技能独占一个目录名称就是技能ID目录内包含一个描述文件、一个执行脚本、一个样例集目录。skills/ check_order/ skill.yaml run.py examples/ case_001.json case_002.json send_followup_email/ skill.yaml run.py examples/ case_001.json每个技能一个目录的好处是隔离性极强多人协作时不容易互相污染。描述文件专门给模型看执行脚本专门给代码跑样例集则同时服务两件事一是给模型提供few-shot示例二是作为离线评测的回归样本。把这三者分开能避免一个常见问题——把大段示例塞进描述文件里导致模型在触发判断时被示例中无关紧要的措辞带偏。2.2 skill.yaml字段设计哪些字段直接决定模型行为skill.yaml是我最看重的文件它直接决定了Agent能不能在正确时机调用正确技能。我写过几个版本最终沉淀下来一套比较稳定的字段结构下面用销售跟进邮件的技能做示例。name: send_followup_email description: 当用户要求向客户发送跟进邮件且已经具备客户姓名或联系方式时使用本技能生成并发送邮件。不适用于首次开发新客户。 input_schema: - name: customer_name type: string required: true description: 客户联系人的姓名必须来自客户资料或对话上下文禁止编造 - name: last_contact_summary type: string required: false description: 上次沟通的关键信息摘要用于生成个性化内容 - name: email_language type: enum values: [zh, en] required: false default: zh description: 邮件语言默认中文 output_format: type: email_draft fields: - subject - body - attachment_ids allowed_callers: [sales_agent, support_agent]这里有几个字段需要特别说明。description字段是整个技能里优先级最高、最容易影响成败的配置它必须写明触发条件还必须写明不触发条件。我在“不适用于首次开发新客户”这句话上就吃过亏最开始没写结果销售Agent在客户完全没接触过产品时也调用了跟进邮件技能生成的邮件让人看得一头雾水。input_schema里每个字段都要带上required标记和描述。我见过很多团队的技能参数只写类型不写含义后果是模型经常把相似字段的值填错位置比如把客户ID填到订单号里。最有效的缓解手段就是在参数描述里写清楚“数据来源”和“禁止编造”这两个约束对模型的引导效果非常显著。output_format很多人会忽略但它在多技能串联时恰恰是保命配置。任何一个技能的输出都不应该是一坨自由文本而应当是一份结构化的中间数据。后面章节我会单独讲一个因为输出格式不统一导致的串联事故。2.3 参数约束与“参数幻觉”的第一道防线技能参数最危险的问题不是模型填错而是它一本正经地编造参数。我见过Agent在没有任何依据的情况下给用户生成了一个完全虚构的订单号并查询成功——当然查询结果是空但整个对话看起来非常可信。这种事发生一两次用户对系统的信任就直接归零。处理参数幻觉不能只靠模型自觉要在schema层和代码层同时设卡。schema层把每个参数标注为“必须来自对话上下文、客户资料或系统数据”代码层则要在技能执行前做前置校验如果上游Agent传入的参数没有来源标记直接拒绝执行并回报缺失参数。我在校验逻辑里还加了一条规则凡是敏感操作类技能关键ID字段必须能通过格式约束校验比如订单号不符合系统的编号规则整单直接打回。这套机制上线后参数编造率从肉眼可见的高频降到偶发再配合后面的路由与评测机制才算是把问题控制住了。3. 从零搭一门技能把“跟进邮件”做成可复用的标准化能力3.1 先把需求拆到不能再拆再考虑写代码拿跟进邮件这个例子来完整过一遍流程。最初销售团队提的需求是“让Agent帮我们写跟进邮件”这个需求太模糊直接落到技能上会变成一团浆糊。我先把需求拆成几个子问题什么情况下算“跟进”跟进邮件要包含哪些必要元素发送前需要谁确认数据从哪个系统取逐一回答之后我划定出技能的行为边界输入是客户姓名和最近沟通摘要输出是一封符合公司模板的邮件草稿发送动作必须由人工确认后触发。注意在初版技能里我没有做真正的自动发送只做到草稿生成。这个决定帮我避开了大量权限、合规和误发送问题等邮件草稿的生成质量被业务方认可后再考虑接发送接口。给技能划定“不做清单”和定义“做什么”同样重要。3.2 描述、示例与内部提示词的分工技能内部实现我采用了一段固定提示词加代码模板的组合。固定提示词负责语言生成代码模板负责结构保障两者各管各的提示词不负责决定邮件整体结构它只负责根据客户信息和沟通摘要填充结构和润色文字代码负责拼装subject、body、attachment_ids这些字段保证输出永远符合output_format。示例的选择也很有讲究。我在examples里放了三个案例一个客户回复过邮件、一个客户完全不回复、一个客户明确表示不感兴趣。这三个case覆盖了三种截然不同的语气策略模型根据输入特征选择匹配的案例作为参照。实测下来有样例和没样例的质量差距非常明显尤其在不回复客户的措辞上没样例时模型总是写得过于客气有样例后明显更成熟。写内部提示词时有一条经验值得记下来不要在提示词里出现“你是一个邮件专家”这种人格设定而应该直接写清任务目标、约束条件、输出规范。人格设定会让模型在生成时过度发挥产生大量装饰性词汇而任务式提示词更容易保持稳定的输出结构。3.3 注册与调用链路让Agent能发现技能技能写好后需要在Agent侧做注册。我用的方式是维护一个全局技能索引索引内容就是每个技能skill.yaml里的name、description、input_schema的摘要信息。Agent每轮对话开始前系统会把技能索引塞进上下文模型根据用户请求和索引信息判断该调哪个技能。索引不能完整展示所有技能的详细信息否则上下文太长所以我在索引里做了两级结构一级是所有技能的一行摘要二级是模型初步选定后再加载对应技能的完整schema。这个两级加载策略是我比较得意的一个局部优化。最开始我把每个技能的完整描述都塞进去上下文疯狂变长模型反而变得犹豫不决经常在两个相似技能之间反复横跳。改成先看摘要、再加载详情之后技能选择的准确率和响应速度同时都变好了。3.4 跑通之后先别急着上线验收清单技能跑通第一版后我列了一张验收清单每项都不过关就打回重做。清单内容包括技能在至少三种不同措辞的请求下都能被正确触发在明显不属于该技能的请求下不会被误触发必填参数缺失时能明确告知用户缺什么输出格式在连续十次调用中保持一致异常情况如客户信息查询不到有明确的返回路径而不是强行编造。这张清单救过我很多次。最典型的案例是误触发问题技能刚写好的时候用户只要提到“邮件”两个字Agent就会调用它哪怕用户其实是在问“我昨天发的邮件为什么被退信了”。我在验收时发现这个问题在description里补充了“仅适用于撰写并发送新邮件不适用于查询邮件状态”误触发率立刻大幅下降。验收测的从来不是能力上线而是边界是否生效。4. 技能路由与冲突消解Agent拿什么判断该调哪门技能4.1 两种主流路由策略向量召回与规则预筛技能数量一多怎么选就变成了核心问题。我见到的主流做法有两种向量召回和规则预筛实际项目中我两者都用了。向量召回是把技能描述和用户请求都做embedding算相似度挑Top K规则预筛则是在模型判断之前先用一套显式规则把明显不匹配的技能过滤掉。向量召回的优点是灵活能容忍用户口语化表达缺点是语义相近的技能会同时召回模型需要在相似项里做选择准确率下降。规则预筛的好处是硬约束杜绝低级误触发比如用户提到“退款”时绝不调用“开发新客户”技能。但纯规则覆盖不了所有真实表达所以我把两者组合成pipeline规则预筛负责缩小候选集向量召回负责把候选集排序最终把候选集连同用户请求一起交给模型做选择。这套组合在我项目里的技能数量超过30个之后仍然保持了可接受的选择准确率。4.2 技能冲突语义相似是最大的坑当技能数量上升后最常出现的头疼问题是两个技能听起来都像正确答案。比如“生成跟进邮件”和“修改跟进邮件模板”用户说“帮我把上周那个邮件改改”模型就很容易在两者之间犯迷糊。我在项目里处理这类冲突时采用了一个简单有效的原则职责边界按数据域划分而不是按动作划分。生成邮件管“写新内容”修改模板管“改动存量模板”两者操作的数据对象完全不同只要描述里把各自操作的数据域说清楚冲突就能化解掉大半。实在有无法通过描述化解的强冲突我会选择直接合并技能而不是硬靠路由区分。合并后技能内部做分支判断虽然单技能变复杂了但路由层的压力显著下降。我的经验是路由层出现的冲突往往暴露的是技能划分本身有问题与其在路由上打补丁不如回去重构技能划分。4.3 置信度阈值什么时候该主动问用户路由判断不可能100%准确所以必须给模型一个“不确定时怎么办”的出口。我在技能选择环节加了一个置信度机制当最佳候选的匹配得分和次佳候选非常接近时不直接执行而是生成一个确认问题问用户。看起来多了一次对话实际上避免的是大量错误执行带来的返工成本。这个阈值不能拍脑袋定我是用历史样本算出来的。取一批已经标记了正确技能的用户请求跑一遍路由打分画出得分差的分布选分界点作为阈值。后续运行过程中再根据线上准确率数据微调。这类机制的成本很低但对体验提升明显用户宁可被多问一次也不愿意看到Agent自作主张做错事。5. 真实排查链路复盘参数幻觉、状态污染与外呼依赖失败的完整处理过程5.1 第一个坑参数幻觉日志里看到系统自己编了个订单号这是一个让我印象很深的case。用户问“帮我查一下订单XS20240301到哪了”Agent正常抽取了订单号返回了物流信息一切看起来正常。但后来我们通过日志回放发现订单号解析正确是因为用户凑巧说全了在另一个测试用例里用户只说了“我上周买的那个东西”Agent直接自己编造了一个形如XS20240311的订单号并“成功”查到了结果。排查链路是这样的先把模型输入输出和技能输入输出全量打了日志定位到参数是从模型生成里抽取的而不是从对话状态里匹配的然后复现测试发现模型在参数缺失时倾向于“补全”而不是“询问”最后修复方案分两层——代码层在技能执行前做订单号格式数据库存在性校验不通过就返回明确的缺参提示模型层在技能描述里追加一句“订单号必须由用户提供或从订单系统查询获得禁止生成”。修复后两周内同类case没有再出现。核心结论是不要指望模型诚实承认自己不知道要在技能执行链路上把它卡住。5.2 第二个坑技能串联时的中间状态污染我的系统里有一条典型链路用户要求“给这周的三个意向客户各写一封跟进邮件”。系统先把客户列表查出来再逐个调用跟进邮件技能。问题出在第二个客户开始邮件正文里偶尔会出现第一个客户的姓名和公司名。查日志发现原因是两个技能共用了同一个上下文缓存第一封邮件的完整输出残留被第二封邮件技能当作参考信息读取了。修复方案说起来不复杂每个技能调用时显式清空与当前任务无关的缓存字段并把output_format渲染后的结果与内部推导过程严格隔离。我给技能执行加了一个“干净上下文”的约束内部处理时用的临时变量在技能结束时统一回收只有声明在output_format里的字段能传递到下一个环节。这条经验后来也写进了团队的技能开发规范技能输入输出之间只允许通过显式字段传值不允许依赖任何隐式状态。5.3 第三个坑外部依赖不可用时的优雅降级技能依赖的很多外部接口不是每一次都稳定。有一回订单查询服务临时抖动技能直接抛异常Agent随之给用户回了一句“系统错误请稍后重试”。用户体验很差但更糟糕的是日志里连具体是哪个环节出错都看不清。我在排查中发现技能异常处理完全没有分层网络超时、数据不存在、权限不足全都混成一种错误返回给上层。我把错误处理重新做了结构化分类。第一类是可重试错误比如网络超时加入自动重试最多三次。第二类是业务错误比如订单不存在直接返回“未查询到对应订单”并建议用户核对信息。第三类是权限错误返回“当前账号无权限查看该数据”。在技能描述里也同步说明了这些返回路径让模型知道当技能返回特定错误码时应该给用户怎样的回应。这个梳理做完后技能面对异常的应对质量明显提升日志可读性也大幅改善。5.4 排查方法论沉淀从结果倒推链路节点三个坑排查完之后我给团队沉淀了一套排查方法论。核心是定位一次Agent异常行为先画执行链路标识每一个可能篡改数据的节点再通过日志逐节点回放。技能层的错误绝大多数不是模型单一原因而是链路中某个节点悄悄改变了数据形态。日志必须覆盖四个层面模型原始输出、技能输入参数、技能执行中间态、技能最终输出。这四个层面缺一个排查效率都会大打折扣。我现在在项目里强制要求任何技能上线前必须打印这四个层面的日志。加了这条硬性要求后新问题的平均定位时间从以小时计降到了以分钟计。这也是我把这一节写这么长的原因——排查能力不是靠天赋是靠可观测性设计。6. 技能上线后的质量运营评测集、行为回放与野生技能治理6.1 离线评测集先让技能过一遍“模拟考”技能是代码加模型行为的混合体不能只看代码测试通过就放行。我给每个技能建了一套离线评测集样例主要来自两个渠道一是上线前的设计案例二是线上真实请求的回放。评测时把每个样例输入给技能检查三件事触发是否准确、输出格式是否合规、内容质量是否达到标注标准。内容质量我采用“标注示例人工打分”的方式。每个样例都有一个人工写好的参考答案技能生成结果与参考答案做相似度评估再由业务同学随机抽检打分。这个流程看起来重但对稳定性要求高的技能非常值得。我见过不少团队忽略了这一层技能每次改完都凭感觉上线结果行为时好时坏业务方对Agent的信任会快速流失。6.2 线上回放与标签体系让每次调用都留下账本线下评测覆盖不了长尾场景更真实的反馈来自线上。我在技能调用链路里加了一套回放系统技能每次执行的所有关键数据全部落库然后通过一个管理页面可以按时间、用户、技能维度回看每次调用的完整链路。有了这套账本后我们每周做一次线上行为抽检把“触发正确”“触发错误”“漏触发”“输出质量低”这些标签打到具体case上。标签体系最有价值的不是统计数字本身而是它能持续告诉我们技能描述哪些地方产生了理解偏差。比如某周大量出现“漏触发”标签我一看案例全跟“用户用缩写提到业务名词”相关就会在对应技能的description里补充缩写说明。这个反馈闭环让技能质量可以持续改善而不是上线后就听天由命。6.3 野生技能治理谁能新增技能必须有流程最后提醒一个组织层面的坑。技能体系成型后大家会越来越愿意往里面加新技能这是好事但没有治理就会乱套。我在项目里见过最夸张的情况是团队里三个人各自建了一个功能几乎一样的技能只是名称和描述风格不同Agent的选择准确率直线下降。后来我做了一套治理规则新增技能必须先在技能索引里查重确认没有功能重叠必须写明与其他技能的边界差异必须附带五个测试样例才能提审。这套规则后来被人开玩笑叫作“野生技能驯化流程”。虽然让新增技能的流程变重了但换来的是技能索引的长期整洁。我个人的体会是Agent项目做到后期拼的不是写出一个惊艳的技能而是能否让几十个技能长期稳定地协同工作。技能数量上了规模之后治理成本甚至会超过开发成本早一点建立流程后期会省下很多精力。如果你现在正被Agent“行为不稳定”折磨我的建议是先别急着花更多钱调更大模型先回头看看技能层是不是该好好整理一遍了。把能力的边界划清楚把参数的来路卡严实把每一个失败路径都留下日志——这一套做完之后你会发现模型还是那个模型但Agent可靠多了。
返回列表