TOON(トゥーン)は「Token-Oriented Object Notation」の略で、JSONと同じデータをLLMのプロンプトに渡すとき、より少ないトークンで書けるように設計されたテキスト形式です。キーを一度だけ宣言して値を行で並べる表形式が中心で、同じ形のオブジェクトが並ぶ配列ほど削減幅が大きくなります。
どんなデータでも減るわけではありません。入れ子が深いデータや形がそろっていない配列では、改行と空白を詰めたJSON(compact JSON)のほうが少なくなる場合があります。2026年10月8日に仕様v4.4が出て、TypeScript・Python・Javaなど主要な実装がそろってv4.4に対応しました。本記事では書き方と5種類のデータでの実測、各言語での使い方、古い版を混ぜたときに起きる誤読を、実際に動かした結果で説明します。
まとめ:TOONの意味とJSONから切り替える条件
- 意味:JSONのデータモデル(オブジェクト・配列・文字列・数値・真偽値・null)を、インデントと表形式で書き直すLLM入力用の形式。仕様はv4.4(2026-10-08・Working Draft)、MITライセンス
- 効く場面:同じフィールドを持つレコードの配列。実測では100行の社員データで整形JSON比-66.8%、compact JSON比-40.7%
- 効かない場面:深い入れ子や形のばらばらな配列では、compact JSON比+20.3%〜+22.8%と逆に増えた。純粋な表ならCSVのほうが13%小さい
- 精度:公式ベンチマークの正答率はTOON 72.2%・JSON 71.4%。差の0.8ポイントは信頼区間の幅(±2.8)より小さく、「TOONで精度が上がる」根拠にはならない
- 実装:TypeScript 4.4.0・Python 1.2.0・Java(JToon)3.0.0の3つは、5種類のデータで互いの出力を正しく読み、エンコード結果もバイト単位で一致した。Python 0.9.0b1などv4未対応の版は入れ子フィールドやキー付き表形式を正しく読めないので、対応仕様を確認して版を固定する
TOONの意味と仕様の現在地
TOONはJohann Schopplich氏が作成し、GitHubのtoon-format組織で仕様と実装が管理されています。仕様書(SPEC.md)は、TOONを「JSONデータモデルを、明示的な構造と最小限の引用符で表す、行指向・インデントベースのテキスト形式」と定義しています。JSONを置き換える汎用フォーマットではなく、コードではJSONのまま扱い、LLMに渡す直前に変換する「翻訳層」としての使い方を想定しています。
| 項目 | 内容(2026年10月22日時点) |
|---|---|
| 正式名称 | Token-Oriented Object Notation |
| 仕様の版 | v4.4(2026-10-08)・Working Draft |
| ライセンス | MIT |
| 拡張子 | .toon |
| メディアタイプ | text/toon(暫定)・常にUTF-8 |
| リファレンス実装 | @toon-format/toon 4.4.0(TypeScript) |
| 公式CLI | @toon-format/cli 4.4.0 |
仕様は短期間に何度も改訂されています。2026年5月にv3.2・v3.3、7月22日に破壊的変更を含むv4.0、7月26日にv4.1、10月6日にv4.2とv4.3、10月8日にv4.4が出ました。v4.2以降の変更はデコーダー側が中心で、CHANGELOGは各版で「仕様どおりの4.xエンコーダーが出力した文書は従来どおりデコードされる」としています。実際に本記事の5種類のデータを4.1.1と4.4.0で変換したところ、出力はすべて同じでした。
第三者の評価では、ThoughtworksのTechnology Radarが2026年4月15日号でTOONを「Assess」(試して影響を見極める段階)に置き、API・データベース・モデルの出力ではJSONの代わりにならないとしています。
JSON・YAML・CSVとの違い(同じデータで比較)
3件の注文データをJSONとTOONで書くと次のようになります。TOONではorders[3]が「要素数3」を、{id,customer{name,pref},total,status}がフィールドの並びを宣言し、各行は値だけを並べます。
{
"orders": [
{ "id": 101, "customer": { "name": "佐藤", "pref": "東京" }, "total": 4800, "status": "shipped" },
{ "id": 102, "customer": { "name": "鈴木", "pref": "大阪" }, "total": 12500, "status": "pending" },
{ "id": 103, "customer": { "name": "高橋", "pref": "福岡" }, "total": 3200, "status": "shipped" }
]
}
orders[3]{id,customer{name,pref},total,status}:
101,佐藤,東京,4800,shipped
102,鈴木,大阪,12500,pending
103,高橋,福岡,3200,shipped
o200k_baseで数えると、掲載したJSONは133トークン、JSON.stringify(data, null, 2)で整形したJSONは157、compact JSONは84、TOONは60でした。TOONをデコードすると元のJSONと完全に一致します。4形式の違いは次のとおりです。
| 観点 | JSON | YAML | CSV | TOON |
|---|---|---|---|---|
| 入れ子 | 波かっこ | インデント | 表せない | インデント |
| キーの記述 | 要素ごと | 要素ごと | ヘッダー1回 | 表形式ではヘッダー1回 |
| 要素数の宣言 | なし | なし | なし | 非空配列は[N]で宣言 |
| 文字列の引用符 | 常に必要 | 条件付き | 条件付き | 条件付き |
| コメント | 不可 | 行末も可 | 不可 | 行頭の#のみ |
| 主な用途 | API・保存 | 設定ファイル | 表計算・集計 | LLMへの入力 |
TOONはYAMLのインデントとCSVの表を組み合わせた形式です。CSVとの違いは、入れ子を表せることと、[N]で行数を宣言するため途中で切れたデータを検出できることです。
TOONの書き方:仕様v4.4の基本構文
オブジェクトとインデント
オブジェクトはキー: 値で書き、入れ子は波かっこの代わりにインデント(既定は半角スペース2つ)で表します。タブによるインデントは使えません。
server:
host: api.example.com
port: 8080
tls:
enabled: true
配列の3つの書き方
配列は中身の形によって書き方が決まります。v4.1以降、表形式の条件を満たす配列は、フィールド付きヘッダーを置ける位置にあれば必ず表形式で出力されます。
- インライン形式:文字列・数値・真偽値・nullからなる配列。
tags[3]: api,web,batch - 表形式:全要素でキーと値の形がそろい、末端の値がプリミティブとなる非空オブジェクトの配列。
users[2]{id,name}:の下に1行1件 - リスト形式:形がそろわない配列。各要素を
-で始める
items[3]:
- id: 1
name: A
- id: 2
name: B
tags[2]: x,y
- id: 3
空配列はtags: []と書きます。v4.1より前のtags[0]:はエンコーダーが出力しなくなりましたが、デコーダーは引き続き受け付けます。
入れ子フィールドとキー付き表形式(v4.0以降)
v4.0では表形式が2方向に広がりました。1つは、先ほどのcustomer{name,pref}のように、入れ子のオブジェクトを列グループとして表の中に畳み込む書き方です。もう1つは、値の形がそろったオブジェクトをキー名[件数:]{フィールド}:で表にする「キー付き表形式」で、機能フラグのようにキー名で引く設定データが1行1件になります。
flags[3:]{enabled,rollout}:
darkMode: true,50
newCheckout: false,0
betaSearch: true,10
同じv4.0で、#で始まる行はコメントになりました。v3で#から始まる値を引用符なしで保存していた場合、v4のデコーダーはその行を捨てます。CHANGELOGは正規表現/^ *#/で該当する文書を探し、v3のデコーダーで読んでから再エンコードするよう案内しています。
一方でkeyFolding(a.b.c: 1のようにキーを連結して入れ子を畳む機能)と、対になるexpandPathsはv4.0で削除され、ドットを含むキーは常に文字どおりのキーとして扱われます。2025年の解説にあるkeyFolding: 'safe'の例は、今のTypeScript版では動きません。
引用符が必要な文字列と区切り文字
文字列は原則として引用符なしで書けます。日本語や絵文字、途中の空白もそのままで構いません。仕様7.2節が引用符を必須とするのは、次のような値です。
- 空文字列、前後に空白がある文字列
true・false・nullと同じ文字列、"05"や"+1"のように数値に見える文字列- コロン、二重引用符、バックスラッシュ、角かっこ、波かっこ、制御文字を含む文字列
- 使用中の区切り文字(既定はカンマ)を含む文字列
-または#で始まる文字列
区切り文字はカンマ・タブ・パイプ(|)から選べ、使う区切り文字はヘッダーにも書かれます。値にカンマを多く含むデータ(住所や文章)はタブにすると引用符が減ります。ただし、100行の社員データではタブにしてもトークン数は1,590のまま変わらず、効果はデータ次第です。
v4.2〜v4.4で厳しくなったデコーダー
v4.2〜v4.4の改訂は、仕様どおりのエンコーダーが出さない「壊れた入力」をどう扱うかを詰めたものです。LLMが書いたTOONや手で直したTOONを読むときに差が出ます。同じ入力を4.1.1と4.4.0のdecodeに渡した結果は次のとおりです。
| 入力 | 4.1.1 | 4.4.0 |
|---|---|---|
コロンのないa[2:]{x} |
{"a[2": "]{x}"}と読む |
エラー |
| 2列の表に3値の行(非strict) | 3つ目を捨てて読む | エラー |
| 宣言3行に実際2行(非strict) | 2行で読む | 2行で読む |
4.1.1は1行目をstrictモードでも黙って誤読し、2行目は値を1つ失ったまま例外を出しません。v4.4では非strictモードで許される救済が仕様14.4節の5つに絞られ、行の値の数が合わない入力はどちらのモードでもエラーになります。LLMの出力を受けるコードは4.4系に上げておくと、壊れた応答を壊れたまま通さずに済みます。
トークン削減効果の実測:データ形状による増減
形の違う5つのデータを@toon-format/toon 4.4.0で変換し、gpt-tokenizer 4.1.0(o200k_base)でトークン数を数えました(2026-10-22・Node.js v26.5.0)。公式ベンチマークと同じトークナイザーです。次のスクリプトで表の全行を再現できます。
import { encode, decode } from '@toon-format/toon'
import { encode as tok } from 'gpt-tokenizer/encoding/o200k_base'
const depts = ['営業', '開発', '人事', '経理']
const prefs = ['東京', '大阪', '福岡', '札幌', '名古屋']
const emp = {
employees: Array.from({ length: 100 }, (_, i) => ({
id: i + 1, name: `社員${i + 1}`, dept: depts[i % 4], pref: prefs[i % 5],
salary: 400 + (i * 7) % 300, active: i % 3 !== 0,
})),
}
const config = {
server: {
host: 'api.example.com', port: 8080,
tls: { enabled: true, cert: { path: '/etc/ssl/api.pem', rotateDays: 90 } },
limits: { rps: 200, burst: { size: 50, windowSec: 10 } },
},
database: { primary: { host: 'db1', pool: { min: 2, max: 20 } }, replica: { host: 'db2', lagMaxMs: 500 } },
features: { search: { engine: 'opensearch', shards: 3 }, cache: { ttlSec: 300 } },
}
const logs = {
events: Array.from({ length: 30 }, (_, i) => i % 2 === 0
? { ts: `2026-10-21T09:${String(i).padStart(2, '0')}:00Z`, level: 'info', msg: `job ${i} done`, durationMs: 120 + i }
: { ts: `2026-10-21T09:${String(i).padStart(2, '0')}:30Z`, level: 'error', error: { code: 'E' + (500 + i), retry: i % 3 === 0 }, path: `/api/v1/items/${i}` }),
}
const orders = { orders: [
{ id: 101, customer: { name: '佐藤', pref: '東京' }, total: 4800, status: 'shipped' },
{ id: 102, customer: { name: '鈴木', pref: '大阪' }, total: 12500, status: 'pending' },
{ id: 103, customer: { name: '高橋', pref: '福岡' }, total: 3200, status: 'shipped' },
] }
const flags = { flags: {
darkMode: { enabled: true, rollout: 50 },
newCheckout: { enabled: false, rollout: 0 },
betaSearch: { enabled: true, rollout: 10 },
} }
for (const [name, d] of Object.entries({ emp, config, logs, orders, flags })) {
console.log(name, {
pretty: tok(JSON.stringify(d, null, 2)).length,
compact: tok(JSON.stringify(d)).length,
toon: tok(encode(d)).length,
roundtrip: JSON.stringify(decode(encode(d))) === JSON.stringify(d),
})
}
measure.mjsとして保存し、npm install @toon-format/[email protected] [email protected]を実行したディレクトリでnode measure.mjsを実行します。5つともroundtripはtrueでした。
| データ | 整形JSON | compact JSON | TOON | compact比 |
|---|---|---|---|---|
| 社員100行(全行同形) | 4,784 | 2,680 | 1,590 | -40.7% |
| 注文3件(入れ子フィールド) | 157 | 84 | 60 | -28.6% |
| 機能フラグ3件(キー付き表) | 75 | 40 | 34 | -15.0% |
| サーバー設定(深い入れ子) | 216 | 114 | 140 | +22.8% |
| イベントログ30件(2種混在) | 1,674 | 1,100 | 1,323 | +20.3% |
表形式にできるデータは整形JSON比で55〜67%減り、compact JSON比でも減りました。表にできない設定とログは、整形JSONより21〜35%少ないものの、compact JSONより20%以上多くなります。TOONが「JSONより30〜60%少ない」と言われるのは整形JSONと比べた数字で、公式READMEのベンチマークでも入れ子や半構造のデータ5種の合計はcompact JSONより1.6%多くなっています。社員データはCSVにすると1,385トークンで、TOONより約13%少なくなりました。
トークン数はモデルのトークナイザーで変わります。o200k_baseはOpenAI系の方式で、ClaudeやGeminiでは絶対値が異なるため、採用前に使うモデルのトークンカウントAPIで測り直してください。日本語の文字列がどう分割されるかはトークナイザーの方式と日本語での違いで詳しく扱っています。
LLMの回答精度:公式ベンチマークの読み方と出力側の結果
公式ベンチマークは、4モデル(claude-haiku-4-5・gemini-3.6-flash・gpt-5.4-nano・grok-4.5)に244問のデータ検索問題を解かせ、形式ごとの正答率を比べています(計5,856回の呼び出し)。
| 形式 | 正答率 | 平均トークン |
|---|---|---|
| TOON | 72.2% ±2.8 | 2,474 |
| JSON(整形) | 71.4% ±2.8 | 4,308 |
| XML | 70.7% ±2.9 | 4,909 |
| YAML | 70.1% ±2.9 | 3,487 |
| JSON(compact) | 69.0% ±2.9 | 2,892 |
±はWilsonの95%信頼区間です。READMEは区間が重なる差を「統計的に意味がない」と扱っていますが、区間の重なりは有意差の有無を厳密に判定するものではなく目安です。観測された正答率の差は0.8ポイントで、「TOONにすると精度が上がる」ではなく「平均トークン数が約4割少なく、正答率の観測値は近かった」と読むのが適切です。モデル別ではgpt-5.4-nanoでTOON 57.0%がJSON 57.4%とXML 59.4%を下回っており、モデルによって順位が入れ替わります。
行が欠けたデータや行数・列数の合わないデータを見抜く構造検証の問題では、TOONが100%、整形JSONが50%でした。ただし対象は5問を4モデルに出した計20件です。[N]と{fields}の宣言があるため、モデルが宣言と実際の行数の食い違いに気づけます。
出力側は事情が異なります。2026年2月8日投稿のarXiv論文(Matveev, 2603.03306)はLLMにTOONを生成させる実験で、素のJSON生成が最も正確だったと報告しています。TOONの書き方をプロンプトで教える「prompt tax」が短い出力では削減分を上回り、単純な構造では、論文のJSON構造化出力(制約付きデコード)のほうがトークンも少なかったとしています。著者は、出力が長くなって構文の節約が上乗せ分を上回る地点から先でだけTOONが有利になる、という仮説も示しています。モデルに返させる形式は、JSON Schemaで出力形式を保証する構造化出力を使い、TOONは入力側に限るのが現実的です。
TypeScript・CLIでJSONをTOONに変換する手順
リファレンス実装のTypeScript版はnpmで入ります。encodeでJSON互換の値をTOON文字列に、decodeでTOONを値に戻します。
npm install --save-exact @toon-format/[email protected]
import { encode, decode } from '@toon-format/toon'
const data = {
users: [
{ id: 1, name: 'Ada', role: 'admin' },
{ id: 2, name: 'Bob', role: 'user' },
],
}
const toon = encode(data)
console.log(toon)
const tsv = encode(data, { delimiter: '\t' })
const back = decode(toon)
console.log(JSON.stringify(back) === JSON.stringify(data))
1つ目のconsole.logはusers[2]{id,name,role}:に続く2行、2つ目はtrueを出力します。decodeは既定でstrict: trueになっており、宣言した行数と実際の行数が合わないと例外を投げます。
try {
decode('users[3]{id,name}:\n 1,Ada\n 2,Bob')
} catch (e) {
console.log(e.message)
// Line 3: Expected 3 tabular rows, but got 2
}
console.log(decode('users[3]{id,name}:\n 1,Ada\n 2,Bob', { strict: false }))
// { users: [ { id: 1, name: 'Ada' }, { id: 2, name: 'Bob' } ] }
LLMの出力をTOONで受け取る場合は、この例外を「途中で切れた応答」の検出に使えます。コードを書かずに変換とトークン数の比較だけしたいときはCLIを使います。
npx @toon-format/cli input.json -o output.toon
npx @toon-format/cli data.toon -o output.json
npx @toon-format/cli data.json --stats
上の2ユーザーのJSONに--statsを付けると、「~27 (JSON) → ~22 (TOON)」と推定トークン数が表示されました。プロンプトに埋め込むときは、公式ガイドのとおりコードブロックの言語名をtoonとし、構文の説明文を書くより表形式の例を1つ見せるほうがモデルは読み取りやすくなります。
PythonでTOONを読み書きする手順(toon-format 1.2.0)
Python版のtoon-formatは、2026年10月3日の1.0.0で正式版になり、最新の1.2.0(10月18日)は仕様v4.4を実装して公式の適合テストをすべて通すとREADMEに書かれています。依存パッケージのない純Python実装で、Python 3.10以上が必要です。標準のjsonモジュールと同じdumps・loadsで使えます。
python -m pip install toon-format==1.2.0
import toon_format
data = {
"users": [
{"id": 1, "name": "Ada", "role": "admin"},
{"id": 2, "name": "Bob", "role": "user"},
]
}
text = toon_format.dumps(data)
print(text)
print(toon_format.loads(text) == data)
try:
toon_format.loads("users[3]{id,name}:\n 1,Ada\n 2,Bob")
except toon_format.ToonDecodeError as e:
print(e.line, e)
print(toon_format.loads("users[3]{id,name}:\n 1,Ada\n 2,Bob", strict=False))
Python 3.12で実行すると、TypeScript版と同じ3行のTOON、True、1 line 1: Declared length 3 but found 2、2件だけの辞書の順に出力されました。ToonDecodeErrorはValueErrorのサブクラスで、line属性に1始まりの行番号を持ちます。encode・decodeという名前も別名として使えます。関数名はTypeScript版と同じでも、オプションはdelimiter="\t"やindent_size=4のようなキーワード引数に書き換える必要があります。
注意が要るのはPython 3.9以前の環境です。1.x系はPython 3.10以上を求めるため、macOS標準の/usr/bin/python3(3.9.6)でpip download toon-formatを実行すると、pipは条件に合う唯一の版である2025年11月のベータ版0.9.0b1を選びました。0.9.0b1はv4より前の実装で、TypeScript 4.4.0が出力した注文データ(入れ子フィールド)はToonDecodeError: Missing colon after keyで止まり、機能フラグ(キー付き表)は例外なしで{'flags[3': ']{enabled,rollout}:'}という壊れた辞書を返しました。requirements.txtにtoon-format>=1.2と書いておけば、古いPythonでは黙って古い版が入る代わりにインストールが失敗します。
0.9系から移るときは、オプションが辞書からキーワード引数に変わり、toonコマンドは削除されています(変換は前述のnpm版CLIを使う)。
JavaでTOONを使う手順(JToon 3.0.0)
JavaにはJToon(dev.toonformat:jtoon)があり、2026年10月時点の最新版は3.0.0です。READMEは仕様4.4への100%準拠をうたい、CIで適合テストを回しています。3.0.0はJava 21が必須で(jarのクラスファイルはmajor version 65)、Java 17の環境では保守ブランチjava17の2.0.x系を使います。2.0.x系の保守期限は2027年第4四半期とされています。
// Gradle(Kotlin DSL)
dependencies {
implementation("dev.toonformat:jtoon:3.0.0")
}
<dependency>
<groupId>dev.toonformat</groupId>
<artifactId>jtoon</artifactId>
<version>3.0.0</version>
</dependency>
3.0.0はJackson 3系(tools.jackson.core:jackson-databind 3.2.3)に依存します。recordやMapをそのままencodeに渡せるほか、JSON文字列を直接変換するencodeJsonと、TOONをJSON文字列に戻すdecodeToJsonがあります。
import dev.toonformat.jtoon.DecodeOptions;
import dev.toonformat.jtoon.JToon;
import java.util.List;
public class Example {
record User(int id, String name, String role) {}
record Data(List<User> users) {}
public static void main(String[] args) {
Data data = new Data(List.of(new User(1, "Ada", "admin"), new User(2, "Bob", "user")));
System.out.println(JToon.encode(data));
String toon = JToon.encodeJson("{\"orders\":[{\"id\":101,\"customer\":{\"name\":\"佐藤\"},\"total\":4800}]}");
System.out.println(toon);
System.out.println(JToon.decodeToJson(toon));
Object rows = JToon.decode("users[3]{id,name}:\n 1,Ada\n 2,Bob", DecodeOptions.withStrict(false));
System.out.println(rows);
}
}
Temurin 25で実行すると、1つ目はTypeScript版と同じusers[2]{id,name,role}:の3行、2つ目はorders[1]{id,customer{name},total}:と101,佐藤,4800の2行、3つ目は元のJSON、4つ目は{users=[{id=1, name=Ada}, {id=2, name=Bob}]}でした。
1つ避けるべき設定があります。JToon 3.0.0には、仕様v4.0で削除されたキーの折り畳みを有効にするEncodeOptions.withFlatten(true)が残っています。既定では無効で、有効にすると階層が崩れました。{"a":{"b":{"x":1,"y":2},"c":{"z":3}}}を変換すると、c.z: 3が本来のa直下ではなく、bの子要素と同じ深さのインデントで出力されます。これをJToon・TypeScript 4.4.0・Python 1.2.0のどれで読んでも、{"a":{"b":{"x":1,"y":2,"c.z":3}}}という別の構造になります。キーの順序を入れ替えた入力では、JToon自身のデコーダーもOver-indented lineで拒否する出力になりました。withFlattenは使わず、既定のまま変換してください。
Apache Camelを使っているなら、2026年10月リリースのCamel 4.23でTOONデータフォーマットがPreviewとして入りました。内部でJToonを使い、ルートの中でJSON互換の値をTOONにmarshalできます。リリース版4.23.0のオプション定義はindent・delimiter・lengthMarker・strict・contentTypeHeaderの5つで、キーの折り畳みの設定はありません。
言語をまたぐときの互換性:v4.4で揃った実装と古い版の誤読
2026年9月時点では実装ごとに追従している仕様の版がばらばらでしたが、10月に入って主要な実装がそろってv4.4を対象にしました。2026年10月22日時点の各レジストリとREADMEの記載は次のとおりです。TypeScript以外は、各リポジトリの説明に「Community-driven」とある有志の実装です。
| 言語 | パッケージ | 最新版 | 対象仕様 |
|---|---|---|---|
| TypeScript | @toon-format/toon | 4.4.0 | v4.4 |
| Python | toon-format | 1.2.0 | v4.4 |
| Java | dev.toonformat:jtoon | 3.0.0 | v4.4 |
| Rust | toon-format | 0.7.0 | v4.4 |
| Go | github.com/toon-format/toon-go | v1.0.1 | v4.4 |
| .NET | Toon.Format | 1.0.0 | v4.4 |
このうちTypeScript 4.4.0・Python 1.2.0・JToon 3.0.0の3つで、本記事の5種類のデータを相互に受け渡しました。TypeScript版の出力をPython版とJToonでデコードすると5種類とも元のJSONと一致し、Python版とJToonのエンコード結果はTypeScript版の出力とバイト単位で同じでした。入れ子フィールドとキー付き表形式も、この3実装・5種類のデータの範囲では問題なく受け渡せました。
問題は古い版が混ざったときです。前述のPython 0.9.0b1はv4の表を誤読し、TypeScript 4.1.1は壊れた行を黙って読み流します。CHANGELOGも、エンコーダーより先にデコーダーを更新するよう求めています。複数のサービスでTOONを読み書きするなら、送り手と受け手のパッケージの版を固定し、デコーダー側から上げてください。サービス間の受け渡しはJSONのままにして、LLMを呼ぶ直前の1か所だけでTOONに変換すれば、版の管理も1か所で済みます。
TOONを採用しない方がよい場面
公式READMEにも「When Not to Use TOON」という節があります。実測の結果と合わせると、次の4つの場面ではJSONやCSVのままにしたほうが得です。
- 深い入れ子や形のそろわない配列:表形式にできる割合が0%に近いと、compact JSONのほうが少なくなる。実測では+22.8%と+20.3%
- 純粋な表データ:CSVのほうが小さい。READMEはTOONの5〜10%の上乗せを「行数や列の宣言と引き換えの信頼性のコスト」と説明している
- 応答速度が最優先の環境:ローカルモデルや量子化モデルでは、トークンが多くてもcompact JSONのほうが速く処理される例があるとREADMEが注記している。初回トークンまでの時間と総時間を自分の環境で測る必要がある
- LLMに出力させる形式:前述の論文のとおり、生成は素のJSONのほうが正確だった
入力トークンの費用を下げる手段はTOONだけではありません。同じシステムプロンプトや参照資料を繰り返し送っているなら、プロンプトキャッシュで繰り返し部分の単価を下げるほうが、形式の変換より手間が少なく効果も大きい場合があります。長いデータがコンテキストウィンドウのトークン上限に収まらないことが課題なら、TOONによる圧縮が効きます。社内データをLLMに渡す仕組みを、形式の選定から設計したい場合は生成AI導入支援でご相談を受け付けています。
よくある質問
TOONの読み方と意味は何ですか?
読み方は「トゥーン」で、Token-Oriented Object Notationの頭文字です。英語のtoonはcartoon(漫画・アニメ)の略語として使われ、3DCGのトゥーンシェーディングなども同じ語ですが、データ形式のTOONとは関係がありません。
TOONはJSONの代わりになりますか?
APIのやり取りや保存用のJSONを置き換えるものではありません。JSONのデータをLLMのプロンプトに入れる直前に変換する用途を想定しています。ほかのシステムとの受け渡しはJSONのままにしてください。
TOONでトークンは何%減りますか?
データの形で変わります。本記事の社員100行では整形JSON比で66.8%、compact JSON比で40.7%減りましたが、深い入れ子や形のそろわない配列では、compact JSONより20%以上増えました。
TOONはPythonで使えますか?
使えます。PyPIのtoon-format 1.2.0が仕様v4.4に対応しており、Python 3.10以上ならpip install toon-formatで入ります。Python 3.9以前では2025年のベータ版0.9.0b1が入り、v4の表を正しく読めないので、Pythonを3.10以上に上げてください。
TOONはJavaで使えますか?
使えます。JToon(dev.toonformat:jtoon)の3.0.0がMaven Centralで公開されており、仕様v4.4に対応しています。Java 21が必須で、Java 17では2.0.x系を使います。キーの折り畳みを有効にするwithFlatten(true)は階層が崩れるため使わないでください。