🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付

如果你正在建立基於人工智慧的功能,你很可能需要向語言模型發送大量的 JSON 資料。而如果你發送了大量的 JSON 資料,那麼你消耗令牌的速度可能比你預期的要快得多。

Kollabe ,我們使用人工智慧產生回顧會議和站會的摘要、行動項和建議。當數十名團隊成員每天提交更新時,JSON 資料量會迅速成長。我們需要在不遺失資訊的情況下縮小資料規模的方法。

目前有一些更新的正式解決方案,例如TOON (標記導向的物件表示法),它可以將 LLM 的 JSON 壓縮高達 40%。它是一個完整的規範,包含基準測試和 SDK。如果您想要一種標準化的方法,值得了解一下。

但有時您需要掌控一切。您不希望增加額外的依賴項。您希望確切地了解發送給模型的內容,並根據您的特定用例進行調整。以下是我們用來減少令牌使用量而不增加複雜性的簡單技巧。

  1. 將長 ID 替換為短 ID

UUID無所不在。它們對資料庫來說很棒,但對令牌效率來說卻很糟糕。

// This UUID is 4-5 tokens
"550e8400-e29b-41d4-a716-446655440000"

// This is 1 token
"u-1"

當你在數百個站立會議記錄中引用同一個使用者時,這些額外的令牌會迅速累積起來。

解決方案:在處理資料時建立一個簡單的映射。遇到的第一個用戶記為u-1 ,第二個記為u-2 ,依此類推。如果再次遇到相同的 UUID,則重複使用已指派的短 ID。

// Before: UUIDs everywhere
{
  odUserId: "550e8400-e29b-41d4-a716-446655440000",
  odQuestionId: "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  odAnswerId: "f47ac10b-58cc-4372-a567-0e02b2c3d479"
}

// After: short, prefixed IDs
{
  uid: "u-1",
  qid: "q-1", 
  aid: "a-1"
}

關鍵在於,同一個 UUID 總是對應同一個短 ID。因此,當 LLM 在不同的答案中多次看到u-1時,它就知道這些條目屬於同一個人。為不同的實體類型使用不同的前綴,以便模型能夠區分使用者 ID 和問題 ID。

  1. 去掉格式

JSON.stringify還有第二個和第三個參數,大多數人都會忽略它們。第三個參數用於新增縮排:

// Pretty printed (wasteful)
JSON.stringify(data, null, 2);

// Minified (efficient)
JSON.stringify(data);

區別如下:

// Pretty: ~80 characters
{
  "name": "Alice",
  "role": "Engineer",
  "team": "Platform"
}

// Minified: ~45 characters
{"name":"Alice","role":"Engineer","team":"Platform"}

如果是小物件,無所謂。但如果是成千上萬個站立式會議紀錄呢?那些空白加起來就不少了。反正LLM(立法程序管理員)也不在乎格式。

  1. 使用更短的鍵名

仔細想想,這似乎顯而易見。對比一下:

// Verbose
type StandupEntry = {
  odUserId: string;
  userName: string;
  yesterdayUpdate: string;
  todayPlan: string;
  blockerDescription: string;
};

// Concise
type StandupEntry = {
  odUid: string;
  name: string;
  yesterday: string;
  today: string;
  blocker: string;
};

當有數百筆記錄時,較短的按鍵可以節省實際的令牌。只需確保鍵足夠易讀,以便語言學習模型 (LLM) 能夠理解上下文。

我們遵循以下幾條規則:

  • 刪除冗餘詞語:如果userId明顯是指使用者物件,則將其改為id

  • 使用常用縮寫:例如,用desc取代description

  • 保持明確: y代表昨天太隱晦,但yest就很好。

  1. 刪除空值和空字串

不要發送不存在的資料:

function removeEmpty<T extends object>(obj: T): Partial<T> {
  return Object.fromEntries(
    Object.entries(obj).filter(([_, v]) => {
      if (v === null || v === undefined) return false;
      if (v === "") return false;
      if (Array.isArray(v) && v.length === 0) return false;
      return true;
    })
  ) as Partial<T>;
}

// Before
{
  "name": "Alice",
  "blocker": null,
  "tags": [],
  "notes": ""
}

// After
{
  "name": "Alice"
}

如果沒有人檢舉阻塞問題,為什麼要告訴LLM呢?

  1. 盡可能扁平化嵌套結構

有時嵌套只是組織上的冗餘:

// Before
{
  "user": {
    "profile": {
      "name": "Alice",
      "team": "Platform"
    }
  },
  "update": "Finished feature"
}

// After
{
  "name": "Alice",
  "team": "Platform",
  "update": "Finished feature"
}

第二個版本以較少的結構標記傳達了相同的訊息。顯然,如果層級結構本身就具有意義,就不應該將其扁平化,但通常並非如此。

  1. 使用陣列代替重複物件

如果您有一系列相似專案,請考慮是否需要每個專案的完整物件結構:

// Before: 3 objects with repeated keys
{
  "entries": [
    { "name": "Alice", "status": "done" },
    { "name": "Bob", "status": "blocked" },
    { "name": "Carol", "status": "done" }
  ]
}

