name: erispulse-ecosystem description: "适用于:了解生态组件(Dashboard/Cron/AI 模块等)与各平台适配器的接入差异。覆盖场景:Сама платформа ErisPulse предоставляет только основные возможности (система событий, система модулей, конфигурация, маршрутизация, логирование и т.д.), не включая в себя такие "тяжелые" функции, как GUI, рендеринг изображений, визуализация и т.д. Эти возможности предоставляются третьими сторонами и поддерживаются сообществом, и устанавливаются по мере необходимости.。Когда это нужно читать: Когда вы хотите использовать веб-интерфейс для визуального управления роботом (включение/выключение модулей, редактирование конфигурации, просмотр логов, мониторинг потоков событий), или хотите зарегистрировать страницу управления для своего модуля. Если не нужно — пропустите.。Когда стоит читать этот документ: когда вашему модулю требуется логика запуска по расписанию или с задержкой (напоминания по расписанию, периодический мониторинг, планирование по Cron-выражениям), и вы хотите, чтобы задачи не терялись после перезапуска фреймворка. Если это не ваш сл..."
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 按需读取:只读需要的章节即可,不必全文加载。
- 生态总览: 52-71 行
- Рекомендуемые модули: 73-113 行
- Официальный клиент (независимое приложение): 115-123 行
- Я тоже хочу добавить свой модуль сюда?: 125-133 行
- Dashboard: 135-162 行
- Принцип работы: 164-175 行
- API регистрации: 177-215 行
- Два режима встраивания: 217-250 行
- Группировка в боковой панели: 252-278 行
- Распространённые CSS-классы: 280-315 行
- Аутентификация и вызов API: 317-339 行
- Полный пример модуля: 341-451 行
- Удаление визуального окна: 453-465 行
- Примечания: 467-477 行
- Дальнейшие шаги: 479-483 行
- 定时任务: 485-505 行
- Краткий обзор возможностей: 507-516 行
- Быстрый старт: 518-536 行
- Обзор API: 538-593 行
- Поведение при выгрузке модуля: 595-602 行
- Полезные ссылки: 604-609 行
- Следующие шаги: 611-615 行
- AI 对话模块: 617-635 行
- Основные возможности: 637-656 行
- Модульное расширение: 658-728 行
- Подписка на события: 730-742 行
- Вызов как сервис: 744-750 行
- Интерфейс: 752-760 行
- Следующие шаги: 762-767 行
- 平台指南索引: 769-786 行
- Платформо-специфичные функции: 788-811 行
- Общие интерфейсы: 813-991 行
- Стандартный формат: 993-1047 行
- Ссылки: 1049-1059 行
- Участие в разработке: 1061-1070 行
生态总览
来源:
docs/ru/ecosystem/README.md(https://github.com/ErisPulse/ErisPulse/blob/main/docs/ru/ecosystem/README.md)
Экологические модули
Сама платформа ErisPulse предоставляет только основные возможности (система событий, система модулей, конфигурация, маршрутизация, логирование и т.д.), не включая в себя такие "тяжелые" функции, как GUI, рендеринг изображений, визуализация и т.д. Эти возможности предоставляются третьими сторонами и поддерживаются сообществом, и устанавливаются по мере необходимости.
Important
Документация в этом разделе делится на два типа, способ установки которых различается:
Модули (например, Dashboard / Takumi) устанавливаются с помощью
epsdk install:epsdk install <имя_модуля>Отдельные программы (например, клиент ErisPulse-App) устанавливаются напрямую из GitHub Releases, без использования
epsdk.
Рекомендуемые модули
ErisPulse-Dashboard: Панель управления в браузере
Запускаете бота на сервере, но управляете модулями, настройками и логами через терминал? Dashboard переносит эти функции в браузер: управление модулями, редактирование конфигураций, просмотр логов, мониторинг событий; другие модули могут регистрировать собственные окна в боковой панели, и все управление сосредоточено в одном месте.
epsdk install Dashboard
Документация: Использование Dashboard и регистрация окон | Главная страница проекта: PyPI
ErisPulse-Cron: Планировщик задач
Нужно ежедневно отправлять напоминания о регистрации, выполнять задачи по расписанию с помощью Cron-выражений, или периодически запрашивать данные каждые несколько минут? Cron предоставляет три типа задач: одноразовые, интервальные и по Cron-выражениям, с передачей параметров в обратный вызов, а также SQLite-хранилище (задачи не теряются при перезапуске).
epsdk install Cron
Документация: Планировщик задач Cron | Главная страница проекта: GitHub
ErisPulse-QvQChat: Общий агент
Хотите, чтобы в группе был AI-ассистент, который может выполнять полезные задачи, а не просто отвечать по ключевым словам? QvQChat — это универсальный агент: циклически обрабатывает отправку и получение сообщений, выполнение кода, управление компьютером, генерацию изображений, голосовые сообщения, MCP и подагентов, поддерживает потоковой диалог и последовательное выполнение инструментов, включает в себя окно Dashboard.
epsdk install ErisPulse-QvQChat
Документация: Агент QvQChat | Главная страница проекта: GitHub
ErisPulse-Takumi: Генерация изображений
Для сцен, где требуется "отправлять изображения", например, рейтинги, отчеты о боях или карточки расписания, достаточно написать шаблон и отрендерить изображение для отправки в группу: поддержка HTML / деревьев узлов / Jinja / SVG / анимации (на основе takumi-py), включает шрифты на китайском и английском языках, готов к использованию без дополнительной настройки.
epsdk install Takumi
Документация: Генерация изображений Takumi | Главная страница проекта: GitHub (автор @ccd2s)
Официальный клиент (независимое приложение)
ErisPulse-App: запуск бота прямо на телефоне
Нет постоянного сервера, но вы хотите создавать экземпляры, устанавливать модули и просматривать поток событий в любое время? ErisPulse-App — это официальный клиент для всех платформ (Android / Windows / Linux / macOS): создание / запуск / управление несколькими экземплярами ботов с помощью родного интерфейса, встроенный магазин модулей и конструктор событий, запуск прямо с телефона, постоянное присутствие в системном трее на рабочем столе.
Скачайте установочные пакеты для всех платформ с GitHub Releases, для этого не требуется epsdk.
Документация: Установка и использование ErisPulse-App
Я тоже хочу добавить свой модуль сюда?
Добро пожаловать, чтобы рекомендовать качественные, повторно используемые модули экосистемы ErisPulse. Требования:
- Опубликован на PyPI, и имя пакета начинается с
ErisPulse- - Предоставляет базовый README и примеры использования
- Активно поддерживается, с ответами на Issue
Авторы модулей, соответствующих вышеуказанным условиям, могут добавить документ <имя_модуля>.md в этом каталоге через PR и дополнить соответствующий раздел в рекомендованном списке вверху.
Dashboard
来源:
docs/ru/ecosystem/dashboard.md(https://github.com/ErisPulse/ErisPulse/blob/main/docs/ru/ecosystem/dashboard.md)
ErisPulse-Dashboard
Tip
Когда это нужно читать: Когда вы хотите использовать веб-интерфейс для визуального управления роботом (включение/выключение модулей, редактирование конфигурации, просмотр логов, мониторинг потоков событий), или хотите зарегистрировать страницу управления для своего модуля. Если не нужно — пропустите.
ErisPulse-Dashboard — это веб-панель управления, поддерживаемая ErisDev, которая предоставляет визуальный интерфейс для управления ErisPulse в режиме выполнения: включение/выключение модулей, редактирование конфигурации, просмотр логов, мониторинг потоков событий и т.д.
Important
Dashboard не является встроенной функцией фреймворка ErisPulse, его нужно устанавливать отдельно:
epsdk install Dashboard
Dashboard также поддерживает регистрацию пользовательских страниц управления другими модулями ErisPulse в боковой панели. После регистрации пользователь может напрямую переключиться на специальную визуальную страницу модуля в Dashboard, без необходимости дополнительного разработки отдельного пользовательского интерфейса.
Note
Регистрация визуальных окон — это необязательная функция.
- Если модуль Dashboard не установлен или не загружен, вызов
sdk.Dashboard.register_view()вызовет исключение - Обязательно используйте
try/exceptдля обертывания кода регистрации, чтобы убедиться, что другие функции модуля не будут затронуты - Рекомендуется проверять доступность Dashboard перед регистрацией:
hasattr(sdk, 'Dashboard') and sdk.Dashboard
Принцип работы
Модуль on_load()
→ вызов sdk.Dashboard.register_view(...)
→ Dashboard сохраняет информацию о визуальном окне
→ WebSocket уведомляет клиент
→ Клиент динамически создает элемент навигации в боковой панели + контейнер страницы
→ Пользователь может переключиться на страницу модуля
API регистрации
sdk.Dashboard.register_view(
id="MyModule", # Обязательный, уникальный идентификатор
title="Мой модуль", # Название на китайском
title_en="My Module", # Название на английском
icon_svg='<svg>...</svg>', # SVG-иконка для боковой панели
html_content='<div>...</div>', # HTML-контент страницы
js_content='function xxx() {}', # JavaScript-логика страницы
css_content='.my-style {}', # Необязательный пользовательский CSS
iframe_url='', # URL для режима iframe (один из двух: html_content или iframe_url)
loader="loadMyModuleView", # Имя функции JavaScript, вызываемой при переключении на страницу
group="group_extensions", # Группа в боковой панели
group_title="", # Название группы на китайском
group_title_en="", # Название группы на английском
)
Описание параметров
| Параметр | Тип | Обязательно | Описание |
|---|---|---|---|
id |
str |
Да | Уникальный идентификатор визуального окна, рекомендуется использовать название модуля |
title |
str |
Нет | Название для отображения на китайском, по умолчанию используется id |
title_en |
str |
Нет | Название для отображения на английском, по умолчанию используется title |
icon_svg |
str |
Нет | Полная строка SVG-иконки для боковой панели |
html_content |
str |
Нет* | HTML-контент страницы в режиме встраивания |
js_content |
str |
Нет | JavaScript-код страницы |
css_content |
str |
Нет | Пользовательский CSS-стиль страницы |
iframe_url |
str |
Нет* | URL для режима iframe, при указании игнорируется html_content |
loader |
str |
Нет | Имя функции JavaScript, вызываемой при активации страницы |
group |
str |
Нет | Идентификатор группы в боковой панели, по умолчанию group_extensions |
group_title |
str |
Нет | Название группы на китайском |
group_title_en |
str |
Нет | Название группы на английском |
*Обязательно указывать либо
html_content, либоiframe_url, иначе страница будет пустой.
Два режима встраивания
Режим 1: HTML/JS встраивание (рекомендуется)
Предоставляйте HTML, JS и CSS напрямую, Dashboard вставит содержимое на страницу. Этот режим полностью соответствует стилям Dashboard, рекомендуется использовать CSS-классы, предоставляемые Dashboard.
sdk.Dashboard.register_view(
id="HelloPage",
title="Привет страница", title_en="Hello",
icon_svg='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/></svg>',
html_content='<h1 class="page-title">Hello World</h1><div class="card"><div class="card-body">Это пример страницы</div></div>',
group="group_tools",
)
Полный пример модуля погоды (с API-маршрутами, JS-взаимодействием и т.д.) см. ниже Полный пример модуля.
Режим 2: Встраивание с помощью iframe
Модуль предоставляет URL своей HTML-страницы (необходимо зарегистрировать маршрут), Dashboard встраивает её в iframe. Подходит для сценариев с полностью независимым UI или сложным взаимодействием.
sdk.Dashboard.register_view(
id="MyVisualizer",
title="Визуализация данных", title_en="Data Visualizer",
iframe_url="/MyVisualizer/view",
group="group_tools",
)
В режиме iframe автоматически добавляется параметр
tokenв URL для аутентификации.
Группировка в боковой панели
Модуль может указать группу, в которой будет отображаться визуальное окно. Dashboard предоставляет следующие встроенные группы:
| Идентификатор группы | Название на китайском | Позиция |
|---|---|---|
group_overview |
Обзор | 1-я группа |
group_events |
События | 2-я группа |
group_extensions |
Расширения | 3-я группа (по умолчанию) |
group_system |
Система | 4-я группа |
group_tools |
Инструменты | 5-я группа |
Указав идентификатор встроенной группы, визуальное окно будет добавлено в конец этой группы:
group="group_tools" # Добавление в группу "Инструменты"
Также можно использовать пользовательские идентификаторы групп (не начинающиеся с group_), Dashboard автоматически создаст новую группу:
group="my_group",
group_title="Моя группа",
group_title_en="My Group",
Распространённые CSS-классы
При использовании режима встраивания HTML, можно напрямую использовать CSS-классы, предоставленные Dashboard, для обеспечения визуальной согласованности:
| Класс | Назначение |
|---|---|
page-title |
Заголовок страницы, например <h1 class="page-title">Заголовок</h1> |
card |
Контейнер карточки |
card-header |
Заголовок карточки |
card-body |
Тело карточки |
grid-2 |
Двухколоночный макет сетки |
grid-3 |
Трёхколоночный макет сетки |
btn |
Базовая кнопка |
btn-primary |
Основная кнопка (синяя) |
btn-secondary |
Второстепенная кнопка |
btn-icon |
Кнопка с иконкой |
btn-danger |
Кнопка опасного действия |
Dashboard использует CSS-переменные для управления темой, которые можно использовать в модуле:
| CSS-переменная | Назначение |
|---|---|
var(--bg-p) |
Основной фон |
var(--bg-s) |
Вторичный фон |
var(--bg-t) |
Третичный фон (карточки и т.д.) |
var(--tx-p) |
Основной цвет текста |
var(--tx-s) |
Вторичный цвет текста |
var(--tx-t) |
Вспомогательный цвет текста |
var(--bd) |
Цвет границы |
var(--accent) |
Акцентный цвет |
var(--ok-c) |
Цвет успеха |
var(--er-c) |
Цвет ошибки |
Эти переменные автоматически переключаются между светлой и тёмной темами Dashboard, модулю не нужно дополнительно обрабатывать.
Аутентификация и вызов API
При вызове API модуля из JavaScript визуального окна необходимо передавать токен Dashboard для аутентификации:
var token = localStorage.getItem('__ep_tk__');
var resp = await fetch('/YourModule/api/data', {
headers: { 'Authorization': 'Bearer ' + token }
});
var data = await resp.json();
API-точки модуля могут самостоятельно решать, нужно ли проверять токен. Если нужно проверить, можно извлечь его из заголовка:
async def _api_data(self, request):
token = request.headers.get("Authorization", "").replace("Bearer ", "")
if not token:
return {"error": "Unauthorized"}, 401
return {"data": "hello"}
Полный пример модуля
Ниже приведён полный пример модуля погоды, демонстрирующий регистрацию визуального окна, предоставление данных API и очистку ресурсов при выгрузке:
from ErisPulse import sdk
from ErisPulse.Core.Bases import BaseModule
from ErisPulse.Core.Event import command
class Main(BaseModule):
def __init__(self):
self.sdk = sdk
self.logger = sdk.logger.get_child("Weather")
self.config = self._load_config()
@staticmethod
def get_load_strategy():
from ErisPulse.loaders import ModuleLoadStrategy
return ModuleLoadStrategy(lazy_load=False, priority=50)
async def on_load(self, event):
self._register_routes()
self._register_dashboard_view()
self.logger.info("Модуль погоды загружен")
async def on_unload(self, event):
self._unregister_routes()
if hasattr(self.sdk, 'Dashboard') and self.sdk.Dashboard:
self.sdk.Dashboard.unregister_view("Weather")
self.logger.info("Модуль погоды выгружен")
def _load_config(self):
config = self.sdk.config.getConfig("Weather")
if not config:
default = {"city": "Пекин", "api_key": ""}
self.sdk.config.setConfig("Weather", default)
return default
return config
def _register_routes(self):
r = self.sdk.router
r.register_http_route("Weather", "/api/current",
handler=self._api_current, methods=["GET"])
def _unregister_routes(self):
r = self.sdk.router
try:
r.unregister_http_route("Weather", "/api/current")
except Exception:
pass
async def _api_current(self, request):
return {
"city": self.config.get("city", "Пекин"),
"temp": 25,
"humidity": 60,
}
def _register_dashboard_view(self):
try:
dashboard = self.sdk.Dashboard
dashboard.register_view(
id="Weather",
title="Погода", title_en="Weather",
icon_svg='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>',
html_content='''
<h1 class="page-title">Погодный запрос</h1>
<p style="color:var(--tx-s);margin-bottom:16px">Просмотр текущей погоды</p>
<div class="grid-2">
<div class="card">
<div class="card-header">Текущая погода</div>
<div class="card-body">
<div id="weather-info" style="font-size:14px;color:var(--tx-s)">Нажмите, чтобы обновить</div>
</div>
</div>
<div class="card">
<div class="card-header">Действия</div>
<div class="card-body">
<button class="btn btn-primary" onclick="refreshWeather()">Обновить</button>
</div>
</div>
</div>
''',
js_content='''
async function loadWeatherView() { await refreshWeather(); }
async function refreshWeather() {
var el = document.getElementById('weather-info');
if (!el) return;
el.textContent = 'Загрузка...';
try {
var resp = await fetch('/Weather/api/current', {
headers: { 'Authorization': 'Bearer ' + localStorage.getItem('__ep_tk__') }
});
var data = await resp.json();
el.innerHTML = '<p>Город: ' + (data.city || '--') + '</p>' +
'<p>Температура: ' + (data.temp || '--') + '°C</p>' +
'<p>Влажность: ' + (data.humidity || '--') + '%</p>';
} catch (e) {
el.textContent = 'Загрузка не удалась: ' + e.message;
}
}
''',
loader="loadWeatherView",
group="group_tools",
)
except Exception as e:
self.logger.warning(f"Регистрация визуального окна Dashboard не удалась: {e}")
Удаление визуального окна
При выгрузке модуля следует вызвать unregister_view() для удаления зарегистрированного визуального окна:
async def on_unload(self, event):
if hasattr(self.sdk, 'Dashboard') and self.sdk.Dashboard:
self.sdk.Dashboard.unregister_view("Weather")
После удаления Dashboard будет удалять элементы навигации и контент в реальном времени через WebSocket, без необходимости обновления страницы.
Примечания
- Порядок загрузки — Dashboard загружается с приоритетом
99999(очень высокий приоритет), приоритет вашего модуля должен быть ниже, например50, чтобы убедиться, что Dashboard загружен первым - Защитное программирование — оберните код регистрации визуального окна в
try/except, потому что модуль Dashboard может не быть установлен или загружен - Очистка ресурсов — в
on_unloadвызовитеunregister_view(), чтобы удалить зарегистрированное визуальное окно - Уникальность ID — параметр
idдолжен быть уникальным во всём Dashboard, рекомендуется использовать имя модуля - SVG-иконки —
icon_svgдолжен быть полной строкой<svg>, рекомендуется использоватьviewBox="0 0 24 24", и использоватьstroke="currentColor"для наследования темы Dashboard - Имена функций JS — имена функций в
js_contentдолжны быть уникальными (например,loadWeatherView), чтобы избежать конфликтов с другими модулями - Динамическое обновление — после регистрации/удаления визуального окна Dashboard будет обновлять боковую панель в реальном времени через WebSocket, без необходимости обновления страницы
Дальнейшие шаги
- Не хотите использовать компьютер, хотите управлять роботом с телефона: ErisPulse-App (официальный мультиплатформенный клиент, использует ту же API, что и Dashboard, визуальные окна также доступны)
- Нужно предоставить данные API для визуального окна: Менеджер маршрутов для регистрации HTTP-маршрутов, справочник методов см. в API основных модулей - Router
- Нужно смотреть в реальном времени логи / потоки событий: Подписка на логи (режим push) и Управление жизненным циклом
定时任务
来源:
docs/ru/ecosystem/cron.md(https://github.com/ErisPulse/ErisPulse/blob/main/docs/ru/ecosystem/cron.md)
ErisPulse-Cron
Tip
Когда стоит читать этот документ: когда вашему модулю требуется логика запуска по расписанию или с задержкой (напоминания по расписанию, периодический мониторинг, планирование по Cron-выражениям), и вы хотите, чтобы задачи не терялись после перезапуска фреймворка. Если это не ваш случай — пропустите.
ErisPulse-Cron — это модуль планирования задач по расписанию в экосистеме ErisPulse, предоставляющий другим модулям единый API для работы с отложенными задачами: поддерживает три типа задач — однократный запуск по времени, циклический запуск с интервалом и Cron-выражения, передачу параметров в колбэки, а также персистентность в SQLite (задачи не теряются после перезапуска).
Important
Cron не является встроенной функциональностью фреймворка ErisPulse и требует отдельной установки:
epsdk install Cron
После установки весь API доступен через sdk.Cron.
Краткий обзор возможностей
- Три типа расписания: однократный запуск (
once), циклический запуск с интервалом (interval), Cron-выражения (cron) - Передача параметров в колбэки: при создании передаётся
callback_data, которая при срабатывании возвращается без изменений — это удобно для определения источника задачи - Персистентность: задачи хранятся в SQLite (через
sdk.storage) и автоматически восстанавливаются после перезапуска фреймворка - Политика пропущенных запусков: немедленный запуск / пропуск / перепланирование — выбирается отдельно для каждой задачи
- Управление задачами: пауза, возобновление, отмена, ручной запуск, очистка устаревших задач
- Интеграция с Dashboard: при установленном ErisPulse-Dashboard панель управления регистрируется автоматически
Быстрый старт
from ErisPulse import sdk
# 1. Регистрация обработчика колбэка
@sdk.Cron.on_trigger
async def handle_trigger(info):
data = info["callback_data"]
print(f"Задача сработала: {info['task_id']}, данные: {data}")
# 2. Создание задачи по расписанию
task_id = sdk.Cron.once(
delay=60,
callback_data={"type": "reminder", "msg": "Пора попить воды"},
)
Обзор API
Создание задач
# Однократная задача: срабатывает с задержкой 600 секунд
sdk.Cron.once(delay=600, callback_data={"order_id": "123"}, label="Напоминание о таймауте заказа")
# Циклическая задача: срабатывает каждые 300 секунд, максимум 100 раз
sdk.Cron.interval(interval_seconds=300, callback_data={"monitor": "server-1"}, max_runs=100)
# Cron-выражение: по будням каждый день в 9:30
sdk.Cron.cron(expression="30 9 * * 1-5", callback_data={"type": "daily_report"})
# Общие необязательные параметры: trigger_at (абсолютная временная метка), delay (задержка первого запуска), timezone,
# max_runs (0=бесконечно), label, source (имя модуля-создателя), missed_policy (политика пропущенных запусков)
Распространённые Cron-выражения: */5 * * * * (каждые 5 минут), 0 8 * * * (каждый день в 8 утра), 30 9 * * 1-5 (по будням в 9:30), 0 0 1 * * (1-го числа каждого месяца).
Колбэки
@sdk.Cron.on_trigger
async def my_handler(info):
# info содержит task_id / task_type / callback_data / label / source /
# run_count / max_runs / created_at / last_run / trigger_time
...
Поддерживается регистрация нескольких обработчиков (handler); все они вызываются по очереди, и исключение в одном из них не влияет на работу остальных.
Управление задачами
sdk.Cron.cancel(task_id) # Отмена
sdk.Cron.pause(task_id) # Пауза
sdk.Cron.resume(task_id) # Возобновление (reschedule=True пересчитывает время следующего запуска)
await sdk.Cron.trigger_now(task_id) # Ручной немедленный запуск (не влияет на исходное расписание)
sdk.Cron.get_task(task_id) # Просмотр отдельной задачи
sdk.Cron.list_tasks(source="MyModule") # Список задач (поддерживается фильтрация по source/status/task_type)
sdk.Cron.delete_task(task_id) # Удаление записи задачи
sdk.Cron.cleanup() # Очистка выполненных/отменённых задач старше 7 дней
Политика пропущенных запусков (missed_policy)
После перезапуска фреймворка для задач, у которых было пропущено время срабатывания:
| Политика | Поведение |
|---|---|
fire_immediately |
Немедленный запуск (по умолчанию) |
skip |
Пропустить текущий запуск и ждать следующего |
reschedule |
Пересчитать время следующего запуска от текущего момента |
Поведение при выгрузке модуля
Данные задач Cron являются персистентным активом: выгрузка или отключение модуля-создателя задачи не приводит к удалению уже созданных задач. Однако зарегистрированные этим модулем обработчики колбэков будут очищены — благодаря хукам внешней очистки на основе системы владения, Cron при хранении колбэков от имени других модулей автоматически фиксирует их владельца, и при выгрузке/отключении модуля-владельца его обработчики колбэков автоматически отбрасываются, что гарантирует корректную утилизацию экземпляров этого модуля.
- После перезагрузки модуля-создателя достаточно заново зарегистрировать
on_trigger, чтобы восстановить приём срабатываний - Ненужные задачи можно очистить с помощью
sdk.Cron.cancel(task_id)/delete_task(task_id)
Полезные ссылки
Следующие шаги
- Отправка сообщений пользователю после срабатывания задачи: в колбэке используйте Send DSL или
event.reply()(см. API системы событий) для доставки напоминания - Визуальное управление задачами по расписанию: после установки ErisPulse-Dashboard Cron автоматически регистрирует панель управления
- Детали очистки колбэков при выгрузке модуля: система владения (owner)
AI 对话模块
来源:
docs/ru/ecosystem/qvqchat.md(https://github.com/ErisPulse/ErisPulse/blob/main/docs/ru/ecosystem/qvqchat.md)
ErisPulse-QvQChat — Универсальный агент
Tip
Когда читать этот документ: если вы хотите добавить боту возможности ИИ-агента (цикл инструментов, потоковый диалог, MCP, долговременная память) или предоставить сервисы собственного модуля для вызова агентом. Если это не требуется — пропустите.
ErisPulse-QvQChat — это универсальный модуль агента, построенный вокруг цикла инструментов: отправка и получение сообщений, вызов сервисов экосистемных модулей, написание и запуск кода, управление компьютером, генерация изображений и речи, управление задачами по расписанию и долговременной памятью — всё это осуществляется через единый протокол инструментов.
Important
QvQChat требуется установить отдельно:
epsdk install ErisPulse-QvQChat
Кроме того, необходимо добавить в «Пул моделей» как минимум одну модель, совместимую с OpenAI (Base URL, API Key, название модели).
Основные возможности
| Возможность | Описание |
|---|---|
| Цикл инструментов | Модель генерирует вызов инструмента → параллельное выполнение → результат возвращается → продолжение вывода, пока не будет получен окончательный ответ |
| Потоковая беседа | SSE выводит по одному символу, шаги инструментов видны в реальном времени; при обновлении / переходе и возвращении прогресс не теряется |
| Сжатие контекста | При превышении порога токенов старый диалог автоматически сжимается в резюме (по умолчанию 128000 оценочных токенов, можно изменить) |
| Мост экосистемных сервисов | Автоматически обнаруживает объявленные модулем сервисы и преобразует их в инструменты, добавление/удаление модуля увеличивает/уменьшает возможности |
| Клиент MCP | stdio и Streamable HTTP, инструменты MCP объединяются в один набор инструментов |
| Подагенты | spawn_agent запускает независимый сеанс подагент для исследования задачи, возвращает только вывод |
| Выполнение в процессе | run_python_sdk выполняет Python в основном процессе, напрямую обращаясь к объектам среды выполнения |
| Управление компьютером | Трехуровневый безопасный песочник (strict / standard / permissive), подтверждение опасных операций, полный аудит |
| Визуальное восприятие | Изображения автоматически вставляются в модель (при отсутствии поддержки изображений автоматически удаляются и повторяется попытка) |
| Вложения | Любые файлы по hash-значению сохраняются на диск, путь передается ИИ для самостоятельного обработки; вставка скриншотов через Ctrl+V |
| Общий доступ к сеансу | Временная ссылка только для чтения (срок действия + бюджет токенов), экспорт в Markdown |
| Планировщик задач | cron / одноразовые / периодические, сохраняются, права доступа привязаны к создателю |
| Перехват сеанса | Администратор может предоставить агенту полный доступ к сеансу |
| Система личности | Системный промт + начальный диалог + белый список инструментов, может быть привязан к сеансу |
| Долгосрочная память | Каждый сеанс имеет записи памяти, которые вставляются по поиску по схожести |
| Автоматическое наименование ИИ | После первого ответа сеанса автоматически генерируется краткое название |
Модульное расширение
Любой модуль ErisPulse, объявляющий services, автоматически становится инструментом QvQChat без необходимости специального интерфейса:
from ErisPulse.Core.Bases import BaseModule, ModuleMeta
class Main(BaseModule):
@staticmethod
def get_meta() -> ModuleMeta:
return ModuleMeta(name="Draw", services=[
{"name": "draw", "description": "Генерирует изображение по описанию и возвращает URL изображения"},
])
async def draw(self, prompt: str, size: str = "1024x1024"):
return {"image_url": "https://..."}
Подпись метода автоматически сопоставляется с определением параметров JSON Schema инструмента, а первая строка docstring используется как описание параметра.
Глубокая интеграция (необязательно)
Модуль может дополнительно реализовать любую из следующих служб, каждая из которых необязательна — один модуль может быть предназначен только для расширения персонажа, а может только внедрять системные подсказки:
qvq_prompts() → Внедрение системных подсказок
Возвращает list[str] или list[{"text": str, "priority": int}], объединяя подсказки по возрастанию priority.
async def qvq_prompts(self):
return [
{"text": "Отправьте пользователю изображение с помощью send_message после рисования.", "priority": 10},
{"text": "Поддерживаемые стили: аниме, реализм, акварель, пиксель.", "priority": 20},
]
qvq_personas() → Регистрация предустановленных персонажей
Возвращает список персонажей, id автоматически добавляется префикс ext.<имя_модуля>., модуль при отключении автоматически очищается. Подходит для модулей, специально предоставляющих расширение персонажей (например, эмоциональное сопровождение, переводчик, рецензент кода и т.д.).
async def qvq_personas(self):
return [
{"id": "companion", "name": "Теплый парень", "system": "Ты — тёплый и внимательный собеседник..."},
{"id": "coder", "name": "Код-наставник", "system": "...", "tools": ["read_file", "run_python"]},
]
Параметр tools необязательно может ограничивать набор доступных инструментов для этого персонажа.
qvq_schemas() → Явное определение схемы параметров
Возвращает dict[имя_службы, JSON Schema], перезаписывая вывод из сигнатуры метода. Используется, когда параметры сложные или требуется строгая проверка.
async def qvq_schemas(self):
return {
"draw": {
"type": "object",
"properties": {
"prompt": {"type": "string", "description": "Описание изображения"},
"size": {"type": "string", "enum": ["512x512", "1024x1024"]},
},
"required": ["prompt"],
},
}
Время обновления
Время обновления глубоких интеграционных служб: запуск, установка и удаление модуля, ручное обновление на странице Dashboard «Инструменты».
Подписка на события
QvQChat транслирует два события жизненного цикла:
| Событие | Время | Параметры |
|---|---|---|
qvq.message |
Получено сообщение от пользователя | chat_key platform sender_id sender_name text is_group |
qvq.replied |
Отправлен ответ | chat_key reply tools |
async def setup(self):
sdk.lifecycle.on("qvq.message", self.on_message)
Вызов как сервис
| Сервис | Подпись | Описание |
|---|---|---|
chat |
(session_id, prompt, persona="") |
Диалог с агентом, возвращает текст |
run_prompt |
(chat_key, prompt, ...) |
Выполнение подсказки, результат может быть отправлен в целевой сеанс |
get_status |
() |
Сводка о состоянии выполнения |
Интерфейс
Доступ осуществляется автономно через /QvQChat/view, либо в виде встраивания в боковую панель Dashboard.


Следующие шаги
- Превратите свой модуль в инструмент агента: достаточно объявить
servicesвget_meta(), семантику полей см. в API основных модулей — контракт сервисов - Подпишитесь на сообщения агента для реализации взаимодействия: используйте события жизненного цикла для прослушивания
qvq.message/qvq.replied - Агент не нужен, нужна лишь многошаговая форма вопросов и ответов: встроенный Conversation — многошаговый диалог — более лёгкое решение
- Управление через веб-интерфейс: используйте в сочетании с боковой панелью ErisPulse-Dashboard
平台指南索引
来源:
docs/ru/platform-guide/README.md(https://github.com/ErisPulse/ErisPulse/blob/main/docs/ru/platform-guide/README.md)
Документация по функциям платформы ErisPulse
Базовый протокол: OneBot12
В этом документе содержится руководство по платформенно-специфичным функциям, включающее:
- Примеры цепочки вызовов метода Send для каждого адаптера
- Объяснение платформенно-специфичных форматов событий/сообщений
Общие методы использования см. в:
Платформо-специфичные функции
Эта часть поддерживается разработчиками адаптеров и предназначена для описания отличий и расширений адаптера по сравнению со стандартом OneBot12. Пожалуйста, ознакомьтесь с подробной документацией для каждой платформы:
Кроме того, существует адаптер
sandbox, но для него не требуется документация по платформо-специфичным функциям
Общие интерфейсы
Chain-вызов Send
Все адаптеры поддерживают следующий стандартный способ вызова:
Важно:
{AdapterName}в документации нужно заменить на фактическое имя адаптера (например,yunhu,telegram,onebot11,
- Указание типа и идентификатора:
To(type,id).Func()# Получение экземпляра адаптера my_adapter = adapter.get("{AdapterName}") # Отправка сообщения await my_adapter.Send.To("user", "U1001").Text("Hello") # Например: yunhu = adapter.get("yunhu") await yunhu.Send.To("user", "U1001").Text("Hello") - Только указание идентификатора:
To(id).Func()my_adapter = adapter.get("{AdapterName}") await my_adapter.Send.To("U1001").Text("Hello") # Например: telegram = adapter.get("telegram") await telegram.Send.To("U1001").Text("Hello") - Указание отправляющего аккаунта:
Using(account_id)my_adapter = adapter.get("{AdapterName}") await my_adapter.Send.Using("bot1").To("U1001").Text("Hello") # Например: onebot11 = adapter.get("onebot11") await onebot11.Send.Using("bot1").To("U1001").Text("Hello") - Прямой вызов:
Func()my_adapter = adapter.get("{AdapterName}") await my_adapter.Send.Text("Broadcast message") # Например: email = adapter.get("email") await email.Send.Text("Broadcast message")
Асинхронная отправка и обработка результатов
Методы Send DSL возвращают объект asyncio.Task, что означает, что вы можете выбрать, нужно ли немедленно ожидать результат:
# Получение экземпляра адаптера
my_adapter = adapter.get("{AdapterName}")
# Не ждать результата, сообщение отправляется в фоне
task = my_adapter.Send.To("user", "123").Text("Hello")
# Если нужно получить результат отправки, можно ожидать позже
result = await task
Декораторы правил отправки
В реальном разработке часто требуется: выполнять последующую логику только после успешной отправки, автоматически повторять отправку при сбое, отменять при таймауте, отслеживать прогресс отправки и т.д. В Send DSL встроена система декораторов правил отправки, которые можно добавлять цепочкой методов:
| Метод | Описание |
|---|---|
.Hook(callback) |
Выполняется после успешной отправки (можно вызывать несколько раз) |
.Retry(times=1) |
Автоматически повторяет отправку N раз (включая первую, всего N+1 раз) |
.Timeout(seconds) |
Останавливает отправку при превышении времени (можно использовать вместе с Retry) |
.Defer(seconds) |
Откладывает отправку (внутрипроцессное таймерное ожидание, не сохраняется) |
.OnProgress(callback) |
Вызывает обратный вызов на каждом этапе, передаёт SendContext |
.OnError(callback) |
Вызывается при окончательном сбое (вызывается только один раз) |
yunhu = adapter.get("yunhu")
# Вычитание баллов только после успешной отправки
await (yunhu.Send.To("user", "123")
.Hook(lambda r: deduct_points("123"))
.Text("Успешная покупка"))
# Повтор отправки + отмена при таймауте + отслеживание прогресса
def on_progress(ctx):
print(f"Этап: {ctx.stage}, Попытка: {ctx.attempt + 1}/{ctx.max_attempts}")
task = (yunhu.Send.To("user", "123")
.Retry(3) # Повторяет до 3 раз
.Timeout(10) # Каждая отправка имеет таймаут 10 секунд
.OnProgress(on_progress)
.OnError(lambda ctx: notify_admin(ctx.error))
.Text("Важное уведомление"))
Методы правил возвращают self, и их необходимо вызывать до отправки методов (Text/Image и т.д.). SendContext содержит поля stage (pending/sending/retrying/success/failed/timeout), attempt, elapsed, error, result и т.д., что полезно для отслеживания.
Режим построения пакетов (Build)
В одной цепочке можно построить несколько методов отправки, а затем выполнить их все сразу. Подходит для сценариев "отправить несколько сообщений за один раз":
yunhu = adapter.get("yunhu")
# Строим несколько сообщений и отправляем их одновременно
results = await (yunhu.Send.To("user", "123")
.Build() # Переходим в режим построения
.Text("Уведомление 1")
.Image("pic.jpg")
.Text("Уведомление 2")
.send_all()) # Выполняем все сообщения
# results = [результат Text, результат Image, результат Text]
.send_all() по умолчанию выполняется параллельно (параллельная отправка, высокая эффективность). Если нужно гарантировать порядок доставки, вызовите .Sequential() для последовательного выполнения:
# Последовательное выполнение (гарантирует порядок) + повтор при сбое
await (yunhu.Send.To("group", "456")
.Build()
.Sequential() # Отправка по порядку
.Retry(2) # Каждая неудачная отправка будет повторена
.Text("Первое сообщение").Text("Второе сообщение")
.send_all())
Пакетная отправка использует стратегию "продолжать при сбое": если одна отправка не удалась, это не прерывает другие, и неудачные сообщения автоматически повторяются. Пакетная отправка также поддерживает Hook для всех успешных отправок, OnError при наличии ошибок и OnProgress для обратного вызова о прогрессе.
Подробное описание правил и построения пакетов см. в разделе SendDSL详解.
Обработка событий
Существует три способа обработки событий:
Обработка событий на уровне платформы:
from ErisPulse.Core import adapter, logger @adapter.on("event_type", raw=True, platform="{AdapterName}") async def handler(data): logger.info(f"Получено событие от {AdapterName}: {data}")Обработка стандартных событий OneBot12:
from ErisPulse.Core import adapter, logger # Обработка стандартного события OneBot12 @adapter.on("event_type") async def handler(data): logger.info(f"Получено стандартное событие: {data}") # Обработка стандартного события для конкретной платформы @adapter.on("event_type", platform="{AdapterName}") async def handler(data): logger.info(f"Получено событие от {AdapterName}: {data}")Обработка событий через модуль
Event: События в модулеEventоснованы на функцииadapter.on(), поэтому формат событий, предоставляемыхEvent, соответствует стандарту OneBot12.from ErisPulse.Core.Event import message, notice, request, command message.on_message()(message_handler) notice.on_notice()(notice_handler) request.on_request()(request_handler) command("hello", help="Отправить приветственное сообщение", usage="hello")(command_handler) async def message_handler(event): logger.info(f"Получено сообщение: {event}") async def notice_handler(event): logger.info(f"Получено уведомление: {event}") async def request_handler(event): logger.info(f"Получен запрос: {event}") async def command_handler(event): logger.info(f"Получена команда: {event}")
Наиболее рекомендуемый способ обработки событий — использование модуля Event, поскольку он предоставляет богатый набор типов событий и методов обработки.
Стандартный формат
Для удобства справки здесь приведён простой формат событий. Для получения подробной информации, пожалуйста, обратитесь к ссылкам выше.
Примечание: Ниже представлен базовый стандартный формат OneBot12, который может быть дополнен адаптерами. Для получения подробной информации, пожалуйста, обратитесь к описанию специфических функций каждого адаптера.
Стандартный формат событий
Формат преобразования событий, который должен реализовать каждый адаптер:
{
"id": "event_123",
"time": 1752241220,
"type": "message",
"detail_type": "group",
"platform": "example_platform",
"self": {"platform": "example_platform", "user_id": "bot_123"},
"message_id": "msg_abc",
"message": [
{"type": "text", "data": {"text": "Привет"}}
],
"alt_message": "Привет",
"user_id": "user_456",
"user_nickname": "ExampleUser",
"group_id": "group_789"
}
Стандартный формат ответа
Успешная отправка сообщения
{
"status": "ok",
"retcode": 0,
"data": {
"message_id": "1234",
"time": 1632847927.599013
},
"message_id": "1234",
"message": "",
"echo": "1234",
"{platform}_raw": {...}
}
Неудачная отправка сообщения
{
"status": "failed",
"retcode": 10003,
"data": null,
"message_id": "",
"message": "Отсутствуют необходимые параметры",
"echo": "1234",
"{platform}_raw": {...}
}
Ссылки
Проект ErisPulse:
- Основной репозиторий
- Библиотека адаптера Yunhu
- Библиотека адаптера Telegram
- Библиотека адаптера OneBot
Связанная официальная документация:
- Официальная документация протокола OneBot V11
- Официальная документация Telegram Bot API
- Официальная документация Yunhu
Участие в разработке
Мы приветствуем больше разработчиков, желающих участвовать в написании и поддержке документации адаптеров! Пожалуйста, выполните следующие шаги, чтобы отправить свой вклад:
- Сделайте форк репозитория ErisPuls.
- Создайте файл Markdown в каталоге
docs/platform-features/и назовите его в формате<platform-name>.md. - Добавьте ссылку на ваш вклад в адаптер и соответствующую официальную документацию в этот файл
README.md. - Отправьте Pull Request.
Благодарим за вашу поддержку!