Chuyển tới nội dung chính

Nội bộ cổng

Cổng nhắn tin là một quy trình dài hạn kết nối Hermes với hơn 14 nền tảng nhắn tin bên ngoài thông qua một kiến ​​trúc thống nhất.

Tệp chính

Tập tinMục đích
gateway/run.pyGatewayRunner — vòng lặp chính, lệnh gạch chéo, gửi tin nhắn (~7.500 dòng)
gateway/session.pySessionStore — tính bền vững của cuộc hội thoại và xây dựng khóa phiên
gateway/delivery.pyGửi tin nhắn đi tới các nền tảng/kênh mục tiêu
gateway/pairing.pyLuồng ghép nối DM để ủy quyền người dùng
gateway/channel_directory.pyÁnh xạ ID trò chuyện thành tên mà con người có thể đọc được để phân phối định kỳ
gateway/hooks.pyKhám phá móc, tải và gửi sự kiện trong vòng đời
gateway/mirror.pyPhản chiếu tin nhắn giữa các phiên cho send_message
gateway/status.pyQuản lý khóa mã thông báo cho các phiên bản cổng có phạm vi hồ sơ
gateway/buildin_hooks/Các hook luôn được đăng ký (ví dụ: hook nhắc hệ thống BOOT.md)
cổng/nền tảng/Bộ điều hợp nền tảng (một bộ điều hợp cho mỗi nền tảng nhắn tin)

Tổng quan về kiến ​​trúc

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

Luồng tin nhắn

Khi có tin nhắn đến từ bất kỳ nền tảng nào:

  1. Bộ điều hợp nền tảng nhận sự kiện thô, chuẩn hóa nó thành MessageEvent
  2. Bộ điều hợp cơ sở kiểm tra bảo vệ phiên hoạt động:
  • Nếu tác nhân đang chạy trong phiên này → thông báo xếp hàng, hãy đặt sự kiện ngắt
  • Nếu /approve, /deny, /stop → bỏ qua bảo vệ (gửi nội tuyến)
  1. GatewayRunner._handle_message() nhận được sự kiện:
  • Giải quyết session key thông qua _session_key_for_source() (định dạng: agent:main:{platform}:{chat_type}:{chat_id})
  • Kiểm tra ủy quyền (xem Ủy quyền bên dưới)
  • Kiểm tra xem đó có phải là lệnh gạch chéo không → gửi đến trình xử lý lệnh
  • Kiểm tra xem tác nhân đã chạy chưa → chặn các lệnh như /stop, /status
  • Ngược lại → tạo AIAgent instance và chạy hội thoại
  1. Phản hồi được gửi lại thông qua bộ chuyển đổi nền tảng

Định dạng khóa phiên

Khóa phiên mã hóa bối cảnh định tuyến đầy đủ:

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

Ví dụ: đại lý:chính:telegram:riêng tư:123456789

Các nền tảng nhận biết luồng (chủ đề diễn đàn Telegram, luồng Discord, luồng Slack) có thể bao gồm ID luồng trong phần chat_id. Không bao giờ tạo khóa phiên theo cách thủ công — luôn sử dụng build_session_key() từ gateway/session.py.

Bảo vệ tin nhắn hai cấp

Khi một tác nhân đang hoạt động tích cực, các tin nhắn đến sẽ đi qua hai bộ bảo vệ tuần tự:

  1. Cấp 1 — Bộ điều hợp cơ sở (gateway/platforms/base.py): Kiểm tra _active_sessions. Nếu phiên đang hoạt động, hãy xếp hàng tin nhắn trong _pending_messages và đặt sự kiện gián đoạn. Điều này sẽ bắt các tin nhắn trước khi chúng đến được người chạy cổng.

  2. Cấp 2 — Người chạy cổng (gateway/run.py): Kiểm tra _running_agents. Chặn các lệnh cụ thể (/stop, /new, /queue, /status, /approve, /deny) và định tuyến chúng một cách thích hợp. Mọi thứ khác đều kích hoạt running_agent.interrupt().

