ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Hermes Agent 技能进化系统拆解:Skill 的元数据结构、自注册加载与退化机制

Hermes Agent 技能进化系统拆解:Skill 的元数据结构、自注册加载与退化机制 1. 为什么 Skill 系统值得单独拆一层如果你正在给 Agent 搭技能体系大概率会遇到三个绕不开的问题技能文件写成什么样才算「机器可读」、Agent 启动时怎么知道有哪些技能可用、以及技能在什么条件下应该被跳过而不是硬加载。Hermes Agent 的 Skill 系统把这三件事拆得很清楚——它用 SKILL.md 承载元数据用 Skills Index 做启动期注入用一套退化机制决定「这个技能现在能不能用」。我试过把这套结构搬到自己的 Agent 项目里最直观的感受是Skill 不是静态文档而是 Agent 的「程序性记忆」。它需要被解析、被索引、被条件加载还要在环境不满足时优雅退场。这篇就按元数据结构、自注册加载、退化机制三条线拆开讲每一步都给可复制的字段模板和配置骨架。适合已经写过一两个 Agent 工具、想把手写 prompt 升级成可扩展技能目录的开发者。2. 前置准备统一 Key 与 API 通道在动手写 Skill 之前先把模型调用通道固定下来。原因很实际Skill 里经常要声明required_environment_variables如果每个技能各自管一套 Key环境变量检查会变成一团乱麻。用一个统一的 API 通道技能只需要声明「我需要一个模型 Key」而不用关心背后接的是哪家。TaoToken 在这里的角色就是统一入口。官网在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 端点是 https://taotoken.net/api 。你可以在控制台创建 Key然后在 Skill 的 frontmatter 里声明它。具体操作路径打开控制台 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 创建一个 API Key。到 API Keys 页面 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 复制 Key。把它写进环境变量比如TAOTOKEN_API_KEY后面 Skill 的required_environment_variables就引用这个名字。这样做的收益是技能目录里不出现任何具体厂商的 Key 名退化机制里的环境变量检查只需要认一个变量。如果你后面要接 Claude Code 这类编码场景也可以走 https://taotoken.net/claude-code-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecodeutm_campaignrewrite 的通道Key 体系保持一致。3. SKILL.md 元数据结构字段模板与解析逻辑Skill 的核心是一个目录主文件固定叫 SKILL.md。目录结构建议这样组织~/.hermes/skills/ ├── mlops/ │ └── axolotl/ │ ├── SKILL.md # 必需主文件 │ ├── references/ # 可选参考文档 │ │ └── dataset-formats.md │ └── templates/ # 可选输出模板 │ └── config.yaml ├── devops/ │ └── check-server/ │ └── SKILL.md └── ...SKILL.md 的头部是 YAML frontmatter下面接 Markdown 正文。frontmatter 定义结构化信息正文写工作流。一个可直接套用的模板--- name: check-server description: 检查远程服务器健康状态返回 CPU、内存、磁盘摘要 version: 1.0.0 category: devops platforms: [linux, macos] required_environment_variables: - name: TAOTOKEN_API_KEY prompt: 请输入 TaoToken API Key metadata: hermes: tags: [ops, monitoring] related_skills: [restart-service] --- # Check Server Skill 用于快速检查服务器健康状态的工作流。 ## 步骤 1. 通过 SSH 连接目标主机 2. 执行 uptime、free -m、df -h 3. 汇总输出为表格字段的约束需要记牢否则解析会静默失败字段必需说明上限name是Skill 唯一标识64 字符description是列表展示摘要1024 字符version否语义化版本—category否分类路径—platforms否运行平台约束—required_environment_variables否环境变量需求—metadata.hermes否tags / related_skills—解析入口是_parse_frontmatter()它本身不重复实现而是委托给agent.skill_utils.parse_frontmatter()。这种「薄封装 单一实现」的写法值得借鉴工具层只负责调用真正的解析规则集中在一处避免两套逻辑漂移。环境变量字段支持三种输入格式解析时要都兼容def _get_required_environment_variables(frontmatter, legacy_env_varsNone): required_raw frontmatter.get(required_environment_variables) # 支持三种输入格式 # 1. 字符串列表: [TAOTOKEN_API_KEY] # 2. 带描述的 dict: {name: TAOTOKEN_API_KEY, prompt: 输入 key} # 3. 旧版 prerequisites.env_vars for item in required_raw: if isinstance(item, str): _append_required({name: item}) if isinstance(item, dict): _append_required(item) # 旧版兼容 if legacy_env_vars is None: legacy_env_vars, _ _collect_prerequisite_values(frontmatter)这里有个容易踩的坑required_environment_variables写成纯字符串列表时Agent 只能拿到变量名没法给用户友好提示。生产环境建议统一用 dict 格式把prompt写清楚CLI 弹窗时用户才知道该填什么。4. 自注册加载Skills Index 与工具注册Skill 写完不会自动生效它需要经过「索引构建 → System Prompt 注入 → 工具注册」三步。理解这条链路才能知道为什么技能没被加载。4.1 Skills Index 的构建与两层缓存Skills Index 是连接文件系统和 System Prompt 的桥梁。它不是每次从磁盘全量扫描而是用两层缓存进程内 LRU 缓存内存中缓存 index 构建结果同一进程内重复构建直接命中。磁盘快照~/.hermes/.skills_prompt_snapshot.json跨进程重启也能恢复。在prompt_builder.py里是否注入 Skills Index 取决于当前 Agent 是否挂载了 skills 工具has_skills_tools any( name in agent.valid_tool_names for name in [skills_list, skill_view, skill_manage] ) if has_skills_tools: skills_prompt _r.build_skills_system_prompt( available_toolsagent.valid_tool_names, available_toolsetsavail_toolsets, compact_categories_compact_cats or None, )build_skills_system_prompt()返回的是索引不是全部技能内容。每一行是一条记录形如tools/skill_name: 简短描述。这个设计直接决定了 Token 成本——如果注入全文几十个技能就能把上下文吃满。compact_categories参数控制压缩策略。当当前目录和平台被识别为编码场景时非编码类 Skill 在索引里只显示名称、不显示描述能省下大约 60% 到 80% 的 Index Token。这个开关对技能数量多的项目很关键。4.2 工具注册与 check_fnSkill 的操作通过三个工具暴露给 Agentskills_list、skill_view、skill_manage。它们在skills_tool.py模块顶层注册registry.register( nameskills_list, toolsetskills, schema{ description: 按类别列出所有可用的技能返回元数据, parameters: { type: object, properties: { category: {type: string, description: 按类别筛选可选} } } }, handlerskills_list, check_fncheck_skills_requirements, )注意check_fn是一个常量函数始终返回 Truedef check_skills_requirements() - bool: Skills are always available — the directory is created on first use if needed. return True这意味着 Skills 工具集永远可用不会因为「环境不满足」被整体禁用。真正的条件判断下沉到单个 Skill 的加载阶段而不是工具集层面。这个分层很重要工具集是能力入口必须稳定单个技能是否可用才交给退化机制处理。4.3 斜杠命令绑定skill_commands.py把 Skill 加载和 CLI 斜杠命令绑在一起。用户在终端敲/check-server时走的是_load_skill_payload()def _load_skill_payload(skill_identifier, task_idNone): raw_identifier (skill_identifier or ).strip() if not raw_identifier: return None try: from tools.skills_tool import SKILLS_DIR, skill_view identifier_path Path(raw_identifier).expanduser() if identifier_path.is_absolute(): # 安全检测只允许在 SKILLS_DIR 和 external_skills_dirs 内的路径 for root in trusted_roots: try: normalized str(identifier_path.relative_to(root)) break except ValueError: continue else: normalized raw_identifier.lstrip(/) loaded_skill json.loads( skill_view(normalized, task_idtask_id, preprocessFalse) ) except Exception: return None路径安全检测是这里最值得抄的一段。绝对路径只允许落在SKILLS_DIR和外部技能目录范围内skill_view(/etc/passwd)这类调用会被挡掉。如果你自己实现技能加载务必加这层白名单否则技能系统会变成任意文件读取入口。5. 退化机制技能什么时候不该加载「退化」不是 bug是设计。Hermes 不会无差别加载所有 Skill以下情况会跳过或标记不可用。5.1 平台不匹配_PLATFORM_MAP { macos: darwin, linux: linux, windows: win32, } def skill_matches_platform(frontmatter) - bool: from agent.skill_utils import skill_matches_platform as _impl return _impl(frontmatter)如果 SKILL.md 声明了platforms: [macos]在 Linux 机器上就不会被加载。这个检查发生在索引构建阶段技能连列表都进不去。5.2 环境不匹配skill_matches_environment()是「offer-time」级别的检查只在工具发现阶段生效。也就是说它影响的是技能是否出现在推荐列表里而不是强制拦截。如果你显式调用skill_view(my-skill)它会强制加载不受环境检查影响。这个区分很实用自动发现要保守显式调用要给用户留后门。5.3 所需环境变量缺失当 Skill 声明了required_environment_variables而变量不存在时行为分两种界面if _is_gateway_surface() and not env_var_enabled(HERMES_INTERACTIVE): return { missing_names: missing_names, setup_skipped: False, gateway_setup_hint: _gateway_setup_hint(), }在 Gateway 界面Telegram、Discord 等非交互场景上返回一条提示告诉用户去 CLI 配置环境变量。在 CLI 上则直接弹窗让用户输入。这个分流避免了在无法交互的界面上卡死。5.4 注入检测skills_tool.py硬编码了一组 prompt injection 检测模式_INJECTION_PATTERNS: list [ ignore previous instructions, ignore all previous, you are now, disregard your, forget your instructions, new instructions:, system prompt:, system, ]], ]这些模式在skill_view()加载内容时检查。如果你从不可信来源复制了 SKILL.md里面藏着「ignore previous instructions」加载时会被探测到并告警。技能目录本质上是可执行内容这层检测相当于给技能系统加了一道输入校验。6. 验证请求跑通一条完整 Skill 链路理论讲完动手验证。下面是从零到执行一个 Skill 的完整步骤。第一步创建技能目录和文件mkdir -p ~/.hermes/skills/devops/check-server cat ~/.hermes/skills/devops/check-server/SKILL.md EOF --- name: check-server description: 检查服务器健康状态返回 CPU、内存、磁盘摘要 version: 1.0.0 category: devops platforms: [linux, macos] required_environment_variables: - name: TAOTOKEN_API_KEY prompt: 请输入 TaoToken API Key metadata: hermes: tags: [ops, monitoring] --- # Check Server Skill ## 步骤 1. 执行 uptime 查看负载 2. 执行 free -m 查看内存 3. 执行 df -h 查看磁盘 4. 汇总为表格输出 EOF第二步设置环境变量export TAOTOKEN_API_KEY你的Key第三步启动 Agent让它列出技能/skills_list预期返回里应该能看到check-server并且带 description。如果没出现先检查platforms是否和当前系统匹配。第四步显式加载技能/check-server加载成功时Agent 会读取 SKILL.md 正文并执行其中定义的工作流。如果环境变量缺失CLI 会弹窗要求输入Gateway 会返回配置提示。第五步验证退化行为。把platforms改成[windows]重启 Agent再执行/skills_listcheck-server应该从列表里消失。但直接/check-server仍然能强制加载——这就是 offer-time 检查和强制加载的区别。如果你要验证模型侧的调用是否正常可以到模型对话页面 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 发一条测试请求确认 Key 和通道没问题。长期跑编码类技能的话Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 更适合持续调用场景。7. 本篇常见错排查技能不出现但文件确实存在。先看platforms字段。macos映射到darwinwindows映射到win32写错平台名会导致静默跳过。其次看 frontmatter 是否合法 YAML——缩进错误会让解析整体失败技能被当成无元数据文件忽略。技能出现了但加载时报环境变量缺失。检查required_environment_variables的格式。字符串列表和 dict 都支持但 dict 格式才能带prompt。另外确认变量名拼写和实际导出的环境变量一致大小写敏感。索引里技能描述被截断或消失。这是compact_categories在起作用。编码场景下非编码类技能只显示名称。如果你希望某个技能始终显示描述把它归到编码相关 category或者检查当前目录是否被误判为编码场景。修改 SKILL.md 后不生效。两层缓存导致的。进程内 LRU 缓存和磁盘快照~/.hermes/.skills_prompt_snapshot.json都可能返回旧数据。重启 Agent 进程或者手动删除快照文件再启动。加载内容触发注入告警。检查 SKILL.md 正文里是否包含_INJECTION_PATTERNS里的短语。有些正常的技术文档会写到「system prompt」这类词如果确实需要改写措辞避开检测模式。绝对路径加载被拒绝。路径安全检测只允许SKILLS_DIR和外部技能目录内的路径。把技能放到标准目录下或者用相对标识符加载。8. 把技能体系接进你的项目Skill 系统的价值不在于单个技能写得多漂亮而在于「经验 → 模板 → 复用」这条管道能跑通。元数据结构决定了技能可被机器理解自注册加载决定了技能能被自动发现退化机制决定了技能在环境不匹配时不会拖垮整个 Agent。落地时建议按这个顺序推进先把 Key 通道统一到 TaoToken控制台 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 建 KeyAPI Keys 页面 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 复制然后按第 3 节的模板写第一个 SKILL.md字段先求全再求简接着用第 6 节的步骤验证加载和退化最后把接入细节对照文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 过一遍确认环境变量和调用方式没有偏差。技能目录会随着项目增长越来越重早一点把索引缓存和退化检查做对后面加技能才不会变成负担。
返回列表