Hermes

Hermes

明廷盛 嘻嘻😁

第零章 写在前面

① plugin
② tools ② tools / plugin / mcp
③ skills ③ skills
④ mcp
⑤ memory....

第一章 Plugin

第一节 Tools

  • 正常的Plugin: link
  • Claude的Tolls解析: link
  • 开启 Plugins
HERMES_ENABLE_PROJECT_PLUGINS=true
  • 调试 Tools
HERMES_PLUGINS_DEBUG=1 hermes plugins list

第二节 Hook

Hermes 里 hook 到底有什么用

- pre_tool_call : 工具执行前拦截,可阻止调用。适合权限校验、参数风控、敏感命令拦截。
- post_tool_call : 工具执行后观察。适合记日志、埋点、统计、触发告警。
- pre_llm_call : 在一轮对话开始前给模型“塞上下文”。适合把外部状态、缓存摘要、当前 app 信息注入给模型。
- transform_tool_result : 改写工具结果再交给模型。适合把原始输出标准化、裁剪、脱敏。
- 其他 session / gateway hooks: 更偏生命周期管理,不是业务编排核心。

第二章 MCP

第三章 Skill

编排 任务流:
输入: 帮我看下我昨天做了什么, 顺便规划下今天的任务
调用 xxxskill -> 调用tool① ->思考 ->调用tool②–>输出

  • Hermes Skill 解释: link
  • Skill 和 Tool 的区别 + 创建Skill: link

第一章 skill的基本格式

  • 格式: link
  • metadata:
    1. tool 与 skill 的调用: link (e.g. 如果toolA使用了, 就不调用这个skill)
  • 测试:
hermes chat --toolsets skills -q "Use the X skill to do Y"

第二节 多个skill加载

第三节 Hermes自动创建skill

第四节 下载 与 发布skill

第四章 子Agent

第五章 调试Hermes

// 查看所有的对话列表
hermes sessions list
// STEP2: 导出指定对话
hermes sessions export session-debug.json --session-id 20260617_104201_99743d
  • role: user | ASSISTANT | tool |
  • mcp是tool的一种

README.md

* dney
- 系统设置
- 隐私与安全性
- 完全磁盘访问权限 ( Full Disk Access )w

示例需求

我对Herms的理解是, 工作流的确定用Skill; 然后skill串联很多的tools
`调用 xxxskill -> 调用tool① ->思考 ->调用tool②-->输出`
tools(plugin注入的)只是工具, 最终让skill去调用
1. 我的理解对吗? 请贴出Hermes的官方文档
2. 对于我的需求, 如果我的理解正确, 那skill很难完成, 因为①scree_time每天能返回什么样的应用是不确定的, 我难道要在skill中这样写吗?

流程开始
①调用screen_time tools获取用户在干什么
②如果 应用有 b站, 调用`b站_tools1`, `b站_tools2`, `b站_tools3` 进行进一步分析
如果应用有chrome浏览器, 调用 `chrome_tools1` .....
③将上述分析内容汇总, 总结告知用户
我想要编排这样的带有拓展的工作流, Hermes如何实现?
你尽可能简洁回复, 不要废话

Skill怎么玩

不能100%完成需求; 但是很灵活

  • 需求的完成情况 和 模型挂钩 (与模型强相关)
2. 你的需求怎么做

- 你这个需求是“动态分支、可扩展编排”, 不要把所有分支硬写死在 skill 里 。
- Skill 可以写成很抽象的策略,例如:
- 先调用 screen_time
- 检查返回的 app 列表
- 对命中的 app,调用对应领域工具
- 汇总结果
- 这在 skill 里是能写的,但它是“指导 agent 思考”, 不是强保证 。

Tool怎么实现

能100%实现, 但是很不灵活

  • 优点: 即使模型的能力很差, 任务也可以完成很好
  • 缺点: 很不灵活, 一点很小的需求, 都要去改代码
如果你要稳定、可扩展
- 最推荐:做一个 编排型 tool ,比如 analyze_daily_app_usage
- 里面做:
1. 调 screen_time
2. 遍历结果里的 app
3. 命中 B站 就调 b站_tools*
4. 命中 Chrome 就调 chrome_tools*
5. 汇总输出
- 这样才是“确定性工作流”

模型配置

1

  • 视觉模型建议
vision:
provider: zai # 从 stepfun 改为 zai
model: glm-4v-flash # 智谱的免费视觉模型
timeout: 120
download_timeout: 30

