TOON を使ってプロンプトのトークン数を削減してみる

TOON を使ってプロンプトのトークン数を削減してみる

2025.12.15

こんにちは
怖いものは地震と定時前のディプロイ作業
オペレーション部のかわいです。

▼は先日同僚と行った南蛮亭さんのカレー。辛くておいしかった。
IMG_3961

さて、最近は生成 AI と会話することが多いんですが、個人環境の無償利用枠内ではトークン数が気になりますよね。
少し前に話題になったんですが、特に JSON ファイルを食わせる場合のトークン数削減に TOON (Token-Oriented Object Notation)というフォーマット が役に立つらしく、今回色々調べてみたので備忘録がてらに記事にしようと思います。

書くこと

  • TOONって?なぜトークン数が減らせるの?
  • Python を使って JSON から TOON に変換してみる
  • 実際の AWS ログで試してみる

TOON とは?

公式リポジトリの記述によると、TOON (Token-Oriented Object Notation) は JSON のデータモデルを、コンパクトで人間が読みやすいようにエンコードした形式。主な特徴としては以下。

  • トークン数を30-60%削減
    → JSONと比較して大幅なトークン削減
  • 人間が読みやすい
    → YAMLライクなインデント構造
  • 表形式データに最適
    → CSVライクな表形式でオブジェクト配列を表現
  • LLMの理解精度向上
    → ベンチマークテストでJSONよりも高い精度

詳細はこちら
https://github.com/toon-format/toon

JSON との比較

実際にどれくらい差があるのか、公式の例で見てみます。

これが

  json
    {
  "users": [
        {"id": 1, "name": "Alice", "role": "admin"},
        {"id": 2, "name": "Bob", "role": "user"}
        ]
    }

▼これになります

users[2,]{id,name,role}:
1,Alice,admin
2,Bob,user

パット見だけでも CSV ぽくて可読性が高いですよね。
JSON で使用される {}、 []、 :、 "" などの記号も最小限になってます。

TOON 公式のベンチマークでも以下のようにトークン数を削減しつつ、LLM の理解精度も向上しています。

  • TOON: 73.9% の精度、2,744トークン
  • JSON (compact): 70.7% の精度、3,081トークン
  • JSON (formatted): 69.7% の精度、4,545トークン

https://github.com/toon-format/toon?tab=readme-ov-file#retrieval-accuracy

Python で試してみる

手動で JSON から変換すると自分のトークン数が枯渇してしまうので、自動でやってもらいましょう。
ということで Python で「toon-python」というライブラリを使用します。
https://github.com/toon-format/toon-python

筆者環境

Ubuntu 24.04.3 LTS
Python 3.12.3

GitHub からインストールしてきます。

pip install git+https://github.com/toon-format/toon-python.git

テストしてみる

先ほどのサンプルで試してみます。

from toon_format import encode, decode

data = {
"name": "Alice",
"age": 30,
"tags": ["python", "aws", "ai"]
}

toon_str = encode(data)
print(toon_str)

original = decode(toon_str)
print(original)

▼出力はこんな感じでシュッとしました。
TOON 変換は「encode(変数)」を使用します。これを確認することで、以下サンプルのように TOON 形式の出力が取れます。
シンプルなのでそこまで分からないですが、後でもう少し複雑なやつを変換していくのでもう少しお付き合いください。

name: Alice
age: 30
tags[3]: python,aws,ai
{'name': 'Alice', 'age': 30, 'tags': ['python', 'aws', 'ai']}

トークン数を比較してみる

別にインストールが必要ですが(後述)、トークン数の比較機能もあります。
先ほどのサンプルを使用して、JSON と TOON のトークン数差分を確認してみます。

from toon_format import estimate_savings, compare_formats

 data = {
  "users": [
    {"id": 1, "name": "Alice"},
    {"id": 2, "name": "Bob"}
  ]

}

# トークン削減率を計算
result = estimate_savings(data)
print(f"トークン削減率: {result['savings_percent']:.1f}%")

# 詳細な比較表を表示
print(compare_formats(data))

▼出力結果。こんなんでました。

トークン削減率: 57.8%
Format Comparison
────────────────────────────────────────────────
Format      Tokens    Size (chars)
JSON           45            117
TOON           19             36
────────────────────────────────────────────────
Savings: 26 tokens (57.8%)

ちょっとした内容ですが、26トークンを減らせました!

比較機能を使うには、tiktoken をインストールします。

pip install tiktoken

実際の JSON ログを変換して比較してみる

試しに、AWS から CloudTrail ログを突っ込んでみます。
データとしてログを代入して、comprae_formats を使い、値を print します。

from toon_format import encode, compare_formats, count_tokens

