Files
LLM-Robot-Control/README.md
T

387 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# LLM Robot Control
Генератор синтетических датасетов для обучения моделей высокоуровневому
планированию промышленной сборки. Создаёт структурированные сцены, строит планы,
проверяет предусловия действий и достижение цели, экспортирует обучающие записи
в JSONL. Для каждого примера сохраняются два варианта ответа: русский текст и JSON с навыками.
Управление физическим роботом и расчёт траекторий не реализованы.
## Установка
Требуются Python 3.13+, uv и Linux (для блокировки файлов используется `fcntl`).
```bash
uv sync
```
## Запуск
Команды выполняются из корня репозитория:
```bash
# Проверить конфигурацию без запросов к модели и создания файлов
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`:
```bash
uv run python -m assembly_dataset generate --count 100 --no-progress
```
## Конфигурация
Настройки находятся в [config.yaml](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`. Содержимое промптов сохраняется в настройках
запуска и проверяется при возобновлении, чтобы не смешивать разные версии корпуса.
Промпты меняют инструкции и формулировки, но не правила логической проверки.
Пример изменения запроса:
```yaml
teacher_user: |
Составь план выполнения следующего задания.
Учитывай доступность деталей и зависимости операций.
${payload}
```
`${payload}` обязателен в трёх шаблонах user/retry. Он заменяется JSON с полями
`instruction` и `scene`; при исправлении добавляется `validation_error`.
В обучающем запросе JSON также содержит `mode`, а для шага — `history`.
Обычные фигурные скобки разрешены; литеральный знак доллара записывается как `$$`.
Системные инструкции и `task_instructions` — обычный текст без подстановок.
Пустые инструкции и неизвестные переменные шаблона отклоняются до API-запроса.
Изменённые промпты генерации требуют отдельного `output_dir`.
Исключение — `structured_system`: эти инструкции используются только при экспорте,
поэтому их можно изменить и выполнить `export` в той же папке. Их полное содержимое сохраняется
в настройках корпуса. Формат ответа `Plan` и правила проверки остаются заданными
кодом: смена текста не добавляет новые навыки или поля JSON.
## Форматы и результаты
```text
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` для обучения):
```json
{"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`:
```json
{"id":"step_1","skill":"pick","object":"деталь №1","on_success":"step_2","on_failure":"replan_required"}
```
Первым выполняется `step_1`. Успех ведёт к следующему ID, а у последнего действия —
к терминальному исходу `ready` или `blocked`. Ошибка ведёт к `replan_required`:
остановиться, обновить сцену и построить новый план. Для пустого плана итог задан
полем `status`.
Две версии имеют одинаковые ID, порядок, разбиение и входные сцены, но разные
системные инструкции. Для обучения одному формату выбирайте только соответствующие
файлы; совместное обучение двум форматам требует сохранять их системные инструкции.
Пример для помощи оператору:
```text
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:
```text
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.
```bash
# Повторно экспортировать 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-совместимого сервера настройте блок:
```yaml
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`:
```yaml
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.
## Структура проекта
```text
assembly_dataset/ # генерация сцен, валидация, teacher, хранение и CLI
config.yaml # модель, размер, сцены и параметры генерации
prompts.yaml # инструкции LLM и тексты обучающих записей
llm/ # провайдеры моделей, авторизация и общие схемы настроек
tests/ # тесты генератора и конвейера
```
Промпты редактируются в [prompts.yaml](prompts.yaml), без изменения Python-кода.
Параметр `prompts_file` в `config.yaml` позволяет выбрать другой файл промптов.
## Проверки
```bash
uv run python -m unittest discover -s tests -v
```
Тесты используют локальную генерацию и тестовый агент без внешних API-запросов.