Skip to content

Hermes Agent 研发快速上手指南 (含 OpenClaw 迁移对比及工程踩坑)

面向读者:产研同学、AI 代理 (Antigravity / Claude Code)
文档目的:快速理解 Hermes 定位、与 OpenClaw 的核心差异,并提供机器可读 (Machine-Readable) 的自动化安装与配置指令,方便 AI 一键完成初始化,同时沉淀数据库文件句柄泄露的实战踩坑经验。


一、 一句话了解 Hermes

Hermes Agent 不是一个“只会聊天的 Bot”,而是一个结合了 CLI + Messaging Gateway + Memory + Skills + Cron + MCP + Profiles 的长期运行代理系统。

  • Profiles(核心机制):将 Agent 身份与状态完全隔离。一台机器上可以同时运行多个独立 Agent(例如同时跑一个 Coder、一个 PM),互不干扰。
  • Gateway:内置强大的消息网关,原生支持接入 Feishu (Lark) / Telegram / Discord / Slack,并支持 Webhook 与 WebSocket 模式。
  • Skills / Memory:支持通过纯 Markdown 定义系统人设 (SOUL.md) 和记忆 (MEMORY.md),并可通过加载 Skills 模块获得“程序化经验包”。

二、 Hermes 与 OpenClaw 的核心区别

如果你熟悉之前的 OpenClaw,可以按以下维度快速对比:

维度OpenClawHermes Agent
主运行时生态Node.js / TypeScriptPython 生态 (推荐 3.11+)
本机状态目录~/.openclaw~/.hermes
多 Agent 组织依赖 agents / bindings / channels 复杂配置profiles 是一等能力,直接按 Profile 物理隔离
飞书 (Feishu) 接入社区桥接 / 深度定制插件官方原生内置支持(含卡片、群组 @、文档评论)
迁移路径-官方内置 hermes claw migrate 指令
当前推进策略现网运行中,结构较重旁路新建 Profile 共存验证,非原地替换

TIP

核心共识:Hermes 的迁移不是“一条命令全自动接管现网机器人的所有复杂路由”,而是推荐利用 Profiles 机制新建独立的飞书机器人进行旁路灰度和验证。


三、 AI Chat 快速初始化配置指南 (Machine-Readable)

IMPORTANT

此部分专为 AI 代理设计。 当接收到“初始化 Hermes”指令时,AI 应严格按以下顺序执行 Bash 脚本,完成从零到一的安装与配置。

3.1 环境准备与核心安装

Hermes 依赖 Python 环境。官方提供了一键安装脚本:

bash
# 推荐使用官方安装脚本 (将在 ~/.hermes/hermes-agent 下创建 venv)
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
source ~/.zshrc

(若遇网络或 Homebrew 卡顿,可采用源码构建:git clone -> uv venv -> uv pip install -e '.[feishu,cron,pty,mcp,web]' -> 软链至 ~/.local/bin/hermes)

3.2 自动化共存与 Profile 创建

为了不影响现有 OpenClaw 的运行,我们在工作区(例如 hermes-learn)中提供了自动化脚本来完成 Profile 的初始化:

1. 准备密钥文件 .env.hermes.local 在当前工作区根目录创建该文件(请确保它被加入 .gitignore):

bash
HERMES_PROFILE_NAME=hermes-feishu
HERMES_TERMINAL_CWD=$(pwd)
FEISHU_APP_ID=cli_xxxxxxxxxxxx
FEISHU_APP_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
FEISHU_DOMAIN=feishu
FEISHU_CONNECTION_MODE=websocket
FEISHU_GROUP_POLICY=allowlist
FEISHU_BOT_NAME=无所不能的助理
HERMES_TIMEZONE=Asia/Shanghai

2. 运行初始化脚本

bash
# 自动读取 .env.hermes.local 并创建 ~/.hermes/profiles/hermes-feishu
npm run hermes:coexist:setup

# 检查环境变量与安装状态是否达标
npm run hermes:coexist:check

