TOON (Token-Oriented Object Notation) 格式規範¶
目次¶
- TOON 在四大不可取代性中的位置
- TOON 要解決的核心問題
- 壓縮策略與對應收益
- 壓縮效益實測數據
- TOON 結構總覽
- 真實資料範例
- 實際欄位總表
- TOON 解讀規則(Prompt Description)
- 限制與邊界
1. TOON 在四大不可取代性中的位置¶
在 time_compass 的四大不可取代性中,TOON 對應的是 壓縮,負責把東西壓縮成模型可有效使用的格式。
👉 不可替代性
2. TOON 要解決的核心問題¶
原始 API JSON 對人類工程師是合理的,但對 LLM 有幾個明顯問題:
-
重複鍵值太多
同一批事件會一再重複summary、start、end、location等 key,浪費大量 token。 -
巢狀結構不利於快速比較
LLM 要做排程、衝突判斷、期限推理時,真正重要的是時間元件與狀態,而不是一層層物件包裝。 -
跨來源資料結構不一致
Calendar、Tasks、Moodle 有不同的欄位命名與分組方式,直接塞給模型會增加理解負擔。 -
高度重複資訊沒有被抽離
地點、重複規則、課程名稱、tasklist metadata 常常在多筆資料中重複出現。 -
單靠 prompt 無法彌補表示層成本
Prompt 可以教模型如何解讀,但無法消除輸入本身的大量冗餘 token。
3. 壓縮策略與對應收益¶
以下為 TOON 格式達成高壓縮率的核心策略,以及各自解決的問題:
| 策略 | 做法 | 直接收益 | 實作位置 |
|---|---|---|---|
| Schema-less Header | 用 start_here[10]{id,summary,...} 一次定義欄位,後續各列只放值 | 消除重複 key 字串 | utils/toon_utils.py safe_encode |
| 索引化 | 地點、重複規則、課程名稱抽到 location_index / recurrence_index / course_index | 降低重複長字串成本 | models_toon.py _build_location_index, _build_recurrence_index |
| 依照時間分組 | 將 ISO DateTime 分解為日期、週幾、時分等元件 | 提升時間比較與推理效率 | models_toon.py _parse_datetime_parts |
| **語義分組 ** | 按月份、任務狀態、學期等自然語意分桶 | 降低模型先自行整理資料的負擔 | build_toon_calendar, build_toon_tasklist, to_toon_moodle |
| 更改值 | null 改為 0,同月改為 same | 進一步降低 token 並保持可判讀性 | models_toon.py build_toon_event, build_toon_task |
4. 壓縮效益實測數據¶
根據 scripts/analyze_toon_compression.py 分析 assets/fixtures/snapshots(清洗後的真實資料)的結果:
- 分析腳本:
scripts/analyze_toon_compression.py - 執行/測試指令 (uv):
uv run python scripts/analyze_toon_compression.py - 資料規模: Google Calendar 188 事件、Google Tasks 47 任務、Moodle 10 課程事件
- Token 計數工具:
tiktoken(Encoding:o200k_base/ GPT-4o) - 資料流:
fixtures JSON -> AllCalendarEventsSnapshot (Layer 2) -> AllCalendarEventsResult.from_snapshot() (Layer 3) -> ResourceContext -> TOON - 完整報告: TOON_STATS_REPORT.md
- TOON 成品: get_time_context_composite.toon
| 指標 | 標準 JSON | TOON 格式 | 改善幅度 |
|---|---|---|---|
| 字元數 (Chars) | 304,082 | 28,789 | -90.5% |
| 精確 Token (GPT-4o) | 96,772 | 15,800 | -83.7% |
| 資訊密度 | 1.0x | 6.1x | 大幅提升 |
格式對比:雙事件範例¶
原始 Google Calendar API JSON(兩筆事件):
[
{
"id": "evt_a",
"summary": "書法及習作(一)",
"location": "臺科大 TR-510 教室",
"start": { "dateTime": "2025-11-03T13:20:00+08:00", "timeZone": "Asia/Taipei" },
"end": { "dateTime": "2025-11-03T15:10:00+08:00", "timeZone": "Asia/Taipei" },
"recurrence": null,
"description": null
},
{
"id": "evt_b",
"summary": "計算機程式與應用實習",
"location": "臺科大 TR-510 教室",
"start": { "dateTime": "2025-11-10T13:20:00+08:00", "timeZone": "Asia/Taipei" },
"end": { "dateTime": "2025-11-10T15:10:00+08:00", "timeZone": "Asia/Taipei" },
"recurrence": null,
"description": null
}
]
TOON 格式(兩筆事件):
location_index[1]{lid,location}:
L1,臺科大 TR-510 教室
month:
"2025-11":
start_here[2]{id,summary,lid,rid,notes,st_d,st_wd,st_hm,en_m,en_d,en_wd,en_hm}:
evt_a,書法及習作(一),L1,0,0,3,1,"13:20",same,3,1,"15:10"
evt_b,計算機程式與應用實習,L1,0,0,10,1,"13:20",same,10,1,"15:10"
關鍵差異:JSON 兩筆事件都重複完整欄位名稱與巢狀 key;TOON 只宣告一次欄位 header,後續每列只放值,並以
L1參照共享 location。
關鍵優勢¶
- 極致節省 Token: 讓 AI 能在單次請求中載入數個月份的完整排程,而非僅限於當週。
- 模型推理精準度: 結構化表格式資料減少模型對冗餘格式噪音的注意力分散。
- 人類可讀性: 雖為壓縮格式,但仍保留層次與索引,方便開發者 debug 與追蹤資料分布。
5. TOON 結構總覽¶
對於 AI 最常用的 time_context 工具來說,TOON 會把資料組成 meta、google_tasks、google_calendar、moodle 等區塊;在各區塊內再依資料源或語意分組。
A. 區塊分組¶
資料依來源分為 meta, google_tasks, google_calendar, moodle 四大區塊。每個區塊下以資源 ID 或資源名稱作為鍵值。
B. 外部索引¶
為了避免在每個項目中重複寫入冗長字串,TOON 會在區塊頂部建立索引:
location_index: 映射為L1,L2...(如L1,臺科大 教室)recurrence_index: 映射為R1,R2...(如R1,每週一重複)course_index: 映射為c1,c2...(如c1,EC1012301 計算機程式與應用實習)
C. CSV 型 Header¶
每個資料列表都有一個帶有欄位定義的標題:
start_here[數量]{欄位1,欄位2,...}
緊接著是數行簡化資料,以逗號分隔。
解讀時,應永遠以 header 內欄位順序為準。
D. 語義分組¶
不同來源採用不同分組方式:
- Google Calendar: 以月份分組;目前實際使用
start_here,另預留end_from_past - Google Tasks: 以月份 + 狀態分組,分為
due_open、due_done、done,另有undated - Moodle: 以學期 -> 月份雙層分組
6. 真實資料範例¶
A. Google Calendar¶
按月份鍵分組("2025-11"),每月內含 start_here(當月開始的事件):
month:
"2025-11":
start_here[23]{id,summary,lid,rid,notes,st_d,st_wd,st_hm,en_m,en_d,en_wd,en_hm}:
event_id,書法及習作,L1,0,0,3,1,"13:20",same,3,1,"15:10"
實作位置:
models_toon.pybuild_toon_calendar
B. Google Tasks¶
每個 Tasklist 下按截止日期月份分組,任務再依狀態細分:
due_open: 有截止日期、尚未完成due_done: 有截止日期、已完成done: 無截止日期、但已完成(按完成時間分月)undated: 無截止日期且未完成的任務
另外,parent_tree 記錄了父子任務的從屬關係,以節省在每支葉任務上重複記錄父任務資訊:
"Project-big":
parent_tree:
"Coding101開發"[6]: 海報&程式碼,出席資料回報,refresh掛回去,掛litellm,...
month:
"2026-03":
due_open[2]{...}:
...
undated[16]{...}:
...
實作位置:
models_toon.pybuild_toon_tasklist,_build_parent_tree
C. Moodle¶
課程名稱提取至 course_index,內部使用 c1, c2 短標識。事件按學期 (semester) -> 月份雙層分組:
moodle:
course_index[3]{cid,course}:
c1,EC1012301 計算機程式與應用實習
c2,EC163A011 物理(上)
semester:
"114-1":
"2025-11":
due[10]{title,description,cid,status,due_d,due_wd,due_hm}:
Week 9作業繳交截止,Masked,c1,Closed,2,7,"00:00"
實作位置:
moodle/models/models_read.pyto_toon_moodle
D. Google Calendar 完整片段¶
google_calendar:
"台科大課表":
location_index[1]{lid,location}:
L1,臺科大 TR-510 教室
recurrence_index[1]{rid,rule}:
R1,每週一重複
month:
"2025-10":
start_here[1]{id,summary,lid,rid,notes,st_d,st_wd,st_hm,en_m,en_d,en_wd,en_hm}:
evt_03iq,線代,L1,R1,"導師:...",13,1,"13:20",same,13,1,"15:10"
欄位解讀重點:
lid / rid: 對應外部索引。若為0則代表無。st_d: Start Day(日期)st_wd: Start Weekday(1-7)st_hm: Start Hour:Minute(如"08:00")same: 代表該欄位與當前分組 month 或起始月份相同,用以減少重複字串。
7. 實際欄位總表¶
上面的範例只是在說明 TOON 的壓縮方式,不是完整 schema。
實際解讀時應以 header 內的欄位列表為準。
A. Google Tasks¶
Tasklist 容器欄位
| 欄位 | 說明 |
|---|---|
source.id | tasklist id |
source.title | tasklist 標題 |
parent_tree | 父任務標題 -> 子任務標題列表 |
month | 以 YYYY-MM 分組的任務集合 |
undated | 無 due 且未完成的任務列表 |
任務列固定 header
{id,title,tl_id,tl_title,notes,done_m,done_d,done_wd,done_hm,due_m,due_d,due_wd,due_hm}
| 欄位 | 說明 |
|---|---|
id | task id |
title | 任務標題;若同 tasklist 內重名,可能被自動改成 標題#1、標題#2 |
tl_id | tasklist id;通常與外層 source.id 相同,缺值為 0 |
tl_title | tasklist 標題;通常與外層 source.title 相同,缺值為 0 |
notes | 任務說明;缺值為 0 |
done_m/done_d/done_wd/done_hm | 完成時間元件;若未完成則整組通常為 0 |
due_m/due_d/due_wd/due_hm | 截止時間元件;若無 due 則整組通常為 0 |
B. Google Calendar¶
Calendar 容器欄位
| 欄位 | 說明 |
|---|---|
source.id | calendar id |
source.summary | calendar 標題 |
location_index | lid -> location 對照表 |
recurrence_index | rid -> recurrence rule 對照表 |
month | 以 YYYY-MM 分組的事件集合 |
事件列固定 header
{id,summary,lid,rid,notes,st_d,st_wd,st_hm,en_m,en_d,en_wd,en_hm}
| 欄位 | 說明 |
|---|---|
id | event id |
summary | 事件標題 |
lid | 地點索引 id;無地點為 0 |
rid | recurrence 索引 id;無 recurrence 為 0 |
notes | 事件 description;缺值為 0 |
st_d/st_wd/st_hm | 開始時間元件;全天事件 st_hm="full_day" |
en_m/en_d/en_wd/en_hm | 結束時間元件;若與開始月份相同則 en_m="same" |
C. Moodle¶
Moodle 容器欄位
| 欄位 | 說明 |
|---|---|
course_index | cid -> course 對照表 |
semester | 以學期 -> 月份雙層分組的截止事件集合 |
截止事件常見 header
{title,description,cid,status,due_d,due_wd,due_hm}
| 欄位 | 說明 |
|---|---|
title | 事件標題 |
description | 事件說明;缺值為 0 |
cid | 課程索引 id |
status | Open / Closed / Not yet open / Overdue (Grace period) |
due_d/due_wd/due_hm | 截止時間元件 |
8. TOON 解讀規則(Prompt Description)¶
以下為目前使用的 TOON 解讀規則字串(提供給模型先讀索引再解讀事件/任務):
此資料為 TOON 壓縮格式,請先讀索引再解讀事件/任務列。
[共通規則]
- 0 表示無值或未提供。
- "same" 表示與當前分組 month 相同。
- weekday: 1=週一, 7=週日。
[google_tasks]
- source: tasklist 基本資訊。
- parent_tree: 父任務標題 -> 子任務標題列表。
- month[YYYY-MM] 分組語意:
- due_open: 有 due 且未完成。
- due_done: 有 due 且已完成。
- done: 無 due 且已完成(按 completed 分月)。
- undated: 無 due 且未完成。
- 任務時間欄位:due_m/done_m = same | 月份數字(1-12) | 0;due_d/done_d=日期;due_wd/done_wd=週幾;due_hm/done_hm=HH:MM 或 0。
[google_calendar]
- source: calendar 基本資訊。
- location_index: lid -> location(事件內用 lid 參照)。
- recurrence_index: rid -> recurrence rule(事件內用 rid 參照)。
- month[YYYY-MM] 分組語意:
- start_here: 事件起始時間在本月。
- end_from_past: 從前月延續到本月(欄位預留,若出現才解讀)。
- 事件時間欄位:st_d/st_wd/st_hm 為開始;en_m/en_d/en_wd/en_hm 為結束。en_m = same | 月份數字 | 0;全天事件 st_hm/en_hm="full_day"。
- lid/rid=0 表示該事件無 location/recurrence。
[moodle]
- course_index: cid -> 課程名稱。
- semester[學期][YYYY-MM].due[]: 截止事件列表。
- 每筆 due 常見欄位:title, description, cid, status, due_d, due_wd, due_hm。