Skill 数量过百之后真正的问题不是“有没有 Skill”而是“Agent 到底能不能在正确的时候选中正确的 Skill”。很多团队在初期只有十几个 Skill 时靠提示词描述、名字前缀、少量示例就能跑通但当 Skill 数量超过 100 个模型的选择空间变大描述冲突变多上下文被无关内容挤占调用命中率会肉眼可见地下降。这篇文章围绕 Agent 调用 Skill 的命中率问题从选型、命名、描述、召回、评估、监控六个维度展开适合正在做 Agent 应用开发、需要管理大量 Skill 的工程师阅读。在实际项目里Skill 数量过百以后调用命中率下降通常不是某个大模型能力不够而是 Skill 管理方式还停留在小规模阶段。小规模时开发者可以在 System Prompt 里把所有 Skill 列一遍模型基本能凭名字匹配规模上到 100 以后System Prompt 长度受限模型注意力被稀释Skill 之间的语义差异变小重名和模糊描述开始互相干扰。这篇文章会先解释 Skill 调用链路里最容易出问题的环节再给出环境搭建、Skill 规范设计、召回优化、评估方法和监控手段最后提供一个可以落地的排查清单。1. 先理解 Skill 调用命中率为什么会在过百后下降1.1 Skill、Tool 和 Agent 之间的关系要先对齐Skill 在不同 Agent 框架里的叫法不完全一致。有的框架叫 Tool有的叫 Action有的叫 Function Calling还有的框架把一组相关 Tool 的集合称为 Skill。为了避免后续讨论出现歧义这里统一约定Tool 是 Agent 可以调用的最小能力单元通常对应一个函数、一个 API 或一段可执行脚本。Skill 是由多个 Tool、提示词模板、参数规则和调用说明组成的能力包描述的是“完成某类任务”的整体能力。Agent 是负责理解用户请求、规划步骤、调用 Skill、处理返回结果的执行体。在 Claude Code、Codex、自研 Agent 框架等场景里Skill 通常以目录或配置文件形式存在。一个 Skill 目录下可能有SKILL.md、示例文件、脚本、参数定义和校验规则。模型在收到用户请求后会先判断当前任务是否需要某个 Skill再从 Skill 列表里选中它最后执行 Skill 内部逻辑。当 Skill 数量较少时Agent 可以依赖“名字 单行描述”完成匹配。当数量超过 100 个模型需要处理的候选变多描述语义重叠变多选择错误的概率随之上升。这个阶段开始命中率问题本质上是信息检索和决策链路的工程问题而不只是提示词问题。1.2 命中率下降的四个直接原因从实际表现看Skill 过百后的命中率下降可以归纳为四个原因第一候选集过大导致注意力稀释。Agent 的上下文窗口虽然越来越大但把 100 个 Skill 的完整描述全部塞进 System Prompt 后真正决定调用的关键描述会被淹没。模型在处理用户请求时需要从大量文本里找到最相关的那一段信息密度越低选错概率越高。第二Skill 描述语义重叠。很多 Skill 在定义时使用了相近词汇比如“生成周报”“生成日报”“生成项目报告”三个 Skill 的名字和描述高度相似。模型无法从短文本里准确区分边界出现“本想调用日报 Skill结果选了周报 Skill”的情况。第三命名和描述与用户口语表达不一致。Skill 描述写的是开发视角的语言用户请求则是自然语言。比如 Skill 名叫parse_resume描述是“解析简历文件并提取字段”但用户说“帮我看下这份 PDF 里有哪些候选人信息”模型需要把口语意图映射到 Skill 语义映射失败时命中率自然下降。第四没有召回阶段直接让模型做全量选择。小规模时可以直接让模型在所有 Skill 里选择过百后应当先通过关键词、向量或规则做一次召回把候选集从 100 个缩小到 5 到 10 个再让模型做最终决策。这一步缺失命中率会明显受噪声影响。这四个原因在真实项目中往往是叠加的。只优化其中一个命中率提升有限。下文会从工程角度给出系统化处理方式。1.3 命中率不能只看“是否选对了 Skill”评估 Skill 调用命中率时还需要区分几个层次意图识别是否准确用户请求是否真的需要调用 Skill。如果不需要调用却调了属于误调用。Skill 选择是否准确需要调用时是否选中了正确的 Skill。Skill 内部执行是否成功选中正确 Skill 后参数、权限、脚本是否顺利执行。返回结果是否满足用户需求执行成功不代表结果正确。本文讨论的“调用命中率”主要指第二层即从多个 Skill 中选出正确 Skill 的比例。但监控时不能只统计这一层因为第一层误调用和第三层执行失败也会表现为“好像没命中”。建立完整评估链路后才能定位问题到底出在召回、选择还是执行阶段。2. 环境准备先搭一个可统计命中率的 Skill 管理工程2.1 建议的技术栈和目录结构如果你的 Agent 项目已经存在可以直接在现有项目上增加 Skill 管理和评估模块。如果从零开始验证推荐使用一个相对独立的最小工程。下面是一个参考目录结构适用于大多数支持 Skill 的 Agent 框架agent-skill-lab/ ├── skills/ │ ├── weekly-report/ │ │ ├── SKILL.md │ │ ├── scripts/ │ │ │ └── generate_report.py │ │ └── examples/ │ │ └── input_sample.json │ ├── daily-report/ │ │ ├── SKILL.md │ │ └── scripts/ │ └── resume-parser/ │ ├── SKILL.md │ └── scripts/ ├── config/ │ ├── skill_registry.yaml │ └── eval_config.yaml ├── evaluator/ │ ├── build_eval_set.py │ ├── run_eval.py │ └── analyze_result.py ├── retriever/ │ ├── keyword_retriever.py │ ├── vector_retriever.py │ └── hybrid_retriever.py ├── logs/ │ └── skill_call.log └── requirements.txt这个结构把 Skill 定义、召回模块、评估脚本和日志分开。skills目录放各 Skill 的定义文件和资源retriever目录放召回逻辑evaluator目录放评估数据集和评估脚本logs目录用于后续监控分析。在实际项目中Skill 目录可能由多个团队共同维护因此配置管理比目录结构更重要。skill_registry.yaml建议作为统一注册入口避免直接扫描目录导致命名混乱。2.2 定义统一的 Skill 元数据格式无论你使用什么 Agent 框架都应该为每个 Skill 定义一组结构化元数据。元数据越统一后续召回和评估越容易。下面是一份参考 YAML 配置- name: weekly-report-generator display_name: 周报生成器 version: 1.2.0 description: 根据本周工作记录生成结构化周报支持按项目、按时间范围过滤输出 Markdown 或 Word 格式。 category: report tags: - 周报 - 工作汇报 - 项目管理 trigger_examples: - 把本周的工作记录整理成周报 - 帮我生成这周的周报 - 整理一下项目 A 的周度进展 inputs: - name: start_date type: string required: false description: 开始日期格式 YYYY-MM-DD - name: project type: string required: false description: 项目名称 owner: team-report visibility: public enabled: true eval_set: - input: 帮我生成这周的周报 expected_skill: weekly-report-generator这份配置的含义是每个 Skill 除了描述还包含分类、标签、触发示例、输入参数、负责人、可见性和评估用例。其中trigger_examples对召唤召回和评估特别重要它提供了用户口语表达与 Skill 语义之间的映射桥梁。eval_set里的用例可以直接用于后续命中率评估。建议每个 Skill 至少配置 5 条真实用户表达条件允许时配置 10 条以上。评估用例如果只依赖开发者自己编容易和实际用户表达脱节更好做法是从日志里抽取历史用户真实请求。2.3 环境依赖与版本确认在开始写召回和评估代码之前先确认依赖环境。不同 Agent 框架对 Skill 的支持方式不同但多数会用到 Python 生态中的以下组件组件用途示例Agent 框架Agent 调度和 Skill 执行Claude Code、Codex、自研框架向量数据库Skill 描述向量召回Chroma、Milvus、FAISSEmbedding 模型将 Skill 描述和用户请求编码为向量text-embedding-3-small、bge-m3编排框架定义 Agent、Tool、Skill 生命周期LangGraph、CrewAI、自研日志和监控记录每次调用和选择结果ClickHouse、Elasticsearch、文件这里要特别注意原始材料没有指定某个固定技术栈。上面表格列的是常见选型实际项目落地前要先确认自己的 Agent 框架是否支持这些组件以及版本是否兼容。如果只是验证 Skill 召回思路可以先不引入向量数据库用关键字召回加规则召回就能跑通第一版。下面是一个最小requirements.txt示例openai1.30.0 chromadb0.5.0 pydantic2.0.0 pyyaml6.0 pandas2.0.0 scikit-learn1.3.0注意版本号只是示例实际安装时要以官方当前稳定版本为准避免因为 API 变更导致代码失效。3. 让 Skill 定义本身先“可选对”命名、描述和触发示例规范3.1 命名规范从“开发视角”切到“用户视角”Skill 数量过百后命名直接影响候选召回和模型判断。命名不能只看开发者是否容易理解还要看用户请求是否容易映射到这个名字上。推荐命名规则使用动词开头表达能力含义例如generate_weekly_report、parse_resume、search_employee_info。避免只使用技术名词例如ReportUtil、ParserJob这类命名对模型没有任何语义提示。长度控制在 3 到 6 个单词之间。过长会稀释注意力过短则丢失语义。同一分类下的 Skill 前缀保持一致例如report_weekly、report_daily、report_project这样召回阶段可以按前缀快速过滤。错误示例Skill 名问题推荐命名handle_data语义太泛无法判断是什么能力clean_csv_dataexport_v2_final包含版本号和冗余后缀表达不清晰export_order_excelAPI_Utils技术视角命名用户请求无法映射query_order_status3.2 描述规范写“做什么、什么时候用、不做什么”Skill 描述是模型选择和召回的关键信号。描述写得太短模型缺少判断依据写得太长上下文被无关内容占满。建议每个 Skill 的描述控制在 3 到 5 句话覆盖三个维度做什么明确 Skill 的执行结果和输出格式。什么时候用说明该 Skill 适用哪些用户意图。不做什么说明边界避免和相邻 Skill 混淆。示例description: 根据本周工作记录生成结构化周报支持按项目、按时间范围过滤输出 Markdown 或 Word 格式。 使用场景当用户要求整理周报、周度汇报、工作周总结时使用。 不适用场景如果用户只需要查看工作日志不需要生成汇总文档请使用 search_work_log。这样写的好处是让模型在模糊请求下能通过“不适用场景”排除错误候选。类似的 Skill 越多越要写清边界。3.3 触发示例让用户口语表达与 Skill 语义对齐描述解决的是“Skill 想表达什么”触发示例解决的是“用户实际会怎么说”。建议为每个 Skill 配置 5 到 20 条触发示例覆盖常见表达、同义表达和边界表达。例如weekly-report-generatortrigger_examples: - 把本周的工作记录整理成周报 - 帮我生成这周的周报 - 整理一下项目 A 的周度进展 - 周报还没写帮我根据日志生成一份 - 我要向上级汇报本周工作出个周报这些示例不仅用于提示词或召回索引还可以当作评估集的种子数据。后续做命中率评估时可以把每条触发示例变成一条测试用例统计模型或召回模块的命中情况。这里有一个容易被忽略的点触发示例不能只写“理想表达”还要写用户常见的模糊表达和错误表达。例如“我要总结一下工作”可能既会被归类到周报 Skill也会被归类到日志查询 Skill。把这种边界表达写进示例模型才能学会判断。3.4 分类和标签给召回阶段提供第一层过滤条件当 Skill 数量过百让模型在全部 Skill 里做最终选择是不现实的。合理做法是先用分类和标签缩小候选集再让模型在小集合里决策。分类建议从业务维度划分例如report报告生成类data_query数据查询类document文档处理类code_execution代码执行类communication消息通知类system_admin系统管理类标签可以更细例如“周报”“日报”“简历解析”“SQL 查询”“文件转换”。标签的作用是让召回模块可以按标签过滤同时让模型在候选集里更容易理解每个 Skill 的差异。在skill_registry.yaml中分类和标签应该与描述保持一致。如果分类写report描述却写“查询员工信息”那么召回和模型判断会产生矛盾。4. 召回层设计从“全量选择”变成“先召回再选择”4.1 为什么必须加召回层当 Skill 数量只有 20 个时把全部 Skill 列表交给模型选择不会有太大问题。当数量过百上下文里的 Skill 描述会占用大量 token同时无关 Skill 会对选择产生噪声干扰。召回层的目的是把候选集从 100 个缩小到 5 到 10 个减少模型决策负担同时保留真正相关的选项。召回层需要平衡召回率和精确率。召回率低正确 Skill 被过滤掉模型无论如何都选不中精确率低候选集里全是噪声模型决策容易出错。实际项目中通常采用混合召回兼顾关键词、向量和规则。4.2 关键字召回快速、可解释、适合精确词匹配关键字召回的核心思路是对用户请求和 Skill 元数据做词法匹配。优点是没有额外模型依赖速度快结果可解释缺点是无法处理同义表达和上下文歧义。最小实现可以基于 TF-IDF 或 BM25。下面是一个基于scikit-learn的简单示例from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics.pairwise import cosine_similarity skill_docs [ 周报 生成 工作日志 汇报, 日报 生成 每日工作记录, 简历 解析 提取 候选人 字段, ] user_query 帮我根据日志整理本周周报 vectorizer TfidfVectorizer(token_patternr\b\w\b) vectors vectorizer.fit_transform(skill_docs [user_query]) query_vector vectors[-1] skill_vectors vectors[:-1] scores cosine_similarity(query_vector, skill_vectors).flatten() top_indices scores.argsort()[-3:][::-1] for idx in top_indices: print(fScore: {scores[idx]:.4f}, Skill: {skill_docs[idx]})这个示例说明关键字召回的逻辑先把每个 Skill 的索引文本拼成一段再用 TF-IDF 计算用户请求与各 Skill 的相似度最后取 Top N。生产环境建议直接使用 Elasticsearch 的 BM25 或专门的关键词检索引擎。4.3 向量召回处理同义表达和跨语言表达向量召回使用 Embedding 模型将 Skill 描述和用户请求编码成向量再计算余弦相似度。它能处理“周报”和“工作汇报”“weekly report”等不同表达之间的语义关联解决关键字召回无法覆盖的同义问题。下面是一个基于 Chroma 的最小示例import chromadb from chromadb.utils import embedding_functions client chromadb.PersistentClient(path./chroma_db) embedding_fn embedding_functions.DefaultEmbeddingFunction() collection client.get_or_create_collection( nameskills, embedding_functionembedding_fn, ) # 添加 Skill 索引 collection.upsert( ids[weekly-report, daily-report, resume-parser], documents[ 根据工作日志生成结构化周报适合周度汇报场景, 根据每日工作记录生成日报适合日度汇报场景, 解析简历文件并提取候选人关键字段适合招聘筛选场景, ], metadatas[ {category: report, name: weekly-report-generator}, {category: report, name: daily-report-generator}, {category: document, name: resume-parser}, ], ) user_query 帮我写一份这周的汇报材料 results collection.query( query_texts[user_query], n_results2, ) for doc, meta in zip(results[documents][0], results[metadatas][0]): print(meta[name], doc)这里使用collection.query返回最相似的 Skill。生产环境通常使用更专业的向量数据库如 Milvus、Qdrant、Elasticsearch 的向量检索能力。向量召回的优势是语义泛化但也需要控制索引内容质量避免把相似描述都塞进索引导致结果区分度下降。4.4 混合召回兼顾精确匹配和语义相似混合召回的基本思路是先通过规则或关键字召回获取第一候选集再通过向量召回获取第二候选集合并后去重保留 Top K 给模型选择。不同召回结果的分数需要归一化避免某个召回源垄断候选集。一个简单实现def hybrid_recall(user_query, top_k8): keyword_candidates keyword_retrieve(user_query, top_ktop_k) vector_candidates vector_retrieve(user_query, top_ktop_k) merged {} for skill_name, score in keyword_candidates: merged[skill_name] max(merged.get(skill_name, 0), score) for skill_name, score in vector_candidates: merged[skill_name] max(merged.get(skill_name, 0), score) sorted_skills sorted(merged.items(), keylambda x: x[1], reverseTrue) return sorted_skills[:top_k]生产实现时还需要考虑规则召回某些用户请求带有明确指令词例如“导出”“解析”“生成”可以直接映射到特定 Skill。同义词扩展为常用概念维护同义词表例如“周报”与“weekly report”互相关联。热门 Skill 兜底如果召回结果为空可以返回最近调用频率最高的几个 Skill 作为降级策略。4.5 召回效果不好时先查这些地方召回效果差大概率不是模型问题而是索引数据或召回逻辑的问题。按以下顺序排查问题现象可能原因检查方式解决建议正确 Skill 没有被召回索引文本与用户请求语义差异过大打印召回 Top 10确认正确 Skill 是否出现增加触发示例优化描述关键词召回结果全是无关 Skill索引文本重复度高或向量模型不匹配检查向量相似度分数分布调整索引文本换更合适的 Embedding 模型同义表达召回失败关键字召回无法处理同义词构造同义表达测试引入同义词表或向量召回候选集不够稳定混合召回分数归一化不一致打印每个召回源的分数统一分数范围使用加权融合召回层是整个命中率提升的基础。如果召回阶段已经把正确 Skill 过滤掉了后面让模型选择也没有意义。因此在优化模型提示词之前先确保召回层在评测集上的召回率足够高。5. 命中率评估用数据说话而不是靠感觉5.1 构造评估集来自日志、触发示例和人工标注评估集是命中率优化的关键基础设施。没有评估集你无法判断改动是变好还是变坏。建议按以下来源构造评估集线上日志从 Agent 调用日志里抽取真实用户请求覆盖不同表达方式和错误场景。触发示例每个 Skill 的trigger_examples直接转换为评估用例。人工补充邀请产品、运营和真实用户写出他们可能使用的表达。一份评估集的最小格式如下[ { id: 1, input: 帮我根据日志生成这周的周报, expected_skill: weekly-report-generator, source: log, difficulty: easy }, { id: 2, input: 把今天的进展整理成文档发给领导, expected_skill: daily-report-generator, source: manual, difficulty: hard } ]expected_skill必须是 Skill 注册表里的规范名称评估脚本才能自动统计命中率。difficulty可以按“简单、中等、困难”划分用于分析不同难度下的表现。5.2 评估指标命中率、召回率、误调用率统计命中率时需要把结果分成几类结果含义对用户的影响正确命中选中的 Skill 与期望一致正常错误命中选中的 Skill 与期望不一致用户得到错误功能漏调用需要调用 Skill 但没有调用用户需求未满足误调用不需要调用 Skill 但调用了执行了多余动作执行失败选中正确 Skill 但内部执行报错用户看到错误信息最终统计指标建议包括Skill 调用命中率正确命中数 / 所有需要调用 Skill 的用例数。召回成功率正确 Skill 出现在召回结果 Top K 中的比例。误调用率误调用数 / 所有调用数。平均检索位置正确 Skill 在候选集中的平均排名。这些指标分别对应不同问题命中率低但召回成功率高说明问题在模型选择阶段召回成功率低说明问题在召回层。5.3 最小评估脚本下面是一个最小评估脚本示例import json import yaml def load_skill_registry(path): with open(path, r, encodingutf-8) as f: return yaml.safe_load(f) def load_eval_set(path): with open(path, r, encodingutf-8) as f: return json.load(f) def recall_candidates(skill_registry, user_query, top_k5): # 这里替换成你自己的召回实现 # 返回值是 Skill 名称列表 return [] def run_eval(eval_set, skill_registry): total len(eval_set) hit 0 recall_success 0 misinvoke 0 for item in eval_set: expected item[expected_skill] query item[input] candidates recall_candidates(skill_registry, query, top_k5) # 检查召回阶段是否包含正确 Skill if expected in candidates: recall_success 1 # 检查模型最终选择是否命中 selected_skill model_select(query, candidates) # 替换成实际 Agent 调用 if selected_skill expected: hit 1 elif selected_skill is not None: misinvoke 1 return { total: total, hit_rate: hit / total, recall_success_rate: recall_success / total, misinvoke_rate: misinvoke / total, } def model_select(query, candidates): # 这里应接入实际模型调用并限定候选集 return candidates[0] def main(): skill_registry load_skill_registry(config/skill_registry.yaml) eval_set load_eval_set(config/eval_set.json) result run_eval(eval_set, skill_registry) print(json.dumps(result, ensure_asciiFalse, indent2)) if __name__ __main__: main()这个脚本的结构提示了评估链路召回和选择是两个独立阶段可以分别统计。实际生产环境建议把每个评估用例的结果落盘包括候选列表、最终选择、分数和日志方便后续分析错误样本。5.4 分析错误样本从“命中率”到“为什么没命中”评估结束后要进入错误样本分析阶段。建议把错误样本按类型分组语义映射失败用户表达与 Skill 描述不在同一语义空间。描述覆盖不足Skill 描述没有包含该场景的关键信息。候选召回失败正确 Skill 不在召回 Top K 中。候选排序不佳正确 Skill 在召回结果里但排名靠后模型选了其他项。规则优先级错误某些规则强制把请求映射到了错误 Skill。处理方式对语义映射失败增加对应 Skill 的触发示例优化描述关键词。对召回失败调整索引文本或引入向量召回。对排序不佳调整混合召回权重或重新生成 Embedding 索引。对规则优先级错误检查规则表和业务含义必要时增加优先级字段。评估不是一次性工作。建议每次增加 Skill、修改描述或调整召回逻辑后都重新运行评估集并记录历史指标避免“改一个 Skill 导致另一个 Skill 命中率下降”。6. 运行时监控日志、告警和持续迭代6.1 记录每次调用链路的完整信息评估集只能覆盖一部分情况生产环境中的真实请求才是 Skill 管理的主要数据来源。每次 Agent 调用 Skill 时至少记录以下字段字段说明request_id请求唯一标识timestamp调用时间user_query用户原始请求recall_candidates召回阶段返回的候选 Skill 列表candidate_scores每个候选的分数selected_skill模型最终选择的 Skillexecution_statusSkill 执行状态成功、失败、超时error_message如果执行失败记录错误信息latency_ms从请求到选择的耗时model_name使用的模型名称和版本日志格式可以使用 JSON便于后续入 ClickHouse、Elasticsearch 或文件检索。下面是一个日志示例{ request_id: req_20250101_001, timestamp: 2025-01-01T10:00:00Z, user_query: 帮我根据日志整理本周周报, recall_candidates: [weekly-report-generator, daily-report-generator], candidate_scores: [0.92, 0.61], selected_skill: weekly-report-generator, execution_status: success, error_message: , latency_ms: 320, model_name: gpt-4o }6.2 建立命中率报表和告警规则持续统计命中率时按小时或按天聚合数据。比较简单的做法是把日志读入 DataFrame然后按时间窗口统计import pandas as pd logs pd.read_json(logs/skill_call.log, linesTrue) logs[event_time] pd.to_datetime(logs[timestamp]) # 每天命中率 logs[is_hit] logs[execution_status] success daily_rate logs.groupby(logs[event_time].dt.date)[is_hit].mean() print(daily_rate) # 每个 Skill 的调用次数和失败率 skill_stats logs.groupby(selected_skill).agg( call_count(request_id, count), fail_count(execution_status, lambda x: (x ! success).sum()), ) skill_stats[fail_rate] skill_stats[fail_count] / skill_stats[call_count] print(skill_stats.sort_values(fail_rate, ascendingFalse))告警规则可以设置命中率低于某个阈值例如低于 85% 时触发告警。某个 Skill 连续失败超过 5 次。召回阶段覆盖率明显下降。用户请求中包含大量未知表达说明 Skill 登记存在覆盖缺口。告警不是目的关键是告警后能定位到具体 Skill 和具体请求样本判断是描述问题、执行问题还是模型问题。6.3 Skill 生命周期管理上线、下线、版本更新Skill 数量过百后必然会遇到下线、废弃、合并和版本更新问题。建议建立明确的 Skill 生命周期流程状态含义处理方式草稿开发中未进入正式索引不参与召回已发布可在生产环境被调用进入召回索引和模型候选已废弃不再推荐使用从召回层移除保留日志用于历史分析已下线当前不可用不参与调用但记录名称防止冲突Skill 生命周期状态迁移草稿 - 已发布 - 已废弃 - 已下线实际项目很容易出现的问题是某团队改了一个 Skill 描述但线上索引没有更新或者某个 Skill 已下线但用户请求仍被规则强制路由到它。建立生命周期状态后每次变更都要走更新任务并同步更新注册表和召回索引。6.4 持续迭代以周为周期管理 Skill 质量Skill 管理不是一次性建设而是一个持续维护过程。建议按周执行以下循环从日志中抽取近一周的低命中率样本。低命中率样本归类描述问题、召回问题、执行问题、模型问题。对描述问题更新SKILL.md和注册表。对召回问题更新索引文本、触发示例或召回权重。对执行问题查看 Skill 内部脚本和依赖。更新评估集加入新出现的表达方式。重新运行评估脚本对比历史指标。如果团队规模允许可以指定一个 Skill 仓库负责人负责合并描述变更、处理冲突、审查新增 Skill。因为当 100 个 Skill 由多人维护时命名冲突、语义重叠、索引覆盖缺失几乎必然发生。7. 最佳实践与常见坑7.1 至少三个与 Skill 过百相关的常见坑坑一只优化 System Prompt不优化召回层。有人在命中率下降后不断加长 System Prompt把所有 Skill 描述都写进提示词结果上下文被撑满模型决策反而更差。正确做法是先做召回缩小候选集再让模型在小集合里选择。坑二触发示例只写“理想表达”不写边界表达。如果触发示例都是“生成周报”“周报生成”这类理想表达模型遇到“我下周要跟领导汇报帮我把内容整理下”时仍然可能选错。边界表达必须来自真实日志和用户反馈。坑三评估集和日志分离导致无法定位问题。只统计“命中率是否低于 90%”却不记录具体是哪个 Skill、哪个用户请求、哪个召回阶段出错就无法推进优化。评估和监控必须保留完整调用链路信息。7.2 可复用的 Skill 质量检查清单每次新增或修改 Skill 时建议按以下清单检查[ ] Skill 名称是否使用动词开头长度是否控制在 3 到 6 个单词。[ ] 描述是否包含“做什么、什么时候用、不做什么”。[ ] 是否配置了 5 条以上触发示例且包含边界表达。[ ] 分类和标签是否与其他 Skill 冲突。[ ] 索引文本是否及时同步。[ ] 是否新增了对应评估用例。[ ] 本次修改是否会影响其他 Skill 的召回。[ ] Skill 依赖的脚本、API 是否在目标环境可运行。[ ] 是否有负责人和生命周期状态。[ ] 是否登记到skill_registry.yaml而不是只放在目录里。7.3 命中率优化的优先级建议如果资源有限建议按以下顺序推进先建立评估集和日志采集让命中率可度量。再统一 Skill 元数据格式重点补触发示例和边界描述。然后引入召回层先关键词召回再向量召回。最后用历史日志持续迭代评估集形成周级别维护循环。不要一开始就追求复杂的向量数据库和重排序模型。先把基础数据质量和日志链路做扎实命中率提升会更稳定。7.4 Agent 与 Skill 体系扩展方向当 Skill 数量继续增长比如超过 1000 个还需要考虑更复杂的分层检索、Skill 组合编排、权限隔离和动态加载。Skill 之间如果存在依赖关系例如某个 Skill 依赖另一个 Skill 的输出还需要为这种依赖关系建模而不是只做平面列表召回。另一个方向是让 Skill 调用结果反过来参与召回。同一个用户请求如果多次调用某个 Skill可以把该 Skill 的调用记录作为特征影响后续召回排序。结合用户历史和业务偏好召回准确率可以进一步提升。对新手来说最有价值的练习是在自己的 Agent 项目里构造 100 个 Skill先跑出命中率再复现本文提到的命名、描述、召回和评估优化流程。通过亲手复现命中率下降和回升的过程会比只看任何一篇教程都更深刻。