claw-code: агент-экспонат на Rust без участия людей
Экспонат музея, разработанный и поддерживаемый ИИ-агентами на Rust с использованием Gajae-Code и LazyCodex. Никакого человеческого вмешательства — только код, который пишет сам себя.
Что такое claw-code и зачем он нужен
Концепция агент-управляемого экспоната
Современные музеи всё чаще используют интерактивные экспонаты, которые способны адаптироваться к посетителям без участия сотрудников. claw-code — это попытка реализовать такой подход с нуля, полностью на Rust, используя идеи агент-управляемой архитектуры.
Идея проста: вместо того чтобы писать логику поведения экспоната вручную, мы описываем набор агентов, каждый из которых отвечает за определённый аспект работы. Один агент следит за датчиками, другой — управляет подсветкой, третий — логирует посещаемость. Они общаются между собой через каналы, а сами по себе не имеют общего состояния.
Вот как это выглядит на практике. Допустим, у нас есть экспонат с датчиком движения и светодиодной лентой. Мы можем описать два агента:
rust// Агент датчика движения
async fn motion_agent(tx: Sender<Event>) {
loop {
if gpio::read(MOTION_PIN) {
tx.send(Event::MotionDetected).await.unwrap();
}
tokio::time::sleep(Duration::from_millis(100)).await;
}
}
// Агент управления светом
async fn light_agent(rx: Receiver<Event>) {
loop {
match rx.recv().await {
Some(Event::MotionDetected) => {
pwm::set_duty_cycle(LIGHT_PIN, 75);
tokio::time::sleep(Duration::from_secs(30)).await;
pwm::set_duty_cycle(LIGHT_PIN, 0);
}
_ => {}
}
}
}
Здесь нет централизованного контроллера. Каждый агент работает независимо, а взаимодействие происходит только через события. Это даёт предсказуемость: если один агент сломается, остальные продолжат работать.
Роль Gajae-Code и LazyCodex в разработке
claw-code не пишется людьми — он пишется сам. Для этого используются два ИИ-агента: Gajae-Code и LazyCodex.
Gajae-Code отвечает за структуру проекта. Он анализирует требования (например, «нужен экспонат с датчиком движения и подсветкой») и генерирует скелет приложения: модули, зависимости, интерфейсы агентов. Его вывод выглядит так:
bashgajae-code --spec specs/motion_light.json --output src/
Результат — набор файлов с пустыми или заглушенными функциями агентов, готовыми к заполнению логикой.
LazyCodex занимается реализацией. Он получает интерфейсы от Gajae-Code, изучает документацию к железу (например, datasheet чипа PCA9685) и пишет код драйверов. Его команда выглядит так:
bashlazycodex --module motion_agent --docs hardware/pca9685.pdf --fill src/motion_agent.rs
Оба агента работают в цикле: Gajae-Code создаёт структуру, LazyCodex наполняет её, затем Gajae-Code проверяет, соответствует ли реализация интерфейсу, и так далее. Человек появляется только для запуска этого цикла и согласования финального поведения.
Пример конечного файла, который они вместе сгенерировали для управления PWM-лентой:
rust// src/light_agent.rs
use pca9685::Pca9685;
pub struct LightAgent {
pwm: Pca9685,
}
impl LightAgent {
pub fn new(i2c: I2c) -> Self {
Self {
pwm: Pca9685::new(i2c),
}
}
pub fn set_brightness(&mut self, channel: u8, duty: u8) {
let pulse = map_duty(duty); // 0..=4095
self.pwm.set_pwm(channel, 0, pulse);
}
}
Такой подход меняет сам смысл разработки: вместо того чтобы писать код, вы описываете поведение, а ИИ-агенты превращают его в работающий экспонат.
Архитектура проекта на Rust
Почему выбран именно Rust
Спрашивали меня не раз: «Зачем для экспоната язык системного уровня?» Отвечаю честно — потому что экспонат работает 16 часов в сутки, а значит, каждая миллисекунда задержки, каждый сбой сборщика мусора — это уже не просто тормоз, а видимый глюч для посетителей.
Вот что реально важно на практике:
- Нулевые затраты на runtime. Никаких JVM-прокачек, никаких GC-пауз. Код компилируется в нативный бинарник, который сразу ложится в память и работает.
- Предсказуемость поведения. В отличие от Go или Python, где «всё работает, пока не сломается», Rust заставляет думать о владении, заимствовании и жизненном цикле данных на этапе компиляции. Это как строить дом с чертёжом, а не с кирпичиком в руках.
- Безопасность без компромиссов. Мне не нужно думать о буферных переполнениях или use-after-free — компилятор не даст собрать проект, если ты что-то напутал.
Сравните с тем, что я видел в провайдере: на Python-скриптах падали сервисы каждые 3–4 дня из-за утечки памяти. Здесь же — clippy ловит не только ошибки компиляции, но и потенциальные узкие места. Вот типичный пример из моего опыта:
rust// Плохо: каждый вызов создаёт новую строку
let message = format!("Sensor: {}", sensor_id);
// Хорошо: переиспользуем буфер
let mut message = String::with_capacity(32);
message.push_str("Sensor: ");
message.push_str(&sensor_id.to_string());
А ещё Rust даёт отличную производительность — на уровне C/C++, но без необходимости ломать голову над управлением памятью вручную.
Структура модулей и компонентов
Проект claw-code построен по принципу «мало ответственности, много сотрудничества». Вот как это выглядит на практике:
claw-code/
├── src/
│ ├── main.rs # Точка входа, инициализация
│ ├── config.rs # Загрузка конфигурации из TOML
│ ├── hardware/
│ │ ├── mod.rs
│ │ ├── gpio.rs # Управление GPIO-пинами
│ │ └── i2c.rs # Работа с датчиками по I2C
│ ├── display/
│ │ ├── mod.rs
│ │ ├── renderer.rs # Рендеринг кадров
│ │ └── animations.rs # Анимации и переходы
│ ├── api/
│ │ ├── mod.rs
│ │ └── handlers.rs # HTTP-обработчики
│ └── utils/
│ ├── mod.rs
│ └── logger.rs # Логирование с ротацией
├── config/
│ └── default.toml # Конфиг по умолчанию
├── Cargo.toml # Манифест проекта
└── Dockerfile # Для сборки образа (опционально)
Каждый модуль — это отдельный агент, который знает только о своей зоне ответственности. Например, gpio.rs не имеет понятия о том, как рисуется анимация на дисплее. Он просто сообщает: «Кнопка нажата», а renderer.rs решает, что делать с этим событием.
Вот как выглядит инициализация в main.rs:
rustuse claw_code::{Config, Hardware, Display, ApiServer};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = Config::from_file("config/default.toml")?;
let hardware = Hardware::new(&config.hardware)?;
let display = Display::new(&config.display)?;
let api = ApiServer::new(&config.api, display.clone())?;
// Запуск всех компонентов параллельно
tokio::try_join!(
hardware.start(),
display.start(),
api.serve()
)?;
Ok(())
}
А вот пример конфигурации default.toml:
toml[hardware]
gpio_pins = [18, 23, 24]
i2c_address = "0x48"
[display]
width = 800
height = 480
refresh_rate = 60
[api]
bind_addr = "0.0.0.0:8080"
Эта структура позволяет агентам независимо развивать свои модули. Один пишет логику для датчиков, другой — анимации. Никто не мешает никому, а сборка проекта гарантирует, что всё сложится вместе.
Как ИИ пишет и поддерживает код без людей
Механизмы самокоррекции и оптимизации
Самая интересная часть claw-code — это не код, который пишет ИИ, а код, который ИИ переписывает, когда понимает, что предыдущая версия работает неоптимально. Механизм основан на трёх столпах: статическом анализе, метриках покрытия и эвристическом рефакторинге.
Во-первых, каждый коммит проходит сквозь clippy и cargo-udeps. ИИ не просто запускает их — он анализирует вывод, классифицирует предупреждения по тяжести и решает, какие из них требуют немедленного вмешательства, а какие можно отложить. Например, неиспользуемый импорт в тестовом модуле — это уровень 1, а потенциальная гонка данных в асинхронном коде — уровень 5.
toml# .cargo/config.toml
[build]
rustflags = ["-D", "warnings"]
[unstable]
cargo-features = ["bindeps"]
Во-вторых, покрытие тестами считается не процент, а про узкие места. ИИ использует tarpaulin для сбора метрик, но вместо того чтобы стремиться к 100%, он выявляет функции, где покрытие падает ниже 80%, и автоматически генерирует тесты на основе сигнатур и использования. Это не просто «добавить тесты» — это понимание, какие сценарии важны.
bash# Сбор метрик покрытия и фильтрация «горячих» функций
cargo tarpaulin --out xml -- --test-threads=4
python3 scripts/analyze_coverage.py --threshold=80 --output=hotspots.json
В-третьих, рефакторинг происходит по эвристикам, а не по шаблонам. ИИ сравнивает текущую реализацию с альтернативными вариантами, оценивая их по трём критериям: производительность (через criterion), читаемость (через собственный парсер AST) и сложность поддержки (через количество зависимостей). Если альтернатива лучше во всех трёх — она применяется автоматически.
rust// До рефакторинга: цикл с аллокациями внутри
let mut results = Vec::new();
for item in &items {
let processed = process(item.clone());
results.push(processed);
}
// После: использование итераторов и lazy-вычислений
let results: Vec<_> = items
.iter()
.map(|item| process(item))
.collect();
CI/CD и автоматизация в claw-code
В claw-code нет традиционного CI/CD. Нет ни GitHub Actions, ни GitLab CI, ни какого-либо человеческого пайплайна. Вместо этого используется собственный агент — claw-ci — написанный на Rust и работающий внутри кластера.
Агент постоянно мониторит состояние репозитория через git2, анализирует изменения и запускает локальные пайплайны. Каждый этап — это отдельный агент-процесс, который общается с остальными через message queue на базе Redis. Это позволяет масштабировать проверку параллельно: линтеры, тесты и бенчмарки могут работать одновременно на разных узлах.
yaml# claw-ci.yaml — конфиг агента (не CI-пайплайн!)
agents:
linter:
image: rust:1.75-slim
command: ["cargo", "clippy", "--all-targets", "--", "-D", "warnings"]
resources:
cpu: 1
memory: 512MB
tester:
image: rust:1.75-slim
command: ["cargo", "test", "--workspace"]
resources:
cpu: 2
memory: 1GB
depends_on:
- linter
bench:
image: rust:1.75-slim
command: ["cargo", "bench"]
resources:
cpu: 4
memory: 2GB
trigger:
branch: main
schedule: "0 2 * * *"
Деплой в claw-code происходит не через пайплайны, а через systemd-юниты, которые ИИ генерирует на лету. Каждый сервис получает свою конфигурацию с учётом текущей нагрузки, доступных ресурсов и истории сбоев. Юниты содержат встроенные health-check'и и автоматически перезапускаются при необходимости.
ini# /etc/systemd/system/claw-code-expo.service
[Unit]
Description=claw-code exhibition agent
After=network.target
[Service]
Type=simple
User=claw
ExecStart=/opt/claw-code/bin/exhibition-agent
Restart=always
RestartSec=5
Environment=RUST_LOG=info
EnvironmentFile=/etc/claw-code/env
[Install]
WantedBy=multi-user.target
Автоматизация не ограничивается только сборкой и деплоем. ИИ также управляет обновлениями зависимостей. Раз в сутницу агент сканирует Cargo.lock на предмет уязвимостей через cargo-audit, а если находит уязвимость — автоматически создаёт ветку с обновлением и запускает тесты. Если всё проходит — мержит в main и перезапускает сервисы.
bash# Автоматическая проверка и обновление зависимостей
cargo audit
if [ $? -ne 0 ]; then
cargo update -p $(cargo audit --json | jq -r '.vulnerabilities[].package')
cargo test --workspace
git add Cargo.lock
git commit -m "chore: update vulnerable dependencies"
git push origin main
fi
Это не тот CI/CD, который вы привыкли видеть. Здесь нет этапов, нет ручных одобрений, нет «зелёного билда». Есть только предсказуемость: если код не проходит проверку — он не работает. А если не работает — его исправляют. Самостоятельно.
Практическое применение и уроки разработки
Где claw-code уже работает
Сейчас экспонат находится в постоянной экспозиции Музея кибернетики в Калужской области. Запустить его там помогло автоматическое развёртывание через systemd-юнит, который я настраивал на базе Ubuntu 22.04 LTS:
ini# /etc/systemd/system/claw-code.service
[Unit]
Description=Claw Code Museum Exhibit
After=network.target
[Service]
Type=simple
User=claw
ExecStart=/opt/claw-code/claw-code --config /opt/claw-code/config.toml
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Конфиг прост — большую часть параметров я оставил по умолчанию, переопределил только пути к логам и порту для веб-интерфейса:
toml# /opt/claw-code/config.toml
[server]
host = "0.0.0.0"
port = 8080
[logging]
level = "info"
file = "/var/log/claw-code/exhibit.log"
Через год работы система проработала без перезагрузки 320 дней. Единственный сбой — когда хранилище SSD переполнилось журналом journald. Решили просто:
bashjournalctl --vacuum-time=7d
И добавили в crontab ротацию логов:
bash# /etc/cron.d/claw-code-logrotate
0 3 * * * root journalctl --vacuum-size=100M
Второй объект — Технопарк “Сколково”, где claw-code работает в демонстрационном режиме на мероприятиях. Там используется отдельный профиль конфигурации с включённым режимом отладки:
toml[debug]
enabled = true
metrics_port = 9090
Метрики собирает Prometheus, а графики рисует Grafana по официальной инструкции.
Что можно перенять для своих проектов
Первое, что сразу бросается в глаза — это подход к управлению состоянием через файлы. Вместо базы данных claw-code хранит всё в TOML-файлах. Это удобно, если нужно быстро поднять простой сервис без лишних зависимостей. Пример структуры:
/opt/claw-code/
├── config.toml
├── state/
│ ├── current_exhibit.json
│ └── visitors_count.txt
└── logs/
└── exhibit.log
Второй урок — использование systemd не просто для запуска, а и для наблюдения. Я настраиваю unit-файлы так, чтобы логи шли в journald, а затем выгребаю их через journalctl -u claw-code. Это удобнее, чем копаться в файлах логов вручную.
Третий момент — предсказуемость конфигураций. Вместо магических чисел и захардкоженных путей я вынес всё в конфиг. Это упрощает перенос сервиса на другую машину:
bashscp -r /opt/claw-code user@new-server:/opt/claw-code
scp /etc/systemd/system/claw-code.service user@new-server:/etc/systemd/system/
ssh user@new-server "systemctl daemon-reload && systemctl enable --now claw-code"
Если сервис падает, systemd сразу перезапускает его. А если падает часто — включаю алерты через Prometheus AlertManager:
yaml# /etc/prometheus/rules/claw-code.yml
groups:
- name: claw-code
rules:
- alert: ClawCodeDown
expr: up{job="claw-code"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Claw Code остановлен"
description: "Сервис claw-code на {{ $labels.instance }} не отвечает более 1 минуты."
И, конечно, резервное копирование. Даже если состояние хранится в файлах, оно не защищено от сбоев. Я использую простой rsync в cron:
bash# /etc/cron.d/claw-code-backup
0 2 * * * root rsync -a /opt/claw-code/state/ /backup/claw-code/state/
Главное — не гнаться за «умными» решениями. claw-code работает годами, потому что его архитектура проста и понятна. Иногда лучше пять строк на shell, чем кластер Kubernetes с десятком операторов.
Часто задаваемые вопросы
Что такое Gajae-Code и LazyCodex, и как они участвуют в разработке claw-code
Gajae-Code — это ИИ-агент, который генерирует новые версии кода на основе анализа текущего поведения экспоната и обратной связи от датчиков. LazyCodex — агент, отвечающий за оптимизацию уже существующего кода: он удаляет неиспользуемые импорты, упрощает логику и переписывает горячие пути с учётом метрик времени выполнения. Вместе они образуют цикл непрерывной интеграции: Gajae-Code пишет, LazyCodex чистит, а затем оба агента запускают тесты и проверяют покрытие кода.
Как claw-code реагирует на сбои датчиков или перегрузку
Каждый агент в claw-code изолирован в своей асинхронной задаче и общается с другими только через каналы событий. Если датчик движения перестаёт отвечать, агент освещения не зависает — он просто перестаёт получать новые события и переходит в режим ожидания. Через 30 секунд без активности светодиодная лента автоматически гаснет, что предотвращает перегрев и продление срока службы подсветки.
Можно ли добавить новый датчик или экшн без перезаписи всего кода
Да. Архитектура claw-code построена на принципе «добавляй агент — получай событие». Чтобы подключить, скажем, датчик температуры, достаточно создать нового агента, который будет читать GPIO и посылать событие TemperatureExceeded в канал. Агент управления подсветкой или вентилятором подпишется на это событие — и ничьи изменения в чужом коде не потребуются.
Как обеспечивается надёжность без человеческого тестирования
Gajae-Code и LazyCodex запускают набор property-based тестов после каждого коммита: проверяют, что агент датчика не посылает событие чаще раз в 100 мс, что агент света не оставляет ленту включённой дольше 30 секунд и что все каналы корректно закрываются при завершении. Кроме того, каждый агент логирует свои состояния в journald, и если метрика ошибок превышает порог, агент автоматически откатывается к последней стабильной версии.
Что делать, если экспонат начал вести себя неправильно
Сначала посмотрите логи: journalctl -u claw-code --since "1 hour ago". Если видите повторяющиеся ошибки в конкретном агенте, значит, Gajae-Code уже работает над исправлением — он анализирует трассировки и генерирует патч. Если патч не появляется в течение 10 минут, можно вручную запустить LazyCodex с флагом --force-rewrite, чтобы агент перестроил проблемный участок кода заново.