// After: header row + data rows
{
  "cols": ["name", "status"],
  "rows": [
    ["Alice", "done"],
    ["Bob", "blocked"],
    ["Carol", "done"]
  ]
}

這樣雖然犧牲了一些可讀性,但卻提高了效率。對於大型資料集來說,這是值得的。

  1. 刪除不必要的元資料

人工智慧處理通常不需要時間戳、審計欄位和內部 ID:

// Before: full database record
{
  odAnswerId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  odUserId: "550e8400-e29b-41d4-a716-446655440000",
  text: "Great sprint!",
  createdAt: "2024-01-15T10:30:00.000Z",
  updatedAt: "2024-01-15T10:30:00.000Z",
  isDeleted: false,
  version: 1
}

// After: just what the LLM needs
{
  uid: "u-1",
  text: "Great sprint!"
}

問問自己:模型真的需要這個欄位才能產生有用的回應嗎?如果不需要,就把它刪掉。

  1. 高效率地表示布林值

對於布林標誌,請考慮當其值為 false 時是否還需要該欄位:

// Before
{ "name": "Alice", "isAdmin": false, "isActive": true, "isVerified": false }

// After: only include truthy flags
{ "name": "Alice", "active": true }

// Or use a flags array for multiple true values
{ "name": "Alice", "flags": ["active", "verified"] }

如果大多數使用者都不是管理員,則不要在每筆記錄中包含isAdmin: false

綜合起來

以下是 Kollabe 產生的一份真實回顧總結中的前後對比圖:

優化前:

{
  "retrospectiveData": {
    "questions": [
      {
        "odQuestionId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
        "questionText": "What went well this sprint?",
        "questionType": "positive"
      },
      {
        "odQuestionId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
        "questionText": "What could be improved?",
        "questionType": "negative"
      }
    ],
    "answers": [
      {
        "odAnswerId": "1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed",
        "odQuestionId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
        "odUserId": "550e8400-e29b-41d4-a716-446655440000",
        "userName": "Alice Chen",
        "answerText": "Team collaboration was excellent during the release",
        "createdAt": "2024-01-15T10:30:00.000Z",
        "voteCount": 3
      },
      {
        "odAnswerId": "6ec0bd7f-11c0-43da-975e-2a8ad9ebae0b",
        "odQuestionId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
        "odUserId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
        "userName": "Bob Smith",
        "answerText": "CI/CD pipeline improvements saved us hours",
        "createdAt": "2024-01-15T10:32:00.000Z",
        "voteCount": 5
      },
      {
        "odAnswerId": "3f333df6-90a4-4fda-8dd3-9485d27cee36",
        "odQuestionId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
        "odUserId": "550e8400-e29b-41d4-a716-446655440000",
        "userName": "Alice Chen",
        "answerText": "Documentation was often outdated",
        "createdAt": "2024-01-15T10:35:00.000Z",
        "voteCount": null
      }
    ]
  }
}

優化後:

{"qs":[{"id":"q-1","text":"What went well this sprint?","type":"positive"},{"id":"q-2","text":"What could be improved?","type":"negative"}],"ans":[{"id":"a-1","qid":"q-1","uid":"u-1","name":"Alice Chen","text":"Team collaboration was excellent during the release","votes":3},{"id":"a-2","qid":"q-1","uid":"u-2","name":"Bob Smith","text":"CI/CD pipeline improvements saved us hours","votes":5},{"id":"a-3","qid":"q-2","uid":"u-1","name":"Alice Chen","text":"Documentation was often outdated"}]}

發生了哪些變化:

  • UUID 被替換為短 ID( q-1a-1u-1

  • 較長的鍵名已縮短( odQuestionId縮短為qidanswerTexttext

  • 已移除包裝物件( retrospectiveData

  • 已刪除空值(無voteCount: null

  • 已移除時間戳記(產生摘要時不需要)

  • 無空格格式

優化後的版本體積縮小了約 50%。對於一個擁有 50 名成員、數百個回覆的團隊來說,這能顯著降低令牌成本並加快推理速度。

何時優化

並非所有 JSON 資料都需要這種處理。如果您發送的是一個小型配置物件或單一使用者查詢,那麼優化帶來的額外開銷就得不償失了。

但是,當你建立需要處理大量結構化資料的功能時,例如我們在 Kollabe 開發的回顧和站會總結功能,這些技巧就顯得尤為重要。它們易於實現,無需外在依賴,並且能立即帶來成效。

掌控資料管道也至關重要。編寫自己的最佳化層,就能完全了解其運作機制。您可以調整短 ID 前綴,決定刪除哪些字段,並隨著資料的變化調整策略。一切都無需擔心,沒有黑箱操作。

最棒的是什麼? LLM 可以完美處理最佳化後的 JSON 資料。它們不需要漂亮的格式或冗長的鍵名就能理解你的資料。它們只需要資訊本身。


哦,最後再厚顏無恥地推銷一下:如果你在敏捷開發團隊工作,不妨試試我開發的免費計劃撲克和回顧工具 Kollabe 。我們利用這些技巧來增強我們的 AI 總結功能。


原文出處:https://dev.to/mattlewandowski93/optimizing-json-for-llms-1dgf


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝7   💬6   ❤️2
168
🥈
我愛JS
💬1  
6
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付