본문으로 건너뛰기

게이트웨이 내부

메시징 게이트웨이는 통합 아키텍처를 통해 Hermes를 20개 이상의 외부 메시징 플랫폼에 연결하는 장기 실행 프로세스입니다.

주요 파일

파일목적
gateway/run.pyGatewayRunner — 메인 루프, 슬래시 명령, 메시지 발송(대형 파일, 현재 LOC는 git 확인)
gateway/session.pySessionStore — 대화 지속성 및 세션 키 구성
gateway/delivery.py대상 플랫폼/채널로 아웃바운드 메시지 전달
gateway/pairing.py사용자 인증을 위한 DM 페어링 흐름
gateway/channel_directory.py크론 전달을 위해 채팅 ID를 사람이 읽을 수 있는 이름으로 매핑합니다.
gateway/hooks.py후크 검색, 로드 및 수명 주기 이벤트 전달
gateway/mirror.pysend_message에 대한 교차 세션 메시지 미러링
gateway/status.py프로필 범위 게이트웨이 인스턴스에 대한 토큰 잠금 관리
gateway/builtin_hooks/항상 등록되는 후크에 대한 확장 지점(제공되지 않음)
gateway/platforms/플랫폼 어댑터(메시징 플랫폼당 하나)

아키텍처 개요

┌─────────────────────────────────────────────────┐
│ GatewayRunner │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Telegram │ │ Discord │ │ Slack │ │
│ │ Adapter │ │ Adapter │ │ Adapter │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ └─────────────┼─────────────┘ │
│ ▼ │
│ _handle_message() │
│ │ │
│ ┌───────────┼───────────┐ │
│ ▼ ▼ ▼ │
│ Slash command AIAgent Queue/BG │
│ dispatch creation sessions │
│ │ │
│ ▼ │
│ SessionStore │
│ (SQLite persistence) │
└───────┴─────────────┴─────────────┴─────────────┘

메시지 흐름

어떤 플랫폼에서든 메시지가 도착하면:

  1. 플랫폼 어댑터는 원시 이벤트를 수신하고 이를 MessageEvent으로 정규화합니다.
  2. 기본 어댑터는 활성 세션 가드를 확인합니다.
    • 이 세션에 대해 에이전트가 실행 중인 경우 → 메시지 큐에 인터럽트 이벤트를 설정합니다.
    • /approve, /deny, /stop인 경우 → 우회 가드(인라인으로 파견)
  3. **GatewayRunner._handle_message()**는 이벤트를 수신합니다.
    • _session_key_for_source()(형식: agent:main:{platform}:{chat_type}:{chat_id})을 통해 세션 키 확인
    • 승인 확인(아래 승인 참조)
    • 슬래시 명령인지 확인 → 명령 핸들러로 전달
    • 에이전트가 이미 실행 중인지 확인 → /stop, /status과 같은 명령을 차단합니다.
    • 그렇지 않은 경우 → AIAgent 인스턴스를 생성하고 대화를 실행하세요.
  4. 응답은 플랫폼 어댑터를 통해 다시 전송됩니다.

세션 키 형식

세션 키는 전체 라우팅 컨텍스트를 인코딩합니다.

agent:main:{platform}:{chat_type}:{chat_id}

예: agent:main:telegram:private:123456789

스레드 인식 플랫폼(Telegram 포럼 주제, Discord 스레드, Slack 스레드)은 chat_id 부분에 스레드 ID를 포함할 수 있습니다. 세션 키를 수동으로 구성하지 마세요 — 항상 gateway/session.pybuild_session_key()을 사용하세요.

2단계 메시지 가드

에이전트가 활발하게 실행 중일 때 들어오는 메시지는 두 개의 순차적 가드를 통과합니다.

  1. 레벨 1 — 베이스 어댑터 (gateway/platforms/base.py): _active_sessions을 확인합니다. 세션이 활성화된 경우 _pending_messages에 메시지를 대기열에 추가하고 인터럽트 이벤트를 설정합니다. 이는 메시지가 게이트웨이 실행자에 도달하기 전에 메시지를 포착합니다.

  2. 수준 2 - 게이트웨이 실행자 (gateway/run.py): _running_agents을 확인합니다. 특정 명령(/stop, /new, /queue, /status, /approve, /deny)을 가로채서 적절하게 라우팅합니다. 그 밖의 모든 것은 running_agent.interrupt()을 트리거합니다.

