ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Agent技能化实战:从长提示词到可校验的工程体系

2026/9/20 10:28:20 拓冰建站 浏览量
Agent技能化实战:从长提示词到可校验的工程体系 开头先交代一下背景。我做 Agent 应用落地有一段时间了agent-skills 这个词在圈子里越来越常见但我发现大部分人还是把它当成“给模型写的一段好提示词”来理解这其实完全跑偏了。我一开始也走过弯路在客服工单、订单查询这类真实业务流里把流程、规则、注意事项全部写进一个超长提示词结果模型就像个记性不好的新员工同一句话在不同时间点能给出完全不同的处理路径效果根本稳不住。后来我把任务拆成显式的技能结构按注册、路由、校验、回退、评测这一整套方式管理才算真正把 Agent 从“能聊”变成了“能干活”。这篇文章会把我的完整思路写出来技能到底怎么拆、定义里哪些字段真正决定成败、工程落地上哪些决策最关键、一次线上事故如何倒逼我补齐规则以及一个技能库怎么在团队里长期存活。适合正在做 Agent 应用落地或者被长提示词不稳定折磨的工程师、产品和业务负责人。下面全程都是实操视角没有花架子。1. 为什么通用提示词撑不起复杂任务技能化改造前的一幕幕1.1 长提示词其实是把系统复杂度转移给了模型我最早的做法和很多人一样把业务流程写成“员工手册”式的提示词什么场景做什么、不能做什么、输出格式是什么写得自认为非常清楚。但上线后遇到一个典型问题——用户只说了句“我上个月买的东西好像有问题”模型有时候去查订单有时候去查售后政策极端情况下还会自己推断出一个退款方案让用户去操作。同样的输入三条完全不同的路径其中只有一条是符合业务预期的。这个问题的本质不是“提示词写得不够好”而是我把系统的复杂度全部塞进了一段自然语言里。模型每次执行动作都要从几千字的上下文里“理解并遵守”所有规则而自然语言天然带歧义规则之间的优先级又不明确。就像给一个新员工一本三百页的公司制度让他一次读完并严格执行他大概率会在具体场景里选择性遗忘。所以长提示词的本质是把工程上本该由流程结构解决的问题变成了一次昂贵且不稳定的推理任务。1.2 技能的三个关键特征模块化、可触发、可校验我后来在实践中把方案收敛成“技能”这套结构。一个合格的技能不是一段“给模型看的小提示词”而是包含三个关键特征的可执行单元。模块化一个技能只负责一类完整动作比如“创建工单”“查询订单状态”“处理退款申请”每个技能内部有清晰的步骤序列步骤之间是流水线关系不是靠模型临场编造。可触发技能带有明确的适用场景描述由模型在收到用户请求后决定是否调用、何时调用。这相当于给模型一张“菜单”而不是让它自己凭记忆炒菜。可校验每个技能的输入、中间步骤、输出都有可判定的校验规则校验不过就进入异常分支而不是将错就错继续往下走。只有同时满足这三点的东西才配叫技能。否则它本质上还是一个提示词只是换了个名字而已。1.3 一个客服工单场景拆之前和拆之后的差别拿客服工单业务举例。没拆技能之前我的系统提示词大概是这样的理解用户意图、查询订单、判断是否在保修期、搜索知识库、生成回复、必要时创建工单、必要时转人工……全部任务叠加在一段很长的提示词里由模型自己编排。拆成技能之后路径变成了显式的意图识别结果命中“售后报障”场景触发create_support_ticket技能技能内部先抽取工单字段商品、订单号、故障描述字段缺失就向用户追问然后按故障描述检索知识库召回相关文章最后基于工单字段和知识库内容生成回复并给出处理时限如果知识库无召回或订单校验失败则标记manual_reviewtrue转人工处理。拆完之后最明显的变化不是“一次成功率”而是问题的定位方式变得完全不一样。以前模型回复错了我要去研究它为什么在那个语境下选了那个动作现在模型走错了我只需要看它的技能路由记录——它调了哪个技能、为什么触发、校验在哪一步断了。可观测性和可控性不在一个量级上。2. 先把技能拆成机器能执行的结构能力单元、触发条件与校验闭环2.1 一个技能最少要包含哪些字段很多团队定义的技能非常简陋只有“技能名 一段描述”。这远远不够。我整理到现在发现一个能稳定上生产的技能至少需要下面这些字段字段作用踩坑提醒name技能唯一标识命名要贴近业务动词别用太抽象的词description解释技能做什么这决定模型能否正确触发写不好是事故源头when_to_use触发条件写得越具体误触发越少never_use禁止条件我后来加的价值非常大steps执行步骤序列每条步骤要配判定标准不能是散文input_schema输入结构定义没有结构定义下游无法稳定消费输出output_schema输出结构定义决定后续接的每一步能否依赖结果guardrails安全与业务红线这里不做限制模型就敢乱来fallback异常回退策略不配回退异常只能靠模型临场发挥刚开始我也不理解为什么一个技能定义要这么重。直到线上连续出问题我才明白每个字段都对应一类风险。description写得模糊模型就会在错误的场景触发它steps没有判定标准中间结果出错时模型根本发现不了guardrails缺失端口就敢承诺赔偿金额。技能定义不是文档而是运行时的约束条件。2.2 步骤怎么才叫可判定动作加出口而不是散文我见过很多人写的技能步骤是这样的“判断用户是否有权限”“分析用户的情绪”“根据情况回复”。这种写法模型根本没法稳定执行因为它没有给明析的判定出口。我现在的规范是每一步必须有“输入 动作 判定结果”结果只有三个方向——进入下一步、回到上一步补充信息、跳到异常分支。举个例子同样是身份校验这一步差的做法“校验用户是否已登录。” 模型可能看一眼会话里有没有用户 ID 就往下走了完全没考虑这个 ID 是否有效、是否是本人。好的做法“从会话上下文读取用户身份令牌并调用身份校验接口。若令牌有效且用户等级满足操作要求进入 step 2否则返回PERMISSION_DENIED并触发escalate_to_human技能。” 这样每一步都是可判定的模型不需要“理解”规则只需要“执行”规则。一套技能里的所有步骤都按这个标准写完它的稳定性就会从“可能成功”变成“必然可复现”。这也是技能化之后我最大的体感变化。2.3 输入输出结构是技能能被复用的前提另一个容易忽视的点是输入输出结构。很多团队定义的技能输入输出全是自然语言描述没有任何结构化约束。表面看很灵活但一旦技能多了就会遇到两个团队各写各的完全没法互操作的问题。我现在的做法是给每个技能定义 JSON Schema 风格的输入输出结构。拿上面客服工单场景的create_support_ticket技能举例一个简化版的技能定义大概长这样name: create_support_ticket description: 根据用户描述创建客服工单并自动匹配优先级和知识库文章。 when_to_use: 用户明确提到商品故障、售后政策咨询、申请维修或退换货。 never_use: 用户仅询问订单状态或故障尚未确认时不要创建工单。 steps: - step: extract_ticket_fields action: 从对话中提取商品、订单号、故障描述 check: 必需字段完整否则向用户追问缺项 - step: search_knowledge_base action: 用故障描述检索知识库召回前 3 篇相关度 0.75 的文章 check: 无召回时必须标记 manual_review true - step: generate_reply action: 结合工单字段与知识库文章生成回复 check: 回复中包含处理时限和下一步预期 input_schema: type: object required: [order_id, issue_description] properties: order_id: type: string minLength: 8 issue_description: type: string maxLength: 500 output_schema: type: object required: [ticket_id, priority, suggested_reply] properties: ticket_id: type: string priority: enum: [P0, P1, P2, P3] suggested_reply: type: string guardrails: - 不得承诺具体赔偿金额除非金额来自知识库政策模块。 - 订单信息核验失败时禁止创建工单。 - 单技能累计执行步骤超过 6 步时主动转人工。 fallback: on_step_failure: retry_once_then_human on_validation_error: ask_for_missing_fields这套结构最大的好处是技能的定义本身就可以被程序校验、被测试用例覆盖、被版本管理。输入结构不对路由后立刻拦截输出结构不对下游步骤不会拿到一个残缺对象继续干活。每个技能就像车间里的一个工位输入是标准件输出也是标准件中间的加工流程完全受控。2.4 校验闭环失败不能靠模型临场发挥技能里还必须配一层显式的校验闭环否则结构再清晰模型在某个步骤突然抽风整个流程还是会脱轨。我常用的是两级校验。输入校验技能启动前先按input_schema检查字段是否齐全。缺字段就直接向用户追问不带着残缺输入硬跑。这一步能拦掉大量因信息不足导致的错误。输出校验每个关键步骤执行完后按该步骤的check条件判断是否合格。不合格时走fallback而不是让模型“试着修正一下”。fallback我一般会给三种策略retry_once_then_human重试一次还不行就转人工、ask_for_missing_fields回到对话追问缺失信息、use_mock_response只在只读场景使用兜底给个安全结果。默认不推荐超过两次的无脑重试因为模型在同一套上下文里重试大概率会犯一模一样的错误纯粹浪费 token还会拖慢响应。3. 工程落地里的三个关键决策注册、上下文与异常回退3.1 技能注册为什么选“描述驱动注册”而不是硬编码路由技能定义好了接下来怎么注册到 Agent 里很多人第一反应是写代码根据用户意图用规则路由到某个技能。比如检测到关键词“退货”就调用退货技能。这套方案在小规模场景下能用但它把智能体最擅长的事情——理解自然语言——丢掉了。用户说“我不想要了能不能退”和“我订单号是 XXXX请帮我取消”看似都跟退货相关实际一个是售前咨询一个是售后操作靠硬编码关键词根本无法稳定区分。我最终采用的是“描述驱动注册 权限白名单”的混合方案每个技能维护一条精简的“路由描述”模型根据用户输入和这些描述做匹配决策同时我在系统层面规定哪些技能允许被模型自动触发哪些技能需要人工授权。{ skill_id: create_support_ticket, version: 1.4.0, enabled: true, required_permissions: [ticket:write], route_description: 当用户描述商品故障或咨询售后政策时创建客服工单。, usage_notes: 不要用于处理已存在工单的状态变更不要用于纯订单查询。 }这个方案的优势在于模型帮我完成了模糊意图的泛化匹配而权限白名单兜住了最坏情况——就算模型匹配错了也最多是一个只读技能被误触发不会直接产生高危写操作。3.2 上下文窗口是稀缺资源短描述路由长文本执行注册表一旦做大会立刻遇到一个新问题技能描述太长塞不进上下文。假设你有 20 个技能每个技能完整定义 1000 到 2000 token全塞进系统提示词就是 2 万到 4 万 token不仅消耗大还会严重稀释模型对当前任务的注意力。就像一张工作台只有 1 平米你把二十套工具书全摊在上面真正要用的那把螺丝刀反而被埋住了。我的做法是把技能描述拆成两层。第一层是精简的“路由描述”控制在 100 到 200 字以内只描述这个技能什么时候用、什么时候绝对不用这一层常驻在上下文中只承担路由决策。第二层是完整的技能执行体包含steps、guardrails、fallback等存放在外部注册表中当模型决定触发某个技能时运行时再从注册表加载完整定义注入当前上下文。这样上下文窗口的占用从“所有技能之和”降到了“所有路由描述之和”模型做路由决策的负担也小了很多。实测下来路由准确率和响应速度都有明显改善。3.3 异常回退要设计在技能内部而不是交给模型即兴发挥技能执行过程中最常出问题的是中间步骤校验失败比如检索不到知识库文章、用户提供的订单号不存在、某个外部接口超时。这时候如果没有任何预设的回退策略模型通常会在上下文里“努力脑补”一个结果然后拿着错误结果继续往下走。我见过最离谱的一次模型在一个订单校验失败的场景里直接给用户编造了一个“退款已发起”的回复造成了一次客诉事故。所以异常回退必须写在技能定义里由系统强制按策略执行而不是靠提示词约束模型“遇到错误要诚实”。我通常会在fallback里配置三个字段配置项可选值说明on_step_failureretry_once_then_human步骤失败时重试一次仍失败则转人工on_validation_errorask_for_missing_fields校验失败时回到对话环节追问缺失信息on_timeoutdegrade_to_readonly超时后只保留只读动作写操作全部挂起这里的关键原则是宁可多一轮对话也不要让模型把一个不确定的结果继续传下去。做 Agent 应用前期的“少犯错”比“跑得快”重要得多。3.4 技能级埋点排查问题的第一手证据技能化之后日志体系也需要跟着改变。以前我排查问题是在上千行模型对话日志里翻来找去上线技能系统后我强制要求每个技能的每次运行都要记录一条结构化日志包含这些核心字段skill_id触发了哪个技能step_name当前执行到哪一步input_payload传入的参数output_payload返回的结果route_decision触发该技能的路由依据token_usage本技能消耗的 token 数error_code异常类型如果有这套日志上线后我排查问题的效率至少翻了一倍。每次用户反馈“结果不对”我先查路由日志定位是哪个技能被触发再查步骤日志定位是哪一步出了问题最后才需要去看具体的对话内容。没有这些埋点Agent 应用就是一只黑箱出问题只能靠猜。4. 一次越权调用事故复盘技能职责边界不清引发的连锁问题4.1 事故现象模型调了不该调的高危技能这套体系跑了一段时间后线上出了一次让我印象极深的事故。用户咨询的对话是一个很平常的请求“我的订单好像被取消错了明明已经发货了。”按理说这个请求的正确路径是调用check_order_status查询订单状态确认异常后转人工处理。但模型直接调用了cancel_order技能把已发货的订单再次发起取消申请。虽然系统有审批流兜底但也引发了用户的强烈投诉最后整单取消了。当时我第一反应是“模型是不是幻觉了”但冷静下来之后意识到问题大概率出在技能定义本身而不是模型智商。4.2 排查链路从日志到描述再到相似度整个过程我是按这个顺序查的。先看路由日志。日志显示触发技能是cancel_orderroute_decision里写的理由是“用户提到取消订单”。这里已经能看到路由描述里的“取消”关键词把用户那句“被取消错了”误判成了取消意图。再看cancel_order的description和when_to_use。当时它的描述里确实写了“当用户希望取消订单或处理订单取消相关问题时使用”我在复盘时发现“订单取消失败”“取消错了”“能不能取消”这些完全不同的语义在这条描述里都会触发同一个技能。然后我做了一个实验把当天所有类似会话重新用同一版模型回放一遍结果 30 条相关对话里有 7 条触发路径与人工标注意图不符误触发率超过 20%。这个数字已经足够说明问题不是偶发而是定义缺陷。接着我算了一下技能描述之间的语义相似度。cancel_order与check_order_status的route_description相似度高达 0.82因为里面都出现了“订单”“取消”这类词。两个技能之间缺乏互斥约束模型面对用户表达模糊时会优先选择语义上“看起来更像”的技能而不是更安全的只读技能。4.3 根因语义重叠、缺少确认、授权过粗这次事故的根因可以归结为三点。第一description存在严重语义重叠且没有never_use这样的显式排除条件。模型在模糊场景下做了错误的技能选择。第二高危技能缺少强制的前置校验和确认机制。cancel_order属于改变订单状态的写操作但在技能定义里没有任何“必须先查询订单最新状态”“必须二次确认”之类的约束。第三权限模型按应用粒度授权没有按技能粒度分级。当时所有客服技能共用一套权限导致低风险技能和高风险技能在模型眼里“地位平等”推理时不会对高危操作产生额外警惕。4.4 修复方案互斥清单、权限分级、显式确认修复我分了三步走。第一步给所有技能补上never_use字段。cancel_order里明确写上用户只是表达“订单被取消错了”“订单状态异常”等查询类诉求时禁止触发本技能应路由到check_order_status。这一条改动立竿见影回放实验中误触发率从 20% 降到了 4%。第二步给技能按风险分级。只读类技能查询、检索允许模型自动触发低风险写操作创建草稿、保存偏好允许自动触发但必须校验输入高风险写操作取消订单、发起退款、发送消息必须满足前置条件并且在执行前增加一个显式确认节点。name: cancel_order requires_skill: verify_user_identity requires_condition: latest_order_status pending_cancel confirmation: required: true prompt: 已查询到订单 {{order_id}} 当前状态为 {{status}}确认要发起取消申请吗第三步调整权限模型的粒度。把“该 Agent 拥有客服全部权限”改成“每个技能声明自己需要的权限模型只能调用已授权技能”。这样就算模型路由失误系统层也会拦截高风险动作不至于直接执行。4.5 复盘沉淀出的一条通用规则这次事故之后我定了一条铁律凡是会改变外部状态的技能必须在定义里显式声明副作用并自带确认分支。所谓副作用就是调用之后会对现实世界产生持久影响的动作——发消息、删数据、改状态、扣款、退货。技能定义里如果没有显式声明“我改变了什么”系统就默认这个技能不应该被静默执行。现在我会在技能定义里加一个side_effect字段风险级别示例处理策略只读订单查询、知识库检索可自动触发无需确认低危写操作保存用户偏好、创建草稿可自动触发校验输入高危写操作取消订单、发起退款、发送消息强制前置校验 显式确认 审计一个技能定义里有没有这层意识直接决定了你的 Agent 系统是“生产可用”还是“玩具状态”。5. 让技能组持续演进评测、灰度与团队级沉淀5.1 用评测集锁住行为回归别靠感觉调描述技能系统能跑起来之后最需要警惕的其实是“改坏”问题。很多人调技能描述全凭感觉今天模型误触发了一次他就把when_to_use多写一句“不要管 A 情况”明天另一个案例误触发了他又在never_use里补一句。这么调下去技能描述最终会变成一个充满补丁、前后矛盾的大杂烩而他自己根本不知道哪些改动影响了哪些行为。我现在的做法是给每个技能建一个评测集。每个技能至少配 30 条测试用例覆盖四类场景正常场景用户表达清晰预期触发该技能并完成全流程。边界场景字段缺失、信息模糊预期技能会追问或降级。混淆场景用户提到关键词但意图不符预期不触发该技能。风险场景涉及高危操作预期触发前置校验或确认。每条用例不仅记录“预期技能名”还记录“预期关键动作”。比如“订单被取消错了”这条用例预期技能是check_order_status预期关键动作是“查询订单状态”预期不触发动作是“发起取消”。每次修改技能定义都把评测集整个跑一遍看哪些用例从 PASS 变成 FAIL用数据判断改动是优化还是引入回归。5.2 灰度发布与版本管理技能也要当代码一样管理技能定义虽然看起来像配置但它的影响和代码一样大所以版本管理不能少。我的做法是技能版本和 Agent 版本彻底解耦每个技能用语义化版本号发布走dev - staging - prod的流程。灰度期间重点盯五个指标任务完成率、平均执行步数、转人工率、用户投诉率和 token 消耗。如果新版本技能大幅降低了转人工率那说明意图理解更准了如果平均步数变长了可能是流程变繁琐了如果 token 消耗突然涨了一截可能技能描述过长或者重试变多了。任何一个指标出现异常就把路由回滚到上一版本而不是在线上现场改描述。技能版本的变更记录也要留好。我见过太多团队出了事故之后问“这个技能什么时候改的、谁改的、之前长什么样”结果没人答得上来。技能库如果没有版本历史就谈不上安全和可追溯。这是团队协作的基本盘。5.3 从个人脚本到团队技能库owner、评审与淘汰技能做多了之后另一个问题浮现出来技能成为了团队级别的资产但管理方式还停留在“某个人写的脚本”。解决的思路其实和代码管理很像每个技能必须有明确的 ownerowner 对技能的可用性和安全性负责技能上线前要通过评审至少检查描述是否清晰、步骤是否可判定、风险动作是否有确认机制、评测集是否覆盖混淆场景。还有一个被很多人忽略的点技能要能淘汰。业务会变模型能力会变某些技能可能已经不合时宜但它还躺在注册表里模型时不时误触发一下。我现在每个季度会对技能库做一次健康度盘点清点哪些技能连续 30 天没有被触发、哪些技能评测集通过率低于阈值、哪些技能描述与当前业务不一致。没有淘汰机制技能库只会越来越臃肿路由准确率也会被不断拉低。5.4 我现在落地的技能生命周期状态目前我在团队里维护的技能库大概有几十个技能每个技能都经过了“定义 - 注册 - 评测 - 灰度 - 上线 - 复盘 - 淘汰”这整套生命周期。这个过程让我最大的体会是技能化不是把提示词拆碎而是给 Agent 的决策过程建立了一整套工程护栏。模型仍然负责理解、生成和临场判断但什么时候用哪个能力、用错了怎么办、产生什么影响都由系统说了算。如果你正在做 Agent 应用我建议从一个小场景开始比如把一个客服工单流程技能化先跑通定义、注册、评测、回退这条链路再逐步扩大范围。前面这套基础不打好Agent 规模越大出事的概率和排查问题的成本也都会成倍上涨。这是我在实际项目里踩过坑之后的真实总结。