Руководство по диагностике модулей
Когда модуль "не отвечает", симптомы можно разделить на три категории проблем, каждая из которых имеет соответствующий инструмент диагностики в рамках фреймворка (RFC EPRFC-2026-001, направление пять):
| Симптом | Инструмент диагностики | Уровень диагностики |
|---|---|---|
| Модуль не загружен | ErisPulse.runtime.explain_module(name) |
Цепочка регистрации и загрузки |
| Событие не обрабатывается | ErisPulse.runtime.explain_event(event) |
Проверка входа в систему распределения |
| Команда не запускается | Цепочка принятия решений о распределении (тестовый DispatchTrace / встроенный в фреймворк trace) |
Цепочка определения команд |
Оба диагностических функции являются чисто чтением операциями, не изменяющими никакого состояния, и могут быть вызваны в любое время; возвращают машинно-читаемый словарь, который можно отформатировать с помощью format_report() для получения текста, удобного для восприятия человеком.
Сценарий 1: Модуль не загружен
from ErisPulse.runtime import explain_module, format_report
report = explain_module("MyModule")
print(format_report(report))
explain_module() проверяет каждый пункт и выдает заключение, охватывая следующие причины:
| Проверяемый элемент | Описание |
|---|---|
| Не зарегистрирован | Пакет не установлен, имя группы entry-point указано неверно, или имя регистрации не совпадает с именем запроса |
| Ленивая загрузка не инициализирована | Нормальное состояние, а не ошибка: модуль ленивой загрузки инициализируется при первом вызове (например, module.call или запуск команды) |
| Отключено в конфигурации | ErisPulse.modules.status.<имя модуля> = false (если не настроено, по умолчанию включено) |
| Зависимости не загружены | В списке depends, объявленном модулем, есть модули, которые не готовы |
| Несовместимая версия SDK | В метаданных модуля указано значение min_sdk_version, которое выше текущей версии фреймворка |
| Ошибка при on_load | Регистрация прошла успешно, но модуль не загружен, и нет вышеуказанных причин — проверьте записи ERROR в журнале запуска, соответствующие имени модуля |
Возвращаемая структурированная структура dict: registered / loaded / lazy / enabled (None означает, что по умолчанию включено, если не настроено) / missing_dependencies / sdk_version_ok / conclusion (заключение в одной фразе) / reasons (список причин).
Сценарий 2: Событие не отвечает
from ErisPulse.runtime import explain_event, format_report
report = explain_event(event) # Event, полученный внутри обработчика, или исходный словарь события
print(format_report(report))
explain_event() выводит результаты в порядке фактической проверки на входе маршрутизации:
- Платформа-адаптер не зарегистрирована: экземпляр адаптера с указанным
platformотсутствует — событие вообще не попадает в фреймворк. - Идентификационный измеритель отвергнут пространством действия: пользователь / сессия / Bot / адаптер заблокированы — событие полностью отбрасывается на входе маршрутизации. Конфигурация пространства действия см. в модульной конфигурации.
- Модуль заблокирован для текущей сессии: различаются текущая сессия
available_modules(доступные) иblocked_modules(заблокированные пространством действия). - Текст напоминает команду, но не соответствует ни одной: текст с префиксом команды, но не соответствует ни одной зарегистрированной команде — проверьте конфигурацию префикса и имена команд.
Если все входные проверки пройдены, но ответа по-прежнему нет, вывод указывает на дальнейшее исследование двух мест:
- Условия фильтрации обработчика: условия
detail_type/pattern=/regex=и т.д. не выполняются; - Среднеслойное отклонение: если среднеслой явно возвращает
False, событие отбрасывается на уровне события и запускает хук жизненного циклаadapter.event.blocked(с именем среднеслоя и полным событием) — можно зарегистрировать этот хук для аудита "кто отбросил событие".
Сценарий 3: Команда не была запущена (цепочка принятия решений при диспетчеризации)
Для того, чтобы сообщение с префиксом действительно выполнило команду, оно должно последовательно пройти через: определение текста команды → сопоставление команды (с подсказкой по опечатке при неудаче) → область действия → пользовательский ACL → проверка владельца → функция разрешений → охлаждение / ограничение скорости / скрытое отбрасывание из-за лимита использования → отказ и уведомление об устаревании → парсинг параметров → выполнение. Рамка фиксирует каждую точку принятия решения как причинно-следственную цепочку и даёт заключение о том, "почему команда не была запущена".
В процессе тестирования: TestBot.dispatch возвращает DispatchTrace
Рекомендуется использовать тестирование для воспроизведения проблемы и непосредственно читать причинно-следственную цепочку (инструкции по использованию инструментов см. в модульном тестировании):
trace = await bot.dispatch(create_command_event("dailyx", user_id="123"))
trace.verdict # executed / rejected / dropped / failed / no_match / passed
print(trace.explain()) # построчное описание причинно-следственной цепочки (в текущем языке)
trace.assert_no_match()
Встроенный модуль trace в рамке
Цепочка принятия решений предоставляется ErisPulse.Core.Event.trace и по умолчанию не имеет дополнительных накладных расходов — если не находится в контексте сбора данных, точки принятия решений просто пропускаются, и производственные пути остаются незамеченными:
from ErisPulse.Core.Event import (
start_dispatch_trace,
format_dispatch_trace,
final_verdict,
)
with start_dispatch_trace() as records:
... # сбор данных о диспетчеризации (включая производные задачи обработчиков)
print(format_dispatch_trace(records)) # человекочитаемая причинно-следственная цепочка (в текущем языке)
print(final_verdict(records)) # общий вывод
Значения final_verdict():
| Заключение | Значение |
|---|---|
executed |
Команда была выполнена |
rejected |
Была отклонена по правилам разрешений (область действия / ACL / владелец / функция разрешений) |
dropped |
Была скрыто отброшена (охлаждение / ограничение скорости / лимит использования / отказ промежуточного слоя) |
failed |
Возникла ошибка при выполнении |
no_match |
С префиксом, но не совпало ни с одной командой |
passed |
Это не текст команды, передано обработчику сообщений |
Записи представлены в виде машинно-читаемого словаря (stage / verdict / message_key / params), пользователь может отфильтровать по stage при настройке собственного отображения (например, показать только cooldown).
Определение скрытого срабатывания правил управления
При срабатывании cooldown= / rate_limit= / usage_limit= по умолчанию происходит скрытое отбрасывание (команда всё равно считается активированной, не передаётся низкоприоритетному обработчику), что может быть ошибочно расценено как "команда сломана": наблюдаемое поведение — некоторые пользователи могут использовать команду, а другие получают нулевой ответ, и в причинно-следственной цепочке появляется запись dropped для соответствующего stage. Команда с deprecated= ведёт себя иначе — при вызове автоматически отправляется сообщение об устаревании (отказ от выполнения при deprecated_reject=True).
Общие рекомендации
- Перед началом отладки установите уровень логирования в
DEBUG/TRACE(конфигурация описана в Руководстве для разработчиков), чтобы увидеть внутренние процессы фреймворка, такие как загрузка модулей, регистрация маршрутов и распределение событий; explain_module/explain_eventможно вызывать в любое время, они чисто для чтения и не имеют побочных эффектов, подходит для привязки к операционным командам или панели управления;- В случае проблем с "незапуском команды" сначала создайте тест с трассировкой
DispatchTraceдля воспроизведения — при неудаче утвержденийassert_executed/assert_rejectedбудет автоматически прикреплена полная цепочка причинно-следственных связей.