에이전트가 차단된 동안 실행자에게 도달해야 하는 명령(예: /approve)은 await self._message_handler(event)을 통해 인라인으로 전달됩니다. 이는 경쟁 조건을 피하기 위해 백그라운드 작업 시스템을 우회합니다.

승인

게이트웨이는 다음 순서로 평가되는 다중 계층 인증 확인을 사용합니다.

  1. 플랫폼별 모든 허용 플래그(예: TELEGRAM_ALLOW_ALL_USERS) — 설정하면 해당 플랫폼의 모든 사용자가 승인됩니다.
  2. 플랫폼 허용 목록 (예: TELEGRAM_ALLOWED_USERS) — 쉼표로 구분된 사용자 ID
  3. DM 페어링 — 인증된 사용자는 페어링 코드를 통해 새로운 사용자를 페어링할 수 있습니다.
  4. 전역 허용(GATEWAY_ALLOW_ALL_USERS) - 설정하면 모든 플랫폼의 모든 사용자가 승인됩니다.
  5. 기본값: 거부 — 승인되지 않은 사용자는 거부됩니다.

DM 페어링 흐름

Admin: /pair
Gateway: "Pairing code: ABC123. Share with the user."
New user: ABC123
Gateway: "Paired! You're now authorized."

페어링 상태는 gateway/pairing.py에 유지되며 다시 시작해도 유지됩니다.

슬래시 명령 파견

게이트웨이의 모든 슬래시 명령은 동일한 해결 파이프라인을 통해 흐릅니다.

  1. hermes_cli/commands.pyresolve_command()은 입력을 표준 이름에 매핑합니다(별칭, 접두사 일치 처리).
  2. 표준 이름은 GATEWAY_KNOWN_COMMANDS에 대해 확인됩니다.
  3. _handle_message()의 핸들러는 표준 이름을 기반으로 디스패치합니다.
  4. 일부 명령은 구성에서 제어됩니다(gateway_config_gateCommandDef).

러닝에이전트 가드

에이전트가 처리하는 동안 실행하면 안 되는 명령은 조기에 거부됩니다.

if _quick_key in self._running_agents:
if canonical == "model":
return "⏳ Agent is running — wait for it to finish or /stop first."

우회 명령(/stop, /new, /approve, /deny, /queue, /status)에는 특수 처리 기능이 있습니다.

구성 소스

게이트웨이는 여러 소스에서 구성을 읽습니다.

소스그것이 제공하는 것
~/.hermes/.envAPI 키, 봇 토큰, 플랫폼 자격 증명
~/.hermes/config.yaml모델 설정, 도구 구성, 표시 옵션
환경변수위의 항목을 재정의합니다.

CLI(하드코딩된 기본값으로 load_cli_config() 사용)와 달리 게이트웨이는 YAML 로더를 통해 직접 config.yaml을 읽습니다. 이는 CLI의 기본 dict에는 있지만 사용자의 구성 파일에는 없는 구성 키가 CLI와 게이트웨이 간에 다르게 작동할 수 있음을 의미합니다.

플랫폼 어댑터

각 메시징 플랫폼에는 gateway/platforms/에 어댑터가 있습니다.

gateway/platforms/
├── base.py # BaseAdapter — shared logic for all platforms
├── telegram.py # Telegram Bot API (long polling or webhook)
├── discord.py # Discord bot via discord.py
├── slack.py # Slack Socket Mode
├── whatsapp.py # WhatsApp Business Cloud API
├── signal.py # Signal via signal-cli REST API
├── matrix.py # Matrix via mautrix (optional )
├── mattermost.py # Mattermost WebSocket API
├── email.py # Email via IMAP/SMTP
├── sms.py # SMS via Twilio
├── dingtalk.py # DingTalk WebSocket
├── feishu.py # Feishu/Lark WebSocket or webhook
├── wecom.py # WeCom (WeChat Work) callback
├── weixin.py # Weixin (personal WeChat) via iLink Bot API
├── bluebubbles.py # Apple iMessage via BlueBubbles macOS server
├── qqbot/ # QQ Bot (Tencent QQ) via Official API v2 (sub-package: adapter.py, crypto.py, keyboards.py, …)
├── yuanbao.py # Yuanbao (Tencent) DM/group adapter
├── feishu_comment.py # Feishu document/drive comment-reply handler
├── msgraph_webhook.py # Microsoft Graph change-notification webhook (Teams, Outlook, etc.)
├── webhook.py # Inbound/outbound webhook adapter
├── api_server.py # REST API server adapter
└── homeassistant.py # Home Assistant conversation integration

