Gateway 多平台架构 —— 从 CLI 到全平台、Gateway 进程、平台适配器、消息路由
简介
在前面的文章中,我们已经深入探讨了 Hermes Agent 的核心组件:Provider 配置、工具集(Toolsets)、技能系统(Skills)以及定时任务。这些都是 Agent 的"内功"——让 Agent 具备思考、学习和执行的能力。
但一个强大的 Agent 如果只能通过终端命令行来交互,它的能力就被极大地限制了。想象一下:
- 你在微信上收到一条消息:"帮我看看这个 Python 项目的依赖有没有安全漏洞" —— 如果 Hermes 能直接在微信里回复你,那该多好。
- 你的团队用 Telegram 沟通,你希望 Agent 作为 Bot 加入群组,自动回答技术问题、审查代码。
- 你管理着 Discord 社区,需要一个 24/7 在线的 AI 助手来处理成员提问。
这就是 Gateway(网关) 存在的意义 —— 它是 Hermes Agent 连接外部世界的桥梁。
本文将带你全面理解 Hermes Agent 的 Gateway 多平台架构:
- 从 CLI 到全平台:为什么需要 Gateway,架构演进的必然
- Gateway 进程模型:生命周期、热重载、多实例管理
- 平台适配器体系:统一接口、插件化扩展
- 消息路由机制:会话管理、上下文隔离、消息转换
- 部署与运维:生产环境最佳实践
Gateway 的核心理念:一次开发,处处运行。让 Agent 的能力无缝延伸到每一个你使用的平台。
目录
从 CLI 到全平台的架构演进
CLI 模式的局限性
Hermes Agent 最初的设计是一个 CLI 工具:
# 最基本的交互方式
hermes run "帮我重构这个模块"
hermes run "审查一下最近的 PR"
# 交互式模式
hermes chatCLI 模式在开发场景中非常好用,但在以下场景中遇到了瓶颈:
- 实时性需求:CLI 是请求-响应模式,无法主动推送消息
- 多用户场景:CLI 通常是单用户,难以同时服务多个用户
- 平台生态:团队已经在微信/Telegram/Discord 上协作,切换到终端有摩擦
- 持续运行:CLI 随终端关闭而退出,无法做 24/7 服务
- 富媒体支持:CLI 只能处理文本,无法发送图片、文件、按钮等
Gateway 架构的引入
为了解决这些问题,Hermes 引入了 Gateway 组件。Gateway 是一个长期运行的后台进程,充当 Agent 与外部平台之间的中介层。
┌─────────────────────────────────────────────────────────────────┐
│ Hermes Gateway 架构 │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌────────┐ │
│ │ WeChat │ │ Telegram │ │ Discord │ │ CLI │ │
│ │ Adapter │ │ Adapter │ │ Adapter │ │Adapter │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └───┬────┘ │
│ │ │ │ │ │
│ └────────────────┼────────────────┼──────────────┘ │
│ ▼ │ │
│ ┌───────────────────┐ │ │
│ │ 消息路由器 │◄───────┘ │
│ │ Message Router │ │
│ └────────┬──────────┘ │
│ │ │
│ ┌────────▼──────────┐ │
│ │ 会话管理器 │ │
│ │ Session Manager │ │
│ └────────┬──────────┘ │
│ │ │
│ ┌────────▼──────────┐ │
│ │ Agent 核心引擎 │ │
│ │ (LLM + Tools) │ │
│ └───────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘Gateway 的核心价值
Gateway 不是简单的"转发器",它承担了以下关键职责:
| 职责 | 说明 |
|---|---|
| 协议转换 | 将不同平台的消息格式统一为 Hermes 内部消息模型 |
| 会话管理 | 为每个用户/群组维护独立的对话上下文 |
| 消息路由 | 根据消息来源、类型、内容决定如何处理 |
| 速率控制 | 防止高频请求导致 API 限流或 Agent 过载 |
| 错误恢复 | 平台断连后的重连、消息重发、状态同步 |
| 安全过滤 | 输入清洗、权限校验、敏感信息脱敏 |
Gateway 进程模型
启动与生命周期
Gateway 是一个独立于 CLI 的长期运行进程:
# 启动 Gateway(前台模式,适合调试)
hermes gateway start
# 启动 Gateway(后台守护模式,适合生产)
hermes gateway start --daemon
# 启动 Gateway 并指定配置文件
hermes gateway start --config ./gateway-config.yaml
# 查看 Gateway 状态
hermes gateway status
# 输出:
# Gateway Status: running
# PID: 12345
# Uptime: 3d 14h 22m
# Active Platforms: wechat, telegram, discord
# Active Sessions: 12
# Memory: 256MB
# CPU: 2.3%
# 停止 Gateway
hermes gateway stop
# 重启 Gateway(平滑重启,不中断现有会话)
hermes gateway restart进程架构图
┌───────────────────────────────────────────────────────┐
│ Gateway 进程 │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 主进程 (Master) │ │
│ │ │ │
│ │ ┌────────────┐ ┌────────────┐ ┌───────────┐ │ │
│ │ │ 信号处理器 │ │ 配置监听器 │ │ 健康检查 │ │ │
│ │ │ SIGTERM/ │ │ 热重载 │ │ /health │ │ │
│ │ │ SIGHUP │ │ SIGHUP │ │ /ready │ │ │
│ │ └────────────┘ └────────────┘ └───────────┘ │ │
│ │ │ │
│ │ ┌───────────────────────────────────────────┐ │ │
│ │ │ 子进程池 (Worker Pool) │ │ │
│ │ │ │ │ │
│ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │
│ │ │ │Worker 1 │ │Worker 2 │ │Worker 3 │ ...│ │ │
│ │ │ │(wechat) │ │(telegram│ │(discord)│ │ │ │
│ │ │ │ │ │ │ │ │ │ │ │
│ │ │ └─────────┘ └─────────┘ └─────────┘ │ │ │
│ │ └───────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└───────────────────────────────────────────────────────┘平滑重启机制
生产环境中,Gateway 需要支持配置更新和代码升级而不中断服务:
# 发送 SIGHUP 信号触发平滑重启
kill -HUP $(cat ~/.hermes/gateway.pid)
# 平滑重启流程:
# 1. 主进程收到 SIGHUP
# 2. 停止接收新消息(但不断开现有连接)
# 3. 等待正在处理的消息完成(超时 30s)
# 4. 重新加载配置
# 5. 启动新的 Worker 进程
# 6. 将流量切换到新 Worker
# 7. 优雅关闭旧 Worker
# 8. 恢复接收新消息配置文件详解
# ~/.config/hermes/gateway.yaml
gateway:
# 监听配置
host: "0.0.0.0"
port: 8080
# 日志配置
log:
level: "info" # debug | info | warn | error
format: "json" # text | json
output: "stdout" # stdout | file
file_path: "/var/log/hermes/gateway.log"
# 会话配置
session:
timeout: 1800 # 会话超时时间(秒),30 分钟
max_concurrent: 50 # 最大并发会话数
cleanup_interval: 300 # 清理过期会话的间隔(秒)
# 速率限制
rate_limit:
global_rps: 10 # 全局每秒请求数
per_user_rps: 2 # 每用户每秒请求数
burst: 5 # 突发请求数
# Worker 配置
workers:
min: 2
max: 8
idle_timeout: 300 # 空闲 Worker 超时回收(秒)
# 健康检查
health_check:
enabled: true
endpoint: "/health"
interval: 30 # 检查间隔(秒)
# 平台适配器配置
adapters:
wechat:
enabled: true
config_file: "./adapters/wechat.yaml"
telegram:
enabled: true
config_file: "./adapters/telegram.yaml"
discord:
enabled: true
config_file: "./adapters/discord.yaml"平台适配器体系
统一适配器接口
所有平台适配器都实现同一个接口,这使得添加新平台变得非常简单:
// 平台适配器统一接口
interface PlatformAdapter {
// 适配器元信息
readonly name: string; // 平台名称: "wechat", "telegram", "discord"
readonly version: string; // 适配器版本
// 生命周期
initialize(config: AdapterConfig): Promise<void>;
connect(): Promise<void>;
disconnect(): Promise<void>;
isHealthy(): boolean;
// 消息处理
onMessage(callback: MessageHandler): void;
sendMessage(message: OutgoingMessage): Promise<void>;
editMessage(messageId: string, content: string): Promise<void>;
deleteMessage(messageId: string): Promise<void>;
// 富媒体支持
sendImage(image: ImageMessage): Promise<void>;
sendFile(file: FileMessage): Promise<void>;
sendButtons(buttons: ButtonMessage): Promise<void>;
// 群组管理
getGroupInfo(groupId: string): Promise<GroupInfo>;
getGroupMembers(groupId: string): Promise<MemberInfo[]>;
// 事件
onEvent(callback: EventHandler): void;
}
// 统一的消息模型(内部使用)
interface HermesMessage {
id: string; // 消息唯一 ID
platform: string; // 来源平台
chatId: string; // 会话 ID
userId: string; // 用户 ID
userName: string; // 用户显示名
content: string; // 消息内容
type: 'text' | 'image' | 'file' | 'voice' | 'video' | 'sticker';
timestamp: Date;
replyTo?: string; // 回复的消息 ID
mentions?: string[]; // @提及的用户
attachments?: Attachment[]; // 附件列表
}适配器加载机制
Gateway 使用插件化机制加载适配器:
# 查看可用的适配器
hermes gateway adapters list
# 输出:
# ┌──────────────────┬─────────┬──────────┬──────────┐
# │ 适配器 │ 版本 │ 状态 │ 描述 │
# ├──────────────────┼─────────┼──────────┼──────────┤
# │ wechat │ v2.3.0 │ ✅ 就绪 │ 微信个人 │
# │ telegram │ v2.1.0 │ ✅ 就绪 │ Telegram │
# │ discord │ v2.0.0 │ ✅ 就绪 │ Discord │
# │ slack │ v1.8.0 │ ⏸️ 禁用 │ Slack │
# │ feishu │ v1.5.0 │ ⏸️ 禁用 │ 飞书 │
# │ webchat │ v1.2.0 │ ⏸️ 禁用 │ 网页聊天 │
# └──────────────────┴─────────┴──────────┴──────────┘
# 启用/禁用适配器
hermes gateway adapters enable slack
hermes gateway adapters disable wechat
# 安装新适配器(社区开发)
hermes gateway adapters install @community/matrix适配器目录结构
~/.hermes/adapters/
├── wechat/
│ ├── adapter.js # 适配器实现
│ ├── config.yaml # 配置文件
│ ├── package.json # 依赖声明
│ └── README.md # 文档
├── telegram/
│ ├── adapter.js
│ ├── config.yaml
│ └── package.json
├── discord/
│ ├── adapter.js
│ ├── config.yaml
│ └── package.json
└── shared/
└── message-utils.js # 共享工具函数消息路由与会话管理
消息路由流程
当一条消息从外部平台到达时,它经历了以下处理流程:
外部消息到达
│
▼
┌───────────────┐
│ 平台适配器 │ ← 解析平台原始消息格式
│ (Adapter) │
└───────┬───────┘
│ 转换为 HermesMessage
▼
┌───────────────┐
│ 输入过滤器 │ ← 敏感词过滤、权限检查、@过滤
│ (Filter) │
└───────┬───────┘
│ 通过/拒绝
▼
┌───────────────┐
│ 消息路由器 │ ← 决定消息去向
│ (Router) │
└───────┬───────┘
│ 路由决策
▼
┌───┴──────────────────┐
▼ ▼
┌─────────┐ ┌───────────┐
│ 直回复 │ │ 转发给 │
│ 简单命令 │ │ Agent 处理│
│ /help │ │ 复杂任务 │
│ /status │ │ │
└─────────┘ └─────┬─────┘
│
▼
┌───────────────┐
│ Agent 核心 │ ← LLM 推理 + 工具执行
│ (Core Engine)│
└───────┬───────┘
│
▼
┌───────────────┐
│ 输出格式化 │ ← 适配平台格式
│ (Formatter) │
└───────┬───────┘
│
▼
发送回原平台会话管理机制
Gateway 为每个唯一的对话维护独立的上下文:
# 会话 ID 生成规则
会话ID = hash(platform + chatId + userId)
# 示例:
# 微信私聊: wechat:wxid_abc123:hermes
# 微信群聊: wechat:room_xyz789:wxid_abc123
# Telegram私聊: telegram:12345678:hermes
# Telegram群: telegram:-100987654:12345678
# Discord频道: discord:guild_123:channel_456:789012会话上下文结构
{
"session_id": "wechat_room_xyz789_wxid_abc123",
"platform": "wechat",
"chat_id": "room_xyz789",
"user_id": "wxid_abc123",
"created_at": "2025-05-22T10:30:00Z",
"last_active": "2025-05-22T11:15:00Z",
"message_count": 42,
"context": {
"messages": [
{"role": "user", "content": "帮我写一个快速排序"},
{"role": "assistant", "content": "好的,这是 Python 实现的快速排序..."},
{"role": "user", "content": "能优化一下吗?"}
],
"variables": {
"language": "python",
"task_type": "algorithm"
},
"skills_active": ["python-algorithms"]
},
"metadata": {
"user_name": "张三",
"chat_name": "技术交流群",
"is_group": true
}
}路由规则配置
你可以定义精细的路由规则来控制不同消息的处理方式:
# 路由规则配置
routing:
rules:
# 规则 1: @提及才响应(群聊默认不响应)
- name: "mention_only_in_group"
condition:
chat_type: "group"
is_mentioned: true
action: "process"
# 规则 2: 私聊直接响应
- name: "direct_chat_always"
condition:
chat_type: "private"
action: "process"
# 规则 3: 特定前缀触发
- name: "prefix_trigger"
condition:
message_prefix: "@hermes"
action: "process"
# 规则 4: 关键词过滤
- name: "keyword_filter"
condition:
contains_keywords: ["代码", "bug", "error"]
action: "process"
# 规则 5: 静默规则(不回复但记录)
- name: "silent_log"
condition:
message_prefix: "/silent"
action: "log_only"
# 规则 6: 拒绝规则
- name: "reject_unsafe"
condition:
contains_keywords: ["删库", "rm -rf", "格式化"]
action: "reject"
reject_message: "⚠️ 检测到危险操作关键词,已拒绝执行"
# 默认行为
default: "ignore" # ignore | process | log_only跨平台消息转换
输入消息标准化
不同平台的消息格式差异很大,适配器负责将它们统一:
原始消息(平台格式) Hermes 统一消息
───────────────── ─────────────────
微信: {
FromUserName: "wxid_abc" platform: "wechat"
Content: "你好" chatId: "wxid_abc"
MsgType: 1 userId: "wxid_abc"
content: "你好"
Telegram: type: "text"
chat: { id: 12345 } }
from: { id: 67890 }
text: "你好"
Discord:
channel_id: "456"
author: { id: "789", name: "Alice" }
content: "你好"输出消息适配
Agent 的回复也需要根据平台特性进行格式化:
// Agent 生成的统一回复
const agentReply = {
content: "代码已修复。修改了 3 个文件:\n1. utils.py - 修复了排序逻辑\n2. test.py - 添加了单元测试\n3. README.md - 更新了文档",
attachments: [
{ type: "code", language: "python", content: "..." },
{ type: "image", url: "..." }
]
};
// 各平台适配后的输出
const platformOutputs = {
wechat: {
// 微信不支持 Markdown,需要转换
content: "代码已修复。修改了 3 个文件:\n1. utils.py - 修复了排序逻辑\n2. test.py - 添加了单元测试\n3. README.md - 更新了文档\n\n[代码片段已转为图片发送]",
attachments: [
{ type: "image" } // 代码转为图片
]
},
telegram: {
// Telegram 支持 Markdown
content: "**代码已修复**。修改了 3 个文件:\n1. `utils.py` - 修复了排序逻辑\n2. `test.py` - 添加了单元测试\n3. `README.md` - 更新了文档",
parse_mode: "MarkdownV2",
attachments: [
{ type: "document", filename: "utils.py", content: "..." }
]
},
discord: {
// Discord 支持 Markdown + 代码块
content: "**代码已修复**。修改了 3 个文件:\n1. `utils.py` - 修复了排序逻辑\n2. `test.py` - 添加了单元测试\n3. `README.md` - 更新了文档",
attachments: [
{ type: "file", name: "utils.py", content: Buffer.from("...") }
],
embeds: [{
title: "代码变更摘要",
color: 0x00ff00,
fields: [
{ name: "修改文件", value: "3", inline: true },
{ name: "新增行", value: "+42", inline: true },
{ name: "删除行", value: "-15", inline: true }
]
}]
}
};平台特性对照表
| 特性 | 微信 | Telegram | Discord |
|---|---|---|---|
| 文本格式化 | ❌ 纯文本 | ✅ Markdown | ✅ Markdown |
| 代码块 | ❌ 转图片 | ✅ 支持 | ✅ 高亮 |
| 内联按钮 | ❌ | ✅ | ✅ |
| 文件发送 | ✅ | ✅ (20MB) | ✅ (25MB) |
| 图片发送 | ✅ | ✅ | ✅ |
| 语音消息 | ✅ | ✅ | ✅ |
| 视频消息 | ✅ | ✅ | ✅ |
| 消息编辑 | ❌ | ✅ | ✅ |
| 消息撤回 | ✅ (2min) | ✅ | ✅ |
| 回复引用 | ✅ | ✅ | ✅ |
| Emoji | ✅ | ✅ | ✅ 自定义 |
| @提及 | ❌ | ✅ | ✅ |
| 群管理 API | 部分 | ✅ | ✅ |
部署与运维
Docker 部署
# 使用 Docker 运行 Gateway
docker run -d \
--name hermes-gateway \
-p 8080:8080 \
-v ~/.config/hermes:/root/.config/hermes \
-v ~/.hermes/adapters:/root/.hermes/adapters \
-v ~/.hermes/sessions:/root/.hermes/sessions \
-e WECHAT_TOKEN=${WECHAT_TOKEN} \
-e TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN} \
-e DISCORD_BOT_TOKEN=${DISCORD_BOT_TOKEN} \
-e OPENROUTER_API_KEY=${OPENROUTER_API_KEY} \
nousresearch/hermes-agent:latest \
gateway start --daemon
# 查看日志
docker logs -f hermes-gateway
# 健康检查
curl http://localhost:8080/health
# {"status":"healthy","uptime":"3d 14h","sessions":12,"platforms":3}Docker Compose 编排
# docker-compose.yml
version: '3.8'
services:
hermes-gateway:
image: nousresearch/hermes-agent:latest
container_name: hermes-gateway
restart: unless-stopped
ports:
- "8080:8080"
volumes:
- ./config:/root/.config/hermes
- ./adapters:/root/.hermes/adapters
- ./sessions:/root/.hermes/sessions
- ./logs:/var/log/hermes
environment:
- OPENROUTER_API_KEY=${OPENROUTER_API_KEY}
- WECHAT_TOKEN=${WECHAT_TOKEN}
- TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}
- DISCORD_BOT_TOKEN=${DISCORD_BOT_TOKEN}
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
deploy:
resources:
limits:
memory: 1G
cpus: '2'
hermes-monitor:
image: grafana/grafana:latest
container_name: hermes-grafana
ports:
- "3000:3000"
volumes:
- ./grafana:/var/lib/grafana监控与告警
# 查看实时指标
hermes gateway metrics
# 输出:
# ═══════════════════════════════════════════════
# Gateway Metrics (实时)
# ═══════════════════════════════════════════════
#
# 进程状态:
# Uptime: 3d 14h 22m
# PID: 12345
# Memory: 256MB / 1024MB
# CPU: 2.3%
# Goroutines: 48
#
# 消息统计 (最近 1 小时):
# 接收消息: 1,234
# 处理消息: 1,198
# 过滤消息: 36
# 错误消息: 2
# 平均响应时间: 3.2s
# P99 响应时间: 12.1s
#
# 平台统计:
# WeChat: 567 消息 (46%)
# Telegram: 445 消息 (36%)
# Discord: 222 消息 (18%)
#
# 会话统计:
# 活跃会话: 12
# 总会话数: 89
# 最长会话: 2h 15m
# 平均消息/会话: 13.5
#
# 错误统计:
# 平台断连: 1 次 (WeChat, 已自动重连)
# API 限流: 3 次 (Telegram, 已退避)
# 处理超时: 0 次日志分析
# 查看 Gateway 日志
hermes gateway logs --follow
# 按级别过滤
hermes gateway logs --level error
# 按平台过滤
hermes gateway logs --platform wechat
# 搜索特定用户
hermes gateway logs --user wxid_abc123
# 导出日志
hermes gateway logs --export ./gateway-logs-2025-05-22.json总结与下篇预告
本文全面介绍了 Hermes Agent 的 Gateway 多平台架构,涵盖了从 CLI 到全平台的架构演进、Gateway 进程模型、平台适配器体系、消息路由机制以及部署运维实践。
核心要点回顾:
- Gateway 是桥梁 —— 将 Agent 的能力从 CLI 扩展到微信、Telegram、Discord 等平台
- 适配器是插件 —— 统一接口设计,新增平台只需实现一个适配器
- 路由器是大脑 —— 根据规则决定消息如何处理,支持精细控制
- 会话是上下文 —— 为每个对话维护独立的历史和状态
- 转换是艺术 —— 不同平台格式各异,需要智能适配
Gateway 让 Hermes Agent 真正实现了 "无处不在" —— 无论你在哪个平台,Agent 都能以你习惯的方式为你服务。
下篇预告
微信 Telegram Discord 平台集成实战
在下一篇中,我们将深入三大平台的具体集成细节:
- 🔐 平台认证配置:各平台的 Token 获取、Bot 创建、权限设置
- ⚙️ Intent 配置详解:如何精确控制 Agent 在什么场景下响应
- 🐛 常见问题排查:连接失败、消息丢失、权限不足等问题的解决方案
- 📨 消息收发实战:从简单对话到富媒体交互的完整示例
敬请期待!
本系列文章持续更新中,欢迎 star 和分享。如有问题,请在评论区留言。