大约半年前我接手了一个有点头疼的内部项目一个要自动处理报表汇总、会议纪要和周报生成的智能助手。第一版我做得很“标准”——把每个功能的执行步骤全部写进系统提示词然后告诉模型“看到A就做B看到C就做D”。初版演示效果确实唬人但随着业务方不断加需求提示词从几百字膨胀到几千字模型开始频繁“自作主张”漏步骤、乱调用、甚至把草稿直接当成邮件发出去。那段时间我几乎每天都在怀疑人生是我指令写得不清楚还是模型本身就不适合这么干后来我彻底换了一套思路不再费劲教模型“怎么做”而是给模型一个“工具箱”让它自己判断什么时候该用哪把工具。这个方案就是我们内部叫agent-skills的技能体系。简单说就是把各种原子能力封装成可复用、可注册、可调度的“技能包”模型只负责理解用户意图并从技能清单里选一个合适的来调用具体执行交给代码而不是交给自己脑补。整个重构过程和后续踩坑我觉得挺值得拿出来聊聊。如果你也在做类似的应用比如智能客服、自动化办公助理、能操作各种工具的多步骤助手这篇文章应该对你有用。我会把自己从设计到落地、再到翻车修复的完整链路写出来包括技能怎么拆、描述怎么写、调度怎么设计、权限怎么卡以及那些文档里不会写但实操中一定绕不开的坑。1. 我为什么放弃了“把逻辑全塞进提示词”这条捷径1.1 提示词越写越厚模型越用越“飘”先说具体的问题。第一版助手的系统提示词里我写了密密麻麻的规则“如果用户提到汇总先读取文件再清洗数据最后生成表格如果用户提到周报先检查时间范围再逐项填充注意不要写废话……”看上去逻辑自洽实际上模型天然是概率推理不是规则引擎一旦规则之间存在交集它就会开始“表演”。印象最深的一次用户让“写一封会议邀请并保存到草稿”模型做了三件事自动调出通讯录、选择了参会人、然后直接点了发送。当时我盯着日志看了五分钟找不到任何一行代码让它“发送”但模型就是觉得“这是最完整的处理方式”。这类问题不是靠多写两行提示词能解决的规则越多互相干扰越严重而且新需求一加老行为就跟着漂移。另一个隐藏成本是 token。几千字的提示词每次会话都要完整传入响应变慢、费用变高而且模型在长文本里找关键指令就像让学生在一百页考纲里找考点找对了算运气找错了才是常态。1.2 从“教模型做事”到“给模型一把工具箱”转折点出现在一次线下讨论。当时大家聊到一个问题如果新来一个实习生你会怎么带他很少有人会事无巨细地教他“点哪个按钮、填哪个字段”我们更倾向于告诉他“遇到什么情况就找哪个负责人”。这个“负责人”就是一个能力单元。模型其实也一样——它最强的是语义理解和意图判断最弱的是稳定执行多步骤操作。所以重构时我给 agent-skills 定了一条核心原则模型负责决策代码负责执行。把“读文件”“解析表格”“发送邮件”“调用某个查询接口”这些行为封装成独立模块每个模块有名字、有描述、有输入输出协议。模型拿到用户诉求之后不是自己琢磨怎么一步步干而是从技能清单里选一个匹配的然后把参数填好交回给执行层。这套思路后来被很多主流大模型 API 所支持在当时的语境下更准确地说是一种“工具调用”模式。但把它工程化落地尤其是整理成一套可持续扩展、可观测、可管控的技能体系才是真正的难点。我到现在都记得第一次跑通“用户说需求→模型自动选中技能→执行代码返回结果→模型接着回答”全链路时那种“我好像找到了正确方向”的感觉。2. 技能拆解构造一个稳定的最小能力单元2.1 技能粒度的把握太大会失控太小会碎成渣做技能体系第一个绕不开的问题是一个技能到底应该多大我一开始特别容易做得“大而全”比如封装一个“处理报销单”技能里面一口气做了票据识别、金额校验、审批流程发起、消息通知四件事。听起来很方便实际用起来非常难受模型根本没有足够细的信息去控制内部逻辑只要其中一步出错整个技能就跟着失败而且失败原因极难定位。更麻烦的是下次其他场景只需要“报销单识别”这一个子功能你又得复制一份代码。后来我们把技能拆成了四层原则一个技能只做一件完整的事输入输出边界必须清晰技能之间不要互相调用如果有复合需求交给模型编排而不是代码编排公共基础操作比如字符串格式化不要做成技能那是函数库的职责。按这个原则“处理报销单”被拆成了“提取报销信息”“校验票据有效性”“生成审批单”“发送状态通知”四个技能各自独立测试、独立部署、独立描述。效果立竿见影——模型的选择准确率上来了因为每个技能的触发场景非常明确。当然也踩过拆得太碎的坑。有一阵子我把“读文件”拆成“读Excel”“读CSV”“读JSON”理由是参数结构不同。后来发现模型经常在JSON文件场景下选了读Excel因为用户说“数据太乱”它就开始猜。最后我还是合并成“读取表格文件”并允许自动探测格式让参数里的格式字段可填“auto”。这个教训让我明白粒度不是越细越好判断标准是“模型能否根据用户的自然表达准确命中该技能”。2.2 技能描述是给模型看的“说明书”这是整个 agent-skills 体系里最容易被低估、也最影响效果的部分。很多开发同学默认描述只是给人看的注释随便写一句“文件复制功能”就完事了。但你要意识到在模型调用时它看到的不是技能源码而是“技能名 描述 参数定义 返回结构”这一小段文本。它完全依赖这段文本来判断“现在该用哪个”。我整理过一个直观对比差描述文件复制功能中描述将文件从源路径复制到目标路径好描述当用户需要把本地文件复制到远程主机且目标路径尚未存在时调用此技能完成一次性复制若目标路径已存在不要使用本技能改用 update_file 技能。差描述为什么不行因为“文件复制”这个词太泛模型在用户提到“把资料传到服务器”“同步一下配置文件”时根本不确定该不该用。而好描述把触发条件、前置条件、边界情况都写清楚了模型判断起来非常省力。后来我们总结了一套描述模板触发场景、前置约束、执行动作、返回值含义、不该使用的情况。尤其是“不该使用的情况”很多人不写结果模型经常把相似技能张冠李戴。写描述还有两个细节。第一不要在描述里出现“强大的”“智能的”“全能”这类形容词模型会被带偏。第二描述要用模型容易理解的自然语言而不是堆术语。一个准确的描述抵得上几千行提示词这句话我现在完全信了。2.3 参数与返回值的协议设计技能描述决定模型“选不选”参数和返回值决定模型“用得顺不顺”。我们统一要求每个技能使用类似 JSON Schema 的方式声明参数字段名、类型、必填、默认值、枚举范围都要写清楚。有一个常见的坑模型提取参数时特别喜欢“自由发挥”比如数字单位、日期格式经常错。用户说“一周内”模型可能就顺手填空“2025-01-01 到 2025-01-08”这就是必须要在参数层做收敛的原因。返回值我设计了统一的三段式结构状态码、结果数据、附加信息。状态码只有三种大类success表示成功retryable表示这次失败但可以重试fatal表示条件不满足或永久失败。这个设计在后来的错误处理里帮了大忙因为它给了模型非常明确的下一步指令——成功就基于结果组织回答可重试就换种方式再试永久失败就直接告诉用户无能为力。还有一个很容易忽略的点技能返回给模型的数据同样占用上下文如果返回一个几千行的 JSON模型后面的推理质量会断崖式下降。所以我们在执行层做了一次“结果摘要”对超大结果只返回统计信息和前 N 条样例模型需要更细内容时再主动调另一个查询技能。这一步让整套系统的稳定性和速度都提升了不少。3. 技能注册与发现让 Agent 知道自己会什么3.1 技能清单与注册中心当技能数量超过十几个以后你不可能每次都把全部技能的完整描述塞给模型。一方面 token 成本受不了另一方面模型面对一大堆选择误选率会明显上升。这时候就需要一个“注册中心”每个技能在服务启动时注册自己的元数据系统按业务域分组会话开始后根据用户的初始意图和上下文只把相关领域的一小撮技能加载进模型。这个发现机制听起来简单做起来需要反复调。第一版我们直接用关键词匹配用户提“邮件”就加载邮件域的全部技能效果一般因为用户表达太灵活关键词覆盖不住。后来改成两段式先让模型对用户需求做一次粗分类输出可能的领域标签再根据标签加载对应技能清单。分类本身也是一个小模型调用成本不高但准确率提升非常明显。加载技能清单之后也不是每轮对话都原样传给模型。我们只会在“模型需要决定是否调用技能”的回合传入技能列表如果当前只是闲聊或补充信息就直接走普通对话不传工具定义。这个看似不起眼的优化把 API 的 token 用量省了差不多三分之一。3.2 元数据里除了描述还需要放什么技能注册时我要求每个技能必须带上这几类元数据缺一不可字段作用示例name技能唯一标识模型调用时使用send_emaildescription给模型看的触发条件说明触发条件、边界条件parameters参数 SchemaJSON key、类型、枚举permission权限等级read / exec / dangerous写文件至少 exec删数据必须 dangeroustimeout单次调用的超时上限10s / 60s / 300sversion技能版本号1.2.0deprecated是否已弃用true / false权限等级在注册时就写死而不是等到执行时才判断好处是可以直接在注册中心做拦截。比如一个技能的权限等级是 dangerous而当前会话没有经过人工鉴权那这个技能压根不会出现在模型的候选清单里。这样既省了模型一次错误的调用机会也避免了执行层反复校验的复杂度。版本号这个字段我一开始没太当回事直到后来踩了“会话人格分裂”的坑后面会讲才意识到它至关重要。现在我们的规则是技能一旦发布不可变更新必须发新版本旧会话继续用旧版本新会话默认用新版本。这个机制给线上排查省了无数时间。3.3 技能冲突与命名规范技能多了之后必然会出现两个技能都想处理同一意图的情况比如“创建日历事件”和“创建待办事项”用户一句“提醒我下周一开会”模型就可能在两者之间犹豫。我们解决冲突的方法有两个一是在描述里写清“不该使用的情况”把场景边界切干净二是给技能设计优先级如果模型输出多个候选技能执行层按优先级和上下文相关性选一个。命名规范也值得立规矩。我们规定技能名为动词开头的蛇形命名比如read_report_file、approve_expense禁止用纯名词。原因很直接动词开头的名称天然包含“做什么”的信息模型看到approve_expense就知道这是审批动作而看到expense可能会误当数据查询。这种细节听起来像洁癖但实际运行中确实明显降低了误调用率。4. 调度执行链路从模型选择到动作落地4.1 模型如何“想到”调某个技能也许有读者会问模型到底是怎么“决定”调用技能的从工程角度看主流大模型 API 会在每次对话请求里接收可选的工具列表列表里的每一项就是技能的 name、description、parameters。模型推理时如果认为当前用户需求与某个工具的描述匹配就会在输出结果里返回一个结构化的“你想调用这个工具参数是这些”信号而不再直接输出普通文本。我们这边再对信号做解析、校验、处理最后把执行结果以“工具返回值”的身份重新传回对话上下文让模型基于真实结果继续回答。这套过程实现起来并不复杂真正麻烦的是要让模型“想得对”。我们在测试阶段见过太多匪夷所思的选择用户明明在问统计报表模型却去调用发送邮件技能因为它觉得“用户可能是想发报表”用户让查一下文件里有没有敏感词模型却调用了删除文件技能。这些误选择大部分不是模型笨而是我们的技能描述提供了错误或模糊的引导调度器只是忠实地执行了错误的决定。所以我在设计调度层时加入了一个“选择确认”环节当模型选出的技能属于高危动作调度器不会直接执行而是把“模型想调用某某技能参数是什么”推给用户确认。这个环节在生产环境里救过我们好几次因为它把模型犯错的影响范围从“直接破坏数据”降级成了“一次错误但可撤回的提议”。4.2 参数提取、校验与执行隔离模型给出的参数几乎不可能一次到位。我们统计过即使描述写得很清楚模型输出的参数仍然有很高的比例存在类型偏差、缺字段、单位错误等问题。所以执行层的第一个动作永远不是跑业务代码而是参数校验和规范化把模型给的字符串日期转成标准格式、把不规范的数字单位补齐、把缺失的必填项标记出来。校验失败的反馈方式也很关键。如果直接把异常扔回去模型通常一脸懵只能胡编一个答案。规范做法是返回一段结构化的错误信息哪个字段有问题、期望什么格式、你刚刚给的是什么。模型看到这段信息往往自己就能修正参数后重新发起调用。我在实际运行中发现一次参数错误大约有七成概率能被模型自己修正剩下的三成才是真正的用户需求表达不清需要追问。执行隔离是另一个必须从第一天就做的事。每个技能调用都应该跑在独立的临时目录、独立的进程或容器里至少要有独立的工作文件夹。我们早期没有隔离两个 Agent 同时调“读取上传文件”技能结果互相覆盖了对方的临时文件导致某次线上演示当场翻车。从那以后所有技能执行都带上了会话 ID 加调用 ID 的隔离目录用完即清。我给调度器画过一张简表帮助团队理解执行链路环节输入输出失败处理意图分类用户消息领域标签列表忽略标签加载全部技能技能选择用户消息技能清单技能名参数无匹配则普通对话参数校验模型参数规范化参数返回修正提示让模型重试执行调用规范化参数结构化结果retryable / fatal结果回填结构化结果模型最终回复生成错误解释再回填4.3 错误处理让 Agent 学会“承认失败”很多第一次做 Agent 的人最害怕的就是模型“硬撑”——明明技能执行失败它却编一个似是而非的成功结果。要避免这个必须从底层切断“它能编”的可能。我们的做法是执行结果回给模型之前先强制转换成标准结构并且只要状态码不是success就把原始错误摘要放在最前面让模型没法忽略。对于retryable错误我们允许模型在同一轮里最多重试两次但每次重试前都必须调整参数或换策略不能原封不动再发一次。配合超时控制能有效防止死循环。对于fatal错误我要求模型做的不是再次尝试而是明确告知用户“这个事我办不了原因是……”并给出下一步可选路径。让模型学会承认失败比让它假装成功更有价值因为假装失败的代价通常是用户自己去填补所有不确定性那才是最差体验。5. 权限边界与安全隔离技能一旦放开约束必须前置5.1 最小权限原则拒绝“万能执行”给 Agent 挂技能时我最担心的从来不是功能不够而是权限失控。一个能正常跑报表的 Agent如果它的技能执行账号同时拥有删除文件的权限那一次误调用就是一次灾难。后来我们强制所有服务套用最小权限每个技能挂一个专用账号只授予完成该技能职责所需的目录和资源权限。写文件技能的账号只能写指定工作目录读数据库技能的账号只有只读权限绝不能一个技能走“管理员权限”一路通吃。权限收敛之后模型误调用造成的影响被压到了最小。即使它错误地调用了“清理临时文件”技能最多也只能删掉隔离目录里的临时文件碰不到正式产物和数据库。我把它理解成“给 Agent 穿上了防爆服”——它依然有可能做错事但伤害范围已经被控制住了。5.2 高危技能的双人复核机制不是所有技能都适合让模型一键执行删除、转账、对外发布这类动作我们统一挂到了高危名单里。模型可以把它们选进候选但真正执行前调度器会生成一条待确认任务推送到人工审批队列由业务方确认后才落地。这套“人机协同”的模式一开始被团队吐槽太重我们自己也嫌慢但上线三周后发生了一件事模型在一个会话里连续三次想把测试环境的数据同步脚本打到生产目录如果不是双人复核挡着后果很难收拾。从那以后再没人抱怨这个机制多余。5.3 审计与配额让每个动作可追溯最后是审计与配额这属于上线前就必须准备好的部分。每个技能调用都要记录哪个会话、哪个用户、什么时间、传了什么参数、返回了什么结果、花费多少 token、耗时多久。审计日志不是用来摆设的它在两类场景里特别有用一是线上问题复盘时能快速还原整个上下文二是排查模型异常行为时能发现那些“看起来成功但选项偏离”的隐性误选。配额则是防止 Agent 在异常循环里烧掉所有资源。我们对每个会话设置了调用次数上限和 token 上限一旦达到上限Agent 会直接停止自动执行并要求人类接管。有一次线上事故就是模型陷入了“重试—失败—重试”的循环三分钟里调用了上百次外部接口如果没有配额那个月的高额账单就要落在我们头上了。6. 实测中的踩坑记录那些重构时才发现的隐藏问题6.1 描述越花哨模型越容易误选技能的描述文案里我吃过一次很具体的亏。某个内部数据查询技能为了让“显得专业”在描述里加了一句“这是一个强大的万能数据分析工具适用于任何场景”。结果上线之后模型的误选率直接翻了一倍用户问天气它调数据分析技能用户问菜谱它也调数据分析技能。原因是那句“万能”给了模型一种幻觉——好像什么需求都能交给它。后来我把描述改回克制、具体、边界清晰的版本误选率才降下来。这个坑非常有代表性不要用营销话术给 AI 写说明书它会当真。6.2 同名技能在不同会话里的“人格分裂”技能迭代是常态但版本管理跟不上就会出现“人格分裂”同一个技能新会话用的是新逻辑老会话因为上下文里还残留着旧技能描述模型依然按旧思路调用导致同一个用户在不同时间问同一个问题得到的处理方式完全不同。有一次业务方投诉“这个 Agent 是不是换了个人”排查半天才发现是技能版本没有在会话维度锁定。解决之后我们明确了一条规则会话创建时快照当前技能版本后续更新只影响新会话旧会话保持兼容运行。6.3 并发调用导致共享资源被冲掉没有隔离之前两个 Agent 同时处理任务只要它们都调用“保存中间结果”这个技能就会写到同一个共享路径后写入的直接把先写的冲掉。我们在一次压力测试里发现并发一上来任务成功率啪地掉到一半以下排查时一度以为是模型变笨了后来才定位到是共享文件在打架。从那以后所有技能执行都带独立隔离空间临时文件加会话 ID 前缀并且目录权限互相不可见。这个问题属于那种“文档不会提醒你但生产环境一定会教你做人”的典型坑。6.4 模型强撑——再好的技能也救不了错乱的提示词还有一次问题出在我们自己身上。某个复杂业务场景里为了引导模型做多步操作我在系统提示词里写了非常长的“主线流程”结果模型反而被这些流程文字带偏频繁跳过技能调用直接自己“脑补结果”。后来我把主线流程从提示词里删除改成只保留目标描述让模型依赖每一步的实际返回结果来动态决定下一步正确率立刻回升。这个教训的普适版本是提示词越想把控制权抓在手里模型越容易发挥失常。把控制权交给技能机制本身反而更稳。7. 技能库的持续演化让 Agent 从“能用”到“好用”7.1 用请求日志反哺技能设计技能体系上线只是开始持续演化才见真功夫。我们每周会拉一次技能调用日志重点关注五类指标各技能调用次数、调用成功率、平均耗时、模型误选率、以及从未被调用的“僵尸技能”。很多技能开发时觉得很刚需实际两个月都没被模型选中一次那就要么是描述不准确模型根本没发现它要么是需求场景不存在。前者我们改描述后者我们直接下架免得干扰模型选择。7.2 像优化代码一样优化技能描述技能描述是消耗品要持续迭代。我们给每个高频技能建了一个“描述修改记录”每一次改动都记录原因。比如某个技能常被模型在“目标文件不存在”的场景下选中我们就在描述里加了一句“仅当目标文件已存在时调用”连续观察两周误选率确实下来了。这种优化方式和修代码 bug 没什么本质区别一个修行为一个修文案但后者成本更低、见效往往更快。7.3 定期重演与回归测试技能库一旦超过几十个最怕的就是改了 A 技能影响了 B 技能的选择。所以我们建了一套回归语料库把典型用户问题、预期应该调用的技能、预期参数范围都录进去每次技能描述或调度逻辑变更后批量跑一遍回归。它可能不会 100% 覆盖线上变化但至少能挡住最明显的回归问题比如“改完描述后模型再也不选这个技能了”。7.4 从技能库走向“能力市场”最后聊一句远期方向。agent-skills 这套体系真正走向成熟是当它允许不同团队各自维护技能、统一注册、按需订阅之后。某个团队做了一个日期识别技能其他团队可以申请调用某个技能质量下滑评分机制会让它慢慢淡出候选清单。这已经不是单纯的技术框架而是一个组织内部的能力市场。这段时间做下来我越来越确信让模型理解并调用技能这件事价值不在“接入多少个大模型 API”而在底层这套技能设计和管理的功力。基础打牢了上面换什么模型、加什么功能都是搭积木的事。如果你正准备做一个 Agent 项目我的建议很简单开工前别急着写执行函数先花两三天把技能的清单、边界、描述文本像产品一样逐条打磨。agent-skills 这个项目走到今天我觉得最值钱的资产不是那些执行函数而是那份越改越清晰的“技能说明书”。一次调用选对技能比模型事后补救省出十倍的时间和沟通成本。先写到这儿后面积累了新的教训我再来更新。