обновлен формат генерации датасета
This commit is contained in:
@@ -0,0 +1,386 @@
|
||||
# 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-запросов.
|
||||
|
||||
Reference in New Issue
Block a user