尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
agent-skills实战:从按需加载到技能路由,构建可复用AI Agent能力
1. 从“写死脚本”到“教模型用工具”agent-skills到底在解决什么问题最近一段时间Agent开发圈里讨论最多的一个词除了MCP就是agent-skills了。我最早看到这个概念是在Claude的Skills功能里后来各家框架都在往这个方向靠。很多人把它理解成“给大模型加几个工具函数”实际上远不止这么简单。先说说我为什么对这个东西特别上心。做了两三年Agent应用最头疼的一件事就是每个新任务都要重新设计一套prompt再写一堆工具函数然后反复调提示词让模型知道什么时候该调哪个函数。换一个场景、换一个模型前面的活儿基本白干。agent-skills的思路是把“完成某类任务的能力”打包成一个标准化的技能单元模型按需加载不用把所有逻辑都塞进system prompt里。1.1 一个真实的开发痛点帮你理解它的价值假设你要做一个代码审查Agent。传统做法是写一个超长的system prompt里面描述“你要审查代码的风格、正确性、安全性”然后挂上三五个工具函数比如read_file、run_lint、check_deps。模型每次请求都要把这一大坨喂进去token开销大而且模型经常在“该用哪个工具”这件事上犯迷糊。用agent-skills的思路就完全不同。你可以把“代码审查”本身定义成一个技能包括技能的目标描述、触发条件、执行步骤以及相关的脚本和配置。Agent启动时并不加载全部细节而是先读到一份技能清单根据用户任务决定要不要激活“代码审查”这个技能。激活后相关的指引、脚本、工具才进入上下文。这种“按需加载”的模式既省token又大幅提升了模型调用的准确率。1.2 它和Tools、Function Calling、MCP有什么本质区别很多人会问我直接用Function Calling不就行了或者干脆用MCP这里面的差别我一开始也是绕了很久才想明白。机制粒度是否带执行逻辑上下文开销典型用途Function Calling单个函数否由模型生成参数、外部执行低查询天气、计算器、数据库查询MCP服务/工具集否提供标准化调用协议中连接外部系统、文件系统、浏览器agent-skills技能/任务单元是包含步骤指引、脚本、示例按需加载代码审查、数据分析、文档撰写区别一句话就能说清Function Calling是“让模型能调用一个功能”MCP是“让模型能按协议连接一个系统”agent-skills是“让模型学会完成一类任务”。技能里面可以包含多个函数调用、可以包含决策步骤、可以包含领域知识和示例。它更像是一份“操作手册”加“工具箱”的合体。2. 核心设计拆解一个技能到底是什么为什么这么设计2.1 技能的最小单元不止是一个脚本社区里比较有共识的技能结构一般是这样一个目录skill-name/ ├── SKILL.md # 核心文件描述技能做什么、何时用、怎么做 ├── scripts/ # 可执行的脚本Python、Shell、Node等 ├── assets/ # 参考文档、模板、数据集 └── requirements.txt # 依赖声明可选其中SKILL.md是整个技能的灵魂。它不是一个普通的README而是给模型看的“说明书”。我会在文件开头写清楚技能名称、一句话摘要然后用一个段落描述使用场景和触发条件再列出执行步骤。重点在于这份说明书要让模型在没有额外人工帮助的情况下自己判断“现在我该不该用这个技能”以及“用了之后怎么按步骤走”。我刚开始写时犯的最大的错就是把SKILL.md写成了给人看的开发文档堆了一堆架构设计、模块划分模型看了半天不知道从哪里下手。后来我改成“先给一句话场景判断再给步骤清单最后给示例”效果立竿见影。2.2 description是技能的灵魂模型靠它做路由判断如果把整个技能结构比作一本书那么SKILL.md里的description摘要描述就是书的目录和序言。Agent在选择技能的时候通常不会把每个技能的完整内容都读一遍而是先读描述再决定是否加载详情。所以description有个“三要素”写法什么场景下用“当用户需要审查代码质量时”比“代码审查工具”好一万倍。输入是什么用户会提供什么需要从哪里读取信息。输出是什么技能执行完会返回什么结果。举两个对比差评A tool for code review. 好评Use this skill when the user wants to review or analyze source code for bugs, style issues, or security vulnerabilities. The skill scans the repository, runs linting checks, and returns a structured report with severity levels and recommended fixes.前者模型大概率会误用后者模型几乎不会判断错。这个技巧不仅适用于agent-skills任何基于LLM的工具描述都适用。2.3 上下文效率为什么技能不能全量塞进system prompt我用一个笨办法验证过上下文开销的问题。最初我把五个完整的技能文档全部拼接进system prompt每个技能包含步骤说明、示例、脚本说明加起来差不多一万多token。效果确实不错但成本上去得吓人而且每次请求都带着这一大堆重复内容响应速度也变慢了。后来改成“技能清单按需加载”的模式system prompt里只有每个技能的标题和一行描述总长不到800 token。模型收到用户请求后如果需要再由一个路由函数把对应的SKILL.md全文拉进上下文。实测token消耗降低了差不多七成模型选择技能的准确率反而提升了因为上下文干净了干扰项变少了。这个“清单-详情”的模式本质上跟人处理信息的方式一样先看目录再翻到具体章节而不是把整本书背下来。3. 实操从零实现一套可用的agent-skills运行时理论说再多不如直接撸代码。下面我会用一个极简的Python实现带大家走一遍完整的技能注册、选择、加载、执行流程。这套代码我实际在内部项目里跑过结构可以直接抄走扩展。3.1 定义技能元信息与加载器首先定义一个技能类用来统一管理每个技能的元信息、描述、脚本路径和执行入口。# skill_model.py from dataclasses import dataclass, field from pathlib import Path from typing import Callable, Optional dataclass class AgentSkill: name: str # 技能唯一ID description: str # 给模型看的触发描述决定路由准确性 trigger_keywords: list # 触发关键词辅助路由 skill_dir: Path # 技能目录 entrypoint: Optional[Callable] None # 执行入口函数 def load_description(self) - str: 从SKILL.md中读取完整说明按需加载 md_path self.skill_dir / SKILL.md if md_path.exists(): return md_path.read_text(encodingutf-8) return self.description这里最需要注意的是load_description()只在真正需要执行该技能时才调用而不是初始化时就全部读入内存。这样能确保“清单-详情”模式是真正落地的而不是形式上写了个类、实际还是全部加载。接着写注册和路由逻辑# skill_registry.py import re from typing import Dict, List, Optional from skill_model import AgentSkill class SkillRegistry: def __init__(self): self._skills: Dict[str, AgentSkill] {} def register(self, skill: AgentSkill): self._skills[skill.name] skill def list_skills(self) - List[str]: 返回技能清单轻量级用于注入system prompt return [ f- {name}: {skill.description.strip().splitlines()[0]} for name, skill in self._skills.items() ] def route(self, user_input: str, top_k: int 2) - List[AgentSkill]: 简单的路由策略基于关键词 描述相关性打分。 生产环境可换向量检索或让LLM直接选择。 scores [] for skill in self._skills.values(): score 0.0 for kw in skill.trigger_keywords: if kw in user_input: score 1.0 # 对description进行朴素的部分匹配 for term in re.findall(r[\u4e00-\u9fa5a-zA-Z], user_input): if term and term in skill.description: score 0.3 scores.append((score, skill)) scores.sort(keylambda x: x[0], reverseTrue) return [skill for score, skill in scores[:top_k] if score 0]这个路由逻辑我故意写得朴素方便你看懂原理。真实场景下我建议对用户输入做Embedding然后再和每个技能的描述向量做余弦相似度这种方案在技能数量超过20个时表现更稳定。不过无论哪种方案description的质量都比路由算法更重要描述写得烂用什么高级算法都白搭。3.2 把技能注入到对话上下文的两种方式技能选出来之后怎么给模型用我实践下来有两种主流的方式各有适用场景。方式一注入法适合已有Function Calling机制的框架把技能里的脚本包装成函数然后通过Function Calling机制将技能描述、参数Schema一起注册给模型。这样模型会在对话中自动确定“要不要用这个技能”以及“用什么参数调用”。实现上就是把AgentSkill.entrypoint包装成JSON Schemadef skill_to_function_schema(skill: AgentSkill) - dict: return { type: function, function: { name: skill.name, description: skill.load_description()[:800], # 截断避免超长 parameters: { type: object, properties: { task: { type: string, description: 用户交给技能的原始指令必要时包含路径、需求细节 } }, required: [task] } } }这种方式的好处是模型对各工具的选择是显式的行为可控出错容易排查。坏处是技能内部如果包含多步决策逻辑全塞进一个Function可能不够灵活需要把内部步骤拆成多个函数。方式二动态Prompt注入法适合通用型对话Agent如果技能本质上是“指导模型完成一系列步骤”而非“调用一个外部函数”那更适合把SKILL.md全文动态插进上下文。具体做法是先让LLM根据用户输入判断需要哪些技能从Registry中取回技能对象调用load_description()读取SKILL.md然后拼接到对话上下文中class SkillAwareAgent: def __init__(self, registry: SkillRegistry, llm_call, system_prompt: str): self.registry registry self.llm_call llm_call self.system_prompt system_prompt def run(self, user_input: str): # 第一步预路由得到候选技能 candidates self.registry.route(user_input) print(f[SkillAwareAgent] 命中技能: {[s.name for s in candidates]}) # 第二步加载技能详情 skill_docs \n\n.join( f {s.name} 技能说明 \n{s.load_description()} for s in candidates ) # 第三步注入上下文并调用LLM messages [ {role: system, content: self.system_prompt}, {role: user, content: f用户任务{user_input}\n\n可用的技能说明参考\n{skill_docs}} ] response self.llm_call(messages) return response这套流程的骨架哪怕你用的是LangChain、LlamaIndex或者自研框架都可以照搬中间的“路由加载动态文档”思路只是把llm_call换成相应的接口。3.3 技能内脚本的封装规范技能里如果包含可执行脚本需要在SKILL.md中明确声明调用方式和输出格式。我在公司内部定了一套简单的规范脚本必须从标准输入或命令行参数接收输入不许硬编码路径。脚本必须以JSON格式输出结果方便后续LLM读取。脚本失败时返回非零退出码并在stderr输出错误原因。举一个更新技能脚本的例子#!/usr/bin/env python3 # scripts/run_code_review.py import json, sys def main(): repo_path sys.argv[1] if len(sys.argv) 1 else . # ... 这里做真实的静态检查逻辑 ... report { errors: [example.py:12 未使用的变量], warnings: [], score: 86 } print(json.dumps(report, ensure_asciiFalse)) if __name__ __main__: main()然后SKILL.md里对应的执行段落写清楚## 执行步骤 1. 调用 scripts/run_code_review.py传入仓库路径。 2. 解析返回的JSON报告。 3. 将报告中的errors/warnings整理成给用户的结论并给出修复建议。模型看到这样的指引基本不会在“怎么用脚本”上面卡壳。4. 技能编写与调优文档里不会写的经验4.1 描述里的关键词和示例怎么写模型才认很多人在技能的description里堆砌“powerful”、“efficient”这种形容词对模型路由没有任何帮助。模型判断技能是否匹配靠的是名词和动词的语义重叠比如“review code”、“scan repository”、“check security”。这些具体词汇才是关键词。我的经验是写完description后自己提三个不同问法的问题看模型能不能正确路由。比如写了一个代码审查技能就要测试“帮我看看这段代码有没有问题”、“检查一下项目的依赖安全”、“我怀疑这个函数有bug可行吗”这三个都命中。如果有一个没中说明description里缺少对应的同义表达。可以专门在description最后加一句“常见触发问法示例”效果很好。还有一点要注意不要在description里写负面条件。比如“这个技能只在用户明确要求时使用其他情况不要使用”。模型对否定式的理解很不稳定实测下来正面表达比负面约束有效得多。4.2 技能内部脚本的输入输出约束脚本的输入输出如果不加约束调试的时候会让人崩溃。最典型的问题是两个第一个是路径问题。技能里如果涉及文件操作一定要在SKILL.md里写清楚“默认工作目录是仓库根目录所有相对路径基于该目录解析”。否则模型或者脚本经常在“从哪里读文件”上产生分歧。第二个是输出格式飘忽不定。有些技能内部脚本会输出中文和英文混合、带颜色控制字符的内容这些噪声会严重影响LLM对结果的解读。所以我在每个脚本里强制规定只能输出纯JSON所有日志走stderr。然后在SKILL.md里放一个输出示例{passed: true, issues: [], summary: 未发现问题}模型有条理地解析结果才不会在后续步骤里基于幻觉继续瞎编。4.3 技能依赖与版本管理技能多了之后依赖冲突是必然出现的。比如技能A要求requests2.28技能B要求requests2.31如果两个技能在同一个Python环境里共存总有一个要挂。我建议每个技能尽量做成“子解释器”或“独立容器”运行。简单点可以用venvrequirements.txt每个技能目录自带一个虚拟环境执行脚本时用对应环境的解释器来跑。复杂点可以上Docker但如果经营的是一个中小型项目Docker的运维成本会偏高用venv性价比更高。版本管理方面SKILL.md里应该显式声明依赖包的版本而不是“最新版”。我在生产环境踩过多次坑比如某个升级版的lint工具换了输出结构脚本解析直接崩了。锁定版本再配合定期的安全更新检查才是稳妥的做法。5. 常见问题与排查实录5.1 问题一技能满了但模型就是不调用这是最常见的坑。排查方向有两个第一个方向检查路由结果。把日志里路由模块的输出打出来看看用户输入是否命中了候选技能。如果route返回空说明description里的关键词和用户输入的重叠度太低这时候不需要调prompt而是改description。第二个方向检查模型是否“看到”了技能。如果技能是通过Function Calling注册的确认一下注册之后模型的tools参数里确实包含了该技能。有时候框架内部的注册顺序错了导致后面的工具覆盖了前面的模型当然就调不到。我遇到过一个很奇怪的情况技能清单是正确的路由也命中了但模型就是不调用。后来发现是技能的名称和描述里的用词不一致比如技能ID叫code_review但描述里通篇说scan_code。模型举棋不定最后干脆不选。把名称统一之后就好了。5.2 问题二加载完整技能后上下文爆炸按需加载虽然省了常驻token但单个技能如果写得过长一加载也是几千token。尤其是SKILL.md里放了大量示例代码一个技能动辄五六千token。解决方法有两个一是压缩SKILL.md把示例从“完整代码”改成“伪代码关键接口说明”。比如要展示怎么调用某个脚本只写命令行加一个输出示例即可不用贴整段脚本源码二是做好技能的“多级粒度”把大技能拆成子技能主技能里只保留路由描述和步骤概览具体子步骤由子技能承载。5.3 问题三多个技能描述语义重叠模型选错技能数量超过十个之后两个技能描述重叠的情况非常常见。比如“代码审查”和“代码格式化检查”语义上就很容易混淆。这种问题靠改description只能缓解不能根治。我更推荐的做法是在路由阶段加一道“意图区分逻辑”比如要求用户输入中出现了“格式”、“风格”、“PEP8”等词就偏向格式化检查出现“安全”、“漏洞”、“注入”则偏向安全审查。这套关键词规则比模型自己判断要稳定得多。把规则写在路由模块里而不是寄希望于模型在prompt里自我克制。问题排查方向推荐解法模型不调用技能路由命中情况 注册顺序优化description、对齐命名上下文爆炸SKILL.md篇幅与加载粒度压缩示例、拆分子技能技能选错/混淆描述语义重叠度路由加意图规则、关键词分流6. 按这套思路做下来我的一些实际体会agent-skills这种设计模式说白了就是把以前靠“人工写死流程”的Agent变成“模型按手册执行”的Agent。它不会让Agent立刻拥有超人能力但会让Agent的行为变得可解释、可维护、可复用。我在实际项目中最大的感受是写技能和写代码一样三分靠实现七分靠迭代。第一版技能一定是不完美的但没关系先跑通一个完整链路再根据日志和失败案例去优化description和步骤说明。大概迭代两到三个版本之后稳定性就会有一个明显的跃升。另外如果你做的Agent不依赖任何特定平台我建议把SKILL.md的格式设计得通用一些不要在文档里绑定特定的模型或框架。这样无论是换底层模型、换前端框架技能资产都能复用。我已经把团队里常用的十几个场景沉淀成了技能库新项目来的时候做得第一件事就是把这套技能库迁过去而不是重新开发。最后分享一个很实用的小技巧每一个技能里都应该内置一个“自测用例”章节里面写两三个示例输入和期望输出方便每次改动技能后快速回归。刚开始我觉得这活儿麻烦后来有一次改动导致技能返回结果格式变化跑了两天都没发现直到下游任务突然全挂才知道。加了自测用例之后改动前先跑一遍十分钟就能发现问题。这个小成本能帮你省掉无数个排查波动原因的夜晚。
RELATED

