
当你在 AI Agent 里不断追加 Skill 时很容易产生一个直觉技能越全Agent 应该越聪明。但实际运行中大量团队发现现象恰好相反——Skill 从十几个涨到几十个之后模型的工具选择开始飘忽任务执行经常绕远路甚至把不该调用的能力误触发整体回答质量反而下降。这个现象背后并不是模型变笨了而是 Skill 在每一轮推理里都在占用上下文、影响选择、改变行为偏好。下面从 Skill 的工作原理讲起拆解 Skill 数量导致 Agent 变笨的原因再给出一套可以落地验证的优化方案帮助你把技能库从“越攒越多”变成“越用越稳”。如果你正在维护一个多技能 Agent或者准备给 Agent 接入新的 Skill先不要急着加功能停下来看几轮日志和 token 使用情况可能比继续堆 Skill 更有效。1. 先搞清楚 Skill 在 Agent 中的地位能力扩展件也是提示词的一部分1.1 Skill 是什么从“工具函数”到“行为指令包”通俗理解Skill 是给 Agent 预设的一套“做某件事的方法说明”。它不只是给模型暴露一个函数接口而是把“什么时候用、按什么顺序做、要避免什么、输出什么格式”这些规则统统打包。单个 Tool 往往是一段可调用的函数或 APISkill 则处于更高的抽象层通常包含触发条件、步骤说明、约束规则、示例输出有时还附带脚本和资源。技术定义上Skill 是 Agent 运行时注入到模型上下文中的一组结构化指令和可选执行代码。它与 Function Calling 的区别在于Function Calling 更多是把函数签名当作模型可选择的动作集合而 Skill 在动作之外还携带完成这件事的方法论。它和 MCP 的关系也值得注意MCP 用于统一不同外部工具和数据的访问协议主要解决“怎么调用”的问题Skill 解决“什么时候调用、按什么规范执行”的问题。两者可以共存但 Skill 的指令并不一定走 MCP 通道它首先影响的是模型在计划阶段的行为。放在当前项目里看当你给自己的 Agent 加一个“根据日志统计异常分布”的 Skill 时你添加的不是一个普通函数而是一段类似软件的操作手册它应该说明日志来源、时间范围、聚合字段、输出格式、阈值提醒方式。模型拿到这个 Skill 后遇到日志分析任务时会按你定义的流程执行而不是临时猜测怎么做。容易误解的地方是Skill 数量代表的是“可用行为空间”不代表“该轮任务实际用的行为”。可用的行为越多模型在计划阶段需要评估的选项就越多。1.2 为什么 Skill 越多并不会让 Agent 更聪明先做一个粗略估算。假设每个 Skill 的元数据和指令平均占用 800 个 token20 个 Skill 就是 1.6 万 token50 个 Skill 就是 4 万 token。如果 Agent 的上下文窗口是 32K50 个 Skill 已经超出窗口即使窗口是 128K这些指令也要和用户问题、历史对话、外部返回结果、模型自己的思考链一起竞争剩余空间。Skill 数量按平均 800 token 计算在 32K 窗口的占用比例在 128K 窗口的占用比例108K25%6.25%2016K50%12.5%5040K超出31.25%10080K超出62.5%更关键的是这些 token 不是只在调用时才出现。在大多数实现里Skill 列表会在每个请求开始时被加载并作为系统提示或工具描述的一部分传给模型。用户输入很短、Skill 描述却很长时模型的注意力会被大量“该做什么、不该做什么”的说明分散。模型并没有变笨而是可用的推理余量被占用了。从系统工程的角度理解Agent 的“智能表现”取决于三者的比值有效指令密度、上下文可用空间、任务信息完整度。Skill 变多时分子不一定增加分母却稳定增加因此整体表现会下降。这也是为什么“Skill 越多越笨”不是玄学而是一个上下文预算问题。2. Agent 表现变差的四个核心机制上下文挤占、选择偏差、指令冲突、评估失灵2.1 上下文挤占每轮推理都要为所有 Skill 买单上下文挤占是最直接的机制。在常见 Agent 架构中一次请求进入模型前先要拼装 system prompt、Skill 集合、历史消息、工具结果等。Skill 越多这条拼接后的输入越长。长上下文有两个代价第一是计算代价。模型处理每个 token 都要参与注意力计算输入越长首 token 延迟和成本越高。第二是效果代价。长上下文里位置靠后的任务型指令容易被后续信息稀释模型可能忘记某个 Skill 的关键约束。实际项目里可以这样量化# 估算 Skill 描述对上下文的占用 # 以 OpenAI tiktoken 为例用于估算 token 数量不代表所有模型 import tiktoken enc tiktoken.get_encoding(cl100k_base) def estimate_tokens(text: str) - int: return len(enc.encode(text)) skills [ skill_log_analyzer: 分析应用日志并输出异常分布, skill_config_checker: 校验配置文件并给出修复建议, ] for name in skills: print(f{name} {estimate_tokens(name)} tokens)这里要注意不同模型分词器不同tiktoken 的估算只用于开发阶段生产环境应以模型 tokenizer 为准。这个脚本可以放进 CI新增 Skill 时自动检查描述长度。如果项目还没有 token 统计基础可以在加入 Skill 前后分别跑同一批测试任务对比输入的 token 总数和任务完成质量。2.2 选择偏差工具列表越长选错工具的概率越高Agent 在执行任务时会先决定调用哪个 Skill。Skill 列表越长选项越多模型需要区分不同 Skill 的边界就越困难。尤其是描述相似的 Skill比如“分析日志”和“搜索日志”“生成周报”和“汇总周报”模型很容易把任务路由到错误的技能上。出现这种问题时典型日志会是这样{ task: 统计今天订单接口的异常数量, chosen_skill: skill_log_search, reason: 任务包含统计和异常先搜索日志再统计, error: skill_log_search 只支持关键词检索无法完成聚合统计 }表面看模型给出了理由但实际上它选择了能力不足的 Skill导致任务多走了一轮。这类“看起来合理实际选错”的情况在 Skill 数量变多后会显著增多。处理方向不是取消选择机制而是降低选择难度。一是把相似 Skill 合并二是给每个 Skill 补充“适合场景”和“不适合场景”三是引入路由层在模型之前先按领域规则剪枝。2.3 指令冲突多个 Skill 的规则会在同一轮任务里相互打架这是最容易忽略的问题。单个 Skill 单独测试时都很好但当 Agent 一次加载多个 Skill 时它们之间可能产生规则冲突。比如一个 Skill 要求“遇到不确定的 SQL 先解释风险”另一个 Skill 要求“所有 SQL 必须加 LIMIT 100”模型同时收到两条规则后无法确定哪条优先。冲突不一定表现为报错更多表现为行为漂移同一任务在两次运行中得到不同做法一次严格加 LIMIT一次没有加。这种不确定性会让人感觉“Agent 变笨了”实际上是被冲突指令逼出了一种随机选择。检测冲突比较有效的办法是把所有 Skill 的“必须”“禁止”“优先”“除非”这类规则词抽取出来放到同一张表里人工审查。也可以用回归测试反复跑同一批任务观察输出是否稳定。2.4 评估失灵没有“边际收益”数据Skill 只能越堆越多最后一个机制来自团队管理层面。很多团队在评估 Agent 时只问一个问题这个 Skill 有没有用。只要任务能跑通就认为 Skill 有效然后不断新增。几乎没有团队会问新增的 Skill 让已有任务的成功率变高还是变低让平均 token 消耗增加多少。缺少边际收益评估是 Skill 爆炸的推手。因为负收益通常不会立刻浮现等全量任务整体下滑时已经很难定位是哪几个 Skill 造成的。为了判断一个 Skill 是否值得保留至少需要记录三组数据加载了这个 Skill 的任务成功率、同样任务在未加载该 Skill 时的成功率、加载前后 token 消耗变化。评估失灵还会造成“Skill 冗余”。当多个 Skill 覆盖同一类需求时模型并不知道哪个是权威版本于是任务结果不稳定。要避免这个问题必须把评估从“能不能用”升级为“加与不加到底差多少”。3. 先判断你的 Agent 是不是被 Skill 拖累了一套可操作的诊断流程3.1 从日志里找“选择脆弱”的信号在开始优化前先确认问题是否由 Skill 体系导致。可以从日志里找几类典型信号第一类是路由漂移。同一任务在不同时间请求了不同 Skill尤其是相似 Skill 之间反复横跳。第二类是能力误判。模型选择了某个 Skill但执行时又说该 Skill 不适合需要二次路由。第三类是上下文超限。请求因为输入超过限制被截断截断部分往往包含最近几轮任务指令或工具输出。如果日志系统还没有记录这些信息建议先加一层请求级日志至少包含以下字段{ request_id: req_20260812_0001, task_type: log_stats, loaded_skills: [skill_log_search, skill_log_analyzer], chosen_skills: [skill_log_search], used_skill: skill_log_search, retry_count: 1, final_success: false, input_tokens: 42113, output_tokens: 1204 }这段 JSON 是示例实际字段可以根据框架调整。重点不是字段名而是能回答“加载了什么、选择了什么、最终用了什么、有没有重试”这四个问题。有了这个基础才能判断问题是出在加载层、选择层还是执行层。3.2 用消融实验量化 Skill 的边际收益消融实验是最直接的判断方法。选一组覆盖主要场景的任务集 Tasks再准备 Skill 集合 S。要判断某个 Skill x 的价值就运行两次一次带 x一次不带 x只控制这个变量。具体流程可以按下面几步走准备固定任务集每条任务记录输入、预期行为和成功标准。在全量 Skill 下运行任务集记录成功率、token 消耗、平均迭代轮数。移除待评估 Skill保持其余配置不变再运行一遍。对比两组数据。如果移除后成功率不变或上升同时 token 下降这个 Skill 就应该重新设计或停用。这个实验不一定要很复杂但必须固定输入和评判标准。否则模型输出的随机性会让你误判 Skill 的实际效果。注意做消融实验时要固定模型版本、温度参数和任务集否则无法判断差异来自 Skill 还是随机性。下表是一个简化示例实验配置任务成功率平均输入 Token平均重试次数全量 45 个 Skill68%412001.8移除 A 后 44 个 Skill71%398001.2移除 B 后 44 个 Skill66%405001.7在这个示例里移除 A 后成功率反而提高说明 A 在当前体系里是负资产移除 B 后成功率下降说明 B 仍有保留价值。这样就能把 Skill 管理从“感觉有用”变成“数据决定”。3.3 建立 Skill 运行的核心指标表诊断时不只看一两个指标建议建立一个小组表格记录每个 Skill 的加载频率、实际调用率、成功率、平均 token 开销和冲突次数。指标含义健康范围经验值异常信号加载次数Skill 被注入上下文的请求数与任务分布匹配与场景无关却频繁加载实际调用次数模型最终选择并执行该 Skill 的次数高于 0且与加载次数接近加载很多但几乎不调用调用成功率调用后任务成功完成的占比建议 80% 以上频繁调用却频繁失败平均 token 开销该 Skill 描述占用的 token 数越少越好单 Skill 超过 1500 token冲突次数与其他 Skill 同时被激活后出现不稳定越低越好两个相似 Skill 互斥规则并存这些指标需要日志或 trace 系统支撑。小团队可以先手动记录一周再决定是否做成自动化报表。生产环境建议在 Agent 框架的中间层统一埋点避免业务代码里到处打日志。4. 让 Skill 系统继续扩展也不会失控按需加载与动态路由4.1 把“全量注入”改成“按域加载”既然问题根源之一是全部 Skill 都进入上下文优化思路就很明确每一轮只加载与当前任务相关的那部分 Skill。实现上可以分两层第一层是粗粒度路由根据用户任务判断属于哪个领域比如“日志分析”“代码审查”“数据可视化”第二层是细粒度选择从该领域内挑选具体 Skill。粗粒度路由可以是一段简单的分类逻辑不一定要用模型。最常见做法是维护一个关键词和领域映射规则。示例domain: log_analysis matches: - 日志 - log - 异常 - exception - 排错 load_skills: - skill_log_analyzer - skill_log_search_prefilter - skill_metric_alarm_checkdomain: code_review matches: - 代码审查 - review - 代码质量 - pr load_skills: - skill_diff_summarizer - skill_security_checker - skill_style_linter这样日志任务只加载日志相关 Skill不加代码审查类 Skill。注意关键词映射适合场景边界清楚的任务如果任务跨度很大则需要在模型层做意图识别再把识别结果用于 Skill 筛选。4.2 用 Skill 元数据做预筛和路由为了让路由更可靠每个 Skill 的定义文件里应当包含结构化的元数据。示例name: skill_log_analyzer version: 2.1.0 domain: log_analysis summary: 对日志做异常分布统计和根因片段提取 when_to_use: - 需要按时间范围统计异常数量 - 需要找出异常集中出现的模块 - 需要生成日志分析摘要 avoid_when: - 只需要关键词检索不需要聚合统计 - 数据源不是日志文本而是结构化指标 priority: 5 dependencies: - skill_log_search_prefilter tags: - logs - stats - alert这个文件的价值在于让路由模块有可计算的信息。先看 when_to_use 是否命中再看 avoid_when 是否排除然后检查依赖是否满足最后按 priority 排序。模型不需要阅读完整 Skill 文本只需要路由模块挑出少数候选。如果项目还没有这样的结构可以先从 YAML 字段补起。新增 Skill 时要求作者必须填写 when_to_use 和 avoid_when否则不允许合并。这样做等于把“是否适合当前任务”的判断从模型运行时提前到了开发和评审阶段。4.3 增加优先级、隔离和降级机制按域加载只能解决一部分问题。同一领域内可能有多个 Skill 竞争这时需要明确的优先级。优先级字段应该回答当多个 Skill 都匹配任务时默认选谁。示例规则def pick_skill(candidates, task_intent): # 示例先排除 avoid 冲突再按 priority 升序选择 usable [ s for s in candidates if not is_avoided(s, task_intent) ] if not usable: return None return min(usable, keylambda s: s.priority)注意这里的 priority 数值越小越优先具体方向要和团队约定一致。比选哪一个更重要的是不允许出现两个同域 Skill 的 behavior 重复。路由逻辑只负责执行选择边界清理要靠 Skill 评审。隔离和降级机制是在生产环境中稳定运行的关键。新增 Skill 应该先进入灰度环境与线上 Skill 完全分离线上只加载标记为 active 的版本。若某个 Skill 出现连续调用失败需要能通过配置开关快速摘除而不用重新发布整个 Agent。配置开关示例skills_registry: skill_log_analyzer: active: true version: 2.1.0 fallback: skill_log_analyzer_v1 max_retry: 2 skill_dashboard_builder: active: false reason: dashboard 渲染不稳定待修复后灰度生产和开发环境应该使用不同的 registry。开发环境可以加载全部 Skill 用于联调但生产环境必须可枚举、可回滚。也可以把 registry 放配置中心运行时调优不需要改代码。注意灰度环境里停用 Skill 不能只改配置还要确认线上流量已经被摘除避免出现部分请求使用新策略、部分请求使用旧策略的情况。5. 可复用的 Skill 设计规范与发布清单5.1 写 Skill 时就要考虑“它会给每一轮带来什么成本”Skill 描述写得越详细模型越容易理解但代价是每轮调用都变长。因此描述要在“机器可路由”和“模型可理解”之间取平衡。下面是一个对比。写法示例问题模糊描述分析日志并给出结论路由时无法判断该用哪个 Skill模型执行时也缺少约束过度详细包含大量业务背景、历史原因、完整流程文档token 占用过高注意力被稀释推荐写法明确目标、触发条件、限制、输出格式路由和生成阶段都能获得足够信息推荐写法的核心是“一句话能说清该不该用三句话能说清怎么做”。完整业务规范可以放外部文档不放 System Prompt。同时每个 Skill 都要写 avoid_when。这看起来是多余信息但能显著降低选择偏差。模型不是只知道什么时候该用还要知道什么时候不该用。5.2 发布前必须走完的检查清单给团队用的发布检查清单可以直接贴进 PR 模板或 CI 脚本里是否填写 name、version、domain、priority 等元数据。是否写明 when_to_use 和 avoid_when。是否新增了相似 Skill 或与现有 Skill 职责重叠。是否记录了描述 token 消耗增量是否可接受。是否完成冲突规则扫描比如“必须”“禁止”“除非”等规则词之间的冲突。是否在测试任务集上做了带/不带该 Skill 的消融对比。是否对所有依赖 Skill 进行了版本确认。是否已准备回滚开关和灰度加载方式。是否影响了既有高频任务的延迟和 token 成本。是否通过生产环境安全审查涉及用户数据时必须说明用途。这份清单是通用版本实际项目需要结合自己的框架字段调整。关键是把检查从“代码能跑”扩展到“运行不会拖累其他技能”。6. 常见“Skill 越多越笨”的坑和对应排查路径6.1 坑一把“加 Skill”当成提高准确率的默认手段现象某个任务集准确率不达标团队第一反应是再加一个 Skill结果多次叠加后整体准确率继续下降。原因任务失败不一定是技能缺失。可能是提示词不明确、上下文不足、训练数据覆盖不够、评估标准不合理也可能是模型在已有 Skill 中选择错误。盲目加 Skill 会让上下文更长、选择更多反而加重失败。排查顺序先看失败任务发生在哪一层是意图理解、Skill 路由、参数生成还是结果处理。用日志定位后再决定是否需要新增 Skill。解决方式优先尝试修提示词、补示例、调整允许工具列表。必须新增 Skill 时先做消融实验确认它带来的收益大于上下文成本。预防把“新增 Skill 必须附消融数据”写进团队规范。6.2 坑二多个 Skill 职责边界模糊模型随机选择现象日志分析和日志搜索两个 Skill 都能处理“看看今天日志有什么异常”这句话模型一会调用这个一会调用那个输出格式不停变化。原因Skill 的 when_to_use 描述互相包含且没有定义优先级。模型没有足够的判别信息只能根据提示词里的细微偏差选择。排查方式把所有 Skill 的 when_to_use 和 avoid_when 抽取出来对比看是否存在交集。也可以用一批中等难度的路由测试检查选择稳定性。解决方式合并相似 Skill或者明确一个为默认版本另一个只处理边界场景。给每个 Skill 增加“与其他 Skill 的关系”字段让路由层能排除互斥项。预防在 Skill 评审阶段每次新增前先搜索已有 Skill 的 domain 和 tags重复度超过阈值的直接打回。6.3 坑三只测单 Skill不做全量回归现象新增 Skill 后单独测试它的场景都能通过但整个 Agent 在历史任务上的表现波动明显有时变差有时不稳定。原因Skill 之间会互相影响。新增 Skill 会改变模型选择策略、上下文长度和输出偏好单测无法发现这些影响只有全量任务回归才能暴露。排查方式准备一套固定回归集覆盖核心业务场景和边界场景。每次 Skill 变更后都全量运行一次统计成功率和 token 变化。重点观察那些不涉及新增 Skill 的任务是否出现回归。解决方式把回归测试接入 CI设置成功率阈值。低于阈值的提交不允许合并。预防把“变更 Skill 必须通过回归集”作为发布条件而不是可选项。7. 回到核心判断把 Skill 当成需要治理的软件模块而不是收藏夹Skill 变多导致 Agent 变笨不是模型能力到了上限而是技能体系缺少治理。每一份 Skill 都会在每一轮推理中消耗上下文、参与选择、改变行为。它和普通代码一样需要版本、元数据、依赖关系、冲突检测、回归测试和回滚机制。对这些问题的处理顺序建议先做诊断再优化。先确认是不是被 Skill 拖累记录每轮加载和调用情况再做消融实验找出真正的负资产然后将全量注入改成按域加载补充元数据和优先级最后把 Skill 变更纳入发布流程。小团队可以只做两件事第一新增 Skill 时记录描述 token 和消融结果第二停止无判断地堆 Skill。先做到这两点Agent 的稳定性就会明显改善。大团队则还需要建立统一注册中心、灰度发布机制和基于指标自动摘除能力。下一步最值得投入的练习是用现有项目做一次“Skill 减肥”保留一个连续一周的日志统计哪些 Skill 从未被调用、哪些调用后经常失败然后把它们暂时停用观察整体表现。这个练习成本很低却最能帮助团队理解“多即是少”这句话在 Agent 系统里的真实含义。以后再看到一个新 Skill 时先问的不应该是“它能不能跑通”而是“它值不值得所占用的每一次推理”。