Когда я писал первую статью на Хабр про openLight в марте, проект состоял из одного коммита, одной Raspberry Pi и одного Telegram-бота.
У меня был Pi с Tailscale, маленький Matrix-сервер и несколько сервисов, за которыми хотелось приглядывать. Я устал печатать ssh pi@raspberrypi.local && systemctl status ... с телефона, поэтому написал небольшой Go-бинарь: Telegram-бот, SQLite и локальный Ollama как fallback, если обычный роутер не понимал запрос.
Прошло два месяца. Бинарь всё ещё весит около 25 МБ. Всё ещё один YAML-конфиг. Всё ещё SQLite. Но под капотом почти всё переписано хотя бы один раз.
И главное, изменилось понимание того, чем вообще стал проект.
openLight – это легковесный операционный слой для личных серверов, а не очередной AI-ассистент общего назначения.
В марте я бы так не сформулировал. Чтобы прийти к этому, понадобилось несколько месяцев реального использования, переписываний и откатов назад.
Это ретроспектива о том:
-
что сработало
-
что оказалось ошибкой
-
и почему я всё больше верю в маленькие локальные системы вместо “автономных AI-агентов”
Пять моментов, ради которых всё это
-
Поздний вечер, я не дома. Synapse упал на VPS. Я нажимаю
Restartпрямо в Telegram. Сервис поднимается. Ноутбук даже не открывал. -
Очередь в магазине. Tailscale начал сыпать warning’ами. Нажимаю
Logs, вижу знакомую проблему с peer’ом, ставлюIgnoreна 15 минут. -
Перелёт. Watch-цикл продолжает работать сам. Когда я приземляюсь, в чате уже лежат сообщения
resolved— всё восстановилось без меня. -
Mac mini дома. На нём Ollama и несколько Docker-сервисов. Watch на CPU > 90% срабатывает из-за моего же фонового джоба, о котором я уже забыл.
-
Удалённая машина.
/restart matrixс телефона уходит в docker-compose на VPS. Для меня это выглядит так же, как локальная Raspberry Pi.
Вся архитектура ниже существует только потому, что такие сценарии реально происходят.
Что изменилось технически
Роутинг: от плоского к deterministic-first
Изначально роутер был очень простым:
-
попробовать slash-команду
-
попробовать regex
-
если ничего не подошло, то отправить запрос в Ollama
Этого хватает примерно на неделю.
Потом начинаешь замечать:
-
половина задержки – это запуск модели на Pi
-
модель иногда выбирает “почти правильный”, но не тот tool
-
а “не понимаю запрос” и “уверен на 51%, выполняю” – это две очень разные ситуации
Сейчас перед LLM есть несколько полностью детерминированных слоёв:
-
slash-команды
-
aliases
-
нормализация
-
rule-based parsing
-
semantic mapping
И только если всё это не сработало, подключается модель.
Скриншот выше, тот же самый skill /status, но без LLM-вызова: русская фраза детерминированно нормализовалась.
skills, watch list, notes против agent.test.yaml.CLI теперь работает через тот же runtime, что и Telegram-бот:
-
тот же registry
-
тот же router
-
тот же auth
-
те же skill’ы
Это позволило использовать его и для smoke-тестов, и для локальной отладки.
От localhost к SSH-нодам
Первая версия openLight умела работать только с той машиной, на которой была запущена.
Но быстро стало понятно, если у тебя есть VPS, Mac mini, Raspberry Pi и ещё пара коробок, то нужен единый интерфейс. Так появилась идея:
-
один openLight
-
много SSH-нод
Нода – это просто именованный SSH-target в конфиге.
Сервис может выглядеть так:
node:vps:compose:/opt/matrix/docker-compose.yml:synapse
Для пользователя это всё ещё просто:
/restart matrix
Но внутри это может быть:
-
systemd
-
docker
-
docker compose
-
локально
-
удалённо через SSH
Самое важное, пользователь этого не видит. Есть:
-
один skill
-
один allowlist
-
один audit path
Именно так и должно быть.
От request-response к monitoring loop
Самое большое изменение – система watch’ей. v0.0.1 был полностью реактивным: я что-то спрашивал, агент отвечал. v0.1.0 добавил monitoring loop:
-
правила
-
polling
-
incidents
-
cooldown’ы
-
и Telegram-алерты с кнопками
Restart, Logs, Status, Ignore.
Но самая важная часть не в polling’е. А в том, что кнопки используют тот же самый skill path, что и ручные команды. Когда я нажимаю Restart в alert’е, вызывается тот же service_restart, что и при обычной команде.
-
тот же allowlist
-
тот же audit
-
тот же logging path
Нет отдельного “режима автоматизации”.
Наверное, это решение нравится мне больше всего. И оно почти противоположно тому, куда движется большая часть AI-agent tooling. Сейчас популярная идея:
-
дать модели shell
-
дать sandbox
-
дать autonomy
-
и надеяться на лучшее
Но для инфраструктуры это выглядит опасно. Инфраструктуре нужен не “автономный агент”. Ей нужен понятный, проверяемый execution path. Автоматизация – это кнопка поверх уже существующего безопасного механизма, а не отдельный уровень привилегий.
Что изменилось стратегически
Skill’ы — единственная настоящая граница безопасности
В ранних версиях у меня был mutating_execute_threshold – порог уверенности для действий, которые меняют состояние. Потом я понял, что это неправильная модель. Правильная модель гораздо проще:
LLM может выбирать только из уже существующих skill’ов, а сами skill’ы обеспечивают безопасность на уровне Go-кода.
Либо функция существует и проходит allowlist-проверки. Либо нет. Модель – это классификатор намерения, а не носитель привилегий.
Core vs Optional
В какой-то момент я заметил странную вещь. Каждый раз, когда я добавлял:
-
vision,
-
browser,
-
voice,
-
OCR,
мне хотелось включить это по умолчанию. Потому что демо выглядит круто. А потом через пару недель я дебажил:
-
Playwright,
-
лишние зависимости,
-
latency,
-
и весь тот “AI assistant creep”, которого изначально хотел избежать.
Поэтому сейчас в проекте есть чёткое разделение:
-
core modules,
-
optional modules.
Если убрать optional-модули, то openLight всё ещё останется openLight. Если убрать core, то проект потеряет идентичность.
Один и тот же реестр виден LLM-классификатору и в ответе на пользовательский /skills. Никакой параллельной поверхности нет.
От Raspberry Pi к personal infrastructure
Где-то на третьем или четвёртом крупном рефакторе я понял, что openLight уже не про Raspberry Pi. Pi был просто самой маленькой машиной, которая оказалась под рукой. На самом деле меня начала интересовать другая категория:
-
маленькие always-on компьютеры
-
локальные серверы
-
Mac mini
-
старые ThinkPad
-
NUC
-
Raspberry Pi
-
домашние ARM-машины
Все они живут в странном промежутке:
-
это уже не ноутбук
-
но ещё и не “облако”
Для них плохо подходит mainstream tooling. Kubernetes здесь почти всегда избыточен. Datadog – тоже. Большие AI-agent frameworks – тем более. Этим машинам нужен:
-
маленький
-
понятный
-
дешёвый
-
repairable слой управления
Mac mini особенно сильно изменил моё восприятие проекта. M1 спокойно тянет:
-
Ollama
-
несколько Docker-сервисов
-
monitoring
-
Telegram-agent
-
и при этом потребляет смешное количество энергии
В какой-то момент openLight перестал быть “ботом для Raspberry Pi”. Он стал агентом для personal infrastructure, который просто использует Telegram как интерфейс. И мне кажется, что в ближайшие годы вокруг этой категории появится очень много интересного софта.
Что оказалось ошибкой
Фрейминг “альтернатива OpenClaw”
В первых README я слишком сильно определял проект через: “мы не такие как X” Это плохая идея. Во-первых, если человек не знает OpenClaw, ему всё равно. Во-вторых, решения начинают приниматься “от противного”: “они делают так, значит мы должны наоборот”. Это не архитектурный принцип.
“Structured tool calling” в раннем roadmap
Тогда мне казалось, что проблема решается более сложным tool-calling. Сейчас я думаю наоборот: проблема решается более сильным deterministic routing. Большая часть запросов вообще не должна доходить до LLM.
Полный registry в prompt
Ранний classifier видел:
-
все skill’ы
-
все описания
-
весь registry
Это:
-
раздувало prompt
-
замедляло routing
-
и ухудшало качество
Двухстадийная классификация решила проблему:
-
сначала только группы
-
потом только skill’ы внутри группы
LLM input budget – это тоже архитектурное ограничение.
Что подтвердилось практикой
Telegram – отличный интерфейс для homelab
Я пробовал:
-
Slack
-
web UI
-
dashboard’ы
Но всё проигрывало сценарию:
“я не дома, сервис упал, достал телефон, нажал Restart”.
SQLite хватает
-
Watch’и
-
Incidents
-
History
-
Settings
-
Skill calls
Всё живёт в одном SQLite-файле. Backup – это literally cp. И мне это нравится.
Один Go-бинарь — правильная форма
Без:
-
Redis,
-
Postgres,
-
service mesh,
-
Helm,
-
runtime dependency zoo.
scp + systemd/launchd – и всё работает.
Локальные LLM уже достаточно хороши
Qwen 0.5B на Raspberry Pi хватает для routing/classification. Мне не нужен GPT-4, чтобы понять:
“что там сломалось?”
Модель здесь не “думает”. Она помогает выбрать intent. И маленькие модели surprisingly хороши в этом.
Почему мне кажется, что это направление важно
Сейчас вся AI-индустрия движется в сторону:
-
огромных cloud-agent systems
-
autonomous workflows
-
giant tool ecosystems
-
generalized assistants
Но чем дольше я работаю над openLight, тем сильнее мне кажется:
-
самая полезная AI-инфраструктура ближайших лет будет не гигантской, а маленькой.
-
Не cloud-first. А local-first.
-
Не “autonomous”. А deterministic by default.
-
Не opaque. А observable.
-
Не “умной”. А repairable.
openLight – это очень маленький проект. Но для меня он стал способом исследовать именно эту идею. Он не пытается заменить инженера. Он пытается уменьшить трение между инженером и теми маленькими системами, которыми этот инженер уже управляет.
Код лежит на github.com/evgenii-engineer/openLight.
Если у тебя дома есть:
-
Raspberry Pi
-
Mac mini
-
VPS
-
homelab
-
или просто несколько always-on машин
возможно, openLight тебе пригодится.
Автор: IsupovEvgenii


