Перейти к содержанию

Internals

На этой странице Подсистема cron обеспечивает выполнение задач по расписанию — от простых одноразовых задержек до повторяющихся заданий с cron-выражениями, с возможностью подключения навыков и кроссплатформенной доставкой.

Ключевые файлы

Файл Назначение
cron/jobs.py Модель задания, хранилище, атомарное чтение/запись jobs.json
cron/scheduler.py Цикл планировщика — обнаружение просроченных заданий, выполнение, отслеживание повторений
tools/cronjob_tools.py Регистрация и обработчик инструмента cronjob для модели
gateway/run.py Интеграция со шлюзом — тик cron в долгоиграющем цикле
hermes_cli/cron.py Подкоманды CLI hermes cron

Модель планирования

Поддерживаются четыре формата расписания:

Формат Пример Поведение
Относительная задержка 30m, 2h, 1d Одноразово, срабатывает через указанный промежуток времени
Интервал every 2h, every 30m Повторяющееся, срабатывает через равные промежутки
Cron-выражение 0 9 * * * Стандартный 5-польный синтаксис cron (минута, час, день, месяц, день недели)
Метка времени ISO 2025-01-15T09:00:00 Одноразово, срабатывает в точное время

Интерфейс для модели — это единый инструмент cronjob с операциями в стиле действий: create, list, update, pause, resume, run, remove.

Хранилище заданий

Задания хранятся в ~/.hermes/cron/jobs.json с атомарной записью (запись во временный файл, затем переименование). Каждая запись задания содержит:

[code] {
"id": "a1b2c3d4e5f6",
"name": "Daily briefing",
"prompt": "Summarize today's AI news and funding rounds",
"schedule": {
"kind": "cron",
"expr": "0 9 * * ",
"display": "0 9 * *
"
},
"skills": ["ai-funding-daily-report"],
"deliver": "telegram:-1001234567890",
"repeat": {
"times": null,
"completed": 42
},
"state": "scheduled",
"enabled": true,
"next_run_at": "2025-01-16T09:00:00Z",
"last_run_at": "2025-01-15T09:00:00Z",
"last_status": "ok",
"created_at": "2025-01-01T00:00:00Z",
"model": null,
"provider": null,
"script": null
}

[/code]

Состояния жизненного цикла задания

Состояние Значение
scheduled Активно, сработает в следующее запланированное время
paused Приостановлено — не сработает до возобновления
completed Лимит повторений исчерпан или одноразовое задание выполнено
running Выполняется в данный момент (переходное состояние)

Обратная совместимость

В старых заданиях может быть одно поле skill вместо массива skills. Планировщик нормализует это при загрузке — одиночный skill повышается до skills: [skill].

Среда выполнения планировщика

Цикл тиков

Планировщик работает по периодическому тику (по умолчанию: каждые 60 секунд):

[code] tick()
1. Acquire scheduler lock (prevents overlapping ticks)
2. Load all jobs from jobs.json
3. Filter to due jobs (next_run <= now AND state == "scheduled")
4. For each due job:
a. Set state to "running"
b. Create fresh AIAgent session (no conversation history)
c. Load attached skills in order (injected as user messages)
d. Run the job prompt through the agent
e. Deliver the response to the configured target
f. Update run_count, compute next_run
g. If repeat count exhausted → state = "completed"
h. Otherwise → state = "scheduled"
5. Write updated jobs back to jobs.json
6. Release scheduler lock

[/code]

Интеграция со шлюзом

В режиме шлюза планировщик работает в выделенном фоновом потоке (_start_cron_ticker в gateway/run.py), который вызывает scheduler.tick() каждые 60 секунд параллельно с обработкой сообщений. В режиме CLI задания cron срабатывают только при выполнении команд hermes cron или во время активных сессий CLI.

Изоляция свежих сессий

Каждое задание cron выполняется в полностью новой сессии агента: * Нет истории диалога с предыдущих запусков * Нет памяти о предыдущих выполнениях cron (если не сохранены в память/файлы) * Запрос должен быть самодостаточным — задания cron не могут задавать уточняющие вопросы * Набор инструментов cronjob отключён (защита от рекурсии)

Задания с навыками

Задание cron может подключать один или несколько навыков через поле skills. Во время выполнения: 1. Навыки загружаются в указанном порядке 2. Содержимое SKILL.md каждого навыка внедряется как контекст 3. Запрос задания добавляется как инструкция задачи 4. Агент обрабатывает объединённый контекст навыков + запрос

Это позволяет использовать переиспользуемые, протестированные рабочие процессы без вставки полных инструкций в запросы cron. Например:

[code] Create a daily funding report → attach "ai-funding-daily-report" skill

[/code]

Задания со скриптами