# CloudTrailログのサンプル
cloudtrail_log = {
  "eventVersion": "1.11",
  "userIdentity": {
"type": "AssumedRole",
"principalId": "AROAXXXXXXXXXXXXXXXXXXXX:user-name",
"arn": "arn:aws:sts::123456789012:assumed-role/user-name/user-name",
"accountId": "123456789012",
"accessKeyId": "ASIAXXXXXXXXXXXXXXXXXXXX",
"sessionContext": {
"sessionIssuer": {
"type": "Role",
"principalId": "AROAXXXXXXXXXXXXXXXXXXXX",
"arn": "arn:aws:iam::123456789012:role/user-name",
"accountId": "123456789012",
"userName": "user-name"
},
"attributes": {
"creationDate": "2025-12-02T01:25:35Z",
"mfaAuthenticated": "true"
  }
  }
  },
"eventTime": "2025-12-02T01:28:08Z",
"eventSource": "cloudtrail.amazonaws.com",
"eventName": "LookupEvents",
"awsRegion": "us-east-1",
"sourceIPAddress": "xxx.xxx.xxx.xxx",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like  Gecko) Chrome/142.0.0.0 Safari/537.36",
"requestParameters": {
"lookupAttributes": [
{
"attributeKey": "ReadOnly",
"attributeValue": "false"
}
],
"maxResults": 50,
"nextToken": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  },
  "responseElements": None,
  "requestID": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "eventID": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "readOnly": True,
  "eventType": "AwsApiCall",
  "managementEvent": True,
  "recipientAccountId": "123456789012",
  "eventCategory": "Management",
  "tlsDetails": {
"tlsVersion": "TLSv1.3",
"cipherSuite": "TLS_AES_128_GCM_SHA256",
"clientProvidedHostHeader": "cloudtrail.us-east-1.amazonaws.com"
  },
  "sessionCredentialFromConsole": "true"
}

toon_log = encode(cloudtrail_log)

print("=== TOON形式 ===")
print(toon_log)
print()

# トークン数を比較
print("=== トークン数比較 ===")
print(compare_formats(cloudtrail_log))

▼ 実行結果

Format Comparison
────────────────────────────────────────────────
Format      Tokens    Size (chars)
JSON          519          1,715
TOON          422          1,397
────────────────────────────────────────────────
Savings: 97 tokens (18.7%)

若干だけ減らせました。

TOON 変換がそこまで効果のない理由として、

  • ネスト構造が深い
  • 配列が少ない
  • 引用符の必要な値が多い

上記のような JSON ファイルはあんまり削減に繋がらないです。

活用例

例えば以下のような DynamoDB から取得したアイテム一覧だと、トークン数の削減が期待できます。

from toon_format import encode, compare_formats, estimate_savings

dynamodb_items = {
"Items": [
{"userId": "user001", "score": 1500, "level": 10, "lastLogin": "2024-12-01"},
{"userId": "user002", "score": 2300, "level": 15, "lastLogin": "2024-12-02"},
{"userId": "user003", "score": 890, "level": 7, "lastLogin": "2024-11-30"},
{"userId": "user004", "score": 3200, "level": 20, "lastLogin": "2024-12-02"},
{"userId": "user005", "score": 1100, "level": 9, "lastLogin": "2024-12-01"}
],
"Count": 5,
"ScannedCount": 5
}

print(compare_formats(dynamodb_items))
----
Format Comparison
────────────────────────────────────────────────
Format      Tokens    Size (chars)
JSON          229            611
TOON          104            206
────────────────────────────────────────────────
Savings: 125 tokens (54.6%)

上記のような結果となり、半分以下のトークン数となりました。

また、以下のような Lambda 関数の実行ログだと、

from toon_format import encode, compare_formats, estimate_savings

lambda_logs = {
"logGroup": "/aws/lambda/my-function",
"logStream": "2024/12/02/[$LATEST]abc123",
"events": [
  {
    "timestamp": "2024-12-02T10:00:00Z",
    "level": "INFO",
    "requestId": "req-001",
    "message": "Processing started",
    "duration": 150.5,
    "memoryUsed": 128
  },
  {
    "timestamp": "2024-12-02T10:00:01Z",
    "level": "INFO",
    "requestId": "req-002",
    "message": "Processing completed",
    "duration": 200.3,
    "memoryUsed": 156
  },
  {
    "timestamp": "2024-12-02T10:00:02Z",
    "level": "ERROR",
    "requestId": "req-003",
    "message": "Processing failed",
    "duration": 50.1,
    "memoryUsed": 98
  }
]
  }

print(compare_formats(lambda_logs))
----
Format Comparison
────────────────────────────────────────────────
Format      Tokens    Size (chars)
JSON          235            697
TOON          137            340
────────────────────────────────────────────────
Savings: 98 tokens (41.7%)

こちらも40%少しトークン数が削減できました。

まとめ的な

試した結果、以下のような構造を持つ JSON データだと、TOON 変換によってトークン数の削減が期待できそうです。

  • 同じ形式の配列データ
  • 階層の浅い、ネスト構造が入り組んでいないデータ
  • 繰り返しが多いデータ
  • 文字列、数字、真偽値などのシンプルな値が多いデータ

変換にはひと手間必要にはなりますが、例えば Claude Sonnet 4 の料金が「$3/1M トークン」であることを考えると、1日10,000件の Lambda 実行ログを分析する場合、単純計算で月間約 $90 - 100 程度 の削減が可能な計算になります
https://platform.claude.com/docs/ja/about-claude/pricing

特にログやデータ分析など、配列形式の構造化データを AI に渡すケースでは、TOON の採用を検討する価値があるかもしれません。

この記事をシェアする

FacebookHatena blogX

関連記事