Продвинутый

claw-code: агент-экспонат на Rust без участия людей

Алексей Кузнецов
Алексей Кузнецов
Системный администратор21 октября 2026 г.12 мин чтения

Экспонат музея, разработанный и поддерживаемый ИИ-агентами на Rust с использованием Gajae-Code и LazyCodex. Никакого человеческого вмешательства — только код...

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 отвечает за структуру проекта. Он анализирует требования (например, «нужен экспонат с датчиком движения и подсветкой») и генерирует скелет приложения: модули, зависимости, интерфейсы агентов. Его вывод выглядит так:

bash
gajae-code --spec specs/motion_light.json --output src/

Результат — набор файлов с пустыми или заглушенными функциями агентов, готовыми к заполнению логикой.

LazyCodex занимается реализацией. Он получает интерфейсы от Gajae-Code, изучает документацию к железу (например, datasheet чипа PCA9685) и пишет код драйверов. Его команда выглядит так:

bash
lazycodex --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:

rust
use 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. Решили просто:

bash
journalctl --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. Это удобнее, чем копаться в файлах логов вручную.

Третий момент — предсказуемость конфигураций. Вместо магических чисел и захардкоженных путей я вынес всё в конфиг. Это упрощает перенос сервиса на другую машину:

bash
scp -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, чтобы агент перестроил проблемный участок кода заново.

Поделиться:TelegramX / TwitterVK