常用命令

①作用 ②格式 ③例子

第一章 开发中的常见问题

日志打印

  • 位置: tool.py
import logging

logger = logging.getLogger(__name__)

logger.debug(f"[handle_get_modified_files_by_apps]:result: {result}") # 看不到!!!
logger.info(f"[handle_get_modified_files_by_apps]:result: {result}") # 在agent.log中显示
logger.warning(f"[handle_get_modified_files_by_apps]:result: {result}") # 在 agent.log + errors.log 显示
logger.error(f"[handle_get_modified_files_by_apps]:result: {result}") # 在 agent.log + errors.log 显示
  • 如何看日志: dashboard中左侧栏”日志”直接查看
  • 文件位置: 见下
日志文件 绝对路径
agent.log /Users/xi/.hermes/logs/agent.log
errors.log /Users/xi/.hermes/logs/errors.log
gateway.log /Users/xi/.hermes/logs/gateway.log

第二章 Plugin

第一节 注册Tools

第二节 Tools捆绑Skills

官方文档: link

  • 需求: 如何实现调用tool前,强行调用skill
  • 解决方法:
    • 原理流程: LLM–>schemas.pydescription字段–>skill_view(xxxSkill)–>tool–>依据skill回复;
    • 理解: 调用tool前**不是100%**会调用skill, 但是这样写可以(极大)提高调用skill的概率
  • 有什么用? ①Hermes是不支持tool内嵌LLM调用的, 但是可以通过skill实现; ②可以规范LLM的输出
modify_file_monitor/
├── __init__.py # 注册入口:register_skill + register_tool
├── plugin.yaml # 插件清单
├── schemas.py # tool schema 定义(给 LLM 看)
├── tools.py # tool handler 实现
└── skills/
└── modify-file-monitor-workflow/
└── SKILL.md # 捆绑 skill
  • 完成需求: schemas.py 主要”description”的修改
GET_MODIFIED_FILES_BY_APPS_SCHEMA = {  
"name": "get_modified_files_by_apps",
"description": ( # 这里!!!
"获取指定日期内指定应用修改过的文件,按应用和工作区聚合返回文件路径与修改时间区间。"
"调用此工具前,必须先通过 skill_view 加载 skill: modify-file-monitor:modify-file-monitor-workflow,"
"该 skill 包含完整的调用流程、参数规范、输出格式和注意事项。"
),
"parameters": {
"type": "object",
"properties": {
"date": {
"type": "string",
"description": "日期,格式必须为 YYYY-MM-DD。",
},
"apps": {
"type": "array",
"description": "要查询的应用名称列表。只允许传入 schemas 中枚举声明的精确字符串。省略或空数组时默认查询全部已支持应用。",
"items": {
"type": "string",
"enum": ["trae", "pycharm"],
},
},
},
"required": ["date"],
},
}

  • 注意: 路径一定skills/<skill的meta的name字段一致>/SKILL.md
  • 完成需求: 这里描述让LLM ①从TOOL中获取什么 ②调用自带的其他什么TOOL
# Modify File Monitor Workflow

## Mandatory Flow

1. 如果返回结果里有文件路径,必须优先使用 Hermes 的 File Operations 能力查看关键文件内容 // 1


## Output Shape
{
"file_name": "文件名称"
"description": "基于 Hermes File Operations 查看到的文件内容生成的一句话描述。" // 2
}
  • 注意: 基本固定这几行
"""Modify file monitor 插件注册入口。"""  # 说明当前文件只负责注册插件能力。

from . import schemas # 导入当前插件的 schema 定义。
from . import tools # 导入当前插件的 handler 实现。
from pathlib import Path


def register(ctx): # 按 Hermes 官方规范提供唯一注册入口。
"""注册捆绑skills"""
skills_dir = Path(__file__).parent / "skills"
for child in sorted(skills_dir.iterdir()):
skill_md = child / "SKILL.md"
if child.is_dir() and skill_md.exists():
ctx.register_skill(child.name, skill_md)

"""注册聚合文件修改查询工具。""" # 说明当前函数只负责把 schema 绑定到 handler。
ctx.register_tool( # 把唯一 tool 注册到 Hermes 运行时。
name="get_modified_files_by_apps", # 声明对外暴露的 tool 名称。
toolset="modify-file-monitor", # 声明当前 tool 所属插件集合名称。
schema=schemas.GET_MODIFIED_FILES_BY_APPS_SCHEMA, # 绑定给模型看的 schema。
handler=tools.handle_get_modified_files_by_apps, # 绑定实际执行的 handler。
) # 结束唯一 tool 的注册逻辑。