어댑터는 공통 인터페이스를 구현합니다.

  • connect() / disconnect() — 수명 주기 관리
  • send_message() — 아웃바운드 메시지 전달
  • on_message() — 인바운드 메시지 정규화 → MessageEvent

토큰 잠금

고유한 자격 증명으로 연결하는 어댑터는 connect()acquire_scoped_lock()disconnect()release_scoped_lock()을 호출합니다. 이렇게 하면 두 프로필이 동일한 봇 토큰을 동시에 사용하는 것을 방지할 수 있습니다.

배송 경로

발송 배송(gateway/delivery.py) 처리:

  • 직접 답장 — 원래 채팅으로 응답을 다시 보냅니다.
  • 홈 채널 전달 — 크론 작업 출력 및 백그라운드 결과를 구성된 홈 채널로 라우팅합니다.
  • 명시적 대상 전달send_message 도구 지정 telegram:-1001234567890
  • 교차 플랫폼 전달 — 원래 메시지와 다른 플랫폼으로 전달

크론 작업 전달은 게이트웨이 세션 기록에 미러링되지 않으며 자체 크론 세션에만 존재합니다. 이는 메시지 교체 위반을 방지하기 위한 의도적인 디자인 선택입니다.

후크

게이트웨이 후크는 수명 주기 이벤트에 응답하는 Python 모듈입니다.

게이트웨이 후크 이벤트

이벤트해고되었을 때
gateway:startup게이트웨이 프로세스가 시작됩니다.
session:start새로운 대화 세션이 시작됩니다
session:end세션이 완료되거나 시간 초과됨
session:reset사용자가 /new을(를) 사용하여 세션을 재설정합니다.
agent:start에이전트가 메시지 처리를 시작합니다.
agent:step에이전트가 하나의 도구 호출 반복을 완료합니다.
agent:end에이전트가 완료하고 응답을 반환합니다.
command:*모든 슬래시 명령이 실행됩니다.

후크는 gateway/builtin_hooks/(확장 지점 — 현재 배송된 배포판에 비어 있음, _register_builtin_hooks()은 무작동 스텁) 및 ~/.hermes/hooks/(사용자 설치)에서 발견됩니다. 각 후크는 HOOK.yaml 매니페스트와 handler.py이 있는 디렉터리입니다.

메모리 제공자 통합

메모리 제공자 플러그인(예: Honcho)이 활성화된 경우:

  1. 게이트웨이는 세션 ID를 사용하여 메시지당 AIAgent을 생성합니다.
  2. MemoryManager은 세션 컨텍스트로 제공자를 초기화합니다.
  3. 제공자 도구(예: honcho_profile, viking_search)는 다음을 통해 라우팅됩니다.
AIAgent._invoke_tool()
→ self._memory_manager.handle_tool_call(name, args)
→ provider.handle_tool_call(name, args)
  1. 세션 종료/재설정 시 정리 및 최종 데이터 플러시를 위해 on_session_end()이 실행됩니다.

메모리 플러시 수명주기

세션이 재설정, 재개 또는 만료되는 경우:

  1. 내장 메모리가 디스크로 플러시됩니다.
  2. 메모리 제공자의 on_session_end() 후크가 발생합니다.
  3. 임시 AIAgent은 메모리 전용 대화 차례를 실행합니다.
  4. 그러면 컨텍스트가 삭제되거나 보관됩니다.

백그라운드 유지 관리

게이트웨이는 메시지 처리와 함께 정기적인 유지 관리를 실행합니다.

  • 크론 틱 — 작업 일정을 확인하고 예정된 작업을 실행합니다.
  • 세션 만료 — 시간 초과 후 버려진 세션을 정리합니다.
  • 메모리 플러시 — 세션이 만료되기 전에 사전에 메모리를 플러시합니다.
  • 캐시 새로 고침 — 모델 목록 및 제공자 상태를 새로 고칩니다.

프로세스 관리

게이트웨이는 다음을 통해 관리되는 장기 프로세스로 실행됩니다.

  • hermes gateway start / hermes gateway stop — 수동 제어
  • systemctl(Linux) 또는 launchctl(macOS) — 서비스 관리
  • ~/.hermes/gateway.pid의 PID 파일 — 프로필 범위 프로세스 추적

프로필 범위 및 전역: start_gateway()은 프로필 범위 PID 파일을 사용합니다. hermes gateway stop은 현재 프로필의 게이트웨이만 중지합니다. hermes gateway stop --all는 글로벌 ps aux 스캐닝을 사용하여 모든 게이트웨이 프로세스를 종료합니다(업데이트 중에 사용됨).