昨天把 Semantic Cache 加進流程之後,我第一個感覺其實很直接,同樣或非常相近的問題不需要每次重新跑 Retrieval、重新送 Prompt、再付一次模型的 Token 成本,尤其是 FAQ、文件查詢這種重複率高的服務,Cache 一加進去,Latency 和 Cost 都會很明顯下降。
但 Cache 跑久之後很快就會遇到另一個問題,昨天留下來的答案,今天還能不能直接拿來用。
假設使用者昨天問:
請問退款期限是幾天?
系統查文件後回答:
商品收到後 7 天內可以申請退款
這個答案被存進 Semantic Cache,隔天另一個人用稍微不同的方式問:
買完東西多久可以退?
Embedding 很接近,Cache Hit,系統直接回傳昨天的答案,速度很快也幾乎沒有額外模型成本。
問題是,如果公司的退款政策今天早上已經從 7 天修改成 14 天,Cache 裡的答案就突然變成錯的,而且它不會因為文件更新而自己消失。
這就是今天要處理的 Cache Invalidation。
第一個想到的方法很直覺,就是每一筆 Cache 都設定有效時間。
例如:
cache_key
answer
created_at
expires_at
如果設定:
TTL = 24 hours
超過一天就重新執行完整流程:
Question
→ Cache Miss
→ Retrieval
→ LLM
→ Save Cache
這個方法很好實作,也可以避免一筆答案永遠留在系統裡,但 TTL 怎麼設定其實很難只有一個標準答案。
如果資料一年才改一次,Cache 只存一小時非常浪費;反過來,如果資料每天都可能更新,Cache 保存七天又太危險。
所以 TTL 最後其實會變成資料特性的一部分。
我開始把資料大概分成幾種。
像產品說明、教學文章、一般 FAQ,更新頻率通常比較低,可以讓 Cache 活久一點;價格、庫存、活動資訊、系統狀態這些變化比較快,TTL 就需要縮短。
甚至有些資訊可能根本不適合使用長時間 Cache,例如:
目前還剩多少庫存?
即使兩個使用者只差十分鐘詢問,答案都有可能不同。
這時候如果只靠 Semantic Similarity 判斷是否命中,其實會漏掉一個很重要的條件,也就是資料的新鮮度。
另一個做法是不要等 TTL 自己過期,當資料來源更新時就主動 Invalidate。
例如 RAG 的文件版本從:
refund_policy_v3
更新成:
refund_policy_v4
系統可以直接找到所有依賴 v3 的 Cache,把它們標記失效。
所以 Cache 裡除了 Question、Embedding、Answer 以外,我開始想加入:
source_document
source_version
created_at
expires_at
這樣答案就不再只是「某個問題以前回答過什麼」,還可以知道「這個答案當初是根據哪一版資料產生的」。
當 Source Version 改變時:
v3 → v4
所有依賴 v3 的 Cache 都可以直接重新計算。
另外一個我原本沒想到的情況是 Prompt。
假設前幾天系統使用:
回答使用者問題
後來改成:
請根據文件回答
如果文件沒有答案就回答不知道
禁止自行推測
模型行為其實已經改變很多,但 Cache 裡還保存著以前 Prompt 產生的答案。
如果這些答案繼續被命中,新版 Prompt 再嚴格都沒有用,因為請求根本不會走到模型那一層。
所以 Cache Key 裡可能還需要考慮:
Prompt Version
Model Version
Knowledge Version
最後可能變成:
semantic_query
+ prompt_version
+ knowledge_version
+ model_version
只要其中一個重要版本改變,就重新產生答案。
昨天剛加入 Cache 時,我主要看到的是省 Token、降 Latency,今天開始處理 Invalidation 之後才發現,一旦 AI 系統真的進 Production,Cache 本身也會變成需要被管理的狀態。
因為每一筆 Cached Answer 都帶著一個產生當下的環境,當時用了哪一版文件、哪一版 Prompt、哪個 Model、什麼時間產生,這些資訊都會影響它今天還能不能繼續使用。
最後我現在比較傾向把 Cache Hit 寫成:
找到相似問題
→ 檢查 TTL
→ 檢查 Knowledge Version
→ 檢查 Prompt Version
→ 條件都成立
→ 回傳 Cache
只要其中一項不成立,就重新跑完整流程。
做到這裡之後,我才開始覺得 Production AI 很多麻煩其實都藏在模型外面,模型可能只花兩秒產生答案,但要讓那個答案可以安全地被重複使用,周圍還需要一整套版本與狀態管理。
明天我想接著處理另一個跟 Production 很有關係的問題,當 Retrieval、LLM、Tool、Cache 都開始串在一起後,只要其中一層變慢,整個 API 就會一起變慢,所以接下來要開始看每一層到底花了多少時間。