简体中文 English 繁體中文 日本語 Русский
本文为静态镜像,内容以交互版为准 在交互式文档中心打开 →

name: erispulse-hot-reload description: "适用于:改代码不重启(热重载)、闲置自动归眠(dormant_after)、运行时注册模块。覆盖场景:何時需要閱讀本文:使用慢載模組或 activate_on 事件驅動激活時。若不需要則可跳過,這不會影響其他功能的使用。跟著這篇走完,你會得到一個能響應 /hello 命令的可執行模組,並了解模組的專案結構、註冊方式與載入 / 卸載鉤子——後續所有模組文件都建立在這套骨架上。"

ErisPulse 热重载与懒加载休眠

This skill is auto-built from the ErisPulse documentation. Repository: https://github.com/ErisPulse/ErisPulse Source paths are annotated per section; see docs/ in the repository for the latest content.

内容大纲

AI 按需读取:只读需要的章节即可,不必全文加载。

懒加载与休眠

来源: docs/zh-TW/advanced/lazy-loading.md(https://github.com/ErisPulse/ErisPulse/blob/main/docs/zh-TW/advanced/lazy-loading.md)

慢載模組系統

Tip


何時需要閱讀本文:使用慢載模組或 activate_on 事件驅動激活時。若不需要則可跳過,這不會影響其他功能的使用。

ErisPulse SDK 的慢載模組系統讓模組延遲到實際被存取時才初始化——啟動階段只註冊占位代理,未被使用的模組不會占用啟動時間與記憶體。

概述

懶加載模組系統是 ErisPulse 的核心特性之一,它透過以下方式運作:

工作原理

LazyModule 類

懶加載系統的核心是 LazyModule 類,它是一個封裝器,在首次存取時才實際初始化模組。

初始化過程

當模組首次被存取時,LazyModule 會執行以下操作:

  1. 獲取模組類的 __init__ 參數資訊
  2. 根據參數決定是否傳入 sdk 引用
  3. 設定模組的 moduleInfo 屬性
  4. 對於繼承自 BaseModule 的模組,呼叫 on_load 方法
  5. 觸發 module.init 生命週期事件

事件驅動懶激活(activate_on)

Note


此功能需要 ErisPulse **2.8.0+**。

lazy_load=True 的模組預設只在首次屬性存取時載入。若模組註冊了命令/事件處理器,
傳統做法只能 lazy_load=False 立即載入。activate_on 提供了第三種選擇:宣告觸發器,
首個匹配事件/命令到達時自動激活模組
——既不常駐記憶體,又不遺失觸發入口。

from ErisPulse.loaders import ModuleLoadStrategy

class MyModule(BaseModule):
    @staticmethod
    def get_load_strategy():
        return ModuleLoadStrategy(
            lazy_load=True,
            activate_on=[
                # ---- 事件觸發(被動到達,無需使用者感知)----
                "message",                                    # 類型級:任何訊息事件
                {"notice": "group_member_increase"},          # 類型 + 單個 detail_type
                {"message": ["private", "group"]},            # 類型 + 多個 detail_type

                # ---- 命令觸發(主動輸入,占位命令對 Help 可見)----
                {"command": "roll"},                          # 簡寫:命令名
                {"command": ["roll", "dice"]},                # 命令名列表
                {"command": {                                 # dict 聲明(name 必填)
                    "name": "dice",
                    "help": "擲一個骰子",
                    "usage": "/dice",
                    "group": "娛樂",
                    "aliases": ["d"],
                    "hidden": False,
                }},
            ],
        )

命令 dict 聲明參數

dict 形式鏡像 @command() 裝飾器的使用者級參數,用於在模組載入前就註冊占位命令:

參數 類型 預設 說明
name str 必填 命令名;須與 on_load 中 @command(name) 一致,否則激活後占位註銷、命令不存在
help str 回退鏈 Help 中顯示的介紹;未聲明時按回退鏈取值(見下)
usage str 自動生成 用法行,預設 {prefix}{name}
group str None 命令分組
aliases list[str] [] 別名同時註冊,輸入別名同樣觸發激活
hidden bool False True 時占位命令同樣隱藏(與激活後真實命令的隱藏語義對齊);知道命令名的使用者輸入仍可觸發

不支援 priority / permission / master:占位命令的使命只是觸發激活,
權限檢查由激活後的真實命令執行(占位階段攔截權限反而會讓「輸入命令激活」失效)。

占位命令 help 回退鏈

模組未載入時 Help 顯示的命令介紹,按以下順序取值(取到即止):

  1. dict 聲明的命令級 help(最精確)
  2. 模組 get_meta() 的 description
  3. 模組 __description__ 屬性
  4. 包元數據的 Summary(PyPI 包簡介)
  5. 通用提示:「此命令來自懶載入模組 X,首次使用將自動載入該模組」

觸發語義

架構圖與完整語義詳見 架構概覽。

懶加載休眠(dormant_after)

Note


此功能需要 ErisPulse **2.11.0+**。

當懶加載的模組長期閒置時,框架可將其歸眠:釋放運行時資源(包括任務、連接、事件註冊等),
但保留懶加載代理與註冊存根——模組回到「已註冊未加載」狀態。下次透過屬性存取、
module.call 或 activate_on 事件將透明地喚醒(重新實例化並執行 on_load),
無需任何額外的喚醒 API。歸眠後的系統狀態與「從未激活」完全一致。

聲明方式

@staticmethod
def get_load_strategy():
    return ModuleLoadStrategy(
        lazy_load=True,
        dormant_after="30m",   # 閒置 30 分鐘自動歸眠;不聲明則永不休眠(預設)
    )

時間長度語法與命令治理的 cooldown 完全一致(例如:90s、1h30m、1d)。
掃描器會以固定間隔(60 秒)進行掃描,實際歸眠時間 = 閾值 + 最多一個掃描間隔;
也可隨時手動歸眠:await sdk.module.sleep("模組名")。顯式呼叫 unload 仍是徹底卸載(懶代理一併移除)。

狀態接續

歸眠前框架會觸發 on_before_unload(reason="dormant")(卸載流程的第一步,資源仍完好),
模組將狀態儲存到 sdk.storage;喚醒後在 on_load 中恢復:

async def on_before_unload(self, event):
    if event["reason"] in ("dormant", "reload", "disable"):
        await self.sdk.storage.aset(f"{self._module_name}:counter", self._count)

async def on_load(self, event):
    self._count = await self.sdk.storage.aget(f"{self._module_name}:counter") or 0

語意與限制

配置懶加載

全局配置

在配置文件中啟用/禁用全局懶加載:

[ErisPulse.framework]
enable_lazy_loading = true  # true=啟用懶加載(預設),false=禁用懶加載

模組層級控制

模組可以透過實作 get_load_strategy() 靜態方法來控制加載策略:

from ErisPulse.Core.Bases import BaseModule
from ErisPulse.loaders import ModuleLoadStrategy

class MyModule(BaseModule):
    @staticmethod
    def get_load_strategy():
        """返回模組加載策略"""
        return ModuleLoadStrategy(
            lazy_load=False,  # 返回 False 表示立即加載
            priority=100      # 加載優先級,數值越大優先級越高
        )

使用懶加載模組

基本使用

對於開發者來說,懶加載模組與普通模組在使用上幾乎沒有區別:

# 透過 SDK 訪問懶加載模組
from ErisPulse import sdk

# 以下訪問會觸發模組懶加載
result = await sdk.my_module.my_method()

統一的模組獲取入口

無論是透過 SDK 屬性、模組管理器屬性訪問,還是透過 module.get() 查詢, 對於「已註冊但尚未加載」的懶加載模組,都會返回同一個懶加載代理,訪問其屬性才會真正觸發初始化:

# 三種方式拿到的都是懶加載代理(在模組未加載時),行為一致、對使用者透明
sdk.my_module          # 觸發加載的入口
sdk.module.my_module   # 同樣返回懶加載代理
sdk.module.get("my_module")  # 也返回懶加載代理,本身不會觸發加載

# 訪問代理的任意屬性才會真正初始化模組
result = await sdk.my_module.my_method()

module.get() 是查詢介面,本身不觸發加載:

如需顯式觸發加載,請使用 await sdk.load_module("my_module")。

異步初始化

對於需要異步初始化的模組,建議先顯式加載:

# 先顯式加載模組
await sdk.load_module("my_module")

# 然後使用模組
result = await sdk.my_module.my_method()

同步初始化

對於不需要異步初始化的模組,可以直接訪問:

# 直接訪問會自動同步初始化
result = sdk.my_module.some_sync_method()

最佳實踐

選擇載入策略時,可參考以下決策流程:

flowchart TD
    A["模組宣告<br/>get_load_strategy()"] --> B{"需要啟動即就緒<br/>或高頻觸發?"}
    B -->|"是"| C["lazy_load=False<br/>立即載入"]
    B -->|"否"| D{"註冊了命令 / 事件處理器?"}
    D -->|"是"| E["lazy_load=True + activate_on<br/>事件/命令到達時激活"]
    D -->|"否"| F["lazy_load=True<br/>首次屬性存取時載入"]
    C --> G["啟動時呼叫 on_load()"]
    E --> H["註冊 stub → 觸發時實例化"]
    F --> I["LazyModule 代理"]

推薦使用懶載入的場景(lazy_load=True)

推薦禁用懶載入的場景(lazy_load=False)

priority 參數控制立即載入模組間的初始化順序,數值越大越先初始化。同優先級的模組按註冊順序載入。

注意事項

  1. 如果您的模組使用了懶加載,如果其他模組從未在 ErisPulse 內進行過呼叫,則您的模組永遠不會被初始化。
  2. 如果您的模組中包含了例如監聽事件的模組,或其它主動監聽類似模組,有兩種選擇:聲明 activate_on 觸發器(保持懶加載,事件到達時自動激活),或聲明需要立即被加載(lazy_load=False),否則會影響您模組的正常業務。
  3. 我們不建議您禁用懶加載,除非有特殊需求,否則它可能為您帶來例如依賴管理和生命週期事件等的問題。
  4. activate_on 的命令 dict 聲明中,name 必須與模組 on_load 中 @command() 註冊的真實命令名一致——否則模組激活後占位命令註銷,聲明與實現不一致的命令將不存在。

相關文件

請依下一步意圖選擇:

模块开发入门(加载策略)

来源: docs/zh-TW/developer-guide/modules/getting-started.md(https://github.com/ErisPulse/ErisPulse/blob/main/docs/zh-TW/developer-guide/modules/getting-started.md)

模組開發入門

Note

L1 核心必讀——首頁「閱讀路徑」中的五篇核心文件之一,模組開發必備。

跟著這篇走完,你會得到一個能響應 /hello 命令的可執行模組,並了解模組的專案結構、註冊方式與載入 / 卸載鉤子——後續所有模組文件都建立在這套骨架上。

項目結構

一個標準的模組結構:

MyModule/
├── pyproject.toml
├── README.md
├── LICENSE
└── MyModule/
    ├── __init__.py
    └── Core.py

pyproject.toml 配置

[project]
name = "ErisPulse-MyModule"
version = "1.0.0"
description = "模組功能描述"
readme = "README.md"
requires-python = ">=3.10"
license = { file = "LICENSE" }
authors = [ { name = "yourname", email = "[email protected]" } ]
dependencies = []

[project.urls]
"homepage" = "https://github.com/yourname/MyModule"

[project.entry-points."erispulse.module"]
"MyModule" = "MyModule:Main"

__init__.py

from .Core import Main

Core.py - 基礎模組

from ErisPulse import sdk
# 推薦根導入(2.10+):常用符號直接從根包導入,深路徑仍相容
from ErisPulse import BaseModule, command

class Main(BaseModule):
    def __init__(self, sdk):
        self.sdk = sdk
        self.logger = sdk.logger.get_child("MyModule")
        self.storage = sdk.storage
    
    @staticmethod
    def get_load_strategy():
        """返回模組加載策略"""
        from ErisPulse.loaders import ModuleLoadStrategy
        return ModuleLoadStrategy(
            lazy_load=True,
            priority=0,
            depends=[],  # 可選:依賴的其他模組列表
            # 可選:事件驅動懶激活——聲明觸發器,首個匹配事件/命令到達時自動加載
            # activate_on=[{"command": {"name": "hello", "help": "發送問候"}}],
        )
    
    async def on_load(self, event):
        """模組加載時調用"""
        @command("hello", help="發送問候")
        async def hello_command(event):
            name = event.get_user_nickname() or "朋友"
            await event.reply(f"你好,{name}!")
        
        self.logger.info("模組已加載")
    
    async def on_unload(self, event):
        """模組卸載時調用"""
        self.logger.info("模組已卸載")

配置讀取:上面的基礎範例未使用配置。需要讀取配置時,推薦聲明嵌套的 ConfigClass 並透過 self.cfg 即時讀取(見 模組核心概念)。手動呼叫 _load_config() 的舊寫法已廢棄。

測試模組

本地測試

# 在專案目錄安裝模組
epsdk install ./MyModule

# 運行專案
epsdk run main.py --reload

測試命令

發送命令測試:

/hello

核心概念

BaseModule 基類

所有模組必須繼承 BaseModule,提供以下方法:

方法 說明 必須
__init__(self, sdk) 建構函數(框架傳入 sdk 實例) 否
get_load_strategy() 返回載入策略 否
get_meta() 返回模組介紹元資訊(可選) 否
on_load(self, event) 模組載入時呼叫 是
on_unload(self, event) 模組卸載時呼叫 是

模組介紹 meta

Note

本特性需要 ErisPulse **2.8.0+**。

透過 get_meta() 聲明模組的介紹元資訊(這個模組是用來做什麼的、屬於哪一類等)。 元資訊是模組的通用介紹資料,供 help 模組、Dashboard 模組列表、模組商店等各類介面/生態模組消費。

與 get_load_strategy() 返回 ModuleLoadStrategy 一致,推薦返回 ModuleMeta 配置類實例(屬性類型、IDE 自動補全),也兼容直接返回 dict:

class MyModule(BaseModule):
    @staticmethod
    def get_meta() -> ModuleMeta:
        return ModuleMeta(
            name="天氣",               # 顯示名(預設註冊名)
            description="查詢城市天氣",  # 模組簡介
            version="1.0.0",
            author="ErisDev",
            group="工具",               # 功能分組
            tags=["天氣", "查詢"],
        )

相容寫法(dict):

class MyModule(BaseModule):
    @staticmethod
    def get_meta() -> dict:
        return {
            "name": "天氣",
            "description": "查詢城市天氣",
            "version": "1.0.0",
            "author": "ErisDev",
            "group": "工具",
            "tags": ["天氣", "查詢"],
        }

meta 字段的 i18n 支援

元資訊字段值可用純字串,或 i18n 字典 {"i18n": "key.path", "default": "兜底文本"}(與配置 description 約定一致)。 翻譯鍵透過 I18nClass 聲明註冊,module.get_meta() 讀取時自動解析為當前語言文本:

class MyModule(BaseModule):
    class I18nClass(BaseI18n):
        meta_description: I18nKey = I18nKey(
            default="Weather lookup",
            zh_CN="查詢城市天氣",
            en="Weather lookup",
        )

    @staticmethod
    def get_meta() -> ModuleMeta:
        return ModuleMeta(
            name="天氣",
            description={"i18n": "MyModule.meta_description", "default": "Weather lookup"},
        )

SDK 物件

透過 sdk 物件存取核心功能:

from ErisPulse import sdk

sdk.storage    # 存儲系統
sdk.config     # 配置系統
sdk.logger     # 日誌系統
sdk.adapter    # 適配器系統
sdk.router     # 路由系統
sdk.lifecycle  # 生命週期系統

繼續學習路徑

寫完之後