Các lệnh phải đến được người chạy trong khi tác nhân bị chặn (như /approve) được gửi nội tuyến thông qua await self._message_handler(event) — chúng bỏ qua hệ thống tác vụ nền để tránh các điều kiện chạy đua.

Ủy quyền

Cổng sử dụng kiểm tra ủy quyền nhiều lớp, được đánh giá theo thứ tự:

  1. Cờ cho phép tất cả trên mỗi nền tảng (ví dụ: TELEGRAM_ALLOW_ALL_USERS) — nếu được đặt, tất cả người dùng trên nền tảng đó đều được ủy quyền
  2. Danh sách cho phép nền tảng (ví dụ: TELEGRAM_ALLOWED_USERS) — ID người dùng được phân tách bằng dấu phẩy
  3. Ghép nối DM — người dùng đã xác thực có thể ghép nối người dùng mới thông qua mã ghép nối
  4. Cho phép tất cả toàn cầu (GATEWAY_ALLOW_ALL_USERS) — nếu được đặt, tất cả người dùng trên tất cả các nền tảng đều được ủy quyền
  5. Mặc định: từ chối — người dùng trái phép sẽ bị từ chối

Luồng ghép nối DM

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

Trạng thái ghép nối vẫn được duy trì trong gateway/pairing.py và vẫn tiếp tục khởi động lại.

Gửi lệnh gạch chéo

Tất cả các lệnh gạch chéo trong cổng đều đi qua cùng một đường dẫn phân giải:

  1. resolve_command() từ hermes_cli/commands.py ánh xạ đầu vào thành tên chuẩn (xử lý bí danh, khớp tiền tố)
  2. Tên chuẩn được kiểm tra dựa trên GATEWAY_KNOWN_COMMANDS
  3. Trình xử lý trong các công văn _handle_message() dựa trên tên chuẩn
  4. Một số lệnh được kiểm soát trên config (gateway_config_gate trên CommandDef)

Người bảo vệ đặc vụ đang chạy

Các lệnh KHÔNG được thực thi trong khi tác nhân đang xử lý sẽ bị từ chối sớm:

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

Các lệnh bỏ qua (/stop, /new, /approve, /deny, /queue, /status) có cách xử lý đặc biệt.

Nguồn cấu hình

Cổng đọc cấu hình từ nhiều nguồn:

NguồnNó cung cấp những gì
~/.hermes/.envKhóa API, mã thông báo bot, thông tin xác thực nền tảng
~/.hermes/config.yamlCài đặt mô hình, cấu hình công cụ, tùy chọn hiển thị
Biến môi trườngGhi đè bất kỳ mục nào ở trên

Không giống như CLI (sử dụng load_cli_config() với các giá trị mặc định được mã hóa cứng), cổng đọc config.yaml trực tiếp thông qua trình tải YAML. Điều này có nghĩa là các khóa cấu hình tồn tại trong lệnh mặc định của CLI nhưng không có trong tệp cấu hình của người dùng có thể hoạt động khác nhau giữa CLI và cổng.

Bộ điều hợp nền tảng

Mỗi nền tảng nhắn tin có một bộ điều hợp trong 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 matrix-nio (optional E2EE)
├── 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
├── webhook.py # Inbound/outbound webhook adapter
├── api_server.py # REST API server adapter
└── homeassistant.py # Home Assistant conversation integration

Bộ điều hợp thực hiện một giao diện chung:

  • connect() / disconnect() — quản lý vòng đời
  • send_message() — gửi tin nhắn đi
  • on_message() — chuẩn hóa tin nhắn gửi đến → MessageEvent

Khóa mã thông báo

Bộ điều hợp kết nối bằng thông tin xác thực duy nhất gọi acquire_scoped_lock() trong connect()release_scoped_lock() trong disconnect(). Điều này ngăn hai hồ sơ sử dụng cùng một mã thông báo bot.

Đường dẫn giao hàng

Quá trình gửi đi (gateway/delivery.py) xử lý:

  • Trả lời trực tiếp — gửi phản hồi trở lại cuộc trò chuyện ban đầu
  • Phân phối kênh gia đình — định tuyến kết quả công việc định kỳ và kết quả nền tới kênh chính đã được định cấu hình
  • Gửi mục tiêu rõ ràng — công cụ send_message chỉ định telegram:-1001234567890
  • Phân phối đa nền tảng — phân phối tới một nền tảng khác với tin nhắn ban đầu