第三节 动态生成SKILL

官方文档: 🈚️

  • 需求: skill我不想写死, 比如我需要让其分析Trae, PyCharm, IDEA....等应用当天修改了哪些内容, 从而推断我做了什么; 每增加一个我除了写 “获取这个IDE修改文件的方法实现” 还需要 去改SKILL
  • 解决方法: TODO: Hermes的一大优势是自己写skill, 当skill不在~/.hermes/skills时, 不知道是否还会写; (还会=>方法一更优; 否则=>方法二)
    • 方法一: ①上面使用ctx.register_skill(child.name, skill_md), 这个skill_md就是读取的skill; ②我直接SKILL写插值语法{{providersApps}} 然后替换不就行了 ③这样一来注册进去的SKILL.md就是动态的了
    • 方法二: 还是把”具插值skill”放到当前目录下, 然后注册tool的时候, 把当前目录的skill改好复制过去;
modify_file_monitor/
├── __init__.py # ①注册入口:register_skill(动态skill) + register_tool
├── plugin.yaml
├── schemas.py # tool schema(tool可以入参的值)
├── tools.py
├── providers/ # 一些实现
│ ├── __init__.py
│ ├── common.py
│ ├── pycharm.py
│ └── trae.py
└── skills/
└── modify-file-monitor-workflow/
└── SKILL.md # ②含 {SUPPORTED_APPS} 占位符,注册时动态替换
  • 完成需求: 这里可以获取那些APP的修改文件, 在description写的很清楚; 避免了想要查看修改的文件, 然后调用Tool才发现不支持的尴尬
---
name: "modify-file-monitor-workflow"
description: "Gets modified files for {SUPPORTED_APPS} on a date and summarizes what the user changed. Invoke when the user asks about app-level file activity, modified files, or file-level content summaries."
version: "1.0.0"
author: "mts"
---
  • 动态替换后
---
name: "modify-file-monitor-workflow"
description: "Gets modified files for ['trae', 'pycharm'] on a date and summarizes what the user changed. Invoke when the user asks about app-level file activity, modified files, or file-level content summaries."
version: "1.0.0"
author: "mts"
---
import tempfile  # 用于创建临时文件存放插值后的 skill 内容。
from pathlib import Path # 用于跨平台路径拼接。
from . import schemas # 导入当前插件的 schema 定义。
from . import tools # 导入当前插件的 handler 实现。

def register(ctx): # 按 Hermes 官方规范提供唯一注册入口。
"""注册捆绑 skill(动态注入支持的应用列表)。"""
skills_dir = Path(__file__).parent / "skills" # 构造 skills 目录绝对路径。
# 从 tools.APP_HANDLERS 提取已注册 provider 列表用作 skill 插值变量
for child in sorted(skills_dir.iterdir()): # 遍历 skills 子目录。
skill_md = child / "SKILL.md" # 构造子目录下的 SKILL.md 路径。
if child.is_dir() and skill_md.exists(): # 必须是有 SKILL.md 的子目录。
# STEP1: 读取原始 SKILL.md 内容。
raw = skill_md.read_text(encoding="utf-8")
# STEP2: ["trae", "pycharm"]注入支持的应用枚举列表。
content = raw.replace("{SUPPORTED_APPS}", repr(list(tools.APP_HANDLERS.keys())))
# STEP3:写临时文件用于注册(register_skill 需要持久路径)
tmp = tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False, encoding="utf-8") # 创建不会自动删除的临时文件。
tmp.write(content) # 写入插值后的内容。
tmp.close() # 关闭文件句柄确保内容落盘。
ctx.register_skill(child.name, Path(tmp.name)) # 用插值后的临时文件注册 skill。

# 注册tool
ctx.register_tool(
name="get_modified_files_by_apps",
toolset="modify-file-monitor",
schema=schemas.GET_MODIFIED_FILES_BY_APPS_SCHEMA,
handler=tools.handle_get_modified_files_by_apps,
)

  • Title: Hermes
  • Author: 明廷盛
  • Created at : 2026-06-30 13:23:26
  • Updated at : 2026-06-16 10:52:00
  • Link: https://blog.20040424.xyz/2026/06/30/⏸️VibeCoding/Hermes/
  • License: All Rights Reserved © 明廷盛