Agent Skills 深度解析:从 Prompt 到工程化技能管理

发布时间:2026/9/1 10:29:51
Agent Skills 深度解析:从 Prompt 到工程化技能管理 Agent Skills 大概是过去一年里被讲得最多、又最容易让人误解的概念之一。打开各类技术社区到处是“手把手带你做 Agent”“一个 Skill 搞定复杂任务”的教程可真到了自己写项目很多人还是会卡在同一个问题上到底什么是 Agent Skill它和一段写得很详细的 Prompt 有什么区别如果任务已经能用 Prompt 完成为什么还要多做一层技能封装这篇文章想把这件事讲透。我的核心判断是Agent Skills 并不是 LLM 的新玩具而是 LLM 应用走向工程化时非常关键的一个设计。它把容易丢失、难复用的任务知识变成了可加载、可路由、可校验的功能单元。对于正在做 AI 应用、Agent 工作流、文档自动化处理的开发者来说理解这一层之后做 Agent 项目的思路会和之前完全不一样。读完这篇文章你会搞明白四件事第一Agent Skills 到底解决了什么问题第二它和 Function Calling、MCP、RAG 这些热门概念如何区分第三如何用 Python 从零实现一个最小的 Agent Skills 工程第四真实项目里落地时最容易踩的坑和推荐做法是什么。1. 为什么 Agent Skills 值得重新认识现在很多人做 LLM 应用已经不只停留在“聊天机器人”阶段了。真正的项目里模型要完成的任务往往是一连串动作读取文档、抽取关键信息、生成结构化结果、调用外部工具、根据结果做下一步判断。这类任务最大的难点不是“模型不够聪明”而是“任务执行不稳定”。如果用传统方式也就是把所有要求都塞进一段很长的 Prompt会遇到几个很现实的问题Prompt 越来越长重要指令被淹没在大量描述里模型反而抓不住重点。同一个任务换一个场景Prompt 基本要重写很难复用。输出格式只能靠模型自觉缺少校验解析阶段经常报错。任务步骤和业务规则混在自然语言里版本一改就是一场灾难。我见过不少团队代码逻辑本身不复杂但 Prompt 维护成本高到离谱。特别是当模型升级、或从一个模型换到另一个模型的时候同样的 Prompt 在能力更强的模型上不一定表现更好有时甚至会输出更啰嗦、更不可控的结果。Agent Skills 解决的核心问题就是“把任务经验变成系统能力”。一个 Skill 不是简单地把 Prompt 换个名字而是把任务描述、触发条件、执行步骤、工具调用、输出格式、约束规则组合成一个结构化单元。系统可以根据用户请求先做路由加载对应的技能再让模型在这个技能的框架内执行。这样一来模型不再需要从一大段混杂的 Prompt 里猜用户想要什么它只需要按照技能定义里的步骤往下走。用更通俗的话说Prompt 是“每次重新交代怎么做”Agent Skill 是“把怎么做沉淀成一套标准工作流”。前者依赖模型临场发挥后者依赖系统提前设计。这个转变很重要。它意味着 Agent 应用从“提示词艺术”走向了“软件工程”。提示词仍然有价值但它只是技能定义中的一个组成部分而不是全部。围绕技能我们可以做加载、路由、校验、日志、评测、版本管理这些才是工程化的关键。2. Agent Skills 到底是什么核心概念与相关术语对比2.1 一段通俗定义Agent Skill可以理解为“给 LLM 使用的一组可复用的任务执行蓝图”。它通常不是一个文件而是一个包含元信息和执行逻辑的模块。在实践里一个 Skill 至少包含以下几个方面组成部分作用举例name技能的唯一标识code_reviewdescription技能能力和触发场景的描述供路由选择对代码片段做多维度评审trigger什么样的问题应该触发这个技能用户要求检查代码质量、安全性、性能steps模型执行任务时必须遵守的步骤提取代码、多维度检查、输出建议tools该技能依赖的工具或能力json_parser、代码执行器、检索接口output_format输出结果的结构约定JSON、Markdown 列表、结构化报告constraints执行边界和禁止行为不直接修改用户代码不编造缺陷有的实现会把 tools、output_format 拆成独立文件有的会加入验证器。核心思想是一致的把完成一个任务所需的全部知识集中放到一个可管理、可复用的单元里。2.2 与 Function Calling 的区别很多人把 Agent Skill 和 Function Calling 混为一谈。实际上两者处在不同的层级。Function Calling 是模型与工具之间的调用协议。它解决的是“模型决定调用哪个函数、按什么参数调用”的问题。你可以理解为给模型准备了一个函数清单模型输出结构化的函数调用请求程序去执行真实函数。Agent Skill 则更偏业务层。一个技能里可以包含多个步骤其中某一步可能会调用 Function也可能不走 Function而只是让模型做分析和生成。比如“代码评审”这个技能它可以不调用任何外部工具只是告诉模型按什么维度去检查代码。也可以设计成先调用静态分析工具拿到警告列表再让模型结合警告列表做总结。所以更准确的对比是Function Calling管控模型对工具的使用。Agent Skill管控模型对整条任务流程的执行。一个 Skill 内部可能使用多个 Function Call也可能一个都不用。二者可以组合不应混为一谈。2.3 与 MCP 的区别MCPModel Context Protocol解决的是“如何让模型和外部数据、工具建立标准连接”的问题。它的核心目标是统一工具接入方式让同一个 Agent 可以通过 MCP 连接文件系统、数据库、各类 API而不需要为每个工具写一套定制适配。如果把 Agent 比作一家公司MCP 是这家公司内部的标准化电力接口任何设备插上就能用。Agent Skill 则是岗位的操作手册告诉员工接到某个任务后按什么流程处理。MCP 关注连接层Skill 关注执行层。实际项目中两者经常搭配出现Skill 定义执行流程MCP 提供工具调用通道LLM 负责在流程中做推理和生成。2.4 与 RAG 的区别RAG检索增强生成的核心是“先检索相关资料再让模型基于资料生成”。它解决的是模型知识过时、缺少私有知识、容易幻觉的问题。RAG 本质上是一条知识通路让模型在生成前获得相关事实。Agent Skill 解决的是“按什么流程去完成一个任务”的问题。一个技能内部完全可以使用 RAG在某个步骤里先检索内部知识库再基于检索结果做分析。所以 RAG 更像是技能可以调用的基础设施而不是技能本身的替代品。一句话总结RAG 负责给模型“找资料”Skill 负责给模型“定流程”Function Calling 和 MCP 负责让模型“用工具”。这四件事可以组合但关注的层次不一样。3. Agent Skills 的应用场景与边界不是所有 LLM 应用都需要 Agent Skills。判断标准很简单如果任务只有一两句话就能说清楚比如“翻译这段话”“总结这篇文章”直接用 Prompt 就够了。如果任务有固定步骤、有输出格式要求、有边界约束而且会被反复执行那才值得封装成 Skill。3.1 适合的场景代码评审与代码解释按安全性、性能、可读性、边界条件等维度检查代码输出问题清单。这类任务步骤固定输出要求明确非常适合技能化。非结构化文档处理从 PDF、Word、邮件里抽取指定字段转成结构化 JSON。这类任务是当前 LLM 应用里需求最旺盛的场景之一。报告与分析生成给定原始数据按固定格式生成日报、周报、故障复盘。输出格式统一便于后续入库或推送。数据清洗与格式修复比如修复损坏的 JSON、识别并纠正文本格式错误。这类任务需要固定的操作步骤和校验逻辑。研究辅助在人文社科混合研究方法论文写作这类场景中可以用 Agent Skill 辅助完成文献分类整理、访谈记录结构化、问卷开放题答案归类、文献综述框架梳理等工作。注意这里辅助的是合法研究流程不是代写论文或学术造假。多 Agent 协作中的专用角色一个 Agent 负责拆解任务多个技能型 Agent 分别处理数据清洗、分析、生成等子任务。3.2 不适合的场景简单问答回答一个事实性问题直接调用模型就行没必要为每个问题建技能。超高精度计算模型做计算本身就不可靠这种场景应该优先使用代码执行工具而不是技能流程。高风险操作比如删除数据库、转账、发布生产配置。这些操作即使封装成技能也必须加入人工审批环节不能由模型自动完成。一次性突发任务只为用一次而封装技能投入产出比太低。先跑通 Prompt等任务变稳定、变高频时再技能化。一个常见误区是“为了技能而技能”。在项目初期先用 Prompt 跑通业务流程没有错。只有当任务开始重复、开始换人维护、开始因为 Prompt 太长而出错时才是引入 Skill 的时机。4. 环境准备与最小工程结构下面我们用 Python 从零实现一个最小的 Agent Skills 工程。这里不依赖重框架重点是把原理讲清楚。4.1 环境准备建议使用 Python 3.9 或更高版本。本示例需要两个 Python 依赖openai用于调用支持 OpenAI 接口格式的大模型服务。PyYAML用于解析技能定义文件。如果你的运行环境中不方便安装外部依赖也可以把技能定义写成 JSON解析部分换成标准库json。为了更贴近大多数项目的实际写法这里使用 YAML。安装命令pip install openai pyyaml大模型方面你可以使用任意支持 OpenAI 兼容接口的服务既可以是云端 API也可以是本地部署的推理服务。把接口地址、密钥、模型名配置到环境变量里即可。这个设计让示例代码不绑定某一家厂商。4.2 环境变量配置创建环境变量前先说明一点API Key 属于敏感信息不要硬编码在代码里更不要提交到代码仓库。推荐使用环境变量或专用的密钥管理服务。export LLM_BASE_URL你的大模型服务接口地址 export LLM_API_KEY你的密钥 export LLM_MODEL你的模型名称Windows 下可以改用set命令或者在 IDE 的启动配置里填写。如果使用本地推理服务接口地址通常是http://localhost:11434/v1模型名以你实际部署的模型为准。4.3 工程目录结构为了演示通用思路我们先创建一个最小工程目录agent_skills_demo/ ├── skills/ │ ├── code_review/ │ │ └── skill.yaml │ └── json_fixer/ │ └── skill.yaml ├── agent.py └── requirements.txtskills目录下每个子目录代表一个技能每个技能包含一个skill.yaml定义文件。agent.py是主程序负责加载技能、选择技能、执行技能。requirements.txt内容如下openai pyyaml从这套结构可以看出新增一个技能其实就是在skills下新增一个目录和 YAML 文件主程序不需要改动。这就是技能化带来的扩展性优势。5. 核心代码实现技能定义、加载、路由与执行5.1 技能定义示例先看两个技能定义。第一个是代码评审技能# skills/code_review/skill.yaml name: code_review description: 对代码片段进行多维度评审输出问题清单和修改建议。适合用户要求检查代码质量、安全性、性能、可读性的场景。 trigger: 用户提供代码并要求评审、优化、检查问题 steps: - 提取用户提供的代码片段 - 从安全性、性能、可读性、边界条件四个维度逐项检查 - 对每个问题给出严重级别高/中/低 - 输出修改建议建议中包含具体代码示例 tools: [] output_format: | 问题列表 - 严重级别问题描述 | 所在位置 | 修改建议 总体结论可发布 / 需要修改 constraints: - 不直接修改用户代码只给出建议 - 不编造代码中不存在的缺陷第二个是 JSON 修复技能# skills/json_fixer/skill.yaml name: json_fixer description: 修复无法解析的 JSON 文本返回可用的 JSON 结构和修复说明。适合用户粘贴不完整 JSON、截断 JSON、格式错误 JSON 的场景。 trigger: 用户提供 JSON 文本并要求修复、解析、整理格式 steps: - 截取文本中疑似 JSON 的部分 - 尝试用 json.loads 解析 - 解析失败时定位常见错误缺少引号、多余逗号、单引号代替双引号、括号不闭合 - 修复后再次解析验证 - 输出修复后的 JSON 和修复说明 tools: [json_parser] output_format: | 修复后的 JSON { json } 修复说明 - 错误位置 - 修复方式 constraints: - 保持原始数据含义不变 - 不确定的字段保留并标注为需人工确认可以看出技能定义本质上是一份“结构化任务说明书”。后面执行时这些内容会注入到系统提示词里引导模型按流程输出。5.2 技能加载模块主程序的第一步是扫描技能目录读取所有技能定义。这里用PyYAML解析 YAML 文件并把技能名作为字典的 key# agent.py 中的技能加载部分 import os import yaml from pathlib import Path SKILLS_DIR Path(skills) def load_skills() - dict: 扫描 skills 目录读取所有技能定义 skills {} for skill_yaml in SKILLS_DIR.glob(*/skill.yaml): data yaml.safe_load(skill_yaml.read_text(encodingutf-8)) skills[data[name]] data return skills这段代码的核心逻辑是Path(skills).glob(*/skill.yaml)会匹配skills目录下所有子目录中的skill.yaml。这样新增技能时不需要修改代码。5.3 两种技能路由方式技能路由的目的是根据用户请求选择最合适的技能。这里提供两种方式关键词路由和 LLM 路由。关键词路由适合离线演示和低延迟场景LLM 路由适合语义复杂的项目。关键词路由实现def route_by_keyword(query: str, skills: dict) - str: 基于关键词匹配选择技能适合本地离线演示 keyword_map { code_review: [评审, 代码质量, 优化, 检查代码], json_fixer: [json, 格式化, 修复json, 解析失败], } for skill_name, keywords in keyword_map.items(): if skill_name not in skills: continue for kw in keywords: if kw.lower() in query.lower(): return skill_name return LLM 路由实现from openai import OpenAI client OpenAI( base_urlos.getenv(LLM_BASE_URL), api_keyos.getenv(LLM_API_KEY), ) def route_by_llm(query: str, skills: dict) - str: 基于大模型语义匹配选择技能 skill_list [ {name: name, description: data[description]} for name, data in skills.items() ] prompt f你是技能路由器。请根据用户请求选择最合适的技能名称只输出技能名称本身不要解释、不要输出其他内容。 可选技能 {skill_list} 用户请求{query} 输出技能名称 resp client.chat.completions.create( modelos.getenv(LLM_MODEL), messages[{role: user, content: prompt}], temperature0, ) return resp.choices[0].message.content.strip()这里把选择技能的任务交给模型但限定了输出格式。温度设为 0让选择结果尽量稳定。5.4 技能执行模块选中技能后把技能定义中的步骤、输出格式、约束组装成系统提示词再让模型基于这些信息响应用户请求def execute_skill(skill: dict, user_query: str) - str: 按技能定义执行任务 steps_text \n.join(f- {step} for step in skill[steps]) constraints_text \n.join(f- {c} for c in skill.get(constraints, [])) system_prompt f你正在执行一个名为 {skill[name]} 的技能。 技能描述{skill[description]} 执行步骤 {steps_text} 输出格式要求 {skill[output_format]} 约束 {constraints_text} 请严格按上述步骤处理用户请求。 resp client.chat.completions.create( modelos.getenv(LLM_MODEL), messages[ {role: system, content: system_prompt}, {role: user, content: user_query}, ], temperature0.2, ) return resp.choices[0].message.content可以看到这个执行逻辑本身并不复杂。真正的核心价值在技能定义文件里步骤写得越清晰约束写得越明确模型输出就越稳定。这也是为什么我说Agent Skills 工程化之后维护重点从“调 Prompt”变成了“维护技能库”。5.5 主程序入口最后把加载、路由、执行串起来def main(): query input(请输入你的请求).strip() if not query: print(请求不能为空。) return skills load_skills() if not skills: print(未加载到任何技能请检查 skills 目录结构。) return # 优先使用 LLM 路由如果没有配置大模型则退回关键词路由 skill_name if os.getenv(LLM_BASE_URL): try: skill_name route_by_llm(query, skills) except Exception as e: print(fLLM 路由失败退回关键词路由{e}) skill_name route_by_keyword(query, skills) else: skill_name route_by_keyword(query, skills) if skill_name not in skills: print(没有匹配到技能走默认对话。) return skill skills[skill_name] print(f匹配技能{skill_name}) result execute_skill(skill, query) print(\n 执行结果 ) print(result) if __name__ __main__: main()主程序里做了一个很实用的处理优先走 LLM 路由如果路由过程出现问题自动退回关键词路由。这样即使大模型服务暂时不可用关键词模式也能兜底保证程序不会直接崩溃。6. 运行验证与效果对比6.1 运行步骤首先安装依赖pip install -r requirements.txt然后配置环境变量。如果使用关键词路由模式不配置也可以因为关键词路由不依赖大模型接口python agent.py输入请帮我评审下面这段 Python 代码 def get_user(id): user db.query(SELECT * FROM users WHERE id id) return user预期输出匹配技能code_review 执行结果 问题列表 - 高存在 SQL 注入风险 | 第2行 | 使用参数化查询代替字符串拼接 - 中函数命名不够清晰 | 第1行 | 建议命名为 get_user_by_id - 低缺少类型注解 | 第1行 | 补充参数和返回值类型 总体结论需要修改如果输入是修复这个 JSON{name: 张三, age: 20,}预期会匹配json_fixer技能输出修复后的 JSON 和修复说明。6.2 如何判断成功判断成功的标准有三个路由选择准确程序能在多个技能中选择出符合用户需求的那个。输出格式稳定多次运行同一请求时输出结构大体一致。步骤完整输出内容覆盖了技能定义中的步骤要求而不是只做了粗略回答。你可以做一个简单回归准备 5 条典型请求分别运行 5 次统计输出结构和关键字段的稳定性。如果某些字段频繁缺失说明技能定义里的步骤或输出格式还不够具体。6.3 失败时先看哪里如果运行失败不要急着改代码。按顺序排查看环境变量LLM_BASE_URL、LLM_API_KEY、LLM_MODEL是否设置。看技能目录skills是否和agent.py在同一级目录YAML 文件是否有语法错误。看路由结果程序是否打印了“匹配技能”如果匹配不到先检查关键词映射或技能描述。看大模型服务日志如果接口调用超时问题大概率在模型服务侧。7. 常见问题与排查方法问题现象可能原因排查方式解决方案程序启动时提示找不到模块openai 或 pyyaml 未安装执行pip list查看是否安装安装依赖pip install openai pyyamlYAML 文件解析报错技能定义文件格式错误用在线 YAML 校验工具检查修正缩进和引号路由一直返回“没有匹配到技能”关键词映射未覆盖用户表述打印用户 query 检查关键词匹配情况增加关键词或改用 LLM 路由LLM 路由选错技能技能描述不够清晰打印模型返回的选择结果完善 description列出明确的触发场景输出格式和 skill.yaml 不一致模型没有严格遵守 output_format将输出格式要求写得更具象并加入示例把期望输出示例直接写进 system prompt接口调用超时模型服务负载高或单个模型推理慢查看服务日志和请求耗时切换到更小的模型或增加超时重试机制每次运行结果差异很大temperature 设置过高检查执行代码中的温度参数降到 0 或 0.2增加约束性描述API Key 出现在日志里环境变量未正确隔离检查代码和配置文件使用环境变量或密钥管理服务不要硬编码还有一个常见的工程问题技能数量变多之后路由质量会下降。因为 LLM 在一个超大列表里选择正确技能的难度会上升。解决思路是给技能分组或者在路由前先用关键词做一轮粗筛再用 LLM 在候选项里做精选。这属于进阶优化初期不需要做得太复杂。8. 工程化最佳实践与进阶方向8.1 技能描述要能支撑路由路由质量很大程度上取决于description写得好不好。写描述时不要只写“这个技能做什么”还要写清楚“什么情况下应该选它、什么情况下不该选它”。好的描述能明显降低选错技能的概率。例如不够好对代码进行评审比较好对代码片段进行多维度评审输出问题清单和修改建议。适合用户要求检查代码质量、安全性、性能、可读性的场景。8.2 给输出加一层校验模型输出再稳定也不能完全信任。生产环境中建议在技能执行后增加一个校验器。对于要求输出 JSON 的技能直接用json.loads校验对于要求固定字段的技能检查关键字段是否存在。可以在技能定义里增加一个validator字段声明输出格式是json还是markdown由程序根据声明自动校验。校验失败时可以自动重试一次或把问题交给人工处理。8.3 工具权限与安全边界技能可以调用工具但这带来一个安全风险模型可能在流程中触发危险操作。实际项目里任何涉及写操作、删除操作、敏感数据访问的工具都应该遵循最小权限原则。在技能定义中可以增加permissions字段声明该技能能访问哪些资源、不能访问哪些资源。程序在执行前检查权限不符合规则的工具调用直接拒绝而不是交给模型做决定。对于高危险操作比如删除数据、生成发布变更必须加上人工审批环节。这个原则不能只在代码层面体现它还应该写进团队协作流程任何人修改技能定义都要走代码评审。8.4 日志与追踪Agent 应用排查问题最难的地方在于我们很难确定问题出在路由、Prompt、模型、工具还是数据上。所以日志记录不能只记录最终结果还要记录几个关键节点用户请求原文。路由选择的技能。拼装后的 system prompt。模型返回的原始输出。校验结果。各节点耗时。这些信息串起来就能形成一条完整的调用链路。配合 trace 工具或简单的日志采集可以大大降低排障成本。8.5 技能版本化技能定义本质上是代码应该和代码一样做版本管理。一个可落地的做法是每个技能增加version字段变更技能时更新版本号并在 commit message 里写明变更原因。如果线上模型表现异常可以快速回滚到上一个技能版本。当技能库扩大到几十个技能时还可以考虑把技能定义和代码一起编译打包避免线上环境技能文件缺失或版本不一致的问题。8.6 与 LLM Wiki、agent.md 的结合最近业界有一个思路很值得关注就是让模型在处理复杂任务时知道自己该查哪些文档。代表性实践是 Karpathy 提出的 LLM Wiki 思路用一系列结构化的 markdown 文档来描述项目背景、约定、工作流和验收标准LLM 在执行任务前先“翻阅”对应的文档再开始输出。agent.md可以理解为一种特别的项目级知识文档。它描述的不是某个简单问答而是一个复杂任务在特定环境下的完整执行上下文。Agent Skill 和 agent.md 的定位不完全一样Skill 更偏向“一个技能对应一套步骤”agent.md 更偏向“一个任务对应一份完整的上下文说明”。但在实际工程中两者可以结合把技能定义放在skills/目录下。把复杂项目的背景知识、历史决策、代码约定放在文档目录里。Agent 执行技能时根据技能需要按需检索或加载对应的文档。文档更新走版本管理和技能一起发布。这种做法适合任务背景复杂、无法用几行 skill.yaml 描述清楚的场景。比如一个涉及公司内部代码规范、部署流程、历史架构约束的代码评审任务光靠技能定义不够还需要让模型读到内部文档。把技能当作流程骨架把文档当作知识补充两者配合比单独用一种方案可靠得多。8.7 进阶方向如果你已经跑通了上面的最小示例下一步可以往这几个方向深入技能自动发现与组合当技能数量足够多时尝试让 Agent 根据任务目标自动组合多个技能而不只是选一个。技能回归测试集为每个技能准备一组典型输入和期望输出每次改动技能后自动回归防止引入负面影响。技能评测体系用一组评测集评估技能在不同模型上的表现辅助模型选型和技能调优。公司内部技能库把通用的数据清洗、文档解析、报告生成技能沉淀为公共模块多个项目共用降低重复建设成本。9. 总结Agent Skills 的本质不是给 Prompt 换了一个更好听的名字而是把 LLM 应用的实现思路从“临场写提示词”转变成“系统化的技能管理”。技能定义承载了任务步骤、输出约束和触发条件加载和路由机制让程序能根据用户请求选择合适的技能模型在技能框架内执行输出自然更稳定、更可控。如果这篇文章对你有一点点帮助建议自己动手把最小示例跑通然后尝试把你日常工作里重复最多、最容易出错的任务改造成第一个 Skill。你会发现当技能库逐渐成型后做新的 Agent 应用会轻松很多。这也引出一个更值得思考的问题我们和 LLM 协作的方式正在从“告诉它每一步怎么做”转向“为它设计可复用、可验证的工作流”。这种转变最终比拼的不只是模型能力而是我们抽象任务、沉淀经验、组织知识的能力。