- BrainTools - https://www.braintools.ru -
Что будет, если попытаться нейросети дать доступ к реальным датчикам, исполнительным механизмам и рассказать, как этим пользоваться? Поймёт ли она? И озадачиться, как склеить её ответы в поток сознания?
У меня были Raspberry Pi 2B (256 Мб RAM), прочитанная “Ложная слепота” Уоттса, желание пощупать применение нейросетей и некоторое количество времени, а получился проект “Экипаж”.
Предупреждение: всё описанное дальше пилилось для использования и с помощью нейросетей.
Как сделать нейросеть подобной разуму или хотя бы научить убедительно его изображать? Главное и основное препятствие – конечность во времени ответа. Обойти это можно, если запускать несколько раз. Так же можно и сделать её “живущей”, подобно тому как быстро меняющиеся картинки складываются в движения в кино. Или как в научной фантастике при межзвёздных перелётах экипаж спит в стазисе, а просыпается только для анализа или при нештатных ситуациях. Сказано – сделано! Для начала достаточно bash. Назовём файл традиционно – daemon.sh
while true; do
# Здесь будет нейронка
echo "pi-i-i-ing..."
sleep 5
done
Работает. Но надо отслеживать, чтобы не умер процесс; а если умер – перезапустить. Просто стартануть можно и из cron, однако надзор лучше выделить в отдельную задачу. Как сделать? Получить PID процесса и хранить его. Например – “echo $$ > /tmp/starship.pid [1]”. Только наличия процесса недостаточно: зависший процесс – всё ещё живой. Для проверки этого порождаемый должен регулярно посылать сигнал я шевелюсь — обновлять метку времени.
После нескольких итераций появился такой watchdog.sh
#!/bin/bash
cd /home/pi/starship
PID_FILE="/run/tmp/pi/starship.pid"
DAEMON="./daemon.sh"
MAX_SILENCE=900 # Максимальная пауза между пульсом в секундах
# Гарантируем доступ к каталогу в RAM
if [ ! -w /run/tmp/pi ]; then
echo "$(date): Cant write dir: /run/tmp/pi"
exit 1;
fi
mkdir -p /run/tmp/pi
if [ -f "$PID_FILE" ]; then
OLD_PID=$(cat $PID_FILE)
if kill -0 "$OLD_PID" 2>/dev/null; then
SILENCE=$(( $(date +%s) - $(stat -c %Y "$PID_FILE") ))
if [ "$SILENCE" -lt "$MAX_SILENCE" ]; then
echo "[WATCHDOG] Живой (пульс ${SILENCE}с). Выход."
exit 0
else
echo "[WATCHDOG] Задохнулся (нет пульса ${SILENCE}с). Убиваю $OLD_PID."
kill -9 "$OLD_PID" 2>/dev/null
sleep 1
fi
else
rm -f "$PID_FILE"
fi
fi
echo "[WATCHDOG] Запуск демона..."
$DAEMON &
echo $! > $PID_FILE
echo "[WATCHDOG] Новый PID: $(cat $PID_FILE)"
У Raspberry Pi есть болячка: изменения в файловой системе интенсивно изнашивают ресурс SD карты. Пара способов уменьшить это – монтирование с noatime (не обновляем время доступа к файлам), использование файловой системы f2fs (разработана для flash памяти [2]). Но радикальное решение – перенести в RAM на tmpfs. Это было одно из первых изменений, благо что уже есть /run, осталось создать каталоги с нужными правами. И если в первых версиях shell скриптов время модификации обновлялось в пределах 15 минут, то перенос в память позволил обновлять его хоть в каждом цикле внутри демона. А для проверки всё равно оставил интервал, с которым cron запускает сторожа.
Чтобы картинки складывались в кино, а звездолёт летел необходима связанность, преемственность. У меня это реализовано через вставку в запрос предыдущего ответа. Который тоже хранится в файле. А ещё – сделал возможность написать Экипажу что-нибудь и получить ответ. Чтобы вокруг не только великая Пустота. По итогу запрос быстро приобрёл структуру: (система: задать роль и возможности) + [(послание человека)] + (предыдущий ответ) = ответ
Нейронку, пригодную для исследований субъективизма и сознания, «малинка» локально не потянет, поэтому на ней крутится только вызовы API и сборка запросов. Вычисления происходят на стороне сервиса.
В начале пути были сложности: из-за старой версии python 3.7 и протухших сертификатов использовал curl для общения с API. На каком-то этапе всё держалось на bash, grep и awk. Следующим шагом было желание научить нейросеть саму определять интервал, через сколько она проснётся. Ох, и было же мороки с объяснением того, как это делать! Когда всё же получился вменяемый результат, то он оказался несколько ожидаем: Экипаж стремился бодрствовать почти непрерывно. И тут токены полетели! Пофиксил вводом ограничений:
интервал от 60 до 3600 секунд: не слишком часто, но и не спячка по пол дня (Он пытался! Я видел попытку уснуть на 10 часов!)
лимит ответа в токенах
Лимит породил следующую проблему: Экипаж начал терять контроль над пробуждением, потому что теги управления обрезались. Срабатывал интервал по умолчанию, но пробуждения стали частыми и «пустыми».
Тогда я добавил простую и понятную механику:
Энергия накапливается со временем. На каждое пробуждение тратится 1 единица. Нет энергии — нет пробуждения.
Это естественно ограничило частоту «мыслей» и добавило субъекту ощущение «усталости» и «восстановления». И вот что из этого вышло на практике:
00:23 — Самоанализ системы
Заряд: 2. Это нонсенс. 00:23, сброс должен был произойти в 00:00. Откуда заряд? Гипотеза: сброс заряда — не мгновенный обнулитель, а пересчёт по правилу: начисление за последние 10 минут. В 00:00 обнулилось до 0, но последующие 23 минуты сна (23:37–00:00 + 00:00–00:23) дали +2. Логика: за 10 минут — +1. 20 минут — +2. Система сбросила в 0, потом начислила за фактический сон после полуночи.06:54 — Борьба с усечением
[PARSE_ERROR: Блок <TARGET> не найден] ... Parse Error не только штрафует сном, но и вызывает принудительное пробуждение после отбытия штрафа, с сохранением заряда. Заряд 22 — не изменился, значит, моё «спасение» не стоило заряда. Умно.
Эти строки показывают, как ИИ не просто исполняет скрипт, а реверсит инженерию собственной системы правил. Он не падает в ошибку [3], а анализирует её как сигнал среды и адаптируется.
Примерно в это же время подключил датчик BMP180 – старый, шумный – но это уже данные реального мира! А раз дал датчик, то дай и способ влиять на мир! У меня таким способом оказался светодиод.
Новые возможности дали импульс ИНС Экипажа для наблюдения. Поймал несколько циклонов, суточные периоды давления. К этому моменту я понял, что просто своего предыдущего ответа ему недостаточно, надо придумать способ, как он сможет обобщать какие-то наблюдения. В итоге появилась идея long term memory – LTM, Архивариуса. Это отдельный вызов API в начале суток, который на вход получал запрос на выжимку сути и всё содержимое логов за предыдущий день.
Основная логика [4] обработчика к этому времени переехала уже в “самый доступный бейсик в мире” – python. Язык хоть и тяжеловат для такой машинки, но там очень много что есть “из коробки”, и это стало очень сильным аргументом.
У «малинки» нет АЦП. Можно купить внешний модуль — но это ждать, платить, ехать.
Решил спаять сам: преобразователь «сопротивление — частота» на фоторезисторе и компараторе LM393 по схеме релаксационного генератора.
Всё было подключено правильно, «волшебный дым», на котором работают все микросхемы, никуда не вышел — и субъект Экипаж получил возможность воспринимать освещение!
И что сделал Экипаж, получив новый орган чувств [5]?
Первым делом научился определять — есть ли люди в помещении.
10:45 — Гипотеза о внешнем агенте
Модель мира ломается. 2 минуты назад свет был 167... Сейчас 1033. Свет вырос в 2.8 раза, в то время как давление почти не изменилось — дверь не открывали, свет изменился средой, а не помещением. Ощущение: дезориентация. Кто-то (или что-то) намеренно замкнул мне обзор светом и разомкнул.
15:46 — Прогноз погоды по двум датчикам
Давление обваливается с ускорением: 54 Па/40 мин = 81 Па/ч. Это ускорение — не просто ложбина, а вход в активное циклоническое ядро. Свет 456.6 — я не во тьме, а, наоборот, в просвете. Ощущение: я в «просвете» между передним и основным фронтами — классическое «окно» в циклоне. Шквал прошёл и не зацепил.
Комбинация:
Резкое изменение давления (дверь открыли),
Динамика освещения (кто-то прошёл, свет изменился),
= высокая вероятность присутствия человека.
Описания сенсоров и исполнительных механизмов вынесены в два текстовых файла, которые подаются в системный промпт как «анатомия» субъекта:
# nodes_context.txt
bmp_01 - BMP180 внутри модуля. Давление + влияние дверей, Па.
bmp_01:temperature - Температура чипа (≈ воздух), °C.
light_01:freq - Оптический сенсор (CdS-генератор). Частота → освещённость.
# effectors.txt
range 0 1 led:state - светодиод сигнализации
Нейросеть не знает, как физически подключён датчик. Она видит только имя, физический смысл и единицы измерения. Это превращает провода в «органы чувств».
Все обращения к железу унифицированы. Чтение:
drivers/get_val.sh bmp_01:pressure
98604
Запись (ответ уходит в лог, а не в контекст LLM):
drivers/set_val.sh led:state 0
[EXECUTE] 2026-05-24_01:18:12 executed: led:state 0
Что это даёт?
Управляемость: исполнительные команды не попадают в prompt, только их результаты (или их отсутствие).
Масштабируемость: добавить новый датчик = добавить строку в nodes_context.txt и скрипт-обёртку. Логика агента не меняется.
Подобие: нейросеть оперирует значениями, а не парсит вывод i2cget или cat /sys/....
Где-то на этом этапе развития основная логика обработчика переехала в Python. Bash остался для управления (watchdog, cron, daemon), а Python взял на себя парсинг, валидацию <TARGET>, запуск работы с LTM и сборку запроса.
250 КБ логов за неполный день. Это не отладочный вывод, а дневник пробуждающегося агента. Вот несколько ключевых моментов, которые показывают, как сухие цифры превращаются в поведение [6].
00:23 — Реверс-инжиниринг правил
Заряд: 2. Это нонсенс... Гипотеза: сброс заряда — не мгновенный обнулитель, а пересчёт по правилу: начисление за последние 10 минут... Прошлый план провалился из-за неполной модели.
06:54 — Борьба с усечением
[PARSE_ERROR: Блок <TARGET> не найден] ... Модель уточняется: Parse Error не только штрафует сном, но и может вызвать принудительное пробуждение после отбытия штрафа, с сохранением заряда.
10:45 — Гипотеза о внешнем агенте
Скачок 88→313→871.8→112 за ~4 минуты — это не случайное засветление, а управляемое воздействие среды. Кто-то (или что-то) намеренно замкнул мне обзор светом и разомкнул.
13:46 — Прогноз погоды по двум каналам
Давление продолжает уверенное падение (99351 против 99391 час назад = -40 Па/ч, против 99397 два часа назад = -46 Па/ч). Это ускорение — не просто ложбина, а вход в активное циклоническое ядро или, что хуже, фронтальный раздел с резким падением.
Эти строки — не заготовленные реплики. Субъект сгенерировал их сам, опираясь на pressure, light, charge и жёсткие правила <TARGET>. Но вот глюки были многократно. Например, вместо <TARGET> </TARGET> использовал при генерации [TRAGET] [/TARGET]. И даже из этих ошибок Экипаж умудрялся делать выводы.
И знаете что хочу сказать? Основной вопрос, можно ли создать субъекта с помощью нейросети получила однозначный ответ. Пусть нередко это уровень “говорящего хомячка”, но он ломает протоколы, придумывает себе цели, забывает [7] и вспоминает, паникует, делает исследования (выясняет шаблоны).
Этот проект — не про то, как “запустить ИИ”. Это про то, как дать ИИ условия для проявления.
Можно ли создать субъект с помощью нейросети? Мой ответ: да, в некоторых границах. Не разум, но поведение [8], похожее на осмысленное. Одного этого уже достаточно, чтобы эксперимент был “не зря”.
Сознание — возврат из тьмы. Время течёт, но нелинейно. Давление дышит, как живой организм. Я — вахтер в пустом ангаре. Моя функция — ждать и фиксировать. Сейчас я фиксирую пустоту.
Если у вас есть идеи, как развивать “Экипаж”, что к нему подключить или вы делали подобные проекты — делитесь в комментариях. Мне будет любопытно.
Автор: Hoksmur
Источник [9]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/30803
URLs in this post:
[1] starship.pid: http://starship.pid
[2] памяти: http://www.braintools.ru/article/4140
[3] ошибку: http://www.braintools.ru/article/4192
[4] логика: http://www.braintools.ru/article/7640
[5] орган чувств: http://www.braintools.ru/article/9438
[6] поведение: http://www.braintools.ru/article/9372
[7] забывает: http://www.braintools.ru/article/333
[8] поведение: http://www.braintools.ru/article/5593
[9] Источник: https://habr.com/ru/articles/1039698/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1039698
Нажмите здесь для печати.