在前面的文章中,我们已经深入探讨了 Hermes Agent 的核心组件:Provider 配置、工具集(Toolsets)、技能系统(Skills)以及定时任务。这些都是 Agent 的"内功"——让 Agent 具备思考、学习和执行的能力。

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 工具:

bash
# 最基本的交互方式
hermes run "帮我重构这个模块"
hermes run "审查一下最近的 PR"

# 交互式模式
hermes chat

CLI 模式在开发场景中非常好用,但在以下场景中遇到了瓶颈:

  1. 实时性需求:CLI 是请求-响应模式,无法主动推送消息
  2. 多用户场景:CLI 通常是单用户,难以同时服务多个用户
  3. 平台生态:团队已经在微信/Telegram/Discord 上协作,切换到终端有摩擦
  4. 持续运行:CLI 随终端关闭而退出,无法做 24/7 服务
  5. 富媒体支持:CLI 只能处理文本,无法发送图片、文件、按钮等

Gateway 架构的引入

为了解决这些问题,Hermes 引入了 Gateway 组件。Gateway 是一个长期运行的后台进程,充当 Agent 与外部平台之间的中介层。

text
┌─────────────────────────────────────────────────────────────────┐
│                    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 的长期运行进程:

bash
# 启动 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

进程架构图

text
┌───────────────────────────────────────────────────────┐
│                   Gateway 进程                          │
│                                                       │
│  ┌─────────────────────────────────────────────────┐  │
│  │                 主进程 (Master)                   │  │
│  │                                                 │  │
│  │  ┌────────────┐  ┌────────────┐  ┌───────────┐ │  │
│  │  │ 信号处理器  │  │ 配置监听器  │  │ 健康检查   │ │  │
│  │  │ SIGTERM/   │  │ 热重载      │  │ /health    │ │  │
│  │  │ SIGHUP     │  │ SIGHUP      │  │ /ready     │ │  │
│  │  └────────────┘  └────────────┘  └───────────┘ │  │
│  │                                                 │  │
│  │  ┌───────────────────────────────────────────┐ │  │
│  │  │            子进程池 (Worker Pool)          │ │  │
│  │  │                                           │ │  │
│  │  │  ┌─────────┐ ┌─────────┐ ┌─────────┐    │ │  │
│  │  │  │Worker 1 │ │Worker 2 │ │Worker 3 │ ...│ │  │
│  │  │  │(wechat) │ │(telegram│ │(discord)│    │ │  │
│  │  │  │         │ │         │ │         │    │ │  │
│  │  │  └─────────┘ └─────────┘ └─────────┘    │ │  │
│  │  └───────────────────────────────────────────┘ │  │
│  └─────────────────────────────────────────────────┘  │
│                                                       │
└───────────────────────────────────────────────────────┘

平滑重启机制

生产环境中,Gateway 需要支持配置更新和代码升级而不中断服务:

bash
# 发送 SIGHUP 信号触发平滑重启
kill -HUP $(cat ~/.hermes/gateway.pid)

# 平滑重启流程:
# 1. 主进程收到 SIGHUP
# 2. 停止接收新消息(但不断开现有连接)
# 3. 等待正在处理的消息完成(超时 30s)
# 4. 重新加载配置
# 5. 启动新的 Worker 进程
# 6. 将流量切换到新 Worker
# 7. 优雅关闭旧 Worker
# 8. 恢复接收新消息

配置文件详解

yaml
# ~/.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"

平台适配器体系

统一适配器接口

所有平台适配器都实现同一个接口,这使得添加新平台变得非常简单:

typescript
// 平台适配器统一接口
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 使用插件化机制加载适配器:

bash
# 查看可用的适配器
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

适配器目录结构

text
~/.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    # 共享工具函数

消息路由与会话管理

消息路由流程

当一条消息从外部平台到达时,它经历了以下处理流程:

text
外部消息到达
    │
    ▼
┌───────────────┐
│  平台适配器    │  ← 解析平台原始消息格式
│  (Adapter)    │
└───────┬───────┘
        │ 转换为 HermesMessage
        ▼
┌───────────────┐
│  输入过滤器    │  ← 敏感词过滤、权限检查、@过滤
│  (Filter)     │
└───────┬───────┘
        │ 通过/拒绝
        ▼
┌───────────────┐
│  消息路由器    │  ← 决定消息去向
│  (Router)     │
└───────┬───────┘
        │ 路由决策
        ▼
    ┌───┴──────────────────┐
    ▼                      ▼
┌─────────┐          ┌───────────┐
│ 直回复   │          │ 转发给    │
│ 简单命令 │          │ Agent 处理│
│ /help    │          │ 复杂任务  │
│ /status  │          │          │
└─────────┘          └─────┬─────┘
                           │
                           ▼
                    ┌───────────────┐
                    │  Agent 核心   │  ← LLM 推理 + 工具执行
                    │  (Core Engine)│
                    └───────┬───────┘
                            │
                            ▼
                    ┌───────────────┐
                    │  输出格式化   │  ← 适配平台格式
                    │  (Formatter)  │
                    └───────┬───────┘
                            │
                            ▼
                      发送回原平台

会话管理机制

Gateway 为每个唯一的对话维护独立的上下文:

yaml
# 会话 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

会话上下文结构

json
{
  "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
  }
}

路由规则配置

你可以定义精细的路由规则来控制不同消息的处理方式:

yaml
# 路由规则配置
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

跨平台消息转换

输入消息标准化

不同平台的消息格式差异很大,适配器负责将它们统一:

text
原始消息(平台格式)              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 的回复也需要根据平台特性进行格式化:

javascript
// 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 部署

bash
# 使用 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 编排

yaml
# 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

监控与告警

bash
# 查看实时指标
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 次

日志分析

bash
# 查看 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 进程模型、平台适配器体系、消息路由机制以及部署运维实践。

核心要点回顾:

  1. Gateway 是桥梁 —— 将 Agent 的能力从 CLI 扩展到微信、Telegram、Discord 等平台
  2. 适配器是插件 —— 统一接口设计,新增平台只需实现一个适配器
  3. 路由器是大脑 —— 根据规则决定消息如何处理,支持精细控制
  4. 会话是上下文 —— 为每个对话维护独立的历史和状态
  5. 转换是艺术 —— 不同平台格式各异,需要智能适配

Gateway 让 Hermes Agent 真正实现了 "无处不在" —— 无论你在哪个平台,Agent 都能以你习惯的方式为你服务。

下篇预告

微信 Telegram Discord 平台集成实战

在下一篇中,我们将深入三大平台的具体集成细节:

  • 🔐 平台认证配置:各平台的 Token 获取、Bot 创建、权限设置
  • ⚙️ Intent 配置详解:如何精确控制 Agent 在什么场景下响应
  • 🐛 常见问题排查:连接失败、消息丢失、权限不足等问题的解决方案
  • 📨 消息收发实战:从简单对话到富媒体交互的完整示例

敬请期待!


本系列文章持续更新中,欢迎 star 和分享。如有问题,请在评论区留言。