跳轉到

TOON (Token-Oriented Object Notation) 格式規範

目次

  1. TOON 在四大不可取代性中的位置
  2. TOON 要解決的核心問題
  3. 壓縮策略與對應收益
  4. 壓縮效益實測數據
  5. TOON 結構總覽
  6. 真實資料範例
  7. 實際欄位總表
  8. TOON 解讀規則(Prompt Description)
  9. 限制與邊界

1. TOON 在四大不可取代性中的位置

time_compass 的四大不可取代性中,TOON 對應的是 壓縮,負責把東西壓縮成模型可有效使用的格式。

👉 不可替代性

2. TOON 要解決的核心問題

原始 API JSON 對人類工程師是合理的,但對 LLM 有幾個明顯問題:

  1. 重複鍵值太多
    同一批事件會一再重複 summarystartendlocation 等 key,浪費大量 token。

  2. 巢狀結構不利於快速比較
    LLM 要做排程、衝突判斷、期限推理時,真正重要的是時間元件與狀態,而不是一層層物件包裝。

  3. 跨來源資料結構不一致
    Calendar、Tasks、Moodle 有不同的欄位命名與分組方式,直接塞給模型會增加理解負擔。

  4. 高度重複資訊沒有被抽離
    地點、重複規則、課程名稱、tasklist metadata 常常在多筆資料中重複出現。

  5. 單靠 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。

關鍵優勢

  1. 極致節省 Token: 讓 AI 能在單次請求中載入數個月份的完整排程,而非僅限於當週。
  2. 模型推理精準度: 結構化表格式資料減少模型對冗餘格式噪音的注意力分散。
  3. 人類可讀性: 雖為壓縮格式,但仍保留層次與索引,方便開發者 debug 與追蹤資料分布。

5. TOON 結構總覽

對於 AI 最常用的 time_context 工具來說,TOON 會把資料組成 metagoogle_tasksgoogle_calendarmoodle 等區塊;在各區塊內再依資料源或語意分組。

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_opendue_donedone,另有 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.py build_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.py build_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.py to_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。