Thêm bộ điều hợp nền tảng
Hướng dẫn này bao gồm việc thêm nền tảng nhắn tin mới vào cổng Hermes. Bộ điều hợp nền tảng kết nối Hermes với dịch vụ nhắn tin bên ngoài (Telegram, Discord, WeCom, v.v.) để người dùng có thể tương tác với đại lý thông qua dịch vụ đó.
Có hai cách để thêm nền tảng:
- Plugin (được khuyến nghị cho cộng đồng/bên thứ ba): Thả thư mục plugin vào
~/.Hermes/plugins/ ` — không cần thay đổi mã lõi. Xem Plugin Path bên dưới.
- Tích hợp: Sửa đổi hơn 20 tệp trên mã, cấu hình và tài liệu. Sử dụng Built-in Checklist bên dưới.
Tổng quan về kiến trúc
` User ↔ Messaging Platform ↔ Platform Adapter ↔ Gateway Runner ↔ AIAgent
``Mọi bộ điều hợp đều mở rộng BasePlatformAdapter
từ gateway/platforms/base.py
` và thực hiện:
- ** `connect()
** — Thiết lập kết nối (WebSocket, thăm dò ý kiến dài, máy chủ HTTP, v.v.) (trừu tượng)
- ** `disconnect()
** — Tắt máy hoàn toàn (trừu tượng)
- ** `send()
** — Gửi tin nhắn văn bản tới cuộc trò chuyện (trừu tượng)
- ** `send_typing()
** — Hiển thị chỉ báo gõ (ghi đè tùy chọn)
- ** `get_chat_info()
** — Trả lại siêu dữ liệu trò chuyện (ghi đè tùy chọn)
Các tin nhắn gửi đến được bộ điều hợp nhận và chuyển tiếp qua `self.handle_message(event)
, lớp cơ sở sẽ định tuyến đến trình chạy cổng.
Đường dẫn plugin (Được khuyến nghị)
Hệ thống plugin cho phép bạn thêm bộ điều hợp nền tảng mà không cần sửa đổi bất kỳ mã Hermes cốt lõi nào. Plugin của bạn là một thư mục có hai tệp:
` ~/.Hermes/plugins/my-platform/ PLUGIN.YAML # Plugin metadata adapter.py # Adapter class + register() entry point
`
PLUGIN.YAML
Siêu dữ liệu plugin.
requires_env và
optional_env chặn tự động điền các mục nhập giao diện người dùng
Hermes config (xem Surfacing Env Vars bên dưới).
name: my-platform
label: My Platform
kind: platform
version: 1.0.0
description: My custom messaging platform adapter
author: Your Name
requires_env:
- MY_PLATFORM_TOKEN # bare string works
- name: MY_PLATFORM_CHANNEL # or rich dict for better UX
description: "Channel to join"
prompt: "Channel"
password: false
optional_env:
- name: MY_PLATFORM_HOME_CHANNEL
description: "Default channel for cron delivery"
password: false
`
### adapter.py
``` python
import os
from gateway.platforms.base import (
BasePlatformAdapter, SendResult, MessageEvent, MessageType,
)
from gateway.config import Platform, PlatformConfig
class MyPlatformAdapter(BasePlatformAdapter):
def __init__(self, config: PlatformConfig):
super().__init__(config, Platform("my_platform"))
extra = config.extra or \{}
self.token = os.getenv("MY_PLATFORM_TOKEN") or extra.get("token", "")
async def connect(self) -> bool:
# Connect to the platform API, start listeners
self._mark_connected()
return True`async def disconnect(self) -> None:
self._mark_disconnected()
async def send(self, chat_id, content, reply_to=None, metadata=None):
# Send message via platform API
return SendResult(success=True, message_id="...")
async def get_chat_info(self, chat_id):
return \{"name": chat_id, "type": "dm"}
def check_requirements() -> bool:
return bool(os.getenv("MY_PLATFORM_TOKEN"))
def validate_config(config) -> bool:
extra = getattr(config, "extra", \{}) or \{}
return bool(os.getenv("MY_PLATFORM_TOKEN") or extra.get("token"))
def _env_enablement() -> dict | None:
token = os.getenv("MY_PLATFORM_TOKEN", "").strip()
channel = os.getenv("MY_PLATFORM_CHANNEL", "").strip()
if not (token and channel):
return None
seed = \{"token": token, "channel": channel}
home = os.getenv("MY_PLATFORM_HOME_CHANNEL")
if home:
seed["home_channel"] = \{"chat_id": home, "name": "Home"}
return seed
def register(ctx):
"""Plugin entry point — called by the Hermes plugin system."""
ctx.register_platform(
name="my_platform",
label="My Platform",
adapter_factory=lambda cfg: MyPlatformAdapter(cfg),
check_fn=check_requirements,
validate_config=validate_config,
required_env=["MY_PLATFORM_TOKEN"],
install_hint="pip install my-platform-SDK",
# Env-driven auto-configuration — seeds PlatformConfig.extra from
# env vars before adapter construction. See "Env-Driven Auto-
# Configuration" section below.
env_enablement_fn=_env_enablement,
# Cron home-channel delivery support. Lets deliver=my_platform cron
# jobs route without editing cron/scheduler.py. See "Cron Delivery"
# section below.
cron_deliver_env_var="MY_PLATFORM_HOME_CHANNEL",
# Per-platform user authorization env vars
allowed_users_env="MY_PLATFORM_ALLOWED_USERS",
allow_all_env="MY_PLATFORM_ALLOW_ALL_USERS",
# Message length limit for smart chunking (0 = no limit)
max_message_length=4000,
# LLM guidance injected into system prompt
platform_hint=(
"You are chatting via My Platform. "
"It supports markdown formatting."
),
# Display
emoji="💬",
)
# Optional: register platform-specific tools
ctx.register_tool(
name="my_platform_search",
toolset="my_platform",
schema=\{...},
handler=my_search_handler,
)
`
### Cấu hình
Người dùng định cấu hình nền tảng trong
`config.yaml
:
``` yaml
gateway:
platforms:
my_platform:
enabled: true
extra:
token: "..."
channel: "#general"
`
``Hoặc thông qua các biến môi trường (mà bộ điều hợp đọc trong
`__init__
).
### Hệ thống plugin tự động xử lý những gì
Khi bạn gọi
`ctx.register_platform()
, các điểm tích hợp sau sẽ được xử lý cho bạn — không cần thay đổi mã lõi:
| Điểm tích hợp | Nó hoạt động như thế nào |
|---|---|
| Tạo bộ điều hợp cổng | Kiểm tra sổ đăng ký trước chuỗi if/elif tích hợp |
| Phân tích cú pháp cấu hình |
Platform._missing_()
` chấp nhận mọi tên nền tảng |
| Xác thực nền tảng được kết nối | Đăng ký
`validate_config()
` có tên |
| Ủy quyền người dùng | Đã kiểm tra
`allowed_users_env
` /
`allow_all_env
` |
| Tự động kích hoạt chỉ Env | Hạt giống
`env_enablement_fn
`
`PlatformConfig.extra
+
`home_channel
` |
| Cầu cấu hình YAML |
apply_YAML_config_fn
` chuyển các khóa
`config.yaml
` thành env vars / extras |
| Giao hàng định kỳ |
cron_deliver_env_var
` giúp
`deliver=<name>
` hoạt động |
| Các mục nhập giao diện người dùng
`Hermes config
` |
`requires_env
` /
`optional_env
` trong
`plugin.YAML
` tự động điền |
| công cụ send_message | Các tuyến thông qua bộ chuyển đổi cổng trực tiếp |
| Phân phối đa nền tảng Webhook | Đã kiểm tra sổ đăng ký cho các nền tảng đã biết |
| Truy cập lệnh
/update
` | Cờ
`allow_update_command
` |
| Thư mục kênh | Nền tảng plugin có trong bảng liệt kê |
| Gợi ý nhắc nhở hệ thống |
platform_hint
` được đưa vào ngữ cảnh LLM |
| Phân đoạn tin nhắn |
max_message_length
` để chia tách thông minh |
| Biên tập PII | Cờ
`pii_safe
` |
|
`Hermes status
` | Hiển thị nền tảng plugin có thẻ
(plugin)
` |
|
`Hermes gateway setup
` | Nền tảng plugin xuất hiện trong menu cài đặt |
|
`Hermes tools
` /
`Hermes skills
` | Nền tảng plugin trong cấu hình mỗi nền tảng |
| Khóa mã thông báo (đa hồ sơ) | Sử dụng
`acquire_scoped_lock()
` trong
`connect()
` của bạn |
| Cảnh báo cấu hình mồ côi | Nhật ký mô tả khi thiếu plugin |
## Cấu hình tự động dựa trên Env
Hầu hết người dùng thiết lập nền tảng bằng cách thả các vars env vào
~/.Hermes/.env
` thay vì chỉnh sửa
`config.yaml
. Móc
`env_enablement_fn
` cho phép plugin của bạn chọn các biến env đó **trước khi** bộ chuyển đổi được tạo, do đó
`Hermes gateway status
,
`get_connected_platforms()
` và phân phối cron sẽ thấy trạng thái chính xác mà không cần khởi tạo SDK nền tảng.
`Python
def _env_enablement() -> dict | None:
"""Seed PlatformConfig.extra from env vars.
Called by the platform registry during load_gateway_config().
Return None when the platform isn't minimally configured — the
caller then skips auto-enabling. Return a dict to seed extras.
The special 'home_channel' key is extracted and becomes a proper
HomeChannel dataclass on the PlatformConfig; every other key is
merged into PlatformConfig.extra.
"""
token = os.getenv("MY_PLATFORM_TOKEN", "").strip()
channel = os.getenv("MY_PLATFORM_CHANNEL", "").strip()
if not (token and channel):
return None
seed = \{"token": token, "channel": channel}
home = os.getenv("MY_PLATFORM_HOME_CHANNEL")
if home:
seed["home_channel"] = {
"chat_id": home,
"name": os.getenv("MY_PLATFORM_HOME_CHANNEL_NAME", "Home"),
}
return seed
def register(ctx):
ctx.register_platform(
name="my_platform",
label="My Platform",
adapter_factory=lambda cfg: MyPlatformAdapter(cfg),
check_fn=check_requirements,
validate_config=validate_config,
env_enablement_fn=_env_enablement,
# ... other fields
)
`
`
## YAML→env Cầu cấu hình
Một số người dùng thích cài đặt khóa
`config.yaml
` (
`my_platform.require_mention
,
`my_platform.allowed_channels
, v.v.) trên các biến env. Móc
`apply_YAML_config_fn
` cho phép plugin của bạn sở hữu bản dịch này thay vì buộc
`gateway/config.py
` lõi phải biết lược đồ YAML của nền tảng của bạn.
``` python
import os`def _apply_YAML_config(YAML_cfg: dict, platform_cfg: dict) -> dict | None:
"""Translate config.yaml
`my_platform:
` keys into env vars / extras.
YAML_cfg — the full top-level parsed config.yaml dict
platform_cfg — the platform's own sub-dict (YAML_cfg.get("my_platform", \{}))
May mutate os.environ directly (use
`not os.getenv(...)
` guards to
preserve env > YAML precedence) and/or return a dict to merge into
PlatformConfig.extra. Return None or \{} for no extras.
"""
if "require_mention" in platform_cfg and not os.getenv("MY_PLATFORM_REQUIRE_MENTION"):
os.environ["MY_PLATFORM_REQUIRE_MENTION"] = str(platform_cfg["require_mention"]).lower()
allowed = platform_cfg.get("allowed_channels")
if allowed is not None and not os.getenv("MY_PLATFORM_ALLOWED_CHANNELS"):
if isinstance(allowed, list):
allowed = ",".join(str(v) for v in allowed)
os.environ["MY_PLATFORM_ALLOWED_CHANNELS"] = str(allowed)
return None # nothing extra to merge into PlatformConfig.extra`def register(ctx):
ctx.register_platform(
name="my_platform",
...,
apply_YAML_config_fn=_apply_YAML_config,
)
`
`Móc được gọi trong
`load_gateway_config()
` sau vòng khóa chung (xử lý các khóa phổ biến như
`unauthoriZed_dm_behavior
,
`notice_delivery
,
`reply_prefix
,
`require_mention
, v.v.) và trước
`_apply_env_overrides()
, vì vậy plugin của bạn chỉ cần kết nối các khóa **dành riêng cho nền tảng**.
Các ngoại lệ do hook đưa ra sẽ được nuốt và ghi lại ở mức gỡ lỗi — một plugin hoạt động sai không bao giờ hủy bỏ việc tải cấu hình cổng.
`
## Giao hàng định kỳ
Để cho phép các công việc định kỳ
`deliver=my_platform
` định tuyến đến kênh chính đã được định cấu hình, hãy đặt
`cron_deliver_env_var
` thành tên var env chứa ID trò chuyện/phòng/kênh mặc định:
``` python
ctx.register_platform(
name="my_platform",
...
cron_deliver_env_var="MY_PLATFORM_HOME_CHANNEL",
)
`
``Bộ lập lịch đọc biến env này khi giải quyết mục tiêu chính cho các công việc
`deliver=my_platform
` và cũng xử lý nền tảng này như một mục tiêu cron hợp lệ trong các kiểm tra kiểu
`_KNOWN_DELIVERY_PLATFORMS
. Nếu
`env_enablement_fn
` của bạn tạo ra lệnh
`home_channel
` (xem ở trên), lệnh đó sẽ được ưu tiên -
`cron_deliver_env_var
` là dự phòng cho các công việc định kỳ chạy trước khi gieo mầm env.
### Phân phối cron ngoài quy trình``cron_deliver_env_var
` làm cho nền tảng của bạn trở thành mục tiêu
`deliver=
` được công nhận. Để gửi thực tế thành công khi công việc định kỳ chạy trong một quy trình riêng biệt với cổng (tức là
`Hermes cron run
` tách biệt với
`Hermes gateway
), hãy đăng ký
`standalone_sender_fn
:
`Python
async def _standalone_send(
pconfig,
chat_id,
message,
*,
thread_id=None,
media_files=None,
force_document=False,
):
"""Open an ephemeral connection / acquire a fresh token, send, and close."""
# ... open connection, send message, return result ...
return \{"success": True, "message_id": "..."}
# or \{"error": "..."}`ctx.register_platform(
name="my_platform",
...
cron_deliver_env_var="MY_PLATFORM_HOME_CHANNEL",
standalone_sender_fn=_standalone_send,
)
`
``Tại sao hook này lại cần thiết: các nền tảng tích hợp sẵn (Telegram, Discord, Slack, v.v.) gửi trình trợ giúp REST trực tiếp trong
`tools/send_message_tool.py
` để cron có thể phân phối mà không cần giữ cổng trong cùng một quy trình. Các nền tảng plugin trước đây phụ thuộc vào
`_gateway_runner_ref()
, trả về
`None
` bên ngoài quy trình cổng, do đó nếu không có
`standalone_sender_fn
` thì việc gửi phía cron không thành công với
`No live adapter for platform '<name'
.
Hàm này nhận được
`pconfig
` và
`chat_id
` tương tự như bộ điều hợp trực tiếp, cùng với các đối số từ khóa
`thread_id
,
`media_files
` và
`force_document
` tùy chọn. Việc trả lại
\{"success": True, "message_id": ...}
` được coi là giao hàng thành công; việc trả về
\{"error": "..."}
` sẽ hiển thị thông báo trong
`delivery_errors
` của cron. Các ngoại lệ được nêu ra bên trong hàm sẽ được bộ điều phối phát hiện và báo cáo là
`Plugin standalone send failed: <reason>
. Việc triển khai tham chiếu có trong
`plugins/platforms/\{irc,teams,Google_chat}/adapter.py
.
## Hiển thị các biến Env trong
`Hermes config
```Hermes_CLI/config.py
` quét
`plugins/platforms/*/plugin.YAML
` tại thời điểm nhập và tự động điền
`OPTIONAL_ENV_VARS
` từ các khối
`requires_env
` và (tùy chọn)
`optional_env
. Sử dụng biểu mẫu rich-dict để đóng góp các mô tả, lời nhắc, cờ mật khẩu và URL thích hợp — giao diện người dùng thiết lập CLI sẽ chọn chúng miễn phí.
``` yaml
# plugins/platforms/my_platform/plugin.YAML
name: my_platform-platform
label: My Platform
kind: platform
version: 1.0.0
description: >
My Platform gateway adapter for Hermes Agent.
author: Your Name
requires_env:
- name: MY_PLATFORM_TOKEN
description: "Bot API token from the My Platform console"
prompt: "My Platform bot token"
url: "https://my-platform.example.com/bots"
password: true
- name: MY_PLATFORM_CHANNEL
description: "Channel to join (e.g. #Hermes)"
prompt: "Channel"
password: false
optional_env:
- name: MY_PLATFORM_HOME_CHANNEL
description: "Default channel for cron delivery (defaults to MY_PLATFORM_CHANNEL)"
prompt: "Home channel (or empty)"
password: false
- name: MY_PLATFORM_ALLOWED_USERS
description: "Comma-separated user IDs allowed to talk to the bot"
prompt: "Allowed users (comma-separated)"
password: false
`
``**Khóa dict được hỗ trợ:**
`name
` (bắt buộc),
`description
,
`prompt
,
`url
,
`password
` (bool; được tự động phát hiện từ
*_TOKEN
` /
*_SECRET
` /
*_KEY
` / Hậu tố
*_PASSWORD
` /
*_JSON
` khi bị bỏ qua),
`category
` (mặc định là
"messaging"
).
Các mục nhập chuỗi trần (
- MY_PLATFORM_TOKEN
) vẫn hoạt động — chúng nhận được mô tả chung được tự động lấy từ
`label
` của plugin. Nếu một mục được mã hóa cứng cho cùng một var đã tồn tại trong
`OPTIONAL_ENV_VARS
, thì mục đó sẽ thắng (tương thích ngược); biểu mẫu plugin.YAML đóng vai trò dự phòng.
## UX LLM chậm dành riêng cho nền tảng
Một số nền tảng có các ràng buộc làm thay đổi cách trình bày phản hồi LLM chậm:
- **LINE** phát hành *mã thông báo trả lời* dùng một lần và hết hạn khoảng 60 giây sau sự kiện gửi đến. Trả lời bằng mã thông báo đó là miễn phí; không quay trở lại API Push có đồng hồ đo. Nếu LLM chưa hoàn thành trước thời hạn, lựa chọn là "đốt hạn ngạch Đẩy trả phí" hoặc "làm điều gì đó thông minh hơn với mã thông báo trả lời trước khi hết hạn."
- **WhatsApp** đánh dấu phiên không hoạt động sau 24 giờ, sau đó chỉ các tin nhắn mẫu mới được chấp nhận.
- **SMS** không có khái niệm về chỉ báo nhập hoặc cập nhật liên tục — phản hồi dài trông giống như bot đang ngoại tuyến.
Đây là những hạn chế thực sự mà
`BasePlatformAdapter
` cơ sở không thể lường trước được. Bề mặt plugin cố tình chừa chỗ cho một bộ chuyển đổi sang lớp UX dành riêng cho nền tảng ở đầu vòng gõ cơ sở mà không mở rộng danh sách kwarg.
### Mẫu: phân lớp
`_keep_typing
` để tạo lớp UX giữa chuyến bay
`BasePlatformAdapter._keep_typing
` là nhịp tim của chỉ báo gõ — nó chạy dưới dạng tác vụ nền trong khi LLM đang tạo và bị hủy khi phản hồi được gửi. Để xếp lớp hành vi dành riêng cho nền tảng ở một ngưỡng (ví dụ: gửi bong bóng "vẫn đang suy nghĩ" ở giây thứ 45), hãy ghi đè
`_keep_typing
` trong bộ điều hợp của bạn, lên lịch tác vụ của riêng bạn cùng với
`super()._keep_typing()
` và chia nhỏ nó trong
`finally
:
``` python
class LineAdapter(BasePlatformAdapter):
async def _keep_typing(self, chat_id: str, *args, **kwargs) -> None:
if self.slow_response_threshold <= 0:
await super()._keep_typing(chat_id, *args, **kwargs)
return`async def _fire_at_threshold() -> None:
try:
await asyncio.sleep(self.slow_response_threshold)
except asyncio.CancelledError:
raise
# Platform-specific work here — for LINE, send a Template
# Buttons "Get answer" bubble using the cached reply token
# so the user can fetch the cached response later via a
# fresh (free) reply token from the postback callback.
await self._send_slow_response_button(chat_id)
side_task = asyncio.create_task(_fire_at_threshold())
try:
await super()._keep_typing(chat_id, *args, **kwargs)
finally:
if not side_task.done():
side_task.cancel()
try:
await side_task
except (asyncio.CancelledError, Exception):
pass
`
``Những điểm chính:
- **Luôn luôn
`await super()._keep_typing(...)
.** Nhịp gõ phím rất hữu ích — đừng thay thế nó, hãy chồng lên nó.
- **Xé nhỏ tác vụ phụ trong
`finally
.** Khi LLM kết thúc (hoặc
/stop
` hủy quá trình chạy), cổng sẽ hủy tác vụ nhập. Nhiệm vụ phụ của bạn cũng phải quan sát việc hủy bỏ đó, nếu không nó sẽ tồn tại và có thể kích hoạt sau khi phản hồi đã được gửi.
- **Ghép nối với
`interrupt_session_activity
** để giải quyết mọi trạng thái UX mồ côi khi người dùng gặp sự cố
/stop
. Đối với LINE, điều này có nghĩa là chuyển mục nhập bộ nhớ đệm đăng lại từ
`PENDING
` sang
`ERROR
` để nút "Nhận câu trả lời" liên tục đưa ra thông báo "Chạy bị gián đoạn" thay vì lặp lại.
### Mẫu: phân lớp
`send
` để định tuyến qua bộ đệm thay vì gửi ngay lập tức
Nếu UX phản hồi chậm của bạn lưu trữ phản hồi để truy xuất sau (luồng đăng lại của LINE), ghi đè
`send
` của bạn cần nhận ra ba chế độ:
1. **Đang chờ đăng lại hoạt động cho cuộc trò chuyện này** → lưu trữ phản hồi trong request_id, không gửi bất kỳ nội dung nào hiển thị.
2. **System busy-ack** (
⚡ Interrupting
,
⏳ Queued
,
⏩ Steered
) → bỏ qua bộ đệm và gửi rõ ràng để người dùng thấy phản hồi của cổng đối với đầu vào của họ.
3. **Phản hồi bình thường** → gửi qua reply-token-hoặc-push như bình thường.
``` python
async def send(self, chat_id: str, content: str, **kw) -> SendResult:
if _is_system_bypass(content):
return await self._send_text_chunks(chat_id, content, force_push=False)
pending_rid = self._pending_buttons.get(chat_id)
if pending_rid:
self._cache.set_ready(pending_rid, content)
return SendResult(success=True, message_id=pending_rid)
return await self._send_text_chunks(chat_id, content, force_push=False)
`
```_SYSTEM_BYPASS_PREFIXES
` là tiền tố xác nhận bận của cổng (
⚡
,
⏳
,
⏩
,
💾
). Luôn cho phép những điều đó được thực hiện một cách rõ ràng, bất kể trạng thái UX được lưu trong bộ nhớ đệm.
### Khi mẫu này phù hợp
Sử dụng phương pháp ghi đè vòng lặp gõ khi:
- API gửi đi của nền tảng có ràng buộc về khoảng thời gian cứng (mã thông báo phản hồi sử dụng một lần, phiên cố định hết hạn, v.v.) VÀ
- Một *bong bóng hiển thị giữa chuyến bay* là trải nghiệm người dùng được chấp nhận trên nền tảng đó.
Sử dụng đường dẫn Luôn đẩy
`slow_response_threshold = 0
` đơn giản hơn khi:
- Nền tảng không có sự phân biệt có ý nghĩa giữa miễn phí và trả phí, HOẶC
- Cộng đồng người dùng thích im lặng "đang tải... đang tải... XONG" thay vì bong bóng tương tác trung gian.
LINE hỗ trợ cả hai: ngưỡng mặc định là 45 giây để tìm nạp đăng lại miễn phí và
`LINE_SLOW_RESPONSE_THRESHOLD=0
` hoàn nguyên về "luôn dự phòng Đẩy".
### Triển khai tham khảo
Xem
`plugins/platforms/line/adapter.py
` để biết cách triển khai đăng lại LINE đầy đủ - máy trạng thái
`RequestCache
` (
`PENDING → READY → DELIVERED
, cộng với
`ERROR
` cho
/stop
), ghi đè
`_keep_typing
` kích hoạt bong bóng Nút mẫu ở ngưỡng, ghi đè
`send
` định tuyến qua bộ đệm và một Ghi đè
`interrupt_session_activity
` để giải quyết các mục nhập đang chờ xử lý mồ côi.
### Triển khai tham chiếu (Đường dẫn plugin)
Xem
`plugins/platforms/irc/
` trong kho lưu trữ để biết ví dụ hoạt động hoàn chỉnh - bộ điều hợp IRC không đồng bộ đầy đủ không có phụ thuộc bên ngoài.
`plugins/platforms/teams/
` bao gồm Bot Framework / Thẻ thích ứng,
`plugins/platforms/Google_chat/
` bao gồm các API REST dựa trên OAuth và
`plugins/platforms/line/
` bao gồm các API nhắn tin dựa trên webhook với UX LLM chậm dành riêng cho nền tảng.
---
## Danh sách kiểm tra từng bước (Đường dẫn tích hợp)
:::note
Danh sách kiểm tra này dùng để thêm nền tảng trực tiếp vào cơ sở mã cốt lõi của Hermes — thường được thực hiện bởi những người đóng góp cốt lõi cho các nền tảng được hỗ trợ chính thức. Nền tảng cộng đồng/bên thứ ba nên sử dụng [Plugin Path](#plugin-path-recommended) ở trên.
:::
### 1. Nền tảng Enum
Thêm nền tảng của bạn vào enum
`Platform
` trong
`gateway/config.py
:
``` python
class Platform(str, Enum):
# ... existing platforms ...
NEWPLAT = "newplat"
`
### 2. Tệp điều hợp
Tạo
`gateway/platforms/newplat.py
:
``` python
from gateway.config import Platform, PlatformConfig
from gateway.platforms.base import (
BasePlatformAdapter, MessageEvent, MessageType, SendResult,
)
def check_newplat_requirements() -> bool:
"""Return True if dependencies are available."""
return SOME_SDK_AVAILABLE`class NewPlatAdapter(BasePlatformAdapter):
def __init__(self, config: PlatformConfig):
super().__init__(config, Platform.NEWPLAT)
# Read config from config.extra dict
extra = config.extra or \{}
self._API_key = extra.get("API_key") or os.getenv("NEWPLAT_API_KEY", "")
async def connect(self) -> bool:
# Set up connection, start polling/webhook
self._mark_connected()
return True`async def disconnect(self) -> None:
self._running = False
self._mark_disconnected()
async def send(self, chat_id, content, reply_to=None, metadata=None):
# Send message via platform API
return SendResult(success=True, message_id="...")
async def get_chat_info(self, chat_id):
return \{"name": chat_id, "type": "dm"}
`
``Đối với tin nhắn gửi đến, hãy tạo
`MessageEvent
` và gọi
`self.handle_message(event)
:
``` python
source = self.build_source(
chat_id=chat_id,
chat_name=name,
chat_type="dm", # or "group"
user_id=user_id,
user_name=user_name,
)
event = MessageEvent(
text=content,
message_type=MessageType.TEXT,
source=source,
message_id=msg_id,
)
await self.handle_message(event)
`
### 3. Cấu hình cổng (
`gateway/config.py
)
Ba điểm tiếp xúc:
1. **
`get_connected_platforms()
`
** — Thêm kiểm tra thông tin xác thực cần thiết cho nền tảng của bạn
2. **
`load_gateway_config()
** — Thêm mục nhập bản đồ env mã thông báo:
`Platform.NEWPLAT: "NEWPLAT_TOKEN"
3. **
`_apply_env_overrides()
** — Ánh xạ tất cả các biến env
`NEWPLAT_*
` sang cấu hình### 4. Chạy cổng (
`gateway/run.py
)
Năm điểm tiếp xúc:
1. **
`_create_adapter()
** — Thêm nhánh
`elif platform == Platform.NEWPLAT:
2. **Bản đồ người dùng được phép
`_is_user_authoriZed()
** —
`Platform.NEWPLAT: "NEWPLAT_ALLOWED_USERS"
3. **
`_is_user_authoriZed()
` cho phép_tất cả bản đồ** —
`Platform.NEWPLAT: "NEWPLAT_ALLOW_ALL_USERS"
4. **Kiểm tra sớm bộ dữ liệu
`_any_allowlist
** — Thêm
"NEWPLAT_ALLOWED_USERS"
5. **Kiểm tra sớm bộ dữ liệu
`_allow_all
** — Thêm
"NEWPLAT_ALLOW_ALL_USERS"
6. **Bộ đông lạnh
`_UPDATE_ALLOWED_PLATFORMS
** — Thêm
`Platform.NEWPLAT
### 5. Phân phối đa nền tảng
1. **
`gateway/platforms/webhook.py
** — Thêm
"newplat"
` vào bộ dữ liệu loại phân phối
2. **
`cron/scheduler.py
** — Thêm vào bộ đông lạnh
`_KNOWN_DELIVERY_PLATFORMS
` và bản đồ nền tảng
`_deliver_result()
### 6. Tích hợp CLI
1. **
`Hermes_CLI/config.py
** — Thêm tất cả các biến thể
`NEWPLAT_*
` vào
`_EXTRA_ENV_KEYS
2. **
`Hermes_CLI/gateway.py
** — Thêm mục vào danh sách
`_PLATFORMS
` với khóa, nhãn, biểu tượng cảm xúc, token_var, setup_instructions và vars
3. **
`Hermes_CLI/platforms.py
** — Thêm mục nhập
`PlatformInfo
` có nhãn và default_toolset (được sử dụng bởi
`skills_config
` và
`tools_config
` TUI)
4. **
`Hermes_CLI/setup.py
** — Thêm chức năng
`_setup_newplat()
` (có thể ủy quyền cho
`gateway.py
) và thêm bộ dữ liệu vào danh sách nền tảng nhắn tin
5. **
`Hermes_CLI/status.py
** — Thêm mục phát hiện nền tảng:
"NewPlat": ("NEWPLAT_TOKEN", "NEWPLAT_HOME_CHANNEL")
6. **
`Hermes_CLI/dump.py
** — Thêm
"newplat": "NEWPLAT_TOKEN"
` vào lệnh phát hiện nền tảng
### 7. Công cụ
1. **
`tools/send_message_tool.py
** — Thêm
"newplat": Platform.NEWPLAT
` vào bản đồ nền tảng
2. **
`tools/cronjob_tools.py
** — Thêm
`newplat
` vào chuỗi mô tả mục tiêu phân phối
### 8. Bộ công cụ
1. **
`toolsets.py
** — Thêm định nghĩa bộ công cụ
"Hermes-newplat"
` với
`_Hermes_CORE_TOOLS
2. **
`toolsets.py
** — Thêm
"Hermes-newplat"
` vào danh sách bao gồm
"Hermes-gateway"
### 9. Tùy chọn: Gợi ý về nền tảng`**
`agent/prompt_builder.py
** — Nếu nền tảng của bạn có các giới hạn hiển thị cụ thể (không đánh dấu, giới hạn độ dài tin nhắn, v.v.), hãy thêm mục nhập vào lệnh
`_PLATFORM_HINTS
. Thao tác này sẽ đưa hướng dẫn dành riêng cho nền tảng vào lời nhắc của hệ thống:
``` python
_PLATFORM_HINTS = {
# ...
"newplat": (
"You are chatting via NewPlat. It supports markdown formatting "
"but has a 4000-character message limit."
),
}
`
``Không phải tất cả các nền tảng đều cần gợi ý — chỉ thêm một gợi ý nếu hành vi của tổng đài viên sẽ khác.
### 10. Kiểm tra
Tạo
`tests/gateway/test_newplat.py
` bao gồm:
- Xây dựng bộ chuyển đổi từ cấu hình
- Xây dựng sự kiện tin nhắn
- Phương thức gửi (giả lập API bên ngoài)
- Các tính năng dành riêng cho nền tảng (mã hóa, định tuyến, v.v.)
### 11. Tài liệu
| Tập tin | Thêm gì |
|------|-------------|
|
`website/docs/user-guide/messaging/newplat.md
` | Trang thiết lập nền tảng đầy đủ |
|
`website/docs/user-guide/messaging/index.md
` | Bảng so sánh nền tảng, sơ đồ kiến trúc, bảng bộ công cụ, phần bảo mật, liên kết các bước tiếp theo |
|
`website/docs/reference/environment-variables.md
` | Tất cả NEWPLAT_* env vars |
|
`website/docs/reference/toolsets-reference.md
` | bộ công cụ Hermes-newplat |
|
`website/docs/integrations/index.md
` | Liên kết nền tảng |
|
`website/sidebars.ts
` | Mục nhập thanh bên cho trang tài liệu |
|
`website/docs/developer-guide/architecture.md
` | Số lượng bộ điều hợp + danh sách |
|
`website/docs/developer-guide/gateway-internals.md
` | Danh sách tập tin bộ điều hợp |
## Kiểm tra tính chẵn lẻ
Trước khi đánh dấu PR nền tảng mới là hoàn chỉnh, hãy chạy kiểm tra tính chẵn lẻ đối với nền tảng đã được thiết lập:
``` bash
# Find every .py file mentioning the reference platform
search_files "BlueBubbles" output_mode="files_only" file_glob="*.py"
# Find every .py file mentioning the new platform
search_files "newplat" output_mode="files_only" file_glob="*.py"
# Any file in the first set but not the second is a potential gap
`
``Lặp lại cho các tệp
.md
` và
.ts
. Điều tra từng khoảng trống - đó là bảng liệt kê nền tảng (cần cập nhật) hay tham chiếu dành riêng cho nền tảng (bỏ qua)?
## Các mẫu phổ biến
### Bộ điều hợp thăm dò ý kiến dài
Nếu bộ điều hợp của bạn sử dụng tính năng bỏ phiếu dài (như Telegram hoặc Weixin), hãy sử dụng tác vụ vòng lặp bỏ phiếu:
``` python
async def connect(self):
self._poll_task = asyncio.create_task(self._poll_loop())
self._mark_connected()
async def _poll_loop(self):
while self._running:
messages = await self._fetch_updates()
for msg in messages:
await self.handle_message(self._build_event(msg))
`
### Bộ điều hợp gọi lại/Webhook
Nếu nền tảng đẩy tin nhắn đến điểm cuối của bạn (như Cuộc gọi lại WeCom), hãy chạy máy chủ HTTP:
`Python
async def connect(self):
self._app = web.Application()
self._app.router.add_post("/callback", self._handle_callback)
# ... start aiohttp server
self._mark_connected()
async def _handle_callback(self, request):
event = self._build_event(await request.text())
await self._message_queue.put(event)
return web.Response(text="success") # Acknowledge immediately
`
``Đối với các nền tảng có thời hạn phản hồi chặt chẽ (ví dụ: giới hạn 5 giây của WeCom), hãy luôn xác nhận ngay lập tức và chủ động gửi phản hồi của tổng đài viên qua API sau đó. Phiên tổng đài viên kéo dài 3–30 phút — việc trả lời nội tuyến trong cửa sổ phản hồi gọi lại là không khả thi.
### Khóa mã thông báo
Nếu bộ điều hợp giữ kết nối liên tục với thông tin xác thực duy nhất, hãy thêm khóa theo phạm vi để ngăn hai hồ sơ sử dụng cùng một thông tin xác thực:
``` python
from gateway.status import acquire_scoped_lock, release_scoped_lock`async def connect(self):
if not acquire_scoped_lock("newplat", self._token):
logger.error("Token already in use by another profile")
return False
# ... connect`async def disconnect(self):
release_scoped_lock("newplat", self._token)
`
## Triển khai tham khảo| Bộ chuyển đổi | Mẫu | Độ phức tạp | Tài liệu tham khảo tốt cho |
|----------|----------|-------------|-------------------|
|
`BlueBubbles.py
` | REST + webhook | Trung bình | Tích hợp API REST đơn giản |
|
`weixin.py
` | Thăm dò ý kiến dài + CDN | Cao | Xử lý phương tiện, mã hóa |
|
`WeCom_callback.py
` | Gọi lại/webhook | Trung bình | Máy chủ HTTP, mật mã AES, đa ứng dụng |
|
`Telegram.py
` | Thăm dò ý kiến dài + API Bot | Cao | Bộ điều hợp đầy đủ tính năng với các nhóm, chủ đề |