3.3 飞书后台发布与网关启动

  1. 在飞书开发者后台,为新建的应用开启机器人能力,并创建版本进行发布
  2. 安装并启动 Hermes Gateway(作为常驻服务):
bash
hermes -p hermes-feishu gateway install
hermes -p hermes-feishu gateway start

(macOS 下会自动写入 LaunchAgents 保证开机自启)

3.4 启动可视化控制面板 (Control Center)

工作区内自带了一个本地 Web 监控面板,用于实时观察 Agent 状态、切换底层 LLM 模型及查看日志:

bash
# 启动本地控制中心服务
npm run control:install

# 访问地址
# http://hermes-control.localhost:8614/

面板亮点功能:可以在面板中一键切换底层 AI 模型引擎(如:从 astron-code-latest 切换至 deepseek-v4-flash),系统将自动处理 config.yaml 的改写并无缝重启网关。同时,面板中支持在请求日志中直接展示处理该请求的底层大模型名称,方便追溯和调试。


四、 研发日常维护与排障命令

日常调试时,你可以使用以下命令检查 Agent 的健康状况:

bash
# 1. 检查网关服务状态 (应显示 loaded 及 PID)
hermes -p hermes-feishu gateway status

# 2. 实时查看 WebSocket 建联与飞书消息日志
tail -f ~/.hermes/profiles/hermes-feishu/logs/gateway.log

# 3. 查看大语言模型(LLM)的思考、调用和工具执行过程
tail -f ~/.hermes/profiles/hermes-feishu/logs/agent.log

# 4. 命令行直接进行对话测试
hermes -p hermes-feishu chat -q 'Reply with exactly: ok'

# 5. 查看本地监控面板运行状态
npm run control:status

五、 🔥 独家工程实战与踩坑经验

在落地本地可视化控制面板(Control Center)时,遇到了极其隐蔽的数据库连接文件句柄泄露问题,供产研同学在自研 Python 监控后台时参考。

5.1 故障现象

控制面板在正常运行数分钟后,页面突然报错打不开,后端返回 JSON 提示:

json
{
  "ok": false,
  "message": "控制中心发生未预期错误。",
  "error_type": "OSError"

查看 server.log,发现大量以下报错:

text
OSError: [Errno 24] Too many open files: '/Users/.../control-center/static/index.html'

5.2 根因排查

由于前端开启了每 5 秒一次的自动刷新轮询,后端会高频查询本地 SQLite 数据库以读取最新的消息和会话状态。 后端代码原本使用了经典的 Python with 语法来连接数据库:

python
# 🚨 错误示范 🚨
def _run_rows(self, sql: str, params: tuple[Any, ...] = ()) -> list[dict[str, Any]]:
    with self._db_lock:
        with self._connect() as conn: # 以为这里会自动关闭数据库连接
            return [dict(row) for row in conn.execute(sql, params)]

致命盲区:在 Python 的 sqlite3 模块中,将 Connection 作为上下文管理器(with sqlite3.connect(...)只会自动处理事务的 commit 和 rollback,绝对不会自动关闭连接(conn.close)! 这导致每一次前端轮询都会悄悄泄露数个 SQLite 文件句柄,直到耗尽操作系统的 ulimit -n 上限(macOS 默认单进程限制为 256),连静态页面 index.html 都因无法分配句柄而报错崩溃。

5.3 解决方案

使用 try-finally 结构重写数据库调用,强制在最后执行 conn.close() 释放系统资源:

python
# ✅ 正确示范 ✅
def _run_rows(self, sql: str, params: tuple[Any, ...] = ()) -> list[dict[str, Any]]:
    if not self.db_path.exists():
        return []
    with self._db_lock:
        conn = self._connect()
        try:
            return [dict(row) for row in conn.execute(sql, params)]
        finally:
            conn.close() # 显式安全关闭,释放文件描述符

修复后再次部署,系统文件描述符稳定在个位数,轮询再无崩溃隐患。