做 AI 智能体Agent这一两年我一直被一个矛盾卡着大模型本身已经很聪明了能理解复杂指令、能写代码、能调用工具但一旦让它连续处理二十个步骤、跨五个领域、还要应对各种意外输入它就很容易飘——要么忘了前文目标要么在一个简单分支上来回打转要么把上下文撑爆。我一度以为问题的解在更强的大模型、更大的上下文窗口。直到我把精力从让 Agent 更聪明转移到把 Agent 的能力拆成可积累、可复用、可测的技能包之后情况才有了质变。这篇文章想分享的就是我这套围绕 agent-skills 理念构建的完整方法论从技能数据模型、分层体系、编写节奏到测试回归和踩坑实录全都在里面。1. 我为什么放弃提示词堆叠转向技能化再造先说一个很反直觉的观察Agent 的失败大多数时候不是因为它不够聪明而是因为它把太多东西混在一个上下文里处理了。1.1 通用大脑的尴尬大模型本质上一个通用推理引擎。它的优势是灵活什么都能聊两句但劣势也恰恰来自这种灵活——没有结构约束时同一类操作它今天怎么执行明天可能就换了一套路径。比如你在一个指令里同时塞了数据分析、生成图表、写邮件、安排日程短时间没问题但任务一长、轮次一多模型就会陷入一种上下文疲劳早期指令的影响越来越弱后面自己生成的中间结果反而成了主导。我做过一个压力测试让同一个 Agent 连续执行 30 个批量处理任务每个任务之间只有很小的差异。前 5 个任务表现完美到了第 15 个左右它开始忽略某些参数约束到第 25 个时甚至会把几个任务的数据混在一起。这跟模型能力无关是纯结构问题——Garbage in, garbage out只是这里的 garbage 不是数据而是没有边界的控制流。1.2 技能到底是什么我的理解是技能不是提示词也不是工具调用而是介于两者之间的一套自包含能力单元。它具备三个特征明确的输入输出契约知道要接收什么知道交付什么也知道失败长什么样。可组合性一个技能可以被更大的技能调用也可以被替换成等价实现。可独立评测单独拉出来测试和放在整个任务链路里测试结论一致。类比一下这就好比一个大型餐厅的后厨。大模型是厨师长什么都懂但你不可能让他同时负责洗菜、切菜、炒菜、摆盘。技能化再造就是给他配了一整套标准化工序洗菜岗有洗菜的标准流程切菜岗有切菜的统一刀法厨师长只需要做核心决策——什么时候开火、放什么佐料。这样哪怕今天换了个厨师长后厨的出品质量依然稳定。1.3 技能 vs 提示词 vs 工具 vs 工作流很多人容易把这几个概念混在一起我用一张表理清它们的分工概念核心载体灵活性稳定性典型场景提示词自然语言指令高低一次性对话、创意发散工具函数/API 封装中高确定性的原子操作如发邮件、查天气技能指令约束回退逻辑中高中高需要多步骤且存在变化的复杂任务工作流硬编码流程低最高路径完全固定、不容偏差的业务流程注意中间那行技能的本质是用提示词做骨架、用结构化逻辑做约束、用回退策略兜底。它既不像工具那样死板到只能做单步操作也不像裸提示词那样全靠模型临场发挥。这是它存在的最重要理由。2. 技能库的骨架从数据模型到分层设计要搭建一套可长期维护的 agent-skills 体系第一件事不是写代码而是设计技能库的通用骨架。骨架不对后面每个技能的维护成本都会指数上涨。2.1 一个技能的最小信息单元在我自己的实现里每个技能都落成一个结构化的技能清单核心字段如下name: fetch_issue_detail description: 根据项目代号和 issue 编号获取详情支持翻页和字段过滤。 version: 1.2.0 triggers: - 获取 issue 详情 - 查看工单 #ID - 查询问题详情 input_schema: issue_id: { type: string, required: true, desc: issue 或工单编号 } project_code: { type: string, required: false, desc: 项目代号缺省则用上下文里的默认项目 } steps: - 解析入参校验 issue_id 格式 - 按 project_code 选择对应的数据源连接配置 - 调用数据源 API 获取主记录 - 若存在需要展开的子列表获取关联数据按需翻页 - 组装为结构化结果并返回 output_schema: issue_id: { type: string, desc: issue 编号 } title: { type: string, desc: 标题 } status: { type: string, desc: 状态 } comments: { type: array, desc: 评论列表 } failure_policies: - condition: 数据源返回 404 action: 标记为不存在不重试 - condition: 网络超时 action: 重试 2 次间隔 1s - condition: 入参校验失败 action: 立即返回错误交由调用方修复参数注意几个容易踩坑的设计点triggers 不要写太窄。很多人会把 trigger 写成获取 issue 详情结果用户说帮我看看那个工单咋样了就识别不到了。我后来直接把 trigger 去中心化了换成一套语义相似度匹配机制让技能名和描述来做召回trigger 只作为候选预筛选。这样覆盖面广得多。steps 是给模型看的不是给机器硬跑的。它是执行指南而不是流程代码。所以我通常会在 steps 里写清楚每个步骤的验收标准比如解析入参之后必须有一个结果对象不能直接拿原始参数往下传。这能显著减少模型跳步骤的概率。failure_policies 必须有。我见过太多技能没有失败处理一旦某一步出错整个 Agent 就陷入死循环报错 → 重试同一路径 → 再报错。有了明确的失败策略模型至少知道这条路不通就换路。2.2 三层技能体系技能多了之后必须分层不然会变成一团乱麻。我自己维护的是三层结构基础技能Base Skills最小原子能力比如发送 HTTP 请求解析 JSON读写本地文件执行 SQL。它们通常直接跟工具层一一对应不依赖其他技能。组合技能Composed Skills由多个基础技能或已有组合技能组装而来比如从远程 API 拉取用户数据并清洗成标准格式。领域技能Domain Skills面向具体业务场景的高层技能比如自动生成项目周报排查线上服务异常完成一个订单的履约流转。领域技能是最容易被复用的也是迭代最频繁的。分层的核心好处是隔离变化。底层基础技能很少动领域技能天天改如果它们混在一个池子里每次改动都要回归测试所有技能。分层之后领域技能的变更只需要测试它依赖的链路即可范围被限制住了。2.3 状态技能应该有记忆吗状态管理是技能系统最容易出问题的点。我的原则很简单默认无状态必要才引入会话上下文。无状态的技能最可靠输入相同输出就相同出了问题也好复现。但有些场景确实绕不开状态比如一个跨多轮对话的收集需求并生成方案技能它需要记住用户上一轮说了什么。这种情况下我会引入一个上下文暂存区它是一个容器只存当前会话的中间变量绝不跨会话残留。而且关键判断是中间变量必须能被技能内部的某个关键步骤显式写入和读取不能依赖模型自己从聊天历史里翻旧账。否则你没法控制它记什么、忘什么。2.4 技能注册表与动态装载最后是技能的入口。我通常会维护一个技能注册表相当于技能的黄页里面记录每个技能的名称、描述、版本、入口函数、依赖的基础技能列表。Agent 运行时不需要一次性把所有技能都加载进上下文。那样太费 token而且会让模型选择困难。我采用的是动态装载先根据用户请求召回最相关的 3~5 个技能然后只把这几个技能的 schema 和描述注入上下文。这招对成本和稳定性都有立竿见影的帮助——实测下来上下文瘦身 60% 以上选择正确率也上去了。3. 从能跑到通用我的技能编写四段论技能不是一次写成就完事的。我总结了一套自己的迭代节奏把它分成四个阶段每个阶段解决不同的问题。3.1 动作动词识别把模糊意图映射到具体技能第一个阶段是把用户需求里的动词对象提取出来映射到技能注册表。比如拉取昨天的销售数据 → 动词是拉取对象是销售数据映射到 fetch_daily_sales 技能。这个阶段不必做得很玄学关键是要建立一个意图 → 技能的索引表。我一开始是用关键词匹配的后来发现准确率不够改成了动词 对象类型的组合匹配。比如查看获取拉取读取这几个动词在业务语义上高度相似就归一化到一类。对象类型则靠技能描述里的语义表征来做召回。实践中这一层用的是向量检索技能描述预先生成向量运行时计算用户请求与技能描述的余弦相似度取 Top-K。这一步对技能体系的扩展性很重要技能多了以后靠关键词枚举根本维护不过来向量召回才扛得住。3.2 子任务拆分可组合性优先但不过度拆分进入技能内部设计时最常见的错误是把技能写得又长又大一步到底。我的经验是反过来任何一个技能如果它内部步骤超过 7 个或者需要跨两个以上数据源就应该拆成多个子技能再由一个编排技能把它们串起来。打个比方这就像写代码时遇到复杂逻辑你不会全塞进一个几百行的函数而是拆成多个函数再组合。组合的边界怎么定我有个简单的判断标准能独立描述、独立测试、独立复用的操作边界就是天然的技能边界。比如从订单系统拉数据和计算订单金额汇总是两个独立能力拆开但解析用户输入的地址字符串并补全省市区明显是一个内聚动作不要拆。过度拆分同样有代价。我见过有人把打开浏览器都做成一个技能最后技能数量爆炸每层都要做召回和编排反而拖慢了整体响应。拆分的粒度大约在一个熟练工程师把这件事当作单步操作的水平就好了。3.3 上下文压缩与多示例引导省 token 的大招Agent 调用技能时最耗 token 的不是技能描述而是示例和上下文回放。我早期在技能定义里塞了大量 few-shot 示例结果一个技能加载进来就吃掉两千多 token三四个技能就上万了。后面我换成两条策略示例分级每个技能只保留 1~2 个标准示例其余放到一个外部示例池里按当前输入与示例的相似度实时召回再注入。绝不做全量注入。上下文压缩对多轮会话的历史轮次做摘要用一句话概括此前已经完成的操作和结论。尤其是技能之间传递的中间结果只保留一个状态快照不保留原始大块数据。这两招配合起来一个复杂任务的 token 消耗能压到原来的三分之一。而且因为压缩后的上下文噪音少模型的执行准确率反而提升了。这是个值得反复实验的收益项。3.4 技能自我更新从失败现场长出新的分支第四阶段是我觉得最有价值但最被忽视的让技能能从失败中长出新分支。具体做法是给每个技能挂一个失败记录表每当技能执行失败就把失败模式、当时的输入、出错位置记录下来。定期人工复盘这些记录把高频失败原因固化成技能内部的新分支或新校验。比如我之前有个生成项目周报的技能经常失败在用户提供的项目代号查不到。失败记录里这个原因占比极高。后来我在技能里加了一个分支项目代号查不到时先尝试模糊匹配项目名匹配不到再反馈给用户确认。改动一行步骤成功率从 72% 拉到 94%。这就是把偶发问题变成确定性兜底的过程是技能体系最核心的成长方式。4. 评测体系没有数值就没有优化方向技能写得好不好不能靠感觉。我给自己定了一套可量化的评测框架这里分享核心思路。4.1 三类关键指标我每天盯的就三个数成功率Success Rate技能最终产出可验收结果的比例。这里有个细节——部分成功算不算成功我的定义是只要产出结果能被下游消费就算成功。哪怕中间重试了 3 次结果正确就计入成功。Token 成本Token Cost单次技能调用的总消耗含注入定义、上下文回放、输出 token。平均恢复时间MTTR技能从失败到恢复自动重试或转人工的平均耗时。这指标常被忽略但它决定了用户对 Agent 的耐心。我用一个简单的雷达图把这几个数画出来一个技能版本一个点迭代前后对照哪种改动真正有效一目了然。4.2 评测集黄金集、对抗集与线上抽样评测集是这套体系的底座。我维护了三类数据黄金集人工挑选的、覆盖各种典型路径的输入输出对约 100~200 条。每次技能改动后必须全量跑一遍黄金集任何一条不通过都不能发布。这是技能回归测试的主防线。对抗集故意刁难的数据比如模糊表达、缺失参数、异常输入、超长文本、少见的边界值。用来检验技能的鲁棒性。我一般每轮迭代往里面加 5~10 条新的对抗样本。线上抽样线上真实请求的随机抽样脱敏后跑离线评测用来发现黄金集覆盖不到的盲区。比较常见的误区是只做黄金集导致技能在标准问题上表现很好一遇边界就崩。对抗集的意义恰恰在于你给技能设计的每个失败策略都必须在对抗集上有对应的验证触发。4.3 技能版本与灰度换技能不能一刀切技能也是要发版、要回滚的。我在做技能迭代时不会直接替换线上版本而是先按流量灰度。比如新版本先承载 10% 的调用流量观察成功率与成本指标对比旧版本。如果新版本的成功率高且成本没有明显上涨再逐步扩大流量比例。有个很重要的指标不能只看平均成功率要看分场景的成功率。有些改动会让 80% 的场景变好但让另外 20% 的场景变差。如果只盯着平均值可能就吃下了这个暗亏。所以我在灰度时会按输入类型、输入长度、数据源等维度拆开对比任何一个维度出现显著下降都要先定位原因再决定是否放量。5. 实践中的坑从自查到重构的真实复盘再好的方法论落到真实环境也有意外。最后分享一段我实际走过的弯路。5.1 语义漂移技能名和描述被改歪了有段时间我让模型参与技能描述的自更新结果它把fetch_issue_detail的技能描述越写越宽最后几乎什么都能涵盖。技能召回就乱了用户问天气它都能匹配到这个技能上。后来我加了规矩技能名和描述改动必须人工确认模型只允许提修改建议。同时加了一个校验描述长度超过 50 个字就触发人工审查。语义漂移这个问题的根因是没人对技能边界负责解决方式就是制度和规则兜底。5.2 技能死锁两个技能谁也等不到谁还有一次线上故障特别典型技能 A 的某个步骤要调用技能 B 的结果但技能 B 的实现里又反向依赖了技能 A 的状态。两个技能都抱着等对方先完成的心态最后整个任务卡死。自查发现是组合技能设计时没考虑依赖方向形成了一张循环依赖图。修复方式很直接在技能注册表里显式声明依赖关系并在加载前做拓扑排序一旦检测到环就阻止发布。这也提醒我技能不是孤岛系统级视角必须在设计阶段就有。5.3 外部依赖脆弱技能稳定但数据源不稳定技能本身的逻辑没问题但外部数据源老超时整个链路就不稳定。刚开始我在每个技能里都写了重试结果流量高峰时所有 Agent 实例一起重试直接打崩了数据源。后来改成全局熔断某个数据源错误率超过阈值技能先进入降级分支返回缓存或提示稍后重试而不是继续硬撞。这种技能 外部依赖一体化设计的思路比单纯在技能内部做重试要靠谱得多。5.4 我最终沉淀的迭代节奏走了一圈之后我现在维护技能库的节奏基本稳定每周一次复盘上周失败记录表挑选 Top 3 失败原因设计修复分支。每两周一次向黄金集和对抗集补充线上抽样新样本全量回归。每月一次技能清理删除 30 天未被召回的技能合并语义高度重叠的两个技能避免注册表膨胀。灰度发布每次改动都走10% 流量 → 50% → 100%的节奏全程盯分场景指标。这套节奏执行下来我的技能库从最初容易失控的大杂烩慢慢变成了一个相对健壮的组织。线上成功率从 68% 提到了 93%token 成本降了 40% 左右最重要的是我再也不用整天盯着单个 Agent 会不会突然犯浑了。如果你也正在被 Agent 的不可控和上下文爆炸折磨我的建议是别急着换更强的模型先试着把你最常用的十类操作变成技能。给它们定义清晰的输入输出、失败策略和触发方式再做一套最小可用的评测集。这个动作本身可能就能解决你 70% 的智能问题。