相关推荐

MySQL日志系统解析:Redo Log、Undo Log与Binlog

MySQL日志系统解析:Redo Log、Undo Log与Binlog

1. MySQL日志系统全景解析在数据库管理系统的核心架构中,日志模块扮演着"黑匣子"的关键角色。作为从业15年的数据库工程师,我处理过数百起数据恢复案例,其中90%的故障最终都依赖日志系统解决。MySQL的日志体系由Redo Log、Undo Log…

📅 2026/10/28 3:09:15
ECC双关语:椭圆曲线密码学与SAP LSMW批导全解读

ECC双关语:椭圆曲线密码学与SAP LSMW批导全解读

如果你在技术圈待得够久,一定遇到过这种场景:某次会议上,安全同事说“建议把证书换成ECC”,你下意识以为是椭圆曲线密码学;旁边的SAP顾问却接话“我们ECC的LSMW批导还没跑完”,场面瞬间变得微妙。同样的三个…

📅 2026/10/28 3:09:15
马尾辫扎法全攻略:从基础手法到脸型适配的进阶指南

马尾辫扎法全攻略:从基础手法到脸型适配的进阶指南

马尾辫这个看似简单的发型,几乎人人都扎过,但真正能扎得好看、扎得持久、扎得显脸小的人,其实并不多。我平时在造型上折腾了不少年,从学生时代随手一抓的橡皮筋马尾,到后来研究头型、脸型、发际线之间的平衡&#xff0…

