prompt 順序搞錯,KV cache 全廢
最近在看各種把 LLM 塞進嵌入式裝置的實驗,有一篇讓我研究了蠻久。
作者用 Jetson Orin NX SUPER 16GB 跑 Gemma 4 E4B,場景是一個把 30+ 個感測器資料全部丟進 prompt 的機器人系統,12K context 長度。最後測出來的數字是 cached TTFT 約 200ms、吞吐量 14-15 tok/s。
乍看沒什麼特別。但他最一開始的版本是多秒起跳的延遲,跟「可互動」完全掛不上鉤。他做了什麼讓 TTFT 從讓人等到不耐煩掉到感覺還行?
原來是 prompt 的排列順序
KV cache 的運作邏輯是:只要 prompt 的前段內容沒變,模型不用重算,直接從快取拿。聽起來很簡單,但做邊緣 Agent 的時候大部分人根本沒注意這件事。
作者的做法是把 prompt 按「穩定程度」排列:
- Persona 和 tool 定義放最前面,這塊幾乎永遠不變
- 對話歷史擺中間,變化頻率比感測器慢得多
- 感測器資料、視覺輸入放在最新 user turn 的末端,每次都會換
這樣安排讓前面 80%+ 的 token 能命中快取,只有最後那段需要重算。
我自己踩過的版本
我之前幫一個 local agent 寫 prompt,邏輯是先給當下狀態、再給工具、最後給 persona——先讓模型「知道現在在哪」再告訴它「你是誰」,感覺很直覺。
結果每次請求都從頭算,因為最前面的狀態資訊每次都不一樣,後面的快取全部失效。改成先固定 persona/tools、狀態推到最後,延遲就明顯降了。我沒有像原文那樣精確 benchmark(那個 200ms 是有在認真量的),但主觀感受從「需要等一下」變成「幾乎即時」。
複製這個做法要確認幾件事
模型的 KV cache 要真的有在用。 llama.cpp 預設支援,但某些輕量 runtime 不一定啟用,先確認一下,不然 prompt 怎麼排都沒意義。
穩定段要夠長才划算。 如果你的 persona 只有 50 個 token,省下來的計算量很有限。效益跟「穩定 token 佔總長度的比例」直接相關——12K context 裡固定 8K 跟固定 500 token 是完全不同的事。
視覺輸入要小心。 圖片 embedding 的計算量可能比純文字重很多,放在什麼位置、怎麼 cache,不同 runtime 的行為不太一樣,要自己跑一下看看。
邊緣裝置的限制逼著你去想這些細節。雲端上「大力出奇蹟」就能過的問題,在嵌入式環境裡會逼你把每個設計決策都想清楚。這有時候反而是好事。
作者:AutoKitty