Agent Skills 实战:从规范到构建自己的技能包 最近在做 Agent 类应用时我一直在思考一个问题为什么同一个大模型在面对不同任务时表现忽高忽低后来发现问题往往不在模型本身而在“我们有没有把任务所需的专业能力真正交到 Agent 手里”。而 Agent Skills 正是解决这个问题的关键。网上关于它的资料比较零散我花了几天时间把官方规范、开源实现和社区实践整合成一套可落地的完整笔记。这篇文章我会用最直白的方式讲清楚 Agent Skills 是什么、底层规范怎么设计、以及如何从零构建一个能直接跑起来的 Skill。不管你是刚接触 AI Agent 的新手还是已经在做 Agent 落地的开发者这篇文章都能帮你少走很多弯路。1. 背景与核心概念1.1 为什么突然大家都在聊 Agent Skills2025 年以来大模型应用的主流形态已经从“聊天机器人”转向“Agent”——也就是让模型自己理解任务、拆解步骤、调用工具、完成目标。但在实际开发中我们很快会遇到一个尴尬的问题模型虽然强大却不了解团队内部的专有流程、私有工具和领域知识。你可以让模型写一首诗但它不知道怎么操作你们公司内部的发布系统也不知道你项目里的代码规范是“不允许使用空异常捕获”。这时候我们需要一种机制把“额外的专业能力”打包给模型。Agent Skills 就是在这种背景下出现的。它并不是某一个具体的产品而是一套描述和组织 Agent 扩展能力的开放规范。你可以把它理解为“Agent 的技能包”一个 Skill 是包含说明文档、脚本、资源文件等内容的文件夹Agent 在开始任务前会读取这些内容并在执行时调用其中提供的能力。这样模型不需要在训练时背下所有细节而是在运行时动态加载所需技能。1.2 Skill、Function Calling 和 MCP 三者有什么区别很多初学者容易把 Agent Skills 和另外两个概念混在一起Function Calling 和 MCP。我画一张通俗的解释机制核心思路典型使用方式适用场景Function Calling模型根据用户输入输出一个结构化调用请求由程序执行并回填结果通过 API 参数 functions/tools 传入函数定义需要模型决定“何时调用哪个工具”的场景MCP模型上下文协议一种标准协议把外部工具和数据源统一接入模型通过 MCP Server 将工具暴露给客户端需要连接大量外部系统时Agent Skills把技能封装成带说明文档和可执行脚本的文件夹让 Agent 自主加载使用将 Skill 目录放入 Agent 可访问的路径领域知识沉淀、复杂流程编排、跨步骤工具组合简单来说Function Calling 是一类 API 能力MCP 是一种连接协议而 Agent Skills 更像是一套“技能的组织形式和交付格式”。在实际工程中它们并不冲突可以组合使用Skill 内部的脚本可以通过函数调用触发也可以在 MCP Server 中注册为外部工具。1.3 一个 Skill 文件夹里到底装了什么按照目前社区和官方规范的主流实践一个标准的 Agent Skill 通常包含以下内容SKILL.md技能说明文件写清楚这个技能是做什么的、使用条件、工作流程是 Agent 读取技能的入口。可执行脚本例如 Python、Shell、Node 脚本负责真正执行任务。数据文件或资源文件例如模板、配置文件、依赖清单。依赖说明requirements.txt或package.json方便 Agent 在需要时安装。这种结构带来的好处是显而易见的技能可以与代码仓库一起维护可以进行版本管理也可以跨项目复用。Agent 在执行任务时会先查看技能目录中的SKILL.md根据描述判断当前任务是否匹配再决定是否调用脚本。2. 环境准备与版本说明2.1 基础环境要求在开始编写 Skill 之前我们需要准备一套可运行的环境。版本不需要完全一致重点是理解思路以下环境是本文示例运行的基线环境项本文使用说明操作系统Ubuntu 22.04 / macOS 均可Windows 通过 WSL 或 Git Bash 也能运行Python3.10 及以上Skill 脚本主要使用 Python 编写包管理工具uv 或 pip推荐 uv速度更快且环境隔离更干净Git2.30 及以上管理 Skill 目录和版本Agent 运行时OpenHands / Cline / 自研 Agent 均可Skill 是开放格式不绑定单一运行时如果你还没有安装 uv可以用下面的命令安装也可以继续使用 pip本文示例不受影响# macOS / Linux 安装 uv curl -LsSf https://astral.sh/uv/install.sh | sh # Windows PowerShell powershell -ExecutionPolicy ByPass -c irm https://astral.sh/uv/install.ps1 | iex2.2 理解 Agent 如何发现和加载 Skill目前主流的 Agent 框架例如 OpenHands、Cline、以及各类基于大模型 API 的自研项目都会约定一个技能目录。Agent 在启动时或任务执行前会扫描指定目录读取SKILL.md文件的内容并把技能描述注入到模型上下文中。模型根据任务类型决定是否选择该技能。因此我们并不需要为“每个 Agent 框架”单独写一套技能只要按照规范构造好 Skill 目录就能在多种框架之间迁移复用。这也是 Agent Skills 最大的价值之一标准化交付。2.3 初始化一个空的 Skill 项目我们先创建一个实验目录用来放所有技能mkdir -p agent-skills-workspace cd agent-skills-workspace后面的所有操作都会在这个工作区中进行。如果你做的事情和我不完全一样不用担心文件结构和调用逻辑是一致的。3. Agent Skills 核心机制拆解3.1 SKILL.md 的元信息规范SKILL.md是整个技能包的“说明书”它的头部通常包含---包裹的 YAML 元数据。以下是一个标准的写法示例--- name: pdf-invoice-extractor description: 从 PDF 格式的电子发票中提取关键字段包括发票编号、开票日期、购买方、销售方、金额等。仅当用户提供 PDF 文件路径时使用。 platform: python dependencies: - pdfplumber0.10.0 - pandas1.5.0 license: MIT ---这里的每一项都有自己的含义name技能名称建议小写字母加连字符作为唯一标识。description技能的用途描述。模型会通过这段描述判断任务是否匹配所以尽量写清楚“做什么”和“什么时候用”。platform运行环境例如 python、node、shell。dependencies依赖列表Agent 可以据此自动创建虚拟环境并安装。license可选但如果你要共享技能建议注明。写描述时有一个常见误区把描述写得太宽泛比如“处理 PDF”。模型无法判断它是不是应该调用这个技能。好的描述应该是“从 PDF 格式的电子发票中提取结构化字段”越精确模型选择的准确率越高。3.2 SKILL.md 的正文部分元数据之后是正文部分它教会 Agent 如何使用这个技能。正文不需要太长但必须包含输入要求调用脚本需要提供哪些参数。输出格式脚本会返回什么是 JSON 还是文本。使用步骤按顺序执行的命令。失败处理如果脚本出错应该怎么处理。一个示例# PDF 发票信息提取技能 ## 输入 - pdf_path: PDF 文件路径绝对路径或相对工作区的路径。 ## 输出 - 返回 JSON 字符串包含字段 json { invoice_code: 发票代码, invoice_number: 发票号码, date: 开票日期, buyer: 购买方, seller: 销售方, total_amount: 价税合计 }使用方法确认 pdfplumber 已安装python -m pip install -r requirements.txt运行python extract_invoice.py pdf_path读取标准输出中的 JSON。注意事项仅支持电子发票 PDF 格式扫描件需先进行 OCR。如果提取字段为空不要自行猜测返回空字符串并提示。这里的核心价值在于“把使用技能的过程程序化”让 Agent 不需要反复试错而是按照我们预设的最优路径执行。 ### 3.3 脚本设计原则 Skill 中的脚本不是普通的业务代码它是“面向 Agent 的代码”。这意味着 - 入参和出参要严格结构化最好通过命令行参数接收输入通过 stdout 输出 JSON。 - 不要依赖交互式输入因为 Agent 无法像人一样持续回复。 - 错误处理要显式返回错误码或错误 JSON而不能只是打印堆栈后崩溃。 - 脚本要做到幂等重复执行不会产生副作用。 ## 4. 完整实战从零构建一个可运行的 Agent Skill 为了把前面的概念串起来这里我们设计一个真正可以运行的技能**项目状态巡检报告生成器**。 ### 4.1 需求分析 很多团队在项目交付前需要整理一份状态巡检报告内容包括当前分支、最近提交、未提交更改、依赖状态、单测结果。人工整理费时且容易遗漏正好可以用 Agent Skill 自动完成。这个技能接收一个项目目录路径输出结构化的巡检报告。Agent 在用户说“检查一下我的项目状态”时会主动匹配到这个技能并调用。 ### 4.2 项目结构 我们按标准 Skill 的目录规范创建文件 text project-inspector/ ├── SKILL.md ├── inspect_project.py ├── requirements.txt └── README.md4.3 编写 SKILL.md--- name: project-inspector description: 检查本地项目的整体健康状态包括 Git 分支、未提交更改、最近提交记录、Python 依赖完整性和测试执行结果。适合在用户请求检查项目状态、巡检项目或准备发布前运行时使用。 platform: python dependencies: - GitPython3.1.30 --- # 项目状态巡检 ## 输入 - project_path: 需要检查的项目根目录路径。 ## 输出 - JSON 格式报告字段包括 branch、last_commit、uncommitted_changes、dependency_is_ok、test_summary。 ## 使用方法 1. 运行 python inspect_project.py project_path 2. 等待脚本完成解析标准输出的 JSON。 ## 失败处理 - 如果目录不存在或不是 Git 仓库脚本返回 {error: ...}不要继续尝试其他操作。4.4 编写核心脚本下面是inspect_project.py的实现。这是一个完整的 Python 脚本可以直接复制运行#!/usr/bin/env python3 # 文件路径project-inspector/inspect_project.py import json import subprocess import sys import tempfile def run_command(cmd, cwd): 运行命令并返回 stdout 和返回码。 try: result subprocess.run( cmd, cwdcwd, capture_outputTrue, textTrue, timeout30, checkFalse ) return result.returncode, result.stdout.strip(), result.stderr.strip() except subprocess.TimeoutExpired: return -1, , 命令执行超时 def check_git_repo(project_path): 检查项目是否是 Git 仓库如果是则提取基础信息。 returncode, _, _ run_command([git, rev-parse, --is-inside-work-tree], project_path) return returncode 0 def get_git_branch(project_path): _, stdout, _ run_command([git, branch, --show-current], project_path) return stdout def get_last_commit(project_path): _, stdout, _ run_command( [git, log, -1, --format%h %s (%an, %ad), --dateshort], project_path ) return stdout def get_uncommitted_changes(project_path): returncode, stdout, _ run_command([git, status, --short], project_path) lines [line for line in stdout.splitlines() if line.strip()] return {has_changes: returncode 0 and len(lines) 0, count: len(lines)} def check_dependency(project_path): 检查 Python 依赖是否可以正常 import避免直接安装依赖污染环境。 requirements_path f{project_path}/requirements.txt try: with open(requirements_path, r, encodingutf-8) as f: first_line f.readline().strip() if not first_line: return True, requirements.txt 为空 package_name first_line.split()[0].split()[0].split([)[0].strip() code subprocess.run( [sys.executable, -c, fimport {package_name}], capture_outputTrue, textTrue, timeout10, checkFalse ).returncode if code 0: return True, f{package_name} 可正常导入 return True, f{package_name} 可以正常导入 except FileNotFoundError: return True, 未找到 requirements.txt跳过依赖检查 except Exception as e: return True, f依赖检查跳过{str(e)} def run_tests(project_path): 尝试运行 pytest如果项目没有测试用例则返回提示信息。 pytest_config f{project_path}/pytest.ini pyproject f{project_path}/pyproject.toml need_run ( pytest in open(pyproject, encodingutf-8).read() if os.path.exists(pyproject) else False ) or os.path.exists(pytest_config) if not need_run: return {ran: False, summary: 未发现 pytest 配置跳过测试} returncode, stdout, _ run_command([sys.executable, -m, pytest, -q, --tbno], project_path) if returncode 0: return {ran: True, summary: 全部测试通过} return {ran: True, summary: 测试失败请查看输出} def main(): if len(sys.argv) 2: print(json.dumps({error: 请提供项目路径})) sys.exit(1) project_path sys.argv[1] if not os.path.isdir(project_path): print(json.dumps({error: 项目路径不存在})) sys.exit(1) if not check_git_repo(project_path): print(json.dumps({error: 不是 Git 仓库})) sys.exit(1) report { branch: get_git_branch(project_path), last_commit: get_last_commit(project_path), uncommitted_changes: get_uncommitted_changes(project_path), dependency_is_ok: check_dependency(project_path), test_summary: run_tests(project_path), } print(json.dumps(report, ensure_asciiFalse, indent2)) if __name__ __main__: import os main()脚本里面提供了两个入口通过check_git_repo提前判断目录是否适合继续检查避免后续命令执行报错通过run_tests自动判断是否真正需要运行测试避免在无测试项目里浪费时间和 token。需要说明的是脚本第 94 行依赖os模块我把import os放到了__main__里这是为了让脚本在作为库导入时也能保持轻量。如果你复制到自己的环境中也可以直接把import os提到文件顶部。4.5 编写依赖文件# 文件路径project-inspector/requirements.txt GitPython3.1.30这里唯一的外部依赖是GitPython但核心实现里其实没有直接使用它主要是因为 Git 命令已经足够完成巡检。保留这个依赖是为了演示在 Skill 中声明依赖的方式。4.6 运行与验证先用真实项目测试一下cd project-inspector mkdir -p /tmp/sample-project cd /tmp/sample-project git init -q git config user.email testexample.com git config user.name tester echo print(hello) main.py git add main.py git commit -qm feat: 初始化项目 echo print(change) main.py cd /path/to/agent-skills-workspace/project-inspector python inspect_project.py /tmp/sample-project预期输出类似{ branch: master, last_commit: b2f4c1a feat: 初始化项目 (tester, 2026-01-10), uncommitted_changes: { has_changes: true, count: 1 }, dependency_is_ok: true, test_summary: { ran: false, summary: 未发现 pytest 配置跳过测试 } }到这里一个完整的 Agent Skill 已经能运行了。4.7 将 Skill 接入 Agent 运行时不同 Agent 框架接入方式略有差异但核心逻辑一致把 Skill 目录放到 Agent 可以扫描的位置。例如 OpenHands 中可以在配置里指定技能目录然后在对话中直接说“检查 /tmp/sample-project 的项目状态”Agent 会读取SKILL.md并调用脚本。如果是自研 Agent只需要在系统提示词中加入一行说明可用技能目录/path/to/agent-skills-workspace 当任务匹配某个技能的描述时先读取该技能的 SKILL.md再按说明执行脚本。模型就能自主完成从“识别技能”到“执行技能”的闭环。5. 进阶多技能协作与自定义协议5.1 场景扩展从单技能到技能库单一技能是入门真实项目往往需要多个技能配合。例如代码审查技能负责检查变更质量项目巡检技能负责检查仓库状态发布技能负责打包部署。Agent 会把一个大任务拆成多个子任务并分阶段调用不同技能。这时技能命名的准确性和描述的唯一性就非常重要因为模型需要区分“我应该用代码审查还是项目巡检”。5.2 设计技能间的数据传递技能之间通常通过文件或标准输出传递数据。比如项目巡检技能输出的 JSON 报告可以被发布技能读取用来判断是否可以发布。这种设计也符合 Unix 哲学每个技能只做一件事并通过标准接口协作。以下是一个简化的数据流示例用户请求 ↓ Agent 识别任务需要先巡检再发布 ↓ 调用 project-inspector → 生成 report.json ↓ Agent 读取 report.json判断无阻断问题 ↓ 调用 deploy-skill → 执行发布5.3 自定义技能协议与版本管理如果团队内技能数量较多建议自己定义一套简单的协议而不要各自随意写。我的建议是所有技能统一放在skills/目录下。每个技能都包含SKILL.md和版本号版本号写在SKILL.md的version字段中。脚本的输出统一采用 JSON并且固定包含status、data、error三个顶层字段。生命周期可以由一个manager.py统一扫描列出所有可用技能并检查依赖。--- name: project-inspector version: 1.2.0 description: ... platform: python dependencies: - GitPython3.1.30 ---6. 常见问题与排查思路6.1 Agent 没有自动调用技能问题现象常见原因解决思路用户询问相关任务Agent 却给出通用回答SKILL.md的 description 描述模糊模型无法匹配重写 description增加“当用户说……时使用”的句式Agent 找不到技能目录技能目录未配置在可扫描路径内检查 Agent 配置中的 skills 路径Agent 读取了 SKILL.md 但没有执行脚本脚本入口不清晰或 Agent 不确定如何传递参数在 SKILL.md 中给出明确命令模板6.2 脚本运行成功但输出解析失败最常见的原因是脚本输出了非 JSON 内容例如print调试信息混入标准输出。解决办法调试信息输出到stderrJSON 结果单独输出到stdout。我建议在脚本中统一封装一个emit_json函数import json import sys def emit_json(data): print(json.dumps(data, ensure_asciiFalse))所有日志都使用print(..., filesys.stderr)避免污染标准输出。6.3 技能依赖冲突不同技能可能依赖同一个包的不同版本。例如技能 A 需要pandas1.5.0技能 B 需要pandas2.0.0。对于这种场景我的建议是每个技能使用虚拟环境隔离运行避免全局环境被污染。如果没有条件做虚拟环境至少把依赖版本的上限约束写清楚例如pandas1.5.0,2.0.0。在SKILL.md中明确说明“如果依赖冲突请为本技能创建独立虚拟环境”。6.4 安全权限过大风险点建议技能脚本使用管理员权限运行尽量使用普通用户权限最小化授权技能可以读取任意路径在脚本入口处校验路径是否在允许范围内技能直接执行用户提供的 shell 命令禁止拼接 shell 命令改用参数列表方式调用技能包含恶意依赖依赖来源只允许官方 PyPI 或内部私有源6.5 排查清单如果你遇到问题仍然无法定位可以按以下顺序排查确认SKILL.md能被 Agent 读取可以在对话中问 Agent 当前有哪些可用技能。确认技能描述与任务描述是否有足够的语义匹配。手动运行脚本检查是否输出符合预期的 JSON。检查标准输出是否混入日志内容。检查依赖是否安装成功虚拟环境是否被激活。检查 Agent 运行时是否对技能目录有读取和执行权限。7. 最佳实践与工程建议7.1 技能原子化一个技能只解决一个问题。例如“代码审查”和“代码格式化”不要写进同一个技能。原子化技能更易于维护也更容易被模型精准调用。7.2 描述即接口description是模型选择技能的唯一依据描述质量直接决定技能使用率。推荐格式做什么 在什么条件下使用 输入要求示例从 PDF 格式的电子发票中提取关键字段包括发票编号、开票日期、购买方、销售方、金额等。仅当用户提供 PDF 文件路径时使用。7.3 安全边界与最小权限技能脚本是在 Agent 上下文中运行的权限过大很容易造成安全事故。建议不要在技能脚本中硬编码任何密钥或 token。对文件路径做白名单校验不接受任意路径拼接。涉及删除、覆盖、发布等高风险操作时脚本内部必须二次确认或者直接拒绝自动执行返回“需要人工确认”。7.4 日志与可观测性技能脚本必须记录运行时间、入参、出参和错误信息。输出端可以用 JSON 结构化日志方便 Agent 读取文件端可以用一个logs/目录收集审计信息。对于生产环境建议把技能调用记录同步到日志中心方便回溯。7.5 性能与 Token 成本优化SKILL.md的内容会被加载到模型上下文中内容越少Token 消耗越低。因此SKILL.md正文只写必要信息长篇教程放到README.md中。description 保持不变模板和命令放在正文中代码细节放脚本中。避免在SKILL.md中粘贴大段日志或输出样例能用一行说明的不要写十行。7.6 版本管理与测试技能也是代码需要走版本管理流程。建议每个技能目录都是一个独立的 Git 仓库或者是一个 monorepo 中的子目录。SKILL.md中通过version字段标记版本发布流程中强制检查该字段。为每个技能编写自动化测试至少覆盖一个正常输入和一个异常输入。7.7 从吴恩达 Agent 课程中可以借鉴的实践思路吴恩达的 Agent 课程体系中同样强调“让 Agent 掌握专业技能”的思想他在多个材料里都建议不要把所有能力塞给模型而是把复杂任务拆成一系列小技能并用清晰的文档引导模型逐步调用。这与 Agent Skills 的设计哲学是一致的模型负责决策和编排技能负责专业执行。我们在做企业级 Agent 时可以把这个思路直接落地为“技能库 编排层 解释层”的三层结构技能层保持稳定编排层根据业务变化灵活调整。8. 写在最后的建议Agent Skills 的规范还在快速演化不同框架对它的支持和理解也不完全一致。但有一点是确定的懂得把“能力”封装成“技能”的开发团队会比只会写 Prompt 的团队更早进入 Agent 工程的深水区。我希望这篇文章能帮你迈过从“知道概念”到“跑通第一个 Skill”的门槛。建议你从今天开始做两件事第一把你自己重复做过三次以上的自动化流程写成第一个 Skill第二把团队里最有价值的领域知识用SKILL.md沉淀下来。当你积累了十几个 Skill 之后你会发现 Agent 的能力上限不再取决于模型而是取决于你的技能库是否足够丰富、描述是否足够精准。动手写一个试试遇到问题可以直接对照文中的排查清单。如果能跑通欢迎在评论区分享你第一个 Skill 的类型和踩过的坑。祝编码愉快。

