资讯详情

Hermes自定义技能实战:从机制原理到工作流编排的完整指南

📅 2026/9/10 16:54:12 | 华诺云谱 👁 阅读
Hermes自定义技能实战:从机制原理到工作流编排的完整指南
先说个真实的场景。你每天都在做的事情里面至少有百分之二十属于“固定套路”把一段描述丢给 Agent让它在固定位置找 production 日志的 error按固定模板整理成本周故障清单再按固定格式发给团队。这种活你说难不难一点不难。但你说烦不烦真的烦。每回都要把一大段带满上下文的指令重新粘贴一遍一旦 Agent 的上下文稍有偏差它就把模板里的日期、模块名、输出格式理解得奇奇怪怪。这不是模型不够聪明而是我们没有把“怎么干这件事”沉淀成一个可复用的技能。Hermes 的自定义技能Skill机制解决的就是这个问题把重复工作流程、固定指令模板、约定俗成的输出结构打包成一个 Agent 能识别、能加载、能执行的能力单元。创建完之后你再召唤它做同类事情一句话就够不用再背一长串咒语。这篇文章我会从技能机制的本质开始讲然后完整走一遍从配置目录、技能描述、执行脚本到最终注册启用的实操流程最后把我在真实项目里踩过的坑和调试技巧一并交代清楚。无论你用的是本地部署的 Hermes Agent还是准备把它接进自己的自动化测试流程这篇文章都按从零到一、从原理到落地的顺序来写你照着抄就行。1. 技能机制的本质为什么 Agent 需要“技能”而不是“插件”先厘清一个概念问题。很多人一上来就喜欢把技能跟插件混为一谈但这两者的设计逻辑完全不同。插件Plugin的核心是“外部能力的接入”——它把第三方 API、工具库、运行时封装成 Agent 可以调用的接口。比如接一个数据库插件Agent 就能查 MySQL接一个浏览器插件Agent 就能打开网页。插件关心的是“我能多拿到哪些外部能力”。技能Skill的核心却是“做事流程的固化”。它关心的不是接入什么新系统而是“某类事情该怎么一步步做完”。同样是用数据库插件一个没有技能的 Agent 接到“查一下昨天订单量 Top 10 的产品”时它会自己临时想一套 SQL 应该怎么写、字段应该怎么读、结果应该怎么展示。而有技能的 Agent 会直接唤起“订单分析”这个技能按照技能里预设的字段映射、SQL 模板、异常兜底、输出格式一步到位把结果整理成既定格式。做个更直白的类比插件是给 Agent 买了一堆工具箱里的电钻、扳手、锤子技能是给 Agent 一份“换轮胎操作手册”。工具告诉你有什么可以用手册告诉你这件事具体怎么做。Agent 有了大模型的基础推理能力之后什么都不缺唯独缺“怎么稳定地做对一件事”的流程约束。技能就是这种约束的载体。从软件工程的角度看自定义技能本质上是在给 Agent 补“过程性知识”Procedural Knowledge。Prompt 里写的那段“请帮我做什么什么”是陈述性知识Agent 每次都要重新理解而技能把过程拆解成步骤、参数、边界条件和输出模板相当于把每次都要重新推理一遍的工作固化成了确定性流程。这也是为什么技能机制的 Agent 在自动化测试、重复数据整理、定时报告生成这类场景里表现格外稳定——它不再“每次重新发明一遍轮子”而是有了一条可复用、可测试、可维护的执行链路。2. 技能包结构与注册原理先搞懂 Hermes 到底怎么认“新能力”很多人在自定义技能时遇到的第一道坎不是写不出代码而是搞不明白自己写的东西该放哪里、该叫什么名、该符合什么格式。Hermes 的技能包结构并不复杂但每一处约定都有它的道理下面按实际开发顺序把关键点拆开说。2.1 技能目录布局与命名约束Hermes 的技能统一存放在配置目录下的skills/文件夹中每个技能一个独立子目录。目录名就是技能的 ID这个 ID 会被 Agent 用来索引和唤起技能所以命名必须做到一点一看就知道这个技能干什么。以一个“日志异常汇总”技能为例目录结构是这样的skills/ ├── log-anomaly-summary/ │ ├── SKILL.md │ ├── main.py │ └── assets/ │ ├── prompt_templates/ │ │ └── summary_template.md │ └── config/ │ └── rules.yamlSKILL.md是技能的名片也是 Hermes 读取技能时第一个解析的文件main.py是技能的执行入口assets/目录用于存放技能运行过程中需要引用的模板、配置、静态资源。这里有一个容易踩的坑小写字母加连字符是技能目录名的默认规范log-anomaly-summary没问题但如果你写成LogAnomalySummary或者log_anomaly_summary部分版本的 Hermes 在跨平台部署时会出现索引不到的情况。Windows 和 Linux 环境对大小写的敏感度不同统一用小写连字符最保险。2.2 SKILL.md一份写给 Agent 看的“职位说明书”SKILL.md是技能能否被正确唤起的关键。它采用的是 Markdown 加 YAML Front Matter 的结构头部定义元数据正文描述技能的适用场景和执行方式。一个典型的最小示例--- name: log-anomaly-summary description: 分析指定日志文件中的 ERROR 和 WARN 级别异常 按模块归类并输出周报格式的汇总结果。 当用户要求汇总日志异常、排查线上错误、整理故障清单时使用。 --- ## 适用场景 - 用户提供日志文件路径要求统计异常次数 - 用户要求按模块维度归拢错误信息 - 用户希望输出可直接粘贴到周报的格式化内容 ## 执行步骤 1. 读取用户提供的日志文件路径确认文件存在 2. 使用脚本解析 ERROR / WARN 级别条目 3. 统计各模块出现频率提取关键错误摘要 4. 按模板生成汇总报告description字段里的每一句话都直接影响 Agent 对技能的唤起判断。不要写“这个技能用于处理日志”这种泛泛的描述而要写“当用户要求A时、当用户要求B时使用本技能”。Agent 不像人一样能通过目录名猜到你的意图它完全依赖 description 里的语义匹配来决定是否唤起这个技能。描述里没有的场景哪怕技能本身能力再强Agent 也只会“视而不见”。执行步骤部分同样关键。这里的描述会作为 Agent 编排计划时的先验指引写清楚“先做什么、再做什么”Agent 在执行时就有了流程锚点不容易在执行中途自己临场发挥跑偏。我还习惯在步骤里明确写出台词式指令比如“如果日志文件不存在直接告知用户文件路径无效不要尝试自动搜索”这种约束在真实场景里非常管用。2.3 执行脚本的加载时机解释型脚本为何是首选Hermes 对技能执行脚本没有硬性限制理论上任何语言都能跑但解释型脚本——尤其是 Python——是最好选择。原因有两点。第一Agent 在运行时需要快速感知技能能力解释型语言免去了编译步骤技能被唤起后可以直接进入执行状态延迟更低。第二Python 的生态覆盖面太广了日志解析、文件处理、数据清洗、HTTP 请求都有现成库一个技能里可能只需要几十行 Python 就能实现完整的业务逻辑。在main.py的入口函数设计上一个值得参考的结构是这样的接收一个params字典里面包含file_path、time_range等由 Agent 解析出来的参数返回值则按约定输出为结构化结果。这套设计与 Agent 的工作方式高度契合——Agent 不关心你怎么实现只关心传入参数和拿回结果。保持入口简单直接技能的可复用性会大幅提升。3. 从零创建第一个技能以“周报自动化生成”为完整样例理论部分说过之后我们直接上手做一个完整技能。这个例子我选了“周报自动化生成”原因很实在几乎每个人都会被周报折磨而且它的流程足够典型——有固定输入、有处理逻辑、有输出模板用来理解技能机制刚好合适。3.1 技能场景拆解把“干活方式”翻译成 Agent 能读懂的参数在动手写代码前先把场景原原本本地剖析一遍。假设我们每周五下午要做这样一件事打开本周的 Git 提交记录筛选出当前项目的变更按“功能开发/缺陷修复/代码重构/文档调整”四个类别整理同时提取每次提交的信息和关联的 Issue 号最后输出一段可以直接粘贴到周报里的 Markdown 文本。这个场景里有三个核心要素输入参数仓库路径、时间范围本周一到周五处理步骤读取 Git 日志 → 按提交信息关键词分类 → 提取 Issue 关联输出格式分类清晰的 Markdown 周报整理成表格就是要素内容示例值输入参数1仓库路径/workspace/my-project输入参数2时间范围2025-06-09~2025-06-13输出分类周报Markdown 格式文本技能场景拆解得越细致后面写 SKILL.md 的 description 就越精准。这里的原则是把人在做这件事时脑子里的判断规则全部显性化。比如“这条提交算功能开发还是缺陷修复”人在判断时靠的是经验技能就必须把它固化成关键词规则——fix、bug、hotfix归入缺陷修复feat、feature、add归入功能开发。3.2 编写 SKILL.md描述词的克制与场景词的覆盖开始写技能定义文件下面是一个实际可用的示例--- name: weekly-report-generator description: 根据指定 Git 仓库在某段时间内的提交记录自动生成周报。 当用户要求生成周报、汇总本周工作、统计提交记录、按类别整理 Git log 时使用。 适合开发者在周五或项目节点输出工作汇总。 --- ## 适用场景 - 用户提供一个本地 Git 仓库路径要求生成本周或任意日期范围的周报 - 用户希望提交记录按功能、缺陷、重构、文档等维度分类展示 - 用户希望提取每条提交关联的 Issue 编号并附在周报中 ## 执行步骤 1. 确认仓库路径存在且是一个有效的 Git 仓库 2. 根据时间范围参数调用 git 命令获取提交日志 3. 按关键词规则对提交进行分类 4. 提取提交信息中的 Issue 编号如 #1234 5. 按模板生成 Markdown 周报并返回 ## 参数说明 - repo_path: Git 仓库本地路径 - start_date: 开始日期格式 YYYY-MM-DD - end_date: 结束日期格式 YYYY-MM-DD注意description里的写法“当用户要求生成周报、汇总本周工作、统计提交记录、按类别整理 Git log 时使用”。这里覆盖了同义表达的不同说法Agent 匹配时就不容易漏。但也不必堆砌所有可能的表达方式过度堆砌反而干扰语义原则是覆盖高频场景词和同义短语而不是穷举。3.3 实现 main.py清晰的执行逻辑与可靠的异常处理技能的执行脚本用 Python 实现。下面是main.py的一个完整设计#!/usr/bin/env python3 import subprocess import re from datetime import datetime # 提交分类规则关键词匹配到哪一类就归到哪一类 CATEGORY_RULES { 功能开发: [feat, feature, add, new, 支持], 缺陷修复: [fix, bug, hotfix, patch, 修复], 代码重构: [refactor, rework, optimize, 重构, 优化], 文档调整: [doc, docs, document, 文档, readme], } ISSUE_PATTERN re.compile(r#(\d)) def classify_commit(message: str) - str: 根据提交信息的关键词判定类别默认归入功能开发 msg_lower message.lower() for category, keywords in CATEGORY_RULES.items(): for keyword in keywords: if keyword in msg_lower: return category return 功能开发 def get_git_log(repo_path: str, start_date: str, end_date: str) - list: 执行 git log 命令并解析提交信息 since f{start_date} 00:00:00 until f{end_date} 23:59:59 cmd [ git, -C, repo_path, log, --since since, --until until, --prettyformat:%h|%an|%ad|%s, --dateformat:%Y-%m-%d %H:%M, ] result subprocess.run(cmd, capture_outputTrue, textTrue, encodingutf-8) if result.returncode ! 0: raise RuntimeError(fgit log 执行失败: {result.stderr}) commits [] for line in result.stdout.strip().splitlines(): parts line.split(|, 3) if len(parts) ! 4: continue commits.append({ hash: parts[0], author: parts[1], date: parts[2], message: parts[3], }) return commits def extract_issue_numbers(message: str) - list: 提取提交信息中的 Issue 编号 return ISSUE_PATTERN.findall(message) def generate_report(commits: list) - str: 按类别组织提交记录生成周报 Markdown grouped {category: [] for category in CATEGORY_RULES} for commit in commits: category classify_commit(commit[message]) issue_nums extract_issue_numbers(commit[message]) issue_str .join([f#{num} for num in issue_nums]) if issue_nums else grouped[category].append( f- {commit[hash]} {commit[message]} {issue_str} ) lines [## 本周工作汇总, ] for category, items in grouped.items(): if not items: continue lines.append(f### {category}) lines.extend(items) lines.append() if all(not items for items in grouped.values()): lines.append(本周暂无提交记录。) return \n.join(lines) def main(params: dict) - str: repo_path params.get(repo_path) start_date params.get(start_date) end_date params.get(end_date) if not repo_path or not start_date or not end_date: return 请提供完整的参数repo_path, start_date, end_date if not start_date end_date: return 起始日期不能晚于结束日期 try: commits get_git_log(repo_path, start_date, end_date) return generate_report(commits) except Exception as e: return f生成周报失败{str(e)} if __name__ __main__: main({})这个脚本有几个细节值得说明。CLASSIFY用中英混合关键词是因为实际项目的 Git 提交信息经常混杂两种语言只匹配英文会漏掉一部分。ISSUE_PATTERN提取#1234这类编号也是从实际场景出发——大多数团队的 Issue 号就嵌在提交信息里。main()入口函数的结构刻意保持得很“薄”参数校验、业务调用、异常兜底。真正的逻辑都拆到独立函数里了。这样的好处是便于 Agent 遇到异常时反馈具体原因而不是抛出一大段堆栈。让 Agent 把get_git_log里的报错信息直接递给用户比它自己猜“可能是参数错了吧”要高效得多。if __name__ __main__分支留了个空调用看起来很怪但这是有意的设计。当你在本地手动调试时可以直接运行看入口逻辑是否正常同时这个设计也提示后续开发者这个技能脚本是一个可被调用的模块而不是只有 Agent 才能使用的黑盒。3.4 在 Hermes 中注册技能并验证加载技能文件放好后需要让 Hermes 重新加载技能索引。不同部署方式的入口略有差异但核心动作是一致的触发一次技能重新扫描。如果你使用的是 Docker 部署命令是docker exec -it hermes-agent hermes skills refresh如果你使用的是本地源码部署hermes skills refresh刷新之后可以用列表命令确认技能是否被成功加载hermes skills list输出中应该能看到weekly-report-generator。如果列表里没有先别急着排查代码回到目录结构看一眼——SKILL.md是不是放在了weekly-report-generator目录的第一层YAML 头部的---是不是正确闭合这两处是技能加载失败最高发的原因。验证技能是否真正可用我建议走一轮完整的调用测试。直接启动 Hermes 的交互模式输入这样一句话“帮我看看 /workspace/my-project 这个仓库这周周一到周五的提交生成一份周报。”这句话里包含了 repo_path、时间范围、输出目标Agent 应该能够正确唤起技能并返回周报。如果 Agent 没唤起技能而是追问你“请提供仓库路径”之类的问题说明 SKILL.md 的 description 跟用户表达之间的语义匹配不够这时候需要回头调整描述词把用户可能用的说法覆盖进去。4. 调试技巧与高频踩坑为什么技能“看起来加载了却没反应”我把这个部分单独拿出来写是因为在真实使用中技能开发的大部分时间其实不是花在写代码上而是花在排查各种“奇奇怪怪”的问题上。以下是几个出现频率最高的坑以及相应的定位思路。4.1 技能没被唤起先查描述词的“语义空洞”最典型的现象是技能明明在列表里但用户发出指令时 Agent 根本不用它而是自己凭推理能力硬答。为什么会这样核心原因是 SKILL.md 里的description与用户输入之间的语义空间没有重叠。比如你把 description 写成“生成工作周报”但用户说的是“帮我看看这周做了啥整理个总结”这两句话在字面上没有共享关键词。Agent 的语义匹配模型可能判定两者关联度不高于是技能没有被触发。解决方式是在 description 里扩展表达维度把用户可能使用的不同说法都纳入进来工作周报、本周总结、汇总一下、整理提交记录。但也别走极端一次塞进一两百个同义词反而会把匹配带的语义中心冲散。一个参考上限是四到六个场景短语把高频说法覆盖住就够了。另一个常被忽略的点是 description 的优先级。Hermes 在唤起技能时会综合评估所有技能如果两个技能的描述高度相近Agent 可能选错。所以描述里最好点出关键区分维度比如“仅用于 Git 仓库周报不适合处理非代码类总结”把边界也交代清楚。4.2 脚本报错但用户看不懂把异常转译成人话技能脚本一旦报错Agent 的原始行为大概率是把堆栈信息直接回复给用户。这在大模型眼里是合理的但对使用者来说体验极差——大多数非技术背景的用户看到KeyError: repo_path时根本不知道该怎么办。我的习惯是代码里不用裸的异常抛出而是用try-except包住后返回一个清晰的错误描述。像上一个例子里的get_git_log函数如果 git 命令执行失败就返回git log 执行失败: {stderr}如果仓库路径无效就返回指定路径不是一个有效的 Git 仓库请确认后再试。这样的结果 Agent 能直接理解用户也能直接行动。排错时一个非常实用的工具是调试模式。Hermes 在调试模式下会输出完整的技能执行日志包括唤起决策、参数提取结果、脚本标准输出。第一次开发技能时建议全程开着调试模式观察行为链路hermes --debug看到日志里“Skill activated: weekly-report-generator”之后再往下看参数提取是否正确、脚本返回是否符合预期。哪一步断了问题就在哪一段。4.3 路径问题为什么本地能跑、Agent 一调用就失败还有一个在 Docker 部署环境下格外突出的问题——路径映射。你在宿主机上测试技能传入的仓库路径是/workspace/my-project一切正常。但 Hermes 跑在容器里容器内的文件系统和宿主机并不是同一套。如果宿主机的同一个目录在容器里的挂载点不同Agent 执行技能时就会提示“路径不存在”。在处理这类问题时我会把路径处理设计得更加宽容技能脚本里先检查路径是否存在如果不存在就尝试从几个常见的挂载前缀中匹配一次。更重要的是部署时应当确认技能目录和常用数据卷的挂载关系把技能脚本、日志目录、仓库目录统一规划到同一套持久化存储里。4.4 执行时间太长怎么办给技能设置明确的超时预期有些技能的执行链路比较长比如要遍历大量日志文件或者调用外部 API。Hermes 对技能执行本身有超时配置但更建议在 SKILL.md 里就对预期执行时间做出说明同时在脚本里设置合理的内部超时。举个例子技能需要调用外部接口时import requests try: resp requests.get(url, timeout10) resp.raise_for_status() except requests.exceptions.Timeout: return 外部接口请求超时请稍后重试或检查接口服务状态把超时时间控制在 10 秒左右比较合理——既能覆盖大多数接口的响应时间又不会让用户等待太长时间才收到反馈。如果接口本身就慢考虑把技能拆成“提交任务 异步查询结果”两步而不是在单次调用里干等。5. 把多个技能串联成自动化工作流从“单个能力”到“固定流水线”单个技能解决的是“单类事情怎么做”的问题。但在真实工作环境里我们面对的往往是复合型任务——比如“跑一遍冒烟测试如果有失败用例就自动分析日志然后把结论整理成日报发出来”。这涉及多个技能的协同也是 Hermes 这类 Agent 框架最接近实际生产力的使用方式。5.1 技能编排的原则让 Agent 做调度者而不是脚本拼接工技能编排不需要另外写一套复杂的调度代码。Agent 本身就具备计划与编排能力你做的只是把多个技能各自定义清楚让它们之间的边界足够清晰Agent 就能在接到复杂指令时自动拆解任务并依次唤起对应技能。这里的关键在于技能边界的划分。如果两个技能功能重叠Agent 可能会选择困难甚至重复执行。比如你已经有了一个log-anomaly-summary技能又想建一个daily-error-report技能两个技能都在处理日志异常只是输出粒度不同——Agent 就很容易选错。解决方式有两个方向要么合并成一个技能在内部通过参数控制输出粒度要么在 description 里清晰地区分各自的服务范围。合并更简单区分更灵活具体看业务需求。5.2 用“技能链”完成一个自动化测试报告场景举一个我在实际项目中用过的组合。日常自动化测试结束后测试报告散布在多个文件里人工汇总费时费力。我设计了三个技能test-report-parser解析测试框架输出的 JUnit XML 文件提取用例总数、失败数、失败原因log-anomaly-summary分析测试失败时对应的应用日志定位根因daily-report-builder把测试结果和日志分析拼装成完整日报用户在 Hermes 中输入“今天的自动化测试结果怎么样了汇总一份报告。”Agent 的调用链路是先运行test-report-parser读取测试报告如果发现失败用例再调用log-anomaly-summary去分析相关日志最后调用daily-report-builder生成最终报告。这个过程中 Agent 实际上在扮演一个项目经理的角色——它不需要理解每个技能内部的实现细节只需要根据客户需求调度正确的资源。技能设计得越独立代理的调度效率就越高。这也是为什么我一直强调技能函数的入口要简洁、输出要结构化、描述要边界清晰。每一条原则都是在给 Agent 的调度减负。6. 技能维护与迭代的实战建议技能是个“活体”不是一次性产物技能创建完成不是终点。跟任何软件一样技能需要持续维护。尤其当你的使用场景或者数据格式变化时技能如果不跟着更新很快就会变得“不那么好用”。我把技能维护中比较关键的几个方面列在这里作为参考。版本记录在 SKILL.md 里增加version字段每次改动都递增版本号。配合注释说明变更内容这样技能多了之后回溯问题时能快速定位是哪个版本的行为。监控使用频率定期看 Hermes 的执行日志统计哪些技能被频繁调用、哪些技能长时间没有被触发。久未触发的技能要么是使用场景已经消失要么是 description 与新需求表达脱节需要更新描述。收集失败案例用户反馈某次执行结果不对时第一时间保留当时的输入参数和 Agent 日志。很可能是技能里的规则没有覆盖到这种特殊情况——比如提交信息里的关键词跟你预设的分类规则不匹配导致内容被归错了类。把这类 case 补充进规则里技能就会越来越稳。保持单一职责技能做多了以后很容易出现“顺手加个小功能”的心态。今天在周报技能里加个统计行数明天再加个发送邮件的功能。一旦一个技能承担了过多职责它的 description 就必然变得冗长唤起准确性也会下降。保持单一职责必要时创建新技能比不断膨胀原有技能更明智。维护的节奏不需要太频繁我通常是在每个迭代周期结束时过一遍所有技能检查有没有规则过时、有没有新需求场景需要新增技能。一次半小时左右跟做代码 review 一样是性价比极高的投入。结尾想说的两句实在话技能机制的引入改变了我和 Agent 配合的方式以前每次跟 Agent 对话都要靠 prompt 临场约束它现在则是在事前把“怎么做”沉淀成技能Agent 要做的只是在正确的时间把正确的事情拿起来执行。这种转变最大的好处是确定性——同样的输入技能产出的结果质量是稳定的不会因为模型状态波动而忽好忽坏。如果你正准备开始用 Hermes 的自定义技能建议不要一上来就设计大而全的复杂技能。先挑一个你每周至少做一次的重复任务把它固化成技能。等你跑通一遍完整的“定义—注册—调用—迭代”闭环之后再慢慢扩展更多技能。这个节奏看似慢但每一步都踩在实地上。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。