📅 2026/10/28 3:04:15
MORE NEWS

更多资讯

📰

推挽电路设计与调试:交越失真、偏置和散热要点

聊推挽电路之前,先说个我见过的真实场面:一位朋友调了大半天的音频功放,示波器上正弦波过零点总有那么一小段台阶,声音听起来发毛、发干,他换了好几款运放、换了耦合电容、甚至怀疑是电源纹波,最后发现问题…

📰

PLC绕线机四轴控制方案:伺服步进选型、接线与检修

简介:这份文档是面向机电设备管理、电气自动化等专业学生与初级技术人员的毕业设计(论文)类资料,聚焦基于PLC的绕线机控制系统设计与检修,可用于课程设计参考、自学PLC控制逻辑或撰写同类课题。全文围绕PLC工作原理、系…

📰

动态规划入门到实战:状态设计、转移方程与背包区间树形模型

动态规划这四个字,几乎是每个写代码的人绕不过去的一道坎。我第一次真正被它按在地上摩擦,是很多年前一场笔试的下午:题目不难,一串数字求最大不降子序列长度,我信心满满写了个递归,本地跑小数据全对&#…

📰

VASPKIT 0.72 配置与VASP输入文件生成避坑指南

简介:《VASPKIT中文手册1》以PDF形式呈现,面向使用VASP开展材料科学、催化与凝聚态计算的研究人员和初学者,介绍VASPKIT轻量级预-后处理工具的配置方法与功能边界,帮助减少输入文件准备和数据处理中的重复操作。压缩包仅含1个PDF文…

📰

Cloudflare OS实战:边缘计算平台架构设计与应用开发指南

1. 重新理解"Cloudflare OS":它不是一个操作系统,而是一套分布式的系统思维先把这个概念说清楚。我最初看到"cloudflare-os"这个标题时,第一反应是:有人把 Cloudflare 的整个边缘平台打包成了一个"操作系…

📰

可信计算线上测试复习:PCR、EK/AK与远程证明实战拆解

简介:围绕华中科技大学可信计算线上测试整理的题目与答案文档,面向网络安全专业学生、可信计算课程学习者以及准备课程测验的读者,用于快速定位考点、核对答题思路并完成考前自测。压缩包内仅有1个docx文件,大小约558KB&#xff0…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