一套在单台 Mac 上为 20 人团队部署 Hermes 的实战方案 —— Profile 隔离、Open WebUI 接入、SSH 远程操控 Windows、危险操作审计。
| 维度 | 设计 |
|---|---|
| 硬件 | 1 台 32GB Mac(服务端) |
| 团队 | 20 人,各自 Windows PC |
| 接入方式 | Open WebUI(PC)+ 飞书机器人(移动端) |
| 远程操控 | Hermes SSH 终端后端 → Windows |
| 隔离方案 | 每人独立 Hermes Profile + Gateway 进程 |
| 审计 | approvals.mode: manual + 飞书审批 + agent.log 事后追溯 |
| 资源占用 | 20 人同时在线 ≈ 8 Gi 内存 |
一台高配 Mac(32GB 内存)作为服务端,让团队 20 人通过各自 Windows 电脑远程使用 Hermes,每人独立 Profile、独立记忆、独立 Skills 管理。
┌──────────────────────────────────────────────────────────┐
│ Mac Server(32G) │
│ │
│ ~/.hermes/ │
│ ├── skills/ ← 你集中管理,全局共享 │
│ ├── config.yaml ← 全局默认配置 │
│ └── profiles/ │
│ ├── alice/ │
│ │ ├── config.yaml ← 独有配置(模型、MCP等) │
│ │ ├── .env ← 独有密钥 + SSH配置 │
│ │ ├── sessions/ ← 独有会话记录 │
│ │ └── ssh_keys/ ← 连接 Alice 电脑的密钥 │
│ ├── bob/ │
│ └── carol/ │
│ │
│ Gateway 进程池(每人一个): │
│ PID 8642 → gateway (profile: alice) → api_server:8642 │
│ PID 8643 → gateway (profile: bob) → api_server:8643 │
│ PID 8644 → gateway (profile: carol) → api_server:8644 │
│ ... │
└──────────────────────┬───────────────────────────────────┘
│ 局域网
┌──────────────┼──────────────┐
▼ ▼ ▼
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Alice 的 Win │ │ Bob 的 Win │ │ Carol 的 Win│
│ OpenSSH 服务 │ │ OpenSSH 服务│ │ OpenSSH 服务│
│ port 22 │ │ port 22 │ │ port 22 │
│ 192.168.1.101│ │192.168.1.102│ │192.168.1.103│
└─────────────┘ └─────────────┘ └─────────────┘
│ │ │
Open WebUI Open WebUI Open WebUI
(Alice 的 Win) (Bob 的 Win) (Carol 的 Win)
| 项目 | 占用 |
|---|---|
| macOS 系统 | ~4 Gi |
| Gateway × 20 进程(每进程 ~200MB) | ~4 Gi |
| 总计 | ~8 Gi |
| 剩余 | ~24 Gi(给其他应用和缓冲) |
对比 ClawManager K8s 方案(每人一个 Pod 占 2Gi,装不下 20 个),纯 Hermes Gateway 方案20 人同时在线的内存成本不到 8GB。
- macOS(32GB 内存,建议 M 系列芯片)
- Hermes Agent 已安装
- 局域网 IP 固定(建议 DHCP 静态分配)
- 防火墙放行
8642-8662端口区间(每位成员 +1)
- Windows 10/11(建议 22H2 以上)
- Open WebUI 或任意 OpenAI 兼容客户端(LobeChat、ChatBox、NextChat 等)
- 与 Mac 处于同一局域网
- SSH 客户端(Win10/11 自带)
- (可选)Docker Desktop 用于跑 Open WebUI 容器
# 为每个成员创建独立 Profile
hermes profile create alice
hermes profile create bob
hermes profile create carol
# ... 以此类推
# 验证
ls ~/.hermes/profiles/# 给 Alice 的 Profile 配模型(交互式)
hermes -p alice model
# 或直接编辑 config.yaml
hermes -p alice config edit
# 在打开的 config.yaml 中设置 model 和 provider# 生成密钥,每人一对
ssh-keygen -t ed25519 -f ~/.hermes/profiles/alice/ssh_keys/id_ed25519 -N ""
ssh-keygen -t ed25519 -f ~/.hermes/profiles/bob/ssh_keys/id_ed25519 -N ""
ssh-keygen -t ed25519 -f ~/.hermes/profiles/carol/ssh_keys/id_ed25519 -N ""编辑每个 Profile 的 .env:
# ~/.hermes/profiles/alice/.env
TERMINAL_ENV=ssh
TERMINAL_SSH_HOST=192.168.1.101 # Alice 的 Windows IP
TERMINAL_SSH_USER=alice
TERMINAL_SSH_PORT=22
TERMINAL_SSH_KEY=~/.hermes/profiles/alice/ssh_keys/id_ed25519# ~/.hermes/profiles/bob/.env
TERMINAL_ENV=ssh
TERMINAL_SSH_HOST=192.168.1.102
TERMINAL_SSH_USER=bob
TERMINAL_SSH_PORT=22
TERMINAL_SSH_KEY=~/.hermes/profiles/bob/ssh_keys/id_ed25519启动脚本示例 start_team_gateways.sh:
#!/bin/bash
# 启动所有团队成员的 Gateway 进程
# 端口分配:从 8642 开始,每人 +1
declare -A PORTS=(
["alice"]=8642
["bob"]=8643
["carol"]=8644
# 继续添加...
)
for profile in "${!PORTS[@]}"; do
port=${PORTS[$profile]}
profile_dir="$HOME/.hermes/profiles/$profile"
# 确保目录存在
if [ ! -d "$profile_dir" ]; then
echo "⚠ Profile $profile 不存在,跳过"
continue
fi
# 端口占用检测
if lsof -Pi :$port -sTCP:LISTEN -t >/dev/null 2>&1; then
echo "⚠ 端口 $port 已占用,跳过 $profile"
continue
fi
echo "🚀 启动 $profile → port $port"
HERMES_HOME="$profile_dir" \
API_SERVER_ENABLED=true \
API_SERVER_PORT="$port" \
API_SERVER_HOST="0.0.0.0" \
nohup hermes gateway run > "$profile_dir/gateway.log" 2>&1 &
sleep 1
done
echo "✅ 所有 Gateway 已启动"
echo " 检查状态:ps aux | grep 'hermes gateway'"注意:如果需要同时对接飞书机器人,在每个 Profile 的
config.yaml中启用对应 Platform 配置即可。Gateway 会同时启用 API Server 和飞书。
# Skills 是全局共享的,放在 ~/.hermes/skills/ 下
# 所有 Profile 的 Hermes 启动时会读取这个目录
# 安装全局 Skill
hermes skills install <skill-name>
# 或直接复制
cp -r ~/my-skills/* ~/.hermes/skills/Skills 是全局共享的。每个 Profile 的 Hermes Gateway 进程读取的是其
HERMES_HOME/skills/目录。由于HERMES_HOME指向各自的 Profile 目录,默认每个 Profile 的 Skills 也隔离。要让 Skills 共享,有两种方式:
- 在每个 Profile 的
config.yaml中配置skills_dir: ~/.hermes/skills/(如果支持)- 用符号链接:
ln -s ~/.hermes/skills/ ~/.hermes/profiles/alice/skills
# 以管理员身份运行 PowerShell
# 1. 安装 OpenSSH Server
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
# 2. 启动并设为自动
Start-Service sshd
Set-Service -Name sshd -StartupType 'Automatic'
# 3. 防火墙放行 22 端口
New-NetFirewallRule -Name 'OpenSSH-Server' -DisplayName 'OpenSSH Server' `
-Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22
# 4. 确认 SSH 服务器运行
Get-Service sshd
# 5. 查看本机 IP
ipconfig
# 记录 IPv4 地址(如 192.168.1.101)方法 A:管理员统一配置(推荐) Windows 用户将公钥发给管理员,管理员在 Mac 上装好公钥。但更方便的是:
方法 B:Windows 用户自行配置
# 在 Windows 上执行
# 1. 创建 .ssh 目录(如果不存在)
mkdir $env:USERPROFILE\.ssh -Force
# 2. 管理员给你的公钥内容,粘贴到 authorized_keys
notepad $env:USERPROFILE\.ssh\authorized_keys
# 粘贴公钥内容,保存
# 3. 设置正确的权限
icacls $env:USERPROFILE\.ssh\authorized_keys /inheritance:r /grant "$env:USERNAME:(R)"
icacls $env:USERPROFILE\.ssh /inheritance:r /grant "$env:USERNAME:(RX)"
# 4. 测试连接(在 Mac 上验证)
# ssh -i ~/.hermes/profiles/alice/ssh_keys/id_ed25519 alice@192.168.1.101# 方式一:Docker 安装(推荐)
# 安装 Docker Desktop 后执行
docker run -d -p 3000:8080 ^
--name open-webui ^
--restart always ^
ghcr.io/open-webui/open-webui:main
# 方式二:直接安装(需要 Python 3.11+)
pip install open-webui
open-webui serve打开 Open WebUI,进入设置 → 连接:
| 设置项 | 值 |
|---|---|
| OpenAI API 地址 | http://你的MacIP:8642/v1(Alice 用 8642,Bob 用 8643...) |
| API Key | 留空或按需设置 |
| 模型名称 | hermes-agent(自动显示) |
# 查看所有 Gateway 运行状态
ps aux | grep 'hermes gateway'
# 查看某个成员的 Gateway 日志
tail -f ~/.hermes/profiles/alice/gateway.log
# 重启某个成员的 Gateway
kill <PID>
HERMES_HOME=~/.hermes/profiles/alice \
API_SERVER_ENABLED=true \
API_SERVER_PORT=8642 \
nohup hermes gateway run > ~/.hermes/profiles/alice/gateway.log 2>&1 &
# 安装全局技能
hermes skills install <skill-name>
# 为某个 Profile 单独安装技能
HERMES_HOME=~/.hermes/profiles/alice hermes skills install <skill-name>
# 停服
kill $(pgrep -f 'hermes gateway')所有操作在自己的 Windows 电脑上完成
打开 Open WebUI:
# 方式一:Docker 运行
http://localhost:3000
# 方式二:pip 安装
http://localhost:8080全流程示例(Alice 的一天):
1. 打开 Open WebUI → http://localhost:3000
2. 确认右上角模型选的是 hermes-agent
3. 输入:"帮我看看桌面有哪些文件"
→ Hermes Mac Gateway 收到请求
→ SSH 连接到 Alice 的 Windows (192.168.1.101:22)
→ 执行 dir %USERPROFILE%\Desktop
→ 结果返回显示在 Open WebUI
4. 继续问:"帮我整理一下,把 .pdf 都放到 PDFs 文件夹"
→ Hermes 通过 SSH 在 Alice 电脑上执行
mkdir %USERPROFILE%\Desktop\PDFs
move %USERPROFILE%\Desktop\*.pdf %USERPROFILE%\Desktop\PDFs\
→ 完成后回复 Alice
5. 或者问:"帮我分析这个项目代码结构"
→ Hermes 通过 SSH 读取 Alice 电脑上的文件
→ 进行分析
→ 返回分析结果
# 在 Mac 上测试 SSH 连接
ssh -v -i ~/.hermes/profiles/alice/ssh_keys/id_ed25519 alice@192.168.1.101
# 常见原因
# - Windows 防火墙未放行 22 端口
# - OpenSSH Server 未启动
# - 密钥权限不正确
# - IP 地址变更# 检查配置文件
HERMES_HOME=~/.hermes/profiles/alice hermes config check
# 全量测试
HERMES_HOME=~/.hermes/profiles/alice hermes doctor
# 查看日志
cat ~/.hermes/profiles/alice/gateway.log- 确认 Mac 和 Windows 在同一网段
- 在 Windows 上 ping MacIP 确认网络连通
- 确认 Mac 防火墙未拦截端口 8642-8662
- 检查 Open WebUI 配置的 API 地址是否正确
- Open WebUI 的 "连接" 地址末尾要带 /v1
在 Profile 的 config.yaml 中额外配置飞书或企业微信平台:
# ~/.hermes/profiles/alice/config.yaml
gateway:
platforms:
feishu:
enabled: true
app_id: cli_xxxxx1
app_secret: xxxx同一个 Gateway 进程同时服务:
- Alice 的飞书机器人私聊(手机端)
- Alice 的 Open WebUI(PC 端)
两者共享同一个 Profile、同一套记忆、同一个 SSH 后端。
如果 SSH 不够用,可以在每台 Windows 上部署 MCP Server 提供精细控制。
使用 macOS 原生 launchd 替代 nohup 管理 Gateway 进程,具备崩溃自动重启、开机自启、日志轮转能力。
launchd plist 模板(每 Profile 一份):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.hermes.gateway.alice</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>-c</string>
<string>
HERMES_HOME=~/.hermes/profiles/alice \
API_SERVER_ENABLED=true \
API_SERVER_PORT=8642 \
API_SERVER_HOST=0.0.0.0 \
/Users/chenzhiqing/.local/bin/hermes gateway run
</string>
</array>
<!-- 用户登录后启动 -->
<key>RunAtLoad</key>
<true/>
<!-- 崩溃后自动重启 -->
<key>KeepAlive</key>
<true/>
<!-- 日志输出 -->
<key>StandardOutPath</key>
<string>~/.hermes/profiles/alice/logs/gateway.log</string>
<key>StandardErrorPath</key>
<string>~/.hermes/profiles/alice/logs/errors.log</string>
<!-- 每 300s 检查一次进程是否存活 -->
<key>ThrottleInterval</key>
<integer>300</integer>
</dict>
</plist>安装步骤:
# 1. 生成 plist 文件
cat > ~/Library/LaunchAgents/com.hermes.gateway.alice.plist << 'PLIST'
...(上述内容)...
PLIST
# 2. 加载服务
launchctl load ~/Library/LaunchAgents/com.hermes.gateway.alice.plist
# 3. 查看状态
launchctl list | grep hermes
# 4. 手动启停
launchctl start com.hermes.gateway.alice
launchctl stop com.hermes.gateway.alice注意:
- plist 中所有路径必须使用绝对路径(
/Users/xxx/),~在 launchd 中不生效hermes命令路径用which hermes确认后填入- 批量生成脚本见 §7.7 一键运维
两套 Profile 各司其职:
| 类型 | 角色 | 进程状态 | 例子 |
|---|---|---|---|
| 员工助手 Profile | 每人一个,绑定团队成员 | 🟢 常驻 Gateway | alice、bob、carol |
| Workflow Profile | 功能性角色,供员工调用 | 🔴 纯目录,用时才启动 | workflow-analysis、workflow-backend |
Workflow Profile 不需要跑任何 Gateway/API Server/MCP Server 进程。它只是 ~/.hermes/profiles/ 下的一个普通目录,有独立的 config.yaml、skills/、sessions/。员工助手的 agent 在 Mac 上用 Python 子进程直接调用。
Mac 上的 ~/.hermes/profiles/
员工助手 Profile(常驻 Gateway) Workflow Profile(纯目录,无进程)
┌─────────────────────┐
┌──────────────────┐ │ workflow-analysis │
│ alice (Gateway) │── execute_code() ──→ │ config.yaml │
│ port 8642 │ │ skills/ │
│ SSH → Alice 的PC │←──── stdout ──────── │ sessions/ │
└──────────────────┘ │ (独立记忆) │
│ └─────────────────────┘
┌──────────────────┐ ┌─────────────────────┐
│ bob (Gateway) │── execute_code() ──→ │ workflow-backend │
│ port 8643 │ │ config.yaml │
│ SSH → Bob 的PC │ │ skills/ │
└──────────────────┘ │ sessions/ │
│ └─────────────────────┘
┌──────────────────┐ ┌─────────────────────┐
│ carol (Gateway) │── execute_code() ──→ │ workflow-writing │
│ port 8644 │ │ ... │
└──────────────────┘ └─────────────────────┘
第一步:创建 Workflow Profile
和创建员工 Profile 一样,只是不启动 Gateway:
# 创建三个 Workflow Profile
hermes profile create workflow-analysis
hermes profile create workflow-backend
hermes profile create workflow-writing
# 给每个 Profile 配置模型和技能
hermes -p workflow-analysis model # 选模型
HERMES_HOME=~/.hermes/profiles/workflow-analysis hermes skills install <skill-name>第二步:员工助手调用 Workflow Profile
员工助手的 agent 通过 execute_code 工具,直接在 Mac 上以子进程方式调用 Workflow CLI:
# Alice 的 agent 收到:"分析这份销售数据" 后内部执行:
import subprocess, os
result = subprocess.run(
[
"hermes", "chat", "-q",
"分析销售数据\n\n"
"背景:本季度销售额,数据通过 SSH 从用户 Windows 读取。"
"输出季度环比增长率、Top5 产品排名。"
],
capture_output=True, text=True,
env={
"HERMES_HOME": os.path.expanduser("~/.hermes/profiles/workflow-analysis"),
**os.environ
},
timeout=120
)
# 把结果返回给 Alice
return result.stdout调用流程图:
Alice: "帮我分析一下这份销售数据"
│
▼
Alice 的助手 agent
│
├─ ① SSH → Alice 的 Windows 读取销售数据文件
│
├─ ② execute_code() → subprocess.run()
│ HERMES_HOME=workflow-analysis
│ hermes chat -q "分析数据:<读到的数据>"
│ │
│ ▼
│ workflow-analysis(新进程,用完退出)
│ │ 继承工作流技能,有独立记忆
│ ▼
│ 返回分析结果 ← stdout
│
└─ ③ 汇总结果,回复 Alice
Workflow Profile 的会话历史自动保存在其目录下,两次调用可以用 --continue 续接:
# 第一次调用
subprocess.run(["hermes", "chat", "-q", "分析上月销售数据"], env={...})
# 后续追问(续接同一会话)
subprocess.run(["hermes", "--continue", "-q", "跟去年比怎么样"], env={...})并行安全性: 每个 subprocess 是独立进程、独立会话。Alice 和 Bob 同时调用同一个 Workflow Profile → 各自启动独立进程、独立会话,互不干扰。
| Workflow Profile | 技能配置 | 员工如何调用 |
|---|---|---|
workflow-analysis |
数据分析 skill + SQL skill | "帮我看一下这个 Excel" |
workflow-backend |
架构设计 skill + 代码审查 skill | "帮我校验这个 API 设计" |
workflow-writing |
文案写作 skill + 文档 skill | "帮我写一份项目周报" |
workflow-security |
安全审计 skill | "帮我检查一下这个配置文件有没有漏洞" |
- Workflow Profile 不需要跑任何守护进程(Gateway / API Server / MCP Server 都不需要)
- 子进程调用有超时(建议设 120-300s),复杂任务设大一点
hermes chat -q是单次对话,非交互模式,适合程序调用- 如果需要 Workflow 中有工具调用(如联网搜索、文件读写),确保其
config.yaml中开了对应工具 - 调用方(员工助手)需要
execute_code工具权限(默认开启)
DHCP 会导致 Windows IP 不定期变化,SSH 连接因此断连。两种方案选其一:
方案 A:Windows 手动设静态 IP
# 在 Windows 上以管理员身份运行
# 查看当前网络配置
ipconfig /all
# 记下:IPv4 地址、子网掩码、默认网关、DNS 服务器
# 设置为静态(替换为你的实际值)
netsh interface ip set address "以太网" static 192.168.1.101 255.255.255.0 192.168.1.1
netsh interface ip set dns "以太网" static 192.168.1.1方案 B:路由器 DHCP 绑定(推荐) 在路由器管理界面中,将每台 Windows 的 MAC 地址与 IP 绑定,这样 DHCP 每次分配同一个 IP。
在 Mac 上维护一个 ~/.hermes/hosts.csv,启动脚本读取此文件:
profile,host,user,port,desc
alice,192.168.1.101,alice,22,设计部-工位A
bob,192.168.1.102,bob,22,开发部-工位B
carol,192.168.1.103,carol,22,产品部-工位C# 定期校验:检查所有 IP 是否可达
#!/bin/bash
# ping_check.sh
echo "📡 IP 连通性检查"
while IFS=',' read -r profile host user port desc; do
[ "$profile" = "profile" ] && continue # 跳过表头
if ping -c 1 -W 2 "$host" >/dev/null 2>&1; then
echo " ✅ $profile ($host) 可达"
else
echo " ❌ $profile ($host) 不可达!"
fi
done < ~/.hermes/hosts.csv# 设置密钥目录权限(必须 700,否则 SSH 拒绝使用)
chmod 700 ~/.hermes/profiles/*/ssh_keys/
chmod 600 ~/.hermes/profiles/*/ssh_keys/*
# 每月密钥轮换脚本
#!/bin/bash
# rotate_ssh_keys.sh
month=$(date +%Y%m)
for profile in ~/.hermes/profiles/*/; do
name=$(basename "$profile")
key_dir="$profile/ssh_keys"
[ ! -d "$key_dir" ] && continue
# 备份旧密钥
tar czf "${key_dir}/../ssh_keys_bak_${month}.tar.gz" -C "$(dirname "$key_dir")" "ssh_keys"
# 生成新密钥
ssh-keygen -t ed25519 -f "${key_dir}/id_ed25519" -N "" -C "${name}-${month}"
echo "✅ $name 密钥已轮换,请将公钥同步到 Windows"
done# ~/.ssh/config
Host 192.168.1.*
ServerAliveInterval 15 # 每 15s 发心跳保活
ServerAliveCountMax 3 # 3 次心跳失败才断开
ConnectionAttempts 3 # 连接失败重试 3 次
ConnectTimeout 10 # 每次连接超时 10s
StrictHostKeyChecking no # 局域网环境跳过 known_hosts 检查
UserKnownHostsFile /dev/null在每台 Windows 的 OpenSSH 服务端也配心跳:
# Windows 上以管理员运行
# 编辑 C:\ProgramData\ssh\sshd_config
# 添加:
# ClientAliveInterval 15
# ClientAliveCountMax 3
# 重启 SSH 服务
Restart-Service sshd# crontab -e,每日凌晨 2 点备份
0 2 * * * /bin/bash -c '\
BACKUP_DIR="/Volumes/Backup/hermes_profiles/$(date +%Y%m%d)" && \
mkdir -p "$BACKUP_DIR" && \
rsync -avz --delete \
--exclude="sessions/" \ # 会话文件量大,按需备份
--exclude="logs/" \ # 日志文件量大,按需备份
~/.hermes/profiles/ \
"$BACKUP_DIR/" && \
echo "✅ 备份完成: $BACKUP_DIR" >> ~/.hermes/backup.log'
# 也备份一份全局配置
0 2 * * * cp ~/.hermes/config.yaml "/Volumes/Backup/hermes_profiles/config.yaml.$(date +%Y%m%d)"备份策略建议:
| 文件 | 频率 | 保留期 | 备注 |
|---|---|---|---|
profiles/*/config.yaml |
每日 | 30 天 | 核心配置 |
profiles/*/.env |
每日 | 30 天 | API Key、SSH 配置 |
profiles/*/ssh_keys/ |
每月 | 12 月 | 轮换前备份旧密钥 |
profiles/*/sessions/ |
按需 | — | 量大,不自动备份 |
skills/ |
每次变更 | — | Git 管理更合适 |
将零散的运维命令封装为统一入口,降低管理 20 个 Profile 的操作成本。
#!/bin/bash
# hermes-ops.sh —— Hermes 团队运维脚本
# 用法:
# ./hermes-ops.sh status 查看所有 Gateway 状态
# ./hermes-ops.sh start 批量启动所有 Gateway
# ./hermes-ops.sh restart <name> 重启某个 Profile
# ./hermes-ops.sh stop all 停止所有 Gateway
# ./hermes-ops.sh logs <name> 查看某个 Profile 的日志
# ./hermes-ops.sh check 校验所有 Profile 配置完整性
HERMES_HOME="$HOME/.hermes"
PORTS_FILE="$HERMES_HOME/hosts.csv"
# 默认端口映射(如无 hosts.csv 则走此表)
declare -A PORTS=(
["alice"]=8642
["bob"]=8643
["carol"]=8644
["workflow-analysis"]=8750
["workflow-backend"]=8751
)
# 读取端口映射
_load_ports() {
if [ -f "$PORTS_FILE" ]; then
while IFS=',' read -r name host user port desc; do
[ "$name" = "profile" ] && continue
PORTS["$name"]=$port
done < "$PORTS_FILE"
fi
}
status() {
echo "═══════════════════════════════════════"
echo "📊 Hermes Gateway 状态报告"
echo "日期: $(date)"
echo "═══════════════════════════════════════"
for profile in "${!PORTS[@]}"; do
port=${PORTS[$profile]}
pid=$(lsof -Pi :$port -sTCP:LISTEN -t 2>/dev/null)
if [ -n "$pid" ]; then
uptime=$(ps -o etime= -p "$pid" | xargs)
echo " ✅ $profile → port $port (PID $pid, 已运行 $uptime)"
else
echo " ❌ $profile → port $port (未运行)"
fi
done
}
start() {
_load_ports
for profile in "${!PORTS[@]}"; do
port=${PORTS[$profile]}
profile_dir="$HERMES_HOME/profiles/$profile"
[ ! -d "$profile_dir" ] && echo "⚠ $profile 目录不存在,跳过" && continue
lsof -Pi :$port -sTCP:LISTEN -t >/dev/null 2>&1 && echo "⚠ $port 已占用,跳过 $profile" && continue
echo "🚀 启动 $profile → port $port"
HERMES_HOME="$profile_dir" \
API_SERVER_ENABLED=true \
API_SERVER_PORT="$port" \
API_SERVER_HOST="0.0.0.0" \
nohup hermes gateway run > "$profile_dir/logs/gateway.log" 2>&1 &
sleep 1
done
echo "✅ 启动完成"
}
stop() {
local target=${1:-all}
if [ "$target" = "all" ]; then
echo "🛑 停止所有 Gateway..."
pkill -f "hermes gateway run" 2>/dev/null
echo "✅ 已停止"
elif [ -n "${PORTS[$target]}" ]; then
local pid=$(lsof -Pi :${PORTS[$target]} -sTCP:LISTEN -t 2>/dev/null)
if [ -n "$pid" ]; then
kill "$pid"
echo "✅ $target 已停止"
else
echo "⚠ $target 未运行"
fi
fi
}
logs() {
local profile=$1
[ -z "$profile" ] && echo "用法: $0 logs <profile>" && exit 1
tail -f "$HERMES_HOME/profiles/$profile/logs/gateway.log"
}
check() {
echo "🔍 校验所有 Profile 配置..."
for profile in "${!PORTS[@]}"; do
dir="$HERMES_HOME/profiles/$profile"
local ok=true
[ ! -f "$dir/config.yaml" ] && echo " ❌ $profile: 缺少 config.yaml" && ok=false
[ ! -f "$dir/.env" ] && echo " ❌ $profile: 缺少 .env" && ok=false
[ -d "$dir/ssh_keys" ] && [ "$(stat -f %A "$dir/ssh_keys")" != "700" ] && echo " ⚠ $profile: ssh_keys 权限不是 700" && ok=false
$ok && echo " ✅ $profile: 正常"
done
}
case "${1:-status}" in
status) status ;;
start) start ;;
stop) stop "$2" ;;
restart) stop "$2"; sleep 1; start ;;
logs) logs "$2" ;;
check) check ;;
*)
echo "用法: $0 {status|start|stop|restart|logs|check}"
echo "示例:"
echo " $0 status 查看所有状态"
echo " $0 start 批量启动"
echo " $0 stop alice 停止 Alice"
echo " $0 stop all 停止所有"
echo " $0 restart bob 重启 Bob"
echo " $0 logs alice 查看 Alice 日志"
echo " $0 check 校验配置"
;;
esac
---
# 八、危险操作审计
## 8.1 审计策略总览
审计策略选型:方案 B — Open WebUI + 飞书审批通道
┌─────────────────────────────────────────────────────────┐ │ Alice 日常使用 │ │ │ │ "帮我删掉 D 盘 temp 文件夹" │ │ │ │ │ ▼ │ │ Open WebUI (Alice 的 Windows) │ │ │ │ │ ▼ │ │ Hermes Gateway (profile: alice) │ │ │ │ │ ├─ 检测到危险命令 ───→ 飞书审批群 ←─ 你在手机上审批 │ │ │ │ │ │ │ ┌──────────────┴──────────────┐ │ │ │ │ 批准 ──→ 执行命令 │ │ │ │ │ 拒绝 ──→ 拦截 + agent.log 记录│ │ │ │ └─────────────────────────────┘ │ │ │ │ │ ▼ │ │ SSH → Alice 的 Windows 执行 │ │ │ │ │ ▼ │ │ 结果返回 Alice 的 Open WebUI │ └─────────────────────────────────────────────────────────┘
事后审计:你定期跑 daily_audit.sh 扫 agent.log
## 8.2 Hermes 自带的危险命令检测
Hermes 内置了 `tools/approval.py` 危险命令检测系统,可以自动识别并拦截高风险操作。
**可以检测到的危险命令模式包括:**
| 类别 | 示例 |
|------|------|
| 破坏性文件操作 | `rm -rf /`、`mkfs.ext4`、`dd if=/dev/zero of=/dev/sda` |
| 系统关机/重启 | `shutdown`、`reboot`、`halt`、`poweroff` |
| 权限提升 | `sudo` 高风险命令、`pkexec`、`chmod -R 777` |
| 远程下载执行 | `curl ... \| bash`、`wget ... \| sh` |
| SSH 密钥操作 | 修改 `~/.ssh/` 下的文件 |
| 容器破坏 | `docker rm -f` 所有容器 |
| 配置文件篡改 | 修改 `~/.hermes/.env`、`config.yaml`、shell rc 文件 |
## 8.3 审批模式配置(方案 B 选定配置)
```yaml
# ~/.hermes/profiles/alice/config.yaml
# 审批模式:manual
# 所有危险操作 → 通过飞书发给管理员审批
# API Server 本身无法交互审批,必须依赖外部 IM 通道
approvals:
mode: manual
timeout: 60 # 超时后自动拒绝,防止永久阻塞
cron_mode: deny
subagent_auto_approve: false
# 审批通道:飞书(主)+ 企业微信(备)
# 两条通道同时在线,一条挂了另一条兜底
gateway:
platforms:
feishu:
enabled: true
app_id: cli_xxxxx # 管理员审核用飞书机器人
app_secret: xxxxx
wecom: # 企业微信兜底
enabled: true
corp_id: xxxx
agent_id: xxxx
secret: xxxx
security:
redact_secrets: true
# 配置命令
hermes -p alice config set approvals.mode manual⚠ 注意:Open WebUI 本身不具备审批交互能力。 危险操作被拦截时,Alice 在 Open WebUI 会看到类似 "操作等待审批" 的提示。 审批请求将通过飞书发送到你的审核群,你在手机上点批准或拒绝。
每个 Profile 有 3 个日志文件,这是你作为管理员做审计的核心依据:
# 日志文件位置(每个 Profile 独立)
~/.hermes/profiles/alice/logs/
├── agent.log # 所有操作的全量日志(核心审计文件)
├── errors.log # 错误和警告(快速排查用)
└── gateway.log # Gateway 层面事件常用审计命令:
# 1. 查看 Alice 今天执行了哪些终端命令
grep "terminal_tool\|SSH\|executing command" ~/.hermes/profiles/alice/logs/agent.log | tail -50
# 2. 查看 Alice 触发过哪些危险命令审批
grep -i "dangerous\|approval\|BLOCKED\|DENY\|WARNING" ~/.hermes/profiles/alice/logs/agent.log | tail -30
# 3. 查看 Alice 的所有错误
tail -50 ~/.hermes/profiles/alice/logs/errors.log
# 4. 实时监控某个 Profile 的操作
tail -f ~/.hermes/profiles/alice/logs/agent.log | grep "terminal_tool\|executing\|SSH"
# 5. 全局审计:所有 Profile 的终端操作一键查询
for p in ~/.hermes/profiles/*/; do
name=$(basename "$p")
count=$(grep -c "terminal_tool\|executing command" "$p/logs/agent.log" 2>/dev/null || echo 0)
danger=$(grep -c "dangerous\|BLOCKED\|DENY" "$p/logs/agent.log" 2>/dev/null || echo 0)
echo "$name: $count 次终端操作, $danger 次危险操作拦截"
done# 列出 Alice 的所有历史会话
HERMES_HOME=~/.hermes/profiles/alice hermes sessions list
# 回溯某个具体会话的全部内容(包括工具调用)
HERMES_HOME=~/.hermes/profiles/alice hermes sessions browse
# 交互式选择会话 → 查看完整对话
# 导出会话为 JSONL 用于存档或分析
HERMES_HOME=~/.hermes/profiles/alice hermes sessions export ~/alice_sessions.jsonl
# 查看会话统计
HERMES_HOME=~/.hermes/profiles/alice hermes sessions stats# ~/.hermes/profiles/alice/config.yaml 中额外配置
security:
# 开启秘密信息脱敏(API Key 等不会写入日志和会话)
redact_secrets: true
# 网站黑名单(禁止 Hermes 访问的域名)
website_blocklist:
- "malware.example.com"
approvals:
# 子 Agent 遇到危险命令 → 自动拒绝并记审计日志(安全默认)
subagent_auto_approve: false
# Cron 任务遇到危险命令 → 自动拒绝(安全默认)
cron_mode: deny当 Alice 触发了危险命令,你在飞书上的审批体验:
飞书审批群收到消息:
┌────────────────────────────────────┐
│ ⚠️ 危险操作需要审批 │
│ │
│ 用户:Alice │
│ 命令:rm -rf D:\temp\* │
│ 原因:批量删除临时文件 │
│ │
│ [✅ 批准] [❌ 拒绝] │
└────────────────────────────────────┘
你点 批准 → 命令在 Alice 的 Windows 上执行
你点 拒绝 → 命令被拦截,Alice 看到"操作被拒绝"
作为管理员(你),建立以下日常检查流程:
#!/bin/bash
# daily_audit.sh —— 每日审计检查
echo "═══════════════════════════════════════"
echo "📋 Hermes 团队每日审计报告"
echo "日期: $(date)"
echo "═══════════════════════════════════════"
for profile_dir in ~/.hermes/profiles/*/; do
name=$(basename "$profile_dir")
log="$profile_dir/logs/agent.log"
echo ""
echo "── $name ──"
if [ ! -f "$log" ]; then
echo " ⚠ 无日志"
continue
fi
# 1. 今日危险操作
danger_count=$(grep "$(date +%Y-%m-%d)" "$log" | grep -ci "BLOCKED\|DENY" || echo 0)
echo " 危险操作拦截: $danger_count 次"
if [ "$danger_count" -gt 0 ]; then
echo " 被拦截的命令:"
grep "$(date +%Y-%m-%d)" "$log" | grep -i "BLOCKED\|DENY" | \
sed 's/.*BLOCKED: //;s/.*DENY: //' | \
sort | uniq -c | sort -rn | head -5 | \
while read count cmd; do
echo " - $cmd($count 次)"
done
fi
# 2. 今日终端命令执行次数
cmds=$(grep "$(date +%Y-%m-%d)" "$log" | grep -c "executing command" || echo 0)
echo " 终端命令执行: $cmds 次"
# 3. 今日 SSH 连接次数
ssh=$(grep "$(date +%Y-%m-%d)" "$log" | grep -c "SSH\|ssh" || echo 0)
echo " SSH 连接次数: $ssh 次"
# 4. 今日错误数
errs=$(grep "$(date +%Y-%m-%d)" "$log" | grep -ci "error\|exception\|traceback" || echo 0)
echo " 错误数: $errs 次"
doneAlice 操作 你在飞书上 日志留存
│ │ │
▼ ▼ ▼
"删掉 D 盘 temp"
│
▼
Hermes 检测到危险命令
│
▼
审批请求 ──────────────────→ 飞书审批群
│
┌────┴────┐
│ 你点批准 │──→ SSH 执行命令 ← agent.log 记录
│ 你点拒绝 │──→ 命令被拦截 ← agent.log 记录 BLOCKED
└─────────┘
Alice 的会话完整记录 → sessions/*.jsonl
agent.log 永久可查 → 事后追溯
---
# 九、架构决策记录
| 决策 | 选择 | 原因 |
|------|------|------|
| 远程终端方案 | SSH | Hermes 原生支持,零开发,成熟稳定 |
| 用户隔离方式 | Hermes Profile | 原生 Profile 机制,`~/.hermes/profiles/` 完全隔离 |
| 用户接入方式 | Open WebUI + api_server | OpenAI 兼容标准,客户端选择多,体验好 |
| Gateway 进程模型 | 每 Profile 一个 | `HERMES_HOME` 隔离保证,PID 防冲突机制原生支持 |
| Skills 管理 | 全局共享 + 符号链接 | 统一管理、灵活隔离 |
| 多 IM 渠道 | 可选,Profile config.yaml 中配置 | 与 API Server 共存于同一 Gateway 进程 |
| **审计策略** | **方案 B:Open WebUI + 飞书审批通道** | **`approvals.mode: manual` + 飞书机器人 → 管理员在手机上审批;`agent.log` 事后审计** |
| **Workflow 调用方式** | **`execute_code()` + `subprocess.run()` 调 CLI** | **Workflow Profile 不跑任何守护进程;员工助手用 `HERMES_HOME` 环境变量以子进程方式直接调用;`hermes chat -q` 单次交互 + `--continue` 续接会话;天然并行,互不干扰** |