Skill 插件化开发入门:给 Agent 装上『可控缰绳』的正确姿势
【免费下载链接】harness-sdkBuild an agent harness and control it end-to-end. Open-source SDK for production AI agents in Python & TypeScript - any model, any cloud.项目地址: https://gitcode.com/GitHub_Trending/sdkpython13/harness-sdk
把 Agent 从"什么都敢试的实习生"变成"只按 SOP 办事的老员工",靠的不是更长的 system prompt,而是把可复用的方法论固化成插件。harness-sdk 的 Skill 机制正是为此设计:它把一段带 YAML frontmatter 的 Markdown 指令(SKILL.md)变成一个可装载、可激活、可追踪的运行时实体,让模型在"元数据可见"与"全量指令加载"之间分层获取信息。本文基于仓库源码拆解 Skill 与普通函数的本质区别、插件加载失败的四类高频问题,并手写一个可直接投入业务使用的 Skill。
Skill 与普通函数的本质区别
很多初学 Agent 开发的人会问:Skill 不就是把一段提示词封装成函数吗?源码给出的答案要微妙得多。在 Skill 数据模型 中,一个 Skill 是一个 dataclass,字段包括name、description、instructions、path、allowed_tools、metadata等,而它的三种加载方式已经暗示了定位差异:
Skill.from_file("./skills/my-skill"):从磁盘目录加载,要求目录内存在SKILL.md;Skill.from_content(content):从原始内容解析;Skill.from_url("https://.../SKILL.md"):从 HTTPS 拉取;Skill.from_directory("./skills/"):批量加载父目录下的所有子技能。
与普通函数相比,Skill 有三个本质区别。
第一,它是"渐进式披露"的载体,而非即时执行的逻辑。普通工具函数是"被调用即执行",Skill 则分两层存在。在 AgentSkills 插件 中可以看到完整的实现:init_agent时插件注册一个名为skills的工具,并在每次模型调用前的_on_before_invocation钩子里,把<available_skills>元数据块注入系统提示词;当模型决定使用某个技能时,再通过skills工具按名称激活,此时才把完整的instructions加载进上下文。这正是"可控缰绳"的第一层含义——模型一开始只看到技能的"简介卡片",而不是全部操作手册,上下文不会被几十个技能的长指令撑爆。
第二,它自带命名与结构校验,是强约定的领域文件格式。_validate_skill_name定义了一套硬性规则:名称必须匹配^[a-z0-9](https://link.gitcode.com/i/e8795eaed4c1ddae956ce1e851e7b629)?$,即 1-64 位小写字母数字加连字符,不能以连字符开头或结尾,不能出现连续连字符;从磁盘加载时,frontmatter 里的name还必须与父目录名一致。这套约定让"技能"成为可被工具链扫描、校验、索引的资产,而不是散落在提示词里的文本。
第三,它是"声明式契约",而非命令式代码。Skill 的指令体(SKILL.md的 body)是给模型读的 Markdown 方法论,frontmatter 的allowed-tools声明了该技能允许使用的工具清单(当前为实验性字段,见 skill.py 的字段注释),metadata、license、compatibility则携带版本与合规信息。这层声明让技能可以被审计、可被复用、可跨 agent 共享。
插件加载失败四类高频问题速查
结合 AgentSkills 的加载实现 与 skill.py 的解析逻辑,插件加载失败几乎都逃不出以下四类,且每一类在源码中都有对应的报错路径。
问题一:路径不存在或不是有效目录。_load_skill_paths在通过 sandbox 列出目录失败、且路径不以skill.md结尾时,会打出skill source does not exist or is not a valid path警告并跳过。对应的测试 test_agent.py 中test_skills_explicit_missing_dir_is_reported_by_the_sdk专门覆盖了"显式传入不存在的目录时 SDK 必须报警告"的行为。注意默认行为的宽容:单个坏路径只告警、不中断整体加载。
问题二:目录里没有 SKILL.md(或大小写不对)。_find_skill_md会优先找大写的SKILL.md,找不到再退而求其次找小写skill.md,两者皆无则抛出FileNotFoundError: no SKILL.md found in skill directory。这是新手最容易踩的坑——建了目录却忘了放SKILL.md,或文件名写成了Skill.md。
问题三:YAML frontmatter 格式损坏。_parse_frontmatter要求内容必须以---开头、且存在闭合的---分隔行,否则抛ValueError。更隐蔽的坑是 YAML 值里出现未加引号的冒号,例如description: Use this skill when: the user asks about PDFs——为此源码专门实现了_fix_yaml_colons兜底逻辑:解析失败后重试,把含冒号的未引用值用双引号包起来(见 skill.py 的容错处理)。此外,frontmatter 缺少name或description字段会直接抛ValueError,这两个字段是必填的。
问题四:命名违反规范。名称超过 64 字符、含大写、含下划线、以连字符开头结尾、或出现--,都会触发_validate_skill_name的告警;若name与父目录名不一致,则会提示skill name does not match parent directory name。默认模式下这些问题仅记录 warning 并照常加载,只有构造AgentSkills(..., strict=True)时才会升级为ValueError中断加载(agent_skills.py)。
还有一个容易忽视的设计值得强调:失败是隔离的。_load_skill_paths对每个路径都做 try/except,单个坏技能只会被跳过,不会拖垮兄弟技能——对应测试test_skills_multiple_dirs_skips_missing与 test_agent_skills.py 中的"坏目录被优雅跳过、好目录照常加载"用例。这意味着你可以把整包技能目录挂上去,坏一个不影响其余。
从零写一个可复用的业务 Skill
以"生成发布说明"这个高频业务场景为例,完整走一遍 Skill 的开发与挂载流程。
第一步:按规范建目录与文件。默认技能根目录是./.agent/skills(见 defaults.py 的 DEFAULT_SKILLS_DIR),每个技能一个子目录,目录名即技能名:
.agent/skills/release-notes/SKILL.mdSKILL.md的骨架与测试用例中的_write_skill完全一致(test_agent.py):
--- name: release-notes description: 从 Git 提交历史生成两个版本之间的发布说明 allowed-tools: shell read write --- # 发布说明生成流程 1. 用 `git log --oneline <from>..<to>` 获取提交列表; 2. 按 feat/fix/docs/refactor 对提交归类; 3. 对每个类别生成一句中文摘要,注明关键 PR 或 commit 号; 4. 用 `write` 工具把结果写入 RELEASE_NOTES.md,格式遵循仓库现有模板。注意三点:name必须与目录名一致且全小写连字符风格;description要写"何时该用这个技能",而不是复述步骤;allowed-tools声明技能运行所需的工具白名单,把缰绳收在明面上。
第二步:挂载到 harness。在 Python 侧,create_harness的skills参数接受多种形态(agent.py 的参数文档):布尔值True表示"若默认目录存在则加载"(这是默认行为,目录不存在时是 no-op);也可以传技能目录路径、SKILL.md文件路径、HTTPS URL、父目录或Skill实例,单值或列表均可:
from strands_harness import create_harness agent = create_harness( model="anthropic/claude-sonnet-4-5", skills=["./.agent/skills"], # 挂载整包技能 # skills=False # 显式关闭 )也可以绕过文件系统,用代码直接构造Skill实例并交给AgentSkills插件:
from strands.vended_plugins.skills import Skill, AgentSkills release_notes = Skill( name="release-notes", description="从 Git 提交历史生成两个版本之间的发布说明", instructions="1. 用 git log 获取提交列表...", ) agent = create_harness(skills=AgentSkills(skills=[release_notes]))TypeScript 侧完全对等:createHarness同样接受skills参数,底层走 agent-skills.ts 的AgentSkills插件,支持Skill实例、目录路径与https://URL。若使用配置文件驱动,skills项的相对路径会基于配置根目录解析,而http(s)://URL 原样保留(见 config.py 的 _resolve_skill_source)。
第三步:理解激活时的运行时行为。挂载后,模型每轮调用前,系统提示词末尾会被注入一段类似这样的 XML(由_generate_skills_xml生成,见 agent_skills.py):
<available_skills> <skill> <name>release-notes</name> <description>从 Git 提交历史生成两个版本之间的发布说明</description> <location>./.agent/skills/release-notes/SKILL.md</location> </skill> </available_skills>当模型决定执行发布说明任务时,会调用skills工具并传入skill_name="release-notes",插件随即返回完整指令,同时把技能的资源文件(scripts/、references/、assets/三个可选目录,默认最多列出 20 个文件)一并附上,并借助agent.state记录激活历史(activated_skills),供会话追踪与审计。这套"元数据常驻、指令按需加载、激活可回溯"的链路,就是"可控缰绳"的完整落地:模型始终知道有什么技能可用,但只有在真正需要时才看到操作细节,而每一次使用都被记录在案。
至此,一个可复用的业务 Skill 就完成了从文件格式、命名规范、挂载配置到运行时激活的全链路。当你需要让 Agent 稳定执行某个高频、低歧义、可验收的任务时,把方法论写进SKILL.md、挂进 harness,远比在 system prompt 里堆砌文字更可控——这也是 Skill 机制区别于普通函数封装、也区别于长提示词工程的根本所在。
【免费下载链接】harness-sdkBuild an agent harness and control it end-to-end. Open-source SDK for production AI agents in Python & TypeScript - any model, any cloud.项目地址: https://gitcode.com/GitHub_Trending/sdkpython13/harness-sdk
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考