擋掉 diff,為甚麼還沒省到額度?一次 Agent SDK 修法的代價

繞過 C26 的收集條件可以改變檔案追蹤,但不能直接證明額度改善。這次歷史 workaround 的教訓,在於你為了修狀態,究竟改掉了哪些證據。

這篇的段落(5)
  1. 為甚麼在對話裡 Read,下一輪仍可能重送?
  2. C26 當時用哪些工具紀錄建表?
  3. 刪掉 Write content,會失去甚麼?
  4. 那次修法到底解決了多少?
  5. 現在要怎麼判斷這類 workaround 值不值得留?

我曾經為了擋掉 Agent SDK 重複注入的檔案 diff,直接改它要讀回的 JSONL 對話紀錄。

做法看起來很有效率:既然狀態重建只認某幾種工具紀錄,那就改掉收集條件,讓它建不出那份追蹤資料。

但後來的成本追查提醒了我兩件事。擋掉 diff,沒有自動換來快取改善;而我原先寫的「不影響對話內容或理解」,也說得太滿。刪掉歷史 Write 的內容,就是改掉了一部分歷史。

這篇說明當時為甚麼會這樣修,以及你要付出的代價。

版本:Claude Code v2.1.71,以及當時逐輪重新呼叫並 resume 的 SDK 用法。C26 是那份 minified 程式裡的函數名,不是穩定 API。

為甚麼在對話裡 Read,下一輪仍可能重送?

因為我當時的下一輪會重新還原狀態,上輪記憶體裡更新過的資料不一定繼續沿用。

在 CLI 持續互動時,Read 能更新當下的檔案追蹤。但我的 SDK 程式每次重新呼叫、resume,都會從保存的工具歷史再建一次 readFileState。因此,這輪補讀有效,不代表下輪重建後仍有相同狀態。

原文把這件事概括成「Agent SDK 每輪都會丟掉狀態」。現在應該說清楚,我追到的是那個版本、那條呼叫路徑;SDK 也有持續輸入的多輪方式,不能只靠產品名稱判斷生命週期。

C26 當時用哪些工具紀錄建表?

依當時的逆向紀錄,它收集符合條件的 Read 與 Write。

工具紀錄收集條件
Readinput.offset === undefined && input.limit === undefined
Writeinput.file_path && input.content
Edit那段收集邏輯未處理

這裡有個容易混淆的地方:讀取「從第一行開始」,和工具輸入裡「明確存在 offset: 1」,在程式判斷上可以不同。模型或呼叫端實際有沒有送這個欄位,要看紀錄,不能因為工具預設從第一行開始,就說它幾乎永遠不會進表。

我當時想到的 workaround,就是讓歷史紀錄不再符合這兩個條件:替缺少 offset 的 Read 補上 offset: 1,再移除 Write 的 content。

這改的是重建函數的輸入,沒有修正重建函數本身。就像把清單上的資料拿掉,後面的程式當然不會再按同一份資料追蹤;但清單同時也少了原本保存的東西。

刪掉 Write content,會失去甚麼?

會失去那次寫入的完整內容,後來的對話與查核可能受影響。

JSONL 裡的 tool_use 記錄了模型當時要求工具做的事。移除 Write.input.content 後,歷史不再完整描述那次操作。

例如後續想比較「第一次寫了哪個版本」與「目前檔案差在哪裡」,如果當時寫入內容已刪,就不能只靠那筆歷史還原。模型接續時是否受影響,則取決於它還看得到哪些其他資訊,不能預先保證完全沒差。

補 Read 的欄位也改動了歷史工具呼叫。即使讀取範圍在某個版本等效,仍應承認這是為了影響還原邏輯而改寫紀錄。

另外,執行中的程序可能仍在追加 JSONL。若另一段程式同時整份讀入、改完再寫回,就有覆蓋新紀錄的風險。備份能保留先前副本,不能代替對寫入時機與還原結果的確認。

因此我不再把原來那段直接修改原檔的 TypeScript,當成今天可直接採用的整合範例。它缺少完整性、同時寫入與接續結果的驗證,也依賴內部儲存格式與路徑規則。

那次修法到底解決了多少?

它針對部分追蹤紀錄的收集條件,沒有證明整體成本已改善。

原來的想法有三段,需要各自成立:

  1. 改寫歷史後,重建出的追蹤資料是否真的改變?
  2. 追蹤資料改變後,要消除的那種 diff 是否真的不再送出?
  3. 同樣工作量下,輸入、快取與整體消耗是否改善?

第一段就算成立,也不能跳過後兩段。其他內部來源仍可能提供檔案或動態資訊;少一種 diff,也可能還留著另一個破壞快取前綴的來源。

後續實驗裡,大量建立快取的情況仍在,紀錄的讀取比例只有約 15–25%。該表沒有把 sanitizer 的獨立開關完整列出,所以不能反過來精算這段程式單獨有多少效益。它能否定的是當初那個太快的宣告:做到繞過一段收集邏輯,還不能說原本的額度問題修好了。

相關問題可接著看〈我逆向了 Claude Code v2.1.71,找到 token 消耗暴增的根因〉。

相關問題可接著看〈Claude Code 實戰:我怎麼用 AI 寫完一整個網站〉。

若要把本文的判準用在自己的工作,可參考課程與工程手冊的適用範圍。

現在要怎麼判斷這類 workaround 值不值得留?

把省下的用量,和改動歷史造成的損失放在一起看。

如果同一問題能用官方支援的 session 生命週期或持續輸入方式處理,先比較那條路。2026-10-29 的 官方 SDK 文件建議使用 query() 的 AsyncIterable<SDKUserMessage> 維持多輪,或用 options.resume 接續已保存的 session;當年的 V2 已移除,不能再視為現行替代方案。

若你在維護鎖定舊版本的系統,必須研究類似修法,最低限度也要能回答:原始紀錄是否完整保留、實驗期間誰會寫入、是否確實少送目標內容,以及接續後能否正確完成原任務。沒有這些證據,只看到「程式沒報錯」還不夠。

當初我動手,是因為問題已經影響實際工作,等不下去。這個動機保留,對效果的判斷則要跟著證據更新。

接著看〈Agent SDK 每句吃 2–3% 額度:我怎麼從失敗修法追到快取問題〉,裡面放了後續比較。它能幫你看出,一次修法消掉症狀之後,還有哪些問題沒有一起消失。

免費 5 層健檢:拿手上的任務自己測 →看 Claude Code 深度工程手冊(免費試看) →60 分鐘專案卡關諮詢 →
Cab(CabLate)作者照片

寫這篇的是 Cab,在第一線教 AI 落地,從農會推廣人員教到外商工程團隊。我的經歷・團隊想導入,看企業內訓。