写 Skill 这件事,最让人上头的不是写不出来,而是写出来以后 AI 不照做。我最近一个月整理了一套自己的 Agent 技能库,前前后后写了二十多个 Skill,从会议纪要、周报生成到数据清洗都有。结果测试的时候发现一个特别普遍的现象:用户明明说“用周报技能”,AI 偏偏自己发挥写了一版;有时候它倒是调了,但输出格式跟我在文档里定的完全不是一回事;还有时候整个流程跑到一半就断掉,连个报错都没有。
一开始我以为是模型理解能力不够,后来排查多了才明白,大部分问题根本不在模型,而在 Skill 本身的设计。AI 能不能按要求执行,不是看你的描述写得有多详细,而是看你在几个关键环节有没有做到位。今天就把踩过的坑整理出来:遇到“Skill 写好了,AI 还是不按要求执行”的情况,先查这四件事——技能有没有被 AI 看见、参数接口有没有对齐、执行步骤够不够原子、结果校验有没有闭环。这四条是我实际调试时最常用的排查路径,基本覆盖了 80% 以上的失效场景。这篇东西主要写给两类人看:一类是在写 Agent 技能时被模型气到的开发者,另一类是天天跟提示词打交道、想让 AI 稳定交付结果的产品同学。
1. 第一件事:AI 到底“看见”你的 Skill 没有
1.1 描述不是说明书,是“搜索摘要”
先说最容易被忽略的一点:AI 不是拿到你的 Skill 文件就从头读到尾的。在绝大多数 Agent 架构里,模型会先根据用户当前这句话的意图,从技能库里挑选几个“可能相关”的技能,然后才读取对应的 Skill 内容。那它是靠什么挑的呢?靠的就是 Skill 描述字段,也就是那个 description。可以把它理解成搜索引擎:用户说“帮我把这周工作理一理”,你的描述里如果压根没有“周”“工作”“整理”这类词,模型就判断这个技能不相关,后面内容写得再完美也不会被打开。
我见过很多 Skill 描述写的是“这是一个周报生成技能,适合需要周报的用户”,这种描述基本等于没有。该说的不是这个技能适合谁,而是用户那句话里哪些词能命中它。更合理的写法是:第一句点明功能、输入、输出,后面把用户可能会用的各种自然表述埋进去。比如描述写成“根据聊天记录或项目数据生成结构化中文周报,包含本周完成、下周计划、风险与阻塞、数据指标摘要,能自动识别日期范围。适用于总结、周报、weekly report、进展汇报、给老板汇报等场景”,这样用户说“写周报给老板”时,命中概率就高得多。还是那句话,描述是给检索用的,不是给模型朗读的。
1.2 命名和边界:让技能库不打架
技能多起来以后,命名也会影响“能不能被看见”。如果 Skill 的标识是一串无意义编码,比如 skill_1234,模型就算匹配上了,你在日志里也看不出它调的是哪个技能,排障的时候两眼一抹黑。更头疼的是同一个 Agent 里塞了太多功能混杂的 Skill,描述里全是“总结、翻译、改写、润色”这种高频词,模型在选型时就会纠结甚至选错。
我的经验是:命名尽量让功能词可见,比如 document_weekly_report;同时一个 Skill 只负责一条职责线。有人喜欢把一个技能写成“万能工具箱”,既能总结又能翻译还能生成图片,表面看是省事,实际是给模型埋雷。多个意图挤在一个 Skill 里,描述字段互相干扰,调用的参数也无法收敛,最后的归宿就是哪个功能都做不深。如果真要支持多个功能,不如拆成多个 Skill,各自维护,触发逻辑清晰,测试也好定位。我在实际调试中发现,职责边界清晰的技能库,比一个庞大的全能技能可靠得多,因为模型每次只需要做一道简单的选择题,而不是在十几个功能里做复杂的意图消歧。
1.3 验证“被看见”的三个办法
发现症状以后,怎么确认到底是不是触发问题?我常用的办法有三个。第一个是看 trace 日志:大多数 Agent 框架都会记录工具调用事件,里面能看到技能标识和传入参数,这是最可靠的证据。第二个是在 Skill 指令开头加一句“执行前先输出‘使用周报技能开始处理’”,让 AI 主动声明调起了哪个技能。这个办法简单粗暴,适合快速验证,尤其是你手头没有完整日志链路的时候。第三个是准备三五个不同表达方式的测试用例,比如“写周报”“汇总一下本周进展”“给老板的汇报材料”,逐个触发,看哪些能命中。
如果技能明明匹配了但还是没触发,再往描述和命名上找原因。这里我列出不同症状对应的排查方向,方便对照:
| 症状 | 可能原因 | 优先排查 |
|---|---|---|
| 完全没有调用记录 | 描述没命中 / 技能被禁用 / 标识拼写错误 | 检查描述关键词和日志 |
| 调用了但回答不对 | 参数映射错误 / 步骤模糊 | 检查参数接口和执行步骤 |
| 时好时坏 | 描述边界模糊 / 多个 Skill 竞争 | 检查命名和职责边界 |
看到“时好时坏”的时候,很多人第一反应是模型随机性,但我查下来,这类问题十有八九是技能描述里的关键词太宽泛,同时有好几个技能都能沾边。模型每次选中的技能不一样,表现自然就不一样。把技能的描述边界写窄一点,把高频词的使用权集中到一个技能上,这种现象就会明显减少。
2. 第二件事:参数接口有没有“对齐”
2.1 输入参数要像函数签名一样严谨
很多人把 Skill 触发问题解决以后,跑了两次就发现:技能是调用了,但 AI 传进来的参数一塌糊涂。原因很简单:你定义参数时只写了“会议内容”“项目进展”这种名称,却没告诉模型这些值从哪来、取多少、哪些才符合要求。
有一种很典型的错误:我写“会议纪要”Skill 时定义了 meeting_content 参数,类型是字符串,必填。结果用户说“把我们刚才讨论的内容做成纪要”,AI 把整个对话历史都塞进来了。为什么?因为模型不知道“会议内容”该怎么提取。后来我在参数描述里加了一句“只提取与议题相关的发言,排除寒暄、提问铺垫和无关内容”,输出立刻干净了很多。所以参数描述不能只写“名称 + 类型 + 必填”,还要写提取规则和边界。你把参数想象成函数入参,只有名称没有类型说明,调用方自然会乱来;而 AI 这个“调用方”比人更需要明确的说明——它不会像人一样问一句“你说的会议内容是指哪一段”,只会默默选择一个自己觉得合理的角度,然后直接开干。
2.2 缺参数、可选参数怎么办
另一个高频问题是“用户没提某个字段,AI 怎么办”。有些 Skill 的参数是必填的,但用户在对话里并不会一次性说全。比如“生成邮件草稿”这个技能,收件人、主题、正文都是字段,用户只说“帮我写封邮件向客户道歉”,收件人和客户名称都是空的。AI 这时要么编一个假邮箱,要么把参数留空导致下游报错,最后你看到的结果就是“技能执行了,但完全不可用”。
我给这类字段补充“缺省行为”后,情况好了很多。比如在参数描述里写“收件人(可选):如果用户没有提供,输出中显示[待补充收件人],不要自行猜测邮箱地址”,把“不做什么”也写进参数说明。事实证明,模型对“不能做”的遵守程度,往往比对“应当做”还要高。这个现象很有意思:你让它“生成一封道歉邮件”,它自由发挥的空间很大;但你告诉它“不许编造邮箱地址,必须写成待补充”,它在这一点上就会很老实。缺省行为本质上是在消灭模型的猜测空间。
2.3 输出也要定“交付标准”
参数对齐不只是输入,输出更关键。很多时候你翻日志发现 AI 确实执行完了,但你看到的结果格式不对、字段缺失,于是你以为它没执行。实际上,问题出在输出定义太松。我见过一个数据总结技能,输出要求只写了“整理成报告”,结果 AI 有时候输出 Markdown 表格,有时候输出纯文本列表,有时候又给一段散文,下游程序根本没法解析。
我给技能写输出规范时,固定给三样东西:结构模板、示例、校验规则。结构模板规定分成哪几个模块,示例让模型知道填好的样子,校验规则要求模型输出前检查“哪些字段不许为空、哪些结构必须存在”。比如周报技能里,我写了风险项如果为空必须写“无”,不能只留个空列表;日期范围要精确到月份,不能写“本周”;每个列表至少一条内容。加了这三样以后,输出的一致性完全是两个量级。要记住:AI 默认的输出习惯是“能省就省、能概括就概括”,你只有把交付标准钉死,它才会老老实实按格式走。
3. 第三件事:执行步骤够不够“原子”
3.1 大步骤是跑偏的重灾区
有些 Skill 触发正常、参数也对,但执行结果就是不对味。这时候要检查技能内部的步骤。AI 在执行一个多步任务时,遇到“分析数据并生成结论”这种宏指令,往往会选择最偷懒的路径:它不分析,直接生成一个听起来很合理的结论。所以“看起来是完成了,实际上什么都没做”。
我的经验是,把步骤写成“原子指令”,也就是“一个动作 + 一个对象 + 一个产出”。拿数据分析技能举例:不要写“分析数据生成结论”,而要拆成“读取数据 → 计算每列均值、中位数、缺失值比例 → 将这些统计指标整理为表格 → 基于表格指标写结论,结论必须引用具体数字”。每一步都有明确的输入对象和输出产物,模型就没有自由发挥的空间。这背后其实是在减少模型的不确定性:模型在含糊指令下会靠概率补全内容,指令越含糊,补全的随机性越大;指令越具体,补全路径越单一。把大步骤拆小,本质上是在做“概率收敛”。
3.2 别让异常情况把流程卡死
技能跑一半停住,很多情况是遇到了 Skill 里没定义的边界情况。模型发现数据为空,或者某一步没有结果,而技能文档没有告诉它怎么办,它就会停下来、重复执行,甚至编造数据。我遇到过最离谱的一次:一个“客户反馈整理”技能,输入数据为空,AI 硬是编了三段“客户反馈”,看起来像真的一样,实际上全是幻觉。
所以我在写多步骤技能时会明确“异常分支”。比如周报技能里,如果输入中没有聊天记录或项目数据,就输出“缺少聊天记录,请先提供,我暂不生成周报”,绝不编造。再比如搜索类步骤如果返回结果为空,就明确“输出无结果提示,而不是给出猜测内容”。把异常行为写成显式规则,模型才会在面对分支时走对岔路。本质上,你是在给模型画一张路线图,标清楚“哪里能走、哪里是悬崖”。没有这张图的模型,完全可能选一条看起来近但其实是错的路径。
3.3 多轮状态别指望模型记住
还有一个隐蔽问题:跨轮次状态。如果你的 Skill 需要和用户交互多轮,比如先收集信息再生成内容,AI 往往会“失忆”——它上一轮已经收集到三个字段了,这一轮还继续问你已经给过的字段。这不是模型笨,而是技能文档没有告诉它“哪些字段已经拿到、下一步要做什么”。
现在我会在 Skill 里增加状态说明,比如“收到新消息时,先更新对应字段,检查是否所有必填字段都已收集完成;如果已完成,直接进入生成阶段,不再提问”。有的框架支持显式的状态变量,即便文档里不支持,用文字规则也能显著改善多轮体验。重点是让模型知道“当前在哪一步、下一步去哪”,而不是每次都从头扫一遍。另外,我还会在步骤里加入“完成标志”的描述,比如“如果用户说‘开始吧’或‘就这样’,则视为信息收集完成,进入结果生成阶段”,这样多轮对话的结束条件也清楚,AI 就不会一直问你问题了。
4. 第四件事:结果校验与反馈闭环
4.1 “执行了”不等于“交付了”
这类问题最迷惑人:日志显示 Skill 调用了、参数也对、步骤也走完了,但最终呈现给用户的结果却是错的。我遇到过几种典型情况:一是输出格式不符合下游解析要求,程序解析不了就直接扔了个报错给用户;二是 AI 生成的内容里有幻觉成分,比如周报里写了一个根本不存在的项目进展;三是流程图跑完但结果里没有把关键信息带回给用户,比如工具执行成功,但 AI 忘记了把工具输出拼进最终回答。
如果你把这类问题误判成“没执行”,就会陷入反复改触发条件的死胡同。正确做法是在 Skill 里补上“输出前自检”和“结果反馈”两个机制。前者让模型自己拦截低级错误,后者让失败信息能重新进入处理循环。这两个机制加不加,对长链路技能的可靠程度影响非常大,尤其是那些包含多个子任务的技能,只要中间一个环节断了,后面全都会跟着歪。
4.2 加一道“输出前自检”
我最近给所有长链路 Skill 都加了最后一步:要求模型在输出之前,按校验规则逐项自查,并把自查结果附在输出末尾。哪怕只是写一句“必填项是否齐全:是/否;风险项是否已标记:是/否”,也能逼着模型在生成完内容以后再看一眼,很多低级错误在这一步就被拦下来了。这个动作尤其适合那种“看起来完成了,但仔细一看缺项漏项”的情况。
自查的动作本质上是让模型多一次推理路径。模型在第一次生成时可能带了很强的惯性,但如果你要求它“检查一遍再输出”,它往往能发现自己刚写的内容里缺项或自相矛盾。我在实践里观察到一个规律:输出前加了自检步骤以后,周报类技能的字段缺失率下降非常明显,大概能少一半以上的低级错误。这个技巧成本很低,收益却很直接,值得作为每个长链路技能的标配。
4.3 错误信息要能回传重试
最后一个容易踩的坑:Skill 里调用其他工具时,如果工具报错,技能文档没有定义后续动作,AI 就会直接把报错原文扔给用户,或者假装没看见继续做。这两种结果都很难看。直接扔报错,用户看不懂;假装没看见,后面生成的结论又建立在错误的中间结果上。
我现在写的技能,会在工具调用步骤后面跟上错误处理逻辑:“任何步骤失败时,记录错误原因,尝试重新执行一次;仍失败则向用户返回‘步骤名称 + 失败原因 + 已完成的中间结果’,不要自行掩盖错误。” 这个规则让 AI 在出错时至少是“可对话”的,而不是僵死或胡编。特别是长流程技能,一旦某一步失败,重试和断点续传的能力比一次跑通更重要。你还应该告诉模型哪些错误可以重试、哪些错误不要重试,比如“网络类错误可以重试两次,业务逻辑错误不要重试,直接返回失败原因”。这样 AI 才不会被一条错误信息卡在死循环里。
5. 一个真实案例的完整复盘
5.1 场景描述
上面四件事说起来抽象,我拿一个真实案例把排查过程串一遍。之前同事让我写一个“项目周报生成”的 Skill,他反馈的症状是:他明明写好指令发给 AI,结果 AI 大多数时候不调用技能,偶尔调用了,生成出来的周报没有风险项,格式也乱。“也没有报错,它就是不好好按吩咐干活”,这是同事的原话。听起来像是模型不行,但我把 Skill 文件打开一看,问题其实都藏在细节里。
5.2 按四件事逐项排查
第一步,查触发。我在日志里看到,同事的常见说法是“给老板写周报”,而技能描述里写的是“生成项目周报”,关键词命中的权重很低——用户的那句话里包含“老板”“写”“周报”,而描述里只有“周报”,匹配率自然不高。于是我把描述改成了“根据聊天记录或项目数据生成结构化中文周报,适用于写周报、进展汇报、给老板的汇报材料等场景”。
第二步,查参数。周报技能定义了一个 project_updates 参数,但我没有说明提取范围。模型把整段对话全塞进去,导致周报里出现寒暄和无关内容。我给参数加了一段“只提取项目进展相关内容,排除无关对话”的说明,这一处修改的直接效果就是周报正文干净了,不会再把“早上好”“好的呢”这种话写进去。
第三步,查步骤。技能里“分析风险”写得太含糊,模型为了省事,经常输出“无风险”。我把这一步改成“按时间、人力、外部依赖三个维度逐一检查,列出具体风险项;如果确实没有则写‘无’,不要留空”。这个改动看着不起眼,但它逼着模型按维度想一遍,风险项漏报的情况立刻就少了。
第四步,查校验。原技能没有输出自检,导致格式漂移。我加了两条规则:日期范围必须精确到月份;每个板块至少一条内容,否则重写。到这里,整个技能才算有了一个完整的闭环。
5.3 修改后的 Skill 长啥样
改完以后基本是这个结构,我给同事整理了一个模板,你也可以参考:
name: document_weekly_report description: 根据聊天记录或项目数据生成结构化中文周报,适用于写周报、进展汇报、给老板的汇报材料等场景。 input_params: project_updates: type: string required: true description: 从对话中提取的项目进展内容,排除无关寒暄。若用户未提供,先询问。 steps: - 提取项目进展内容,过滤无关对话 - 按时间、人力、外部依赖三个维度检查风险 - 生成周报,板块包含:本周完成、下周计划、风险项、一句话总结 - 输出前自检:日期精确到月份;每个列表至少一条内容;风险为空必须写“无” error_handling: - 缺少输入材料时:提示用户补充,不编造 - 单次执行异常时:记录原因,重试一次这个模板并没有多复杂,但每个字段都有了明确职责。同事照着改完以后,用五组输入做了回归测试,包括“写周报给老板”“这周干了啥帮我总结”“客户想看项目进展”“下周计划是什么”等等。五组里有四组能正确触发技能,触发后输出格式也正常了;剩下那一组是纯“下周计划”的问题,我把它加进描述关键词后也通了。整个过程不到一小时,全靠四件事排查清单一步步筛。
5.4 排查速查表
我把最常用的排查顺序压成了一张表,每次 Skill 不听话就先按这个跑一遍:
| 顺序 | 检查点 | 关键问题 | 快速验证 |
|---|---|---|---|
| 1 | 触发 | AI 是否调用了技能?描述是否覆盖用户表达? | 查 trace、让 AI 声明技能名 |
| 2 | 参数 | 输入提取规则是否明确?缺省行为是否声明?输出格式是否有模板? | 单独传一个测试输入看参数值 |
| 3 | 步骤 | 大步骤是否拆成原子指令?异常分支是否定义?多轮状态是否说明? | 走一遍全流程,观察卡点 |
| 4 | 校验 | 是否有输出前自检?错误信息能否回传重试? | 故意制造一次失败,观察表现 |
这张表我打印出来贴在工位上,每次改技能或者调 Agent 的时候都会先对照一遍。别看它简单,很多复杂的问题查到最后,其实就是表里某一行的细节没做到位。
6. 动手写过二十多个 Skill 以后的经验
6.1 一次只改一个变量
排查时最容易犯的错是同时改好几个地方,结果问题修好了也不知道是哪个改动起了作用。我在测试中会强制自己一次只改一个变量,改了描述就先只测触发,触发没问题再改参数,参数没问题再动步骤。这样做虽然慢,但每次改动都有了明确的因果证据,积累下来的经验才真正可复用。尤其是当你同时在维护十几个 Skill 的时候,如果不养这个习惯,出了 bug 根本定位不到源头。
6.2 用例子代替形容词
写步骤描述时,少用“仔细”“高质量”“充分分析”这类程度词,多用例子来对齐模型的理解。比如“输出一封语气诚恳的邮件”,模型不知道什么算诚恳;但如果你给出一封示例邮件,模型就知道“诚恳”对应什么样的句式、节奏和标点。模型对示例的依赖比对形容词强得多,一个例子胜过十句话。这也是为什么我现在给每个 Skill 都配一个示例输出,而不是只写规则。规则是抽象的,示例是具体的,模型天生更擅长从具体样例里学模式。
6.3 测试集要留好
写完一个 Skill,我习惯顺手把测试输入和期望结果存在一个固定目录里,下次改动以后直接跑回归。很多人没有这个习惯,改完一个技能就上线,结果用户换了种说法,技能立刻失灵。测试集不用多,覆盖主流程、边界情况、异常输入三到五个样例就够用,性价比极高。我自己的目录里已经攒了上百条测试用例,每次改完技能就跑一遍,心里才有底。这看起来费时间,其实是在帮你省未来排查问题的几倍时间。
6.4 别一上来就给高权限
最后说一个有点反直觉的经验:Skill 的权限和自由度,一开始应该给得越小越好。很多失效问题其实是“太自由导致的跑偏”。先给 AI 严格的步骤、严格的输出、严格的校验,确认整个链路能跑通,再逐步放宽,让它提供更多灵活表达。反过来做的话,你根本分不清问题是出在自由度还是实现细节上。我见过不少团队上来就给 Agent 配了全套工具权限,结果模型一顿自由发挥,出了错都不知道是哪个环节的锅。
我个人在实际调试中的体会是:Skill 不叫“写好了”,而叫“调好了”。写,只是第一步;真正让它变可靠,靠的是按这四件事一遍一遍地查、改、验。如果下次遇到 AI 不听话,别急着怀疑模型,先按触发的顺序把这一套跑完,你会发现大多数问题,答案早就在自己的技能设计里了。