28 KiB
LLM Robot Control
Генератор синтетических датасетов для обучения моделей высокоуровневому планированию промышленной сборки. Создаёт структурированные сцены, строит планы, проверяет предусловия действий и достижение цели, экспортирует обучающие записи в JSONL. Для каждого примера сохраняются два варианта ответа: русский текст и JSON с навыками. Управление физическим роботом и расчёт траекторий не реализованы.
Установка
Требуются Python 3.13+, uv и Linux (для блокировки файлов используется fcntl).
uv sync
Запуск
Команды выполняются из корня репозитория:
# Проверить конфигурацию без запросов к модели и создания файлов
uv run python -m assembly_dataset --dry-run
# Создать 100 обучающих записей
uv run python -m assembly_dataset generate --count 100
# Продолжить корпус до 500 000 записей
uv run python -m assembly_dataset generate --count 500000
# Проверить сохранённые эпизоды и экспорт
uv run python -m assembly_dataset validate
--count задаёт точное суммарное число примеров по выбранным режимам.
Каждый пример экспортируется в двух вариантах: --count 100 даёт 100 текстовых
и 100 структурированных записей с одинаковыми ID. examples и прогресс считают
пары один раз; examples_per_variant в manifest показывает размеры обоих вариантов.
Без этого аргумента используется target_examples из конфигурации.
Одна сцена может давать несколько записей; количество сцен выводится отдельно.
Ноль допустим; отрицательные и дробные значения запрещены.
step_sampling: one выбирает один воспроизводимый шаг из каждого эпизода.
all экспортирует каждый выполненный шаг. Общий лимит действует и в этом случае:
последний эпизод может быть экспортирован частично. Если нужен корпус из полных
планов для 500 000 исходных сцен, выберите modes: [full] и --count 500000.
При трёх форматах число сцен отображается отдельно как scenarios.
Прогресс выполнения
При запуске CLI автоматически отображаются два индикатора: генерация записей и экспорт в JSONL. Каждый показывает количество записей относительно цели, процент выполнения, скорость, прошедшее и приблизительное оставшееся время. При продолжении корпуса генерация начинается с уже сохранённого количества.
Счётчик генерации обновляется после сохранения пакета проверенных эпизодов, а не после каждого API-запроса. Во время вызовов модели отображается «ожидание LLM». Экспорт отдельно показывает запись строк и подготовку контрольных сумм. Оценка времени появляется после первых обновлений и может меняться из-за скорости модели, повторных попыток и длины планов.
В терминале полосы обновляются на месте; при перенаправлении вывода сохраняется
итоговая строка каждого этапа. Индикаторы выводятся в stderr, итоговая статистика
JSON — в stdout. Для отключения используйте --no-progress:
uv run python -m assembly_dataset generate --count 100 --no-progress
Конфигурация
Настройки находятся в config.yaml:
target_examples— размер итогового корпуса.modes— форматыfull,step,branch.step_sampling— один шаг или все шаги эпизода.seed,domains,min_components,max_components,blocked_fraction— параметры создания сцен.split— доли train, validation и test; записи одной сцены не разделяются.shard_size— максимальное число строк в одном файле JSONL.output_dir— папка результатов относительно файла конфигурации.teacher— провайдер, модель, доля сцен для LLM, параллельность и повторные попытки.
В поставляемом конфиге включён Codex для всех сцен. Для работы требуется доступ
к указанной модели. При отсутствии файла учётных данных запускается авторизация
через браузер. OpenAI-совместимые серверы настраиваются через teacher.llm;
ключ и адрес сервера берутся из переменных окружения или корневого .env.
Для генерации без API задайте teacher.enabled: false.
Другой конфиг передаётся через --config PATH, другая папка результатов — через
--output-dir PATH. Изменение параметров сцен или модели требует отдельной папки
корпуса. Лимит записей и размер шардов можно менять при продолжении запуска.
Где что менять
| Задача | Файл и параметр |
|---|---|
| Размер датасета | config.yaml → target_examples или CLI --count |
| Модель и провайдер | config.yaml → teacher.llm |
| Сложность и типы сцен | config.yaml → domains, min_components, max_components, blocked_fraction |
| Инструкции генератору планов | prompts.yaml → teacher_system |
| Текст запроса со сценой | prompts.yaml → teacher_user |
| Запрос исправления ошибочного плана | prompts.yaml → teacher_retry |
| Инструкции обучаемой модели для JSON | prompts.yaml → structured_system.full, .step, .branch |
| Инструкции обучаемой модели для русского текста | prompts.yaml → training_system.full, .step, .branch |
| Текст входа обучающей записи | prompts.yaml → training_user |
| Формулировки заданий по типам сборки | prompts.yaml → task_instructions |
| Названия объектов | prompts.yaml → object_names |
| Русские описания для локального режима и просмотра сцены | prompts.yaml → action_templates |
В шаблонах teacher_user, teacher_retry и training_user нужно оставить
${payload}: сюда подставляется JSON с заданием и сценой. В запросе исправления
JSON также содержит validation_error. Остальной текст можно менять свободно.
После изменения промптов используйте новую папку, например
--output-dir dataset/prompt-experiment. Содержимое промптов сохраняется в настройках
запуска и проверяется при возобновлении, чтобы не смешивать разные версии корпуса.
Промпты меняют инструкции и формулировки, но не правила логической проверки.
Пример изменения запроса:
teacher_user: |
Составь план выполнения следующего задания.
Учитывай доступность деталей и зависимости операций.
${payload}
${payload} обязателен в трёх шаблонах user/retry. Он заменяется JSON с полями
instruction и scene; при исправлении добавляется validation_error.
В обучающем запросе JSON также содержит mode, а для шага — history.
Обычные фигурные скобки разрешены; литеральный знак доллара записывается как $$.
Системные инструкции и task_instructions — обычный текст без подстановок.
Пустые инструкции и неизвестные переменные шаблона отклоняются до API-запроса.
Изменённые промпты генерации требуют отдельного output_dir.
Исключение — structured_system: эти инструкции используются только при экспорте,
поэтому их можно изменить и выполнить export в той же папке. Их полное содержимое сохраняется
в настройках корпуса. Формат ответа Plan и правила проверки остаются заданными
кодом: смена текста не добавляет новые навыки или поля JSON.
Форматы и результаты
dataset/assembly-ru/
episodes.sqlite3 # сцены, планы, история состояний, графы, provenance
sft/
manifest.json # точные размеры, распределения, SHA-256 файлов
train/full/000000.jsonl # ответ русским текстом
train/full/000000.structured.jsonl # ответ status/plan
train/step/000000.jsonl
train/branch/000000.jsonl
validation/...
test/...
Каждая строка содержит messages (system/user/assistant), record_id,
scenario_id, mode, split, source, validation_level.
В 000000.jsonl ответ assistant.content — обычный русский текст:
- full — нумерованная последовательность действий.
- step — одна русская фраза с названием объекта. Вход содержит состояние перед действием и историю выполненных действий.
- branch — нумерованный план с условиями «если выполнено успешно» и «если произошла ошибка». После ошибки требуется уточнить сцену и перепланировать.
В 000000.structured.jsonl поле assistant.content содержит JSON в виде строки
(стандартный формат messages для обучения):
{"status":"ready","plan":[{"skill":"pick","object":"деталь №1"},{"skill":"align","object":"деталь №1","target":"сборочная позиция №1"}]}
Это фрагмент примера; полный план включает остальные действия. Названия объектов
и мест берутся из сцены. target есть у действий с местом назначения; у pick,
release, inspect его нет. Навыки остаются на английском. return_home относится
к объекту «робот» с целью «исходное положение».
В step массив plan содержит одно следующее действие со статусом ready.
Без доступных действий он пуст; статус — ready при достигнутой цели или
blocked при блокировке. В full и branch статус характеризует весь сценарий;
при blocked массив может содержать выполнимую часть плана.
В branch у каждого действия дополнительно есть id, on_success, on_failure:
{"id":"step_1","skill":"pick","object":"деталь №1","on_success":"step_2","on_failure":"replan_required"}
Первым выполняется step_1. Успех ведёт к следующему ID, а у последнего действия —
к терминальному исходу ready или blocked. Ошибка ведёт к replan_required:
остановиться, обновить сцену и построить новый план. Для пустого плана итог задан
полем status.
Две версии имеют одинаковые ID, порядок, разбиение и входные сцены, но разные системные инструкции. Для обучения одному формату выбирайте только соответствующие файлы; совместное обучение двум форматам требует сохранять их системные инструкции.
Пример для помощи оператору:
1. Определить положение объекта «распределительная коробка №1».
2. Переместиться к объекту «распределительная коробка №1».
3. Захватить объект «распределительная коробка №1».
4. Переместить объект «распределительная коробка №1» к оператору.
5. Удерживать объект «распределительная коробка №1» в неподвижном состоянии возле оператора.
6. Дождаться подтверждения завершения сборки объекта «распределительная коробка №1», продолжая удерживать его.
7. Переместить объект «распределительная коробка №1» в место «зона хранения №1».
8. Отпустить объект «распределительная коробка №1».
9. Вернуться в исходное положение.
Домен operator_assistance описывает удержание объекта у оператора. collaborative
описывает передачу объекта оператору: это другая задача, с другим порядком отпускания.
Чтобы получить только сценарии удержания, задайте domains: [operator_assistance].
Технические навыки, предусловия, эффекты и графы сохраняются внутри канонического эпизода для проверки. Обучаемая модель получает сцену с русскими названиями объектов, их положением, доступностью и зависимостями. Статусы находятся в метаданных записи; при невыполнимости текст ответа явно сообщает о блокировке.
Версия экспорта — 3, версия хранилища эпизодов — 2. Сохранённые эпизоды версии 2
можно переэкспортировать в оба варианта без запросов к LLM, сохранив настройки
генерации. Корпуса версии 1 несовместимы:
используйте новую папку. В конфигурации задана dataset/assembly-ru, чтобы не смешивать
русские ответы с прежним техническим JSON.
SQLite служит транзакционным checkpoint и индексом уникальности. Он хранит полные эпизоды, включая trace и teacher-ошибки. Пакет в памяти ограничен размером очереди; SFT экспортируется потоково по шардам. После прерывания повторите команду: принятые эпизоды сохраняются, незавершённый пакет повторяется. При teacher-режиме запросы незавершённого пакета могут повториться. Одновременные писатели в одну папку блокируются. Неизменённый процедурный запуск воспроизводим побайтно.
Полный экспорт пересобирается во временную папку и затем заменяет предыдущий.
На время переэкспорта нужно место для обеих копий. Старый экспорт до завершения
нового остаётся доступен. SQLite и SFT для 500 тысяч строк могут занимать десятки
гигабайт: размер зависит от числа компонентов, режимов и step_sampling.
CSV для просмотра
Рядом с каждым шардом JSONL автоматически создаётся одноимённый CSV:
sft/train/full/000000.jsonl
sft/train/full/000000.csv
sft/train/full/000000.structured.jsonl
sft/train/full/000000.structured.csv
Одна строка CSV соответствует одной обучающей записи. Первые колонки:
«Задание», «Сцена», «План», «Статус», «Формат», «Источник текста».
В колонке «План» находится содержимое assistant.content: русский текст в обычном
CSV и JSON в .structured.csv.
Метаданные и полный вход находятся после основных колонок.
Кодировка — UTF-8 с BOM, разделитель — точка с запятой (;). При импорте в
Excel или LibreOffice выберите этот разделитель и включите перенос текста
в ячейках. Поля с кавычками, разделителями и переносами строк экранируются.
CSV включены в контрольные суммы manifest и проверяются командой validate.
CSV не увеличивают examples: это представления тех же записей.
Для добавления CSV к существующему корпусу выполните export с нужным лимитом;
запросов к LLM при этом нет.
Продолжение и переэкспорт
После остановки повторите команду generate с тем же конфигом и папкой результатов.
Для изменения количества строк используйте --count. Меньший лимит уменьшает
экспорт, но сохраняет канонические эпизоды в SQLite.
# Повторно экспортировать 100 записей из сохранённых эпизодов
uv run python -m assembly_dataset export --count 100
Без --count команда export использует лимит из YAML. Если столько записей ещё
не подготовлено, сначала выполните generate или укажите меньший лимит.
Модель мира и ограничения
В world.py находятся одиннадцать расширяемых рецептов: перемещение, механосборка,
крепёж, прессовая посадка, электромонтаж, электроника, клей, сварка, совместная
сборка, удержание объекта у оператора и контроль. Сцены различаются количеством компонентов, начальными
положениями, уже завершёнными стадиями, отсутствующими деталями и DAG-зависимостями.
Текущее положение задают предикаты at/holding в состоянии; поле
components[].source_location обозначает исходное место подачи детали.
Это каталог базовых абстрактных процессов, не исчерпывающая модель всей промышленности.
Новый процесс требует рецепта, корректных предусловий/эффектов и тестов.
У каждой операции есть requires, forbids, add, delete. Полный план
проверяется повторным исполнением; ветвящийся — обходом всех доступных путей.
Состояния и эффекты задаёт генератор, teacher не может их переписать.
blocked проверяется эталонным решателем для этого ограниченного ациклического
домена. Это не универсальный PDDL-планировщик.
Структурная сигнатура строится из сцены с фиксированными каноническими ID. Номер кандидата, формулировка задания и ответ LLM не увеличивают разнообразие. Дедупликация выполняется уникальным индексом SQLite; при исчерпании малого пространства параметров генератор завершится с ошибкой. Варианты одного сценария получают один split по сигнатуре. Родственные, но различные сцены могут попасть в разные split — это не строгий тест переноса на неизвестные семейства изделий. Для маленьких корпусов присутствие validation/test не гарантируется.
validation_level: logical_only: нет проверки геометрии, столкновений, усилий,
качества сварки/клея и реальной выполнимости навыков. VLM/VLA и разметка изображений
не поддерживаются.
Teacher-модели
В поставляемом config.yaml включён Codex для всех сцен (enabled: true,
fraction: 1.0). При enabled: false планы строятся локально. Для подключения
OpenAI-совместимого сервера настройте блок:
teacher:
enabled: true
fraction: 0.15 # доля сцен; 1.0 — все сцены
concurrency: 4
timeout_seconds: 180
retries: 2
llm:
provider: openai
model: your-provider/your-model
api_key_env: API_KEY
base_url_env: BASE_URL
generation:
temperature: null
max_tokens: 8192
Ключ и адрес сервера берутся из окружения или корневого .env.
Для Codex замените llm:
llm:
provider: codex
model: <доступная вам модель>
credential_path: .config/codex-credentials.json
Подключение выполняет llm.factory.LLMModelProvider. Teacher возвращает полный
план: для каждого действия — внутренние skill/object/target и русское описание
text. Код проверяет последовательность внутренних действий и сохраняет текст
модели без замены шаблонной формулировкой. Затем из полного плана создаются
пошаговые записи и условия ветвления; сами условия ошибки формирует код.
Логическая проверка подтверждает внутренние действия. Для текста проверяются
наличие русского описания, количество шагов, названия объектов и отсутствие
технических ID. Текст также должен совпадать с action_templates после нормализации
пунктуации и ограниченного набора синонимов. Допускаются «Сначала/Затем/Далее»,
«взять/захватить», «перенести/переместить», «проконтролировать/проверить»,
«возвратиться/вернуться», «местоположение/положение». Другие формулировки отклоняются
и запрашиваются заново: свободный текст не считается доказательством корректности.
Для изменения основного стиля редактируйте action_templates в prompts.yaml.
После неудачных попыток используется символьный план и русские шаблоны из
action_templates. Это помечается как source: symbolic, text_source: template
и teacher_error. У успешного ответа модели text_source: teacher.
В CSV это видно в колонке «Источник текста». Смена action_templates не
переписывает сохранённые формулировки teacher.
Реальные API-вызовы не входят в локальные тесты. Для сравнения моделей используйте
отдельные папки и проверяйте sources и teacher_failures в manifest.
Структура проекта
assembly_dataset/ # генерация сцен, валидация, teacher, хранение и CLI
config.yaml # модель, размер, сцены и параметры генерации
prompts.yaml # инструкции LLM и тексты обучающих записей
llm/ # провайдеры моделей, авторизация и общие схемы настроек
tests/ # тесты генератора и конвейера
Промпты редактируются в prompts.yaml, без изменения Python-кода.
Параметр prompts_file в config.yaml позволяет выбрать другой файл промптов.
Проверки
uv run python -m unittest discover -s tests -v
Тесты используют локальную генерацию и тестовый агент без внешних API-запросов.