Задания также могут подключать Python-скрипт через поле script. Скрипт выполняется перед каждым шагом агента, и его стандартный вывод внедряется в запрос как контекст. Это позволяет реализовать сбор данных и обнаружение изменений:

[code] # ~/.hermes/scripts/check_competitors.py
import requests, json
# Fetch competitor release notes, diff against last run
# Print summary to stdout — agent analyzes and reports

[/code]

Тайм-аут скрипта по умолчанию — 120 секунд. _get_script_timeout() определяет лимит через цепочку из трёх уровней: 1. Переопределение на уровне модуля_SCRIPT_TIMEOUT (для тестов/monkeypatching). Используется только когда отличается от значения по умолчанию. 2. Переменная окруженияHERMES_CRON_SCRIPT_TIMEOUT 3. Конфигcron.script_timeout_seconds в config.yaml (читается через load_config()) 4. По умолчанию — 120 секунд

Восстановление провайдера

run_job() передаёт настроенные резервные провайдеры и пул учётных данных пользователя в экземпляр AIAgent: * Резервные провайдеры — читает fallback_providers (список) или fallback_model (устаревший словарь) из config.yaml, по аналогии с _load_fallback_model() шлюза. Передаётся как fallback_model= в AIAgent.__init__, который нормализует оба формата в цепочку резервирования. * Пул учётных данных — загружается через load_pool(provider) из agent.credential_pool, используя имя разрешённого провайдера времени выполнения. Передаётся только когда в пуле есть учётные данные (pool.has_credentials()). Обеспечивает ротацию ключей того же провайдера при ошибках 429/превышении лимита запросов.

Это повторяет поведение шлюза — без этого агенты cron не смогли бы восстановиться после превышения лимитов запросов.

Модель доставки

Результаты заданий cron могут быть доставлены на любую поддерживаемую платформу:

Цель Синтаксис Пример
Исходный чат origin Доставка в чат, где было создано задание
Локальный файл local Сохранение в ~/.hermes/cron/output/
Telegram telegram или telegram:<chat_id> telegram:-1001234567890
Discord discord или discord:#channel discord:#engineering
Slack slack Доставка в домашний канал Slack
WhatsApp whatsapp Доставка в WhatsApp
Signal signal Доставка в Signal
Matrix matrix Доставка в главную комнату Matrix
Mattermost mattermost Доставка в Mattermost
Email email Доставка по email
SMS sms Доставка по SMS
Home Assistant homeassistant Доставка в беседу HA
DingTalk dingtalk Доставка в DingTalk
Feishu feishu Доставка в Feishu
WeCom wecom Доставка в WeCom
Weixin weixin Доставка в Weixin (WeChat)
BlueBubbles bluebubbles Доставка в iMessage через BlueBubbles
QQ Bot qqbot Доставка в QQ (Tencent) через Official API v2

Для тем Telegram используйте формат telegram:<chat_id>:<thread_id> (например, telegram:-1001234567890:17585).

Оборачивание ответов

По умолчанию (cron.wrap_response: true) доставки cron оборачиваются с: * Заголовком, указывающим имя задания cron и задачу * Нижним колонтитулом, отмечающим, что агент не может видеть доставленное сообщение в диалоге

Префикс [SILENT] в ответе cron полностью отключает доставку — полезно для заданий, которым нужно только записывать файлы или выполнять побочные эффекты.

Изоляция сессии

Доставки cron НЕ зеркалируются в историю диалога сессии шлюза. Они существуют только в собственной сессии задания cron. Это предотвращает нарушения чередования сообщений в диалоге целевого чата.

Защита от рекурсии

В сессиях, выполняемых cron, набор инструментов cronjob отключён. Это предотвращает: * Создание новых заданий cron из запланированного задания * Рекурсивное планирование, которое могло бы взорвать использование токенов * Случайное изменение расписания задания изнутри самого задания

Блокировка

Планировщик использует межпроцессную файловую блокировку (fcntl.flock на Unix, msvcrt.locking на Windows), чтобы предотвратить выполнение одной и той же партии просроченных заданий дважды из-за перекрывающихся тиков — даже между внутрипроцессным тикером шлюза и отдельным вызовом hermes cron / ручным tick(). Если блокировку не удаётся получить, tick() сразу возвращает 0.

Интерфейс CLI

CLI hermes cron предоставляет прямое управление заданиями:

[code] hermes cron list # Показать все задания
hermes cron create # Интерактивное создание задания (псевдоним: add)
hermes cron edit # Редактирование конфигурации задания
hermes cron pause # Приостановить выполняющееся задание
hermes cron resume # Возобновить приостановленное задание
hermes cron run # Запустить немедленное выполнение
hermes cron remove # Удалить задание

[/code]

Связанные документы