Việc phân phối công việc định kỳ KHÔNG được phản ánh vào lịch sử phiên cổng - chúng chỉ tồn tại trong phiên định kỳ của riêng chúng. Đây là sự lựa chọn thiết kế có chủ ý để tránh vi phạm việc luân phiên thông báo.

Móc

Móc cổng là các mô-đun Python phản hồi các sự kiện trong vòng đời:

Sự kiện móc cổng

Sự kiệnKhi bị sa thải
cổng:khởi độngQuá trình cổng bắt đầu
phiên:bắt đầuPhiên trò chuyện mới bắt đầu
phiên:kết thúcPhiên hoàn thành hoặc hết thời gian
phiên:đặt lạiNgười dùng đặt lại phiên bằng /new
đại lý:bắt đầuĐại lý bắt đầu xử lý tin nhắn
đại lý:bướcĐại lý hoàn thành một lần lặp gọi công cụ
đại lý:cuốiĐại lý kết thúc và trả lời phản hồi
lệnh:*Bất kỳ lệnh gạch chéo nào đều được thực thi

Móc được phát hiện từ gateway/buildin_hooks/ (luôn hoạt động) và ~/.hermes/hooks/ (do người dùng cài đặt). Mỗi hook là một thư mục có tệp kê khai HOOK.yamlhandler.py.

Tích hợp nhà cung cấp bộ nhớ

Khi plugin nhà cung cấp bộ nhớ (ví dụ: Honcho) được bật:

  1. Gateway tạo một AIAgent cho mỗi tin nhắn với ID phiên
  2. MemoryManager khởi tạo nhà cung cấp với ngữ cảnh phiên
  3. Các công cụ của nhà cung cấp (ví dụ: honcho_profile, viking_search) được định tuyến thông qua:
AIAgent._invoke_tool()
→ self._memory_manager.handle_tool_call(name, args)
→ provider.handle_tool_call(name, args)
  1. Khi kết thúc/đặt lại phiên, on_session_end() kích hoạt để dọn dẹp và xóa dữ liệu cuối cùng

Vòng đời xóa bộ nhớ

Khi một phiên được đặt lại, tiếp tục hoặc hết hạn:

  1. Bộ nhớ tích hợp được xóa vào đĩa
  2. Hook on_session_end() của nhà cung cấp bộ nhớ kích hoạt
  3. AIAgent tạm thời chạy lượt hội thoại chỉ trong bộ nhớ
  4. Ngữ cảnh sau đó sẽ bị loại bỏ hoặc lưu trữ

Bảo trì nền

Cổng này chạy bảo trì định kỳ cùng với việc xử lý tin nhắn:

  • Cron tích tắc — kiểm tra lịch trình công việc và sa thải công việc đến hạn
  • Phiên hết hạn — dọn sạch các phiên bị bỏ rơi sau khi hết thời gian chờ
  • Xóa bộ nhớ — chủ động xóa bộ nhớ trước khi hết phiên
  • Làm mới bộ đệm — làm mới danh sách mô hình và trạng thái nhà cung cấp

Quản lý quy trình

Cổng hoạt động như một quy trình lâu dài, được quản lý thông qua:

  • bắt đầu cổng hermes / dừng cổng hermes — điều khiển thủ công
  • systemctl (Linux) hoặc launchctl (macOS) — quản lý dịch vụ
  • Tệp PID tại ~/.hermes/gateway.pid — theo dõi quy trình trong phạm vi hồ sơ

Trong phạm vi hồ sơ so với toàn cầu: start_gateway() sử dụng các tệp PID trong phạm vi hồ sơ. dừng cổng hermes chỉ dừng cổng của cấu hình hiện tại. hermes Gateway Stop --all sử dụng tính năng quét ps aux toàn cầu để loại bỏ tất cả các quy trình cổng (được sử dụng trong quá trình cập nhật).

Tài liệu liên quan