All news
ASCodeworksReleases

SYNX 3.7:SYNXL が AI データセットに型付きレコードストリームをもたらす

by APERTURESyndicate · 2026年8月1日

SYNX 3.7:SYNXL が AI データセットに型付きレコードストリームをもたらす

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 Привет

フィールドの一覧は一度だけ宣言します。レコードは 0 桁目から始まり、スカラーのフィールドは ; で区切ります。[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 は報告します——欠けたフィールド、余分なフィールド、失敗した型変換、未知のブロックキーを、レコード番号と行番号つきで。何も言わずに列を落とすデータセットは、大きな音を立てて失敗するものより質が悪いのです。

  • レコードは独立しています。 レコードの境界は 1 バイトで判定できるため、安全に追記でき、きれいに分割でき、並列に解析できます。

  • ファイルの途中でスキーマを変えられる。 列が増えたら、新しい !fields 行を書いて追記を続けるだけです。それ以前のレコードは有効なまま、元の形を保ちます。

メモリに収まらないファイルのために

SYNX が文書に課している 16 MiB の入力上限は、SYNXL ではレコードごとに適用されます。データセットはふつうにギガバイト級になり、レコードは互いに独立しているため、ファイル全体の上限はレコードごとの上限以上には何も守りません。

ストリーミングの読み取りは、同時に 1 レコードしか保持しません。CLI で 142 MB・240 万レコードのファイルを実測したところ、validateparsesplit のいずれでもピークメモリはおよそ 10 MB で平坦なままでした。同じファイルをまるごとメモリに読み込めば、少なくともファイル自身の大きさぶんはかかります。

3.7 のもう半分:|+

言語そのものに増えたのはひとつだけです。|+——インデントを保つ複数行の開始記号です。ふつうの | は継続行の字下げを削ります。散文なら問題ありませんが、空白に意味がある内容では破壊的です。|+ は最初の本文行で基準となる字下げを固定し、それより右をそのまま保ちます。コードも ASCII 図もプロンプトの雛形も、値の中で無傷のまま残ります。

SYNX 3.6 は凍結された相互運用の基準であり続けます。3.7 は加算的で、3.7 固有の記法を使わない文書であれば、3.6 のパーサーは適合したままです。

コードから読む

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.ionpmPyPINuGet で公開しています。

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 から進めます。仕様は安定し、適合性テストも書き終えているので、第二波は探索ではなく機械的な作業です。実装より先にテストを書く意味は、まさにそこにあります。

Other stories

All stories
SYNX 3.7:SYNXL が AI データセットに型付きレコードストリームをもたらす | APERTURESyndicate