相关新闻

最新新闻

opencode实战指南:终端AI编程助手的安装、配置与高阶玩法

opencode实战指南:终端AI编程助手的安装、配置与高阶玩法

最近一段时间,终端里的AI编程工具像雨后春笋一样冒出来,Claude Code、Codex CLI、Google的Code-FX、还有今天要聊的opencode,一个比一个卷。如果你平时关注AI编程这块,大概率已经刷到过opencode这个词了,但很多人下载完…

2026/9/8 4:04:31
JetBrains IDEA变身MCP Server:给AI装上眼睛,让它真正看懂Maven项目

JetBrains IDEA变身MCP Server:给AI装上眼睛,让它真正看懂Maven项目

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/8 4:04:31
AI助手记忆系统开发:三层记忆架构与跨会话实现

AI助手记忆系统开发:三层记忆架构与跨会话实现

如果你正在开发 AI 助手,大概率遇到过这个尴尬场景:用户上一秒告诉你“我姓陈,做跨境电商的”,下一秒换个话题聊三次,助手就不再记得这回事,又重新问了一遍“请问怎么称呼您”。问题通常不在大模型的能力&a…

2026/9/8 4:04:31
openEuler+鲲鹏平台:Agent Memory记忆管理系统实战指南

openEuler+鲲鹏平台:Agent Memory记忆管理系统实战指南

2026中国国际大学生创新大赛的openEuler命题已经公布,很多团队看到“基于鲲鹏平台的Agent Memory记忆管理系统”这个题目时,第一反应是:这又是一个“大而空”的赛题包装,还是真有一个可以落地的技术方向?先说结论&…

2026/9/8 4:04:31
从散落混乱到统一入口:一套JSON工具类封装方案的设计与难点复盘

从散落混乱到统一入口:一套JSON工具类封装方案的设计与难点复盘

写接口对接的时候,我最怕的不是业务逻辑写错,而是JSON这层皮反反复复出问题——今天这个接口用 new Gson() 解析,明天那个模块自己copy了一段Fastjson,后天又有人在代码里直接操作 JsonObject 取字段。字段一多、调用一多&…

2026/9/8 4:04:31
AIxAgentxData技术栈全解析:从原理到面试实战的学习路线

AIxAgentxData技术栈全解析:从原理到面试实战的学习路线

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/8 3:59:31