SYNX 3.7: SYNXL приносит типизированные потоки записей в датасеты для ИИ
by APERTURESyndicate · 1 августа 2026 г.
Вышел SYNX 3.7. В язык добавлен ровно один оператор, а рядом появился целый новый формат файлов — SYNXL: формат потока записей для датасетов, сделанный под две задачи, в которые обычно втискивают JSONL и CSV и с которыми они справляются плохо.
Коротко: объявите поля один раз и дальше потоком пишите типизированные записи. Вложенность и многострочный текст едут вместе с ними, а битые строки не исчезают молча, а попадают в отчёт.
Чем JSONL и CSV мешают на датасетах
Откройте любой обучающий набор в JSONL и посмотрите, за что вы на самом деле платите. Каждая запись повторяет каждый ключ, каждую скобку, каждую кавычку. На корпусе в несколько сотен тысяч диалоговых примеров одни только имена полей — заметная доля токенов, которые вы скармливаете модели, и после первой строки они не несут никакой информации.
С многострочным текстом ещё хуже. Промпт, кусок кода, ответ модели — всё схлопывается в шум из экранированных \n, который никто не читает и который калечит любой diff.
CSV избегает повторов, но отдаёт почти всё остальное: ни типов, ни вложенности — и тот самый нерешённый вопрос, к которому рано или поздно приходит любой CSV-конвейер: пустая ячейка — это пустая строка или отсутствующее значение? Добавьте в поле запятую, и вы уже в правилах экранирования, которые в каждом диалекте свои.
SYNXL на одном экране
!synxl 1
!fields id[type:int] ; score[type:float] ; messages[block]
1 ; 0.91
messages
- role system
content You are a helpful assistant.
- role user
content |+
def f(x):
return x + 1
2 ; 0.74
messages
- role user
content ПриветСписок полей объявляется один раз. Записи начинаются с нулевой колонки, скалярные поля разделяются символом ;, а любое поле, помеченное как [block], раскрывается под записью в полном SYNX — списки, вложенные объекты, многострочный текст. Именно это позволяет выражать данные в форме диалога напрямую, а не сплющивать их в экранированную строку.
Этот пример проецируется в обычный JSON:
[{"id":1,"messages":[{"content":"You are a helpful assistant.","role":"system"},
{"content":"def f(x):\n return x + 1","role":"user"}],"score":0.91}, …]Что формат действительно гарантирует
Типы там, где им место.
[type:int],[required],[enum:a|b|c]живут в заголовке, а не в отдельном файле схемы, и не выводятся заново для каждой строки.Пусто — это не ничто. Пустое поле — это
null; пустая строка записывается как"". Двусмысленности CSV здесь нет по построению.Ничего не теряется молча. SYNX тихо пропускает битую структуру. SYNXL о ней сообщает — пропущенные поля, лишние поля, неудавшиеся приведения типов, неизвестные ключи блока — каждый раз с номером записи и номером строки. Датасет, который теряет колонку и не говорит об этом, хуже того, который падает громко.
Записи независимы. Границу записи видно по одному байту, поэтому формат безопасно дописывается, аккуратно шардируется и разбирается параллельно.
Схема может меняться прямо в файле. Появилась новая колонка? Напишите новую строку
!fieldsи продолжайте дописывать. Ранние записи остаются валидными и сохраняют исходную форму.
Сделано для файлов, которые не влезают в память
Ограничение входа в 16 МиБ, которое SYNX применяет к документу, в SYNXL действует на каждую запись: датасеты спокойно занимают гигабайты, а записи независимы, поэтому ограничение на весь файл не защищает ничего сверх того, что защищает ограничение на запись.
Потоковый ридер держит в памяти ровно одну запись. Замер через CLI на файле в 142 МБ с 2,4 миллиона записей: пиковая память остаётся ровной, около 10 МБ, на validate, parse и split — а чтение того же файла целиком в память стоило бы как минимум его собственного размера.
Вторая половина 3.7: |+
Сам язык получил ровно одно новшество: |+ — открыватель многострочного значения, сохраняющий отступы. Обычный | обрезает каждую строку продолжения: для прозы это нормально, для всего, где пробелы значимы, — разрушительно. |+ фиксирует базовый отступ по первой содержательной строке и сохраняет всё, что правее, — поэтому код, ASCII-схемы и каркасы промптов остаются внутри значения целыми.
SYNX 3.6 остаётся замороженной базой совместимости. 3.7 только добавляет: парсер 3.6 остаётся соответствующим стандарту для любого документа, где нет конструкций только из 3.7.
Как читать из кода
Сегодня SYNXL существует в четырёх реализациях, и за каждой стоит одинаковая семантика:
// Rust
let doc = synx_core::synxl::parse_lines(&text)?;
for record in &doc.records { /* … */ }
// TypeScript
import { parseSynxl, streamSynxlFile } from '@aperturesyndicate/synx-format'
for (const record of streamSynxlFile('dataset.synxl')) { /* … */ }
# Python
import synx_native as synx
for record in synx.synxl_stream_file("dataset.synxl"):
train(record["messages"])И из командной строки, включая конвертеры в обе стороны:
synx synxl parse dataset.synxl --format ndjson
synx synxl validate dataset.synxl --constraints
synx synxl convert corpus.jsonl # jsonl → synxl
synx synxl split dataset.synxl -n 500000 # shards that are valid on their ownГде доступно
3.7.1 опубликована на crates.io, npm, PyPI и NuGet.
Сам SYNXL реализован на Rust, TypeScript, Python и в CLI. Нативные парсеры C++, Dart, .NET, Go, Java и Swift поддерживают SYNX 3.7, включая |+, но пока не читают .synxl — до тех пор конвертируйте через CLI или читайте одной из четырёх реализаций.
Спецификация и соответствие
У SYNXL собственная ось версий — версия формата 1, независимая от версии языка, — потому что документ SYNXL не является документом SYNX: на верхнем уровне у него последовательность записей, а не объект.
Нормативный текст — SYNXL-1-NORMATIVE.md, а за ним стоит набор из 67 тестов соответствия, выведенных из этого текста независимо от какой-либо реализации. Раннер на Rust вдобавок перечитывает каждый принятый случай всеми тремя своими ридерами и падает, если они расходятся.
Полная документация и живая песочница: synx.aperturesyndicate.com
Что дальше
Поддержка SYNXL в остальных парсерах, начиная с Go и .NET. Спецификация стабильна, набор тестов соответствия написан, поэтому вторая волна — механическая работа, а не разведка. Ровно ради этого тесты и пишутся раньше реализаций.



