Architecture Journal

Agent 成本,不只看模型單價:從 Context Architecture 談快取、壓縮與記憶

重點摘要

最近大家在談 agent cost,常見的討論方式是比較不同模型的價格表:

  • input token 單價
  • output token 單價
  • context window

但對真正跑起來的 agent 系統來說,成本通常不只是 model pricing 問題。

更常見的問題其實是:

你是不是在每一輪 request,都重新支付同一份 context 的成本?

如果答案是 yes,那你要處理的就不只是「換便宜模型」,而是 context architecture

Google ADK 提供了一個很好的切分方式,把這個問題拆成三層:

  • Context caching:重用相同前綴
  • Context compaction:壓縮持續膨脹的 session
  • Session / State / Memory:管理短期與長期上下文

這三者常常都被叫做「快取」,但它們其實在解完全不同的問題。

而把這三層設計成一套可觀測、可優化、可演進的系統,本來就是 harness engineering 的工作範圍之一。


Problem

最近 agent 成本會變成熱門話題,不難理解。

不管是 coding agent、research agent、customer support agent,還是內部 automation agent,這些系統都在做同一件事:

  • 把更多工具接進來
  • 把更多上下文塞進 prompt
  • 把更多步驟拆成多輪 interaction

於是每個 request 的組成都變得越來越重。

一個現代 agent request 很可能同時包含:

  • system prompt
  • tool definitions / schema
  • policies / guardrails
  • repo 或文件摘要
  • session history
  • memory retrieval 結果
  • 最新 user 任務
  • 最新 tool result

很多團隊到這個階段,才第一次明確意識到:

agent 很貴,不一定是因為模型太貴, 而是因為同一份昂貴的 context 被反覆重送。


真正的浪費,常常發生在 Prefill

談 LLM 成本時,大家很容易把注意力放在 output token。

但在 agent 系統裡,很多時候真正被低估的是 prefill 成本

所謂 prefill,簡單講就是模型在開始生成前,先把整段輸入讀進去並建立中間表示的過程。

如果你的 agent 每次都把這些東西重送一次:

  • 一大段 system instruction
  • 一堆 tools schema
  • 相同的 repo policy
  • 相同的 reference docs
  • 幾乎沒變的 conversation prefix

那你其實是在反覆支付同一段前綴的計算成本。

這也是為什麼 prompt caching / prefix caching 這幾年會成為主流能力。

它不是附加小優化,而是在處理 agent 架構中最常見的一種浪費:

重複處理相同前綴。


ADK Context Caching 在解什麼問題

Google ADK 的 Context caching 很適合作為理解這件事的起點。

它的出發點非常直接:

當 agent 反覆使用長指令或大型資料集時,每次 request 都重送這些內容會慢、會浪費 token,也會增加成本。

在 ADK 中,context caching 是在 app / agent configuration 層提供的能力,透過 ContextCacheConfig 來控制行為。文件中提到的主要參數包括:

  • min_tokens:小於這個 token 門檻就不啟用 cache
  • ttl_seconds:cache 存活多久
  • cache_intervals:同一份 cached content 最多重用幾次後刷新

它適用的場景很明確:

  • 大量且穩定的 system instructions
  • 反覆分析同一批大型文件
  • 多次 query 同一份 codebase / repo context
  • 多輪 agent interaction 中幾乎不變的 prompt prefix

從工程觀點看,ADK context caching 的重點不是 API 本身,而是它在提醒你一件事:

有些 context 應該被視為 reusable prefix,而不是每次重新拼裝的 request body。

這對 coding agent 特別重要。

因為 coding agent 的 prompt 常常正好符合這個模式:

  • 前綴非常重
  • 前綴變化很慢
  • 真正高頻變動的是後綴

例如:

  • coding rules
  • repo conventions
  • allowed tools
  • approval policies
  • review rubrics
  • project architecture summary

這些資訊如果每一輪都重新付費一次,成本與 latency 很容易失控。


Cache 不是萬靈丹,它只解決「可重用前綴」

這裡有一個很重要的邊界。

Context caching 很有用,但它只在某一類問題上特別有效:

相同前綴會被反覆使用。

如果你的 prompt 結構很亂,導致本來應該穩定的部分常常被改動,那 cache 命中率就會很差。

例如:

  • system prompt 版本頻繁變動
  • tool schema 順序不穩定
  • 每次 request 都把動態資料插進前綴中段
  • retrieval 結果的位置或排序不穩定

這時候你不是沒有 cache feature。

你是沒有 cache discipline

這也是為什麼 OpenAI 的 Prompt Caching、Anthropic 的 Prompt Caching、以及 Gemini API 的 Caching 都一直強調同一件事:

  • 把 static content 放前面
  • 把 dynamic content 放後面
  • 讓前綴儘可能保持完全一致

換句話說,cache 能不能發揮效益,並不是只靠 provider 提供 feature。

還取決於你是否把 prompt 結構設計成「可快取」。


ADK Context Compaction 解的是另一個問題

很多人在第一次看到 ADK 的 Context compaction 時,直覺會把它當成另一種 cache。

但嚴格說,它不是。

ADK context compaction 的目標是:

當 session event history 持續變長時,用摘要來控制 context 膨脹。

這是一個完全不同的問題。

如果 context caching 解的是:

  • 重複送出同樣前綴

那 context compaction 解的就是:

  • 單一 session 隨時間推進,不可避免地越來越胖

ADK 文件裡的做法,本質上是 sliding window 式的 event compaction。 當 session 事件累積到某個門檻後,就對較舊的事件做摘要,保留較新的原始事件與摘要結果。

這裡的 trade-off 很明顯:

  • 好處是 context 可以維持在比較受控的大小
  • 代價是細節可能被摘要損失

所以 compaction 的本質不是 reuse,而是 lossy compression

這點很重要,因為它代表你的優化目標也不一樣。

你評估 caching 時通常關心:

  • cache hit rate
  • cached token ratio
  • latency 改善
  • input cost 降低

但你評估 compaction 時更應該關心:

  • 摘要是否丟失關鍵資訊
  • 長任務成功率有沒有下降
  • agent 在多輪互動後是否還能維持正確 state

一句話總結:

Caching 是重用同樣內容,Compaction 是用摘要換空間。

這兩個能力常常要一起用,但不能混成同一件事。


Session、State、Memory 也不是 Cache

ADK 在 SessionsMemory 的設計上,也把概念切得很清楚。

可以這樣理解:

  • Session:單次會話的事件流
  • State:該 session 內部的短期狀態
  • Memory:跨 session 的長期可搜尋知識

很多實務系統會把這些東西通通叫「記憶」或「快取」,但它們其實在系統行為上完全不同。

Session / State

這一層比較像是當前會話的工作上下文。

它負責的是:

  • 目前聊到哪
  • 已經做過哪些工具呼叫
  • 現在有哪些暫時狀態需要保留

這不是 cache。

因為它不是為了重用相同前綴,也不是為了降低 prefill 成本設計的。

Memory

Memory 更接近 searchable recall。

它的核心問題是:

agent 要怎麼從過去對話或外部知識中,把對現在有用的資訊找回來?

這也不是 cache。

因為 memory 通常是 selective retrieval:

  • 你查什麼,拿回來的內容就不同
  • 它不是固定前綴
  • 它甚至可能讓 prompt 結構更動態

所以如果把 memory 誤當成 cache,你會很容易得到錯的成本預估。

memory 可能提升 recall 與回答品質, 但它不一定會降低 request 成本; 有時甚至會增加 token 消耗。

因此更精準的說法是:

  • cache 解決 reuse
  • compaction 解決 growth
  • memory 解決 recall

這三者都有機會影響成本,但機制完全不同。


從 Provider 與 Infra 角度看,大家其實都在收斂到同一件事

如果把視角從 ADK 拉高,你會發現主流 provider 與推論 infra 都在往類似方向收斂。

Gemini API

Gemini API 的 Caching 分成 implicit caching 與 explicit caching。

  • implicit caching:自動啟用,命中時可能帶來成本與延遲改善
  • explicit caching:顯式建立 cached content,可控制 TTL,成本模型更可預期

這其實對應兩種工程哲學:

  • 平台盡量自動幫你省
  • 或你自己把 reusable prefix 當成一級資源來管理

OpenAI

OpenAI 的 Prompt Caching 本質也很清楚:

  • exact prefix match 才有機會命中
  • static content 應該放在 prompt 開頭
  • dynamic content 應該放在後面

這說明了一件事:

prompt caching 不是魔法,它依賴 prompt 結構紀律。

Anthropic

Anthropic 的 Prompt Caching 文件,對 breakpoint、lookback window、automatic caching、explicit caching 都講得非常細。

對工程師來說,這份文件的價值不只在功能介紹,而是在提醒你:

  • cache 命中是可以被設計的
  • cache miss 也常常是結構問題,不只是運氣問題

Amazon Bedrock

Bedrock 的 Prompt Caching 進一步把這件事帶到平台治理層。

它談的是 cache checkpoints、TTL、不同模型限制,以及在平台 API 中如何管理這些能力。

這表示 prompt caching 已經不只是模型小技巧,而是正式進入 production architecture。

vLLM

從 infra 層看,vLLM 的 Automatic Prefix Caching 更直接點出本質:

prefix caching 是在重用已經計算過的 KV cache blocks,避免重複 prompt computation。

這個視角很好,因為它讓我們知道:

  • 應用層看到的是 prompt caching
  • infra 層看到的是 prefix reuse / KV reuse

不同抽象層的說法不同,但在解的其實是同一種浪費。


真正要設計的,不只是 Cache Feature,而是 Context Architecture

到這裡可以得到一個更完整的架構視角。

如果你在做 agent,context 不應該被視為一整塊沒有結構的 prompt blob。

它至少可以被拆成下面三類:

flowchart TD
    A[Stable Prefix] --> B[Context Caching]
    C[Growing Session History] --> D[Context Compaction]
    E[Long-Term Knowledge] --> F[Memory / Retrieval]
    B --> G[Runtime Context]
    D --> G
    F --> G

1. Stable Prefix

這類內容變化很慢,最適合拿去做 caching:

  • system prompt
  • policies
  • tool definitions
  • coding rules
  • architecture guide
  • common examples

2. Growing Session History

這類內容會隨互動膨脹,適合用 compaction 控制:

  • 多輪對話
  • 多步驟 tool traces
  • 中間推理軌跡的摘要表示

3. Searchable Long-Term Knowledge

這類內容不應該每次硬塞進 prompt,而應該以 retrieval / memory 方式按需載入:

  • 過去 session 的重要知識
  • 使用者偏好
  • 長期文件庫
  • 跨任務知識

當你這樣切分後,很多所謂的 agent 成本問題就會開始變得可處理。

因為你終於可以問對問題:

  • 哪些內容值得 cache?
  • 哪些內容應該被 summary?
  • 哪些內容應該被 retrieve?
  • 哪些內容根本不該進 prompt?

這就是 Harness Engineering 的一部分

從 agent 系統設計角度看,這一整塊工作本來就是 harness engineering 的一環。

如果把 agent 想成一個持續演進的系統,那很多真正影響成本與穩定性的東西,其實不在單一 feature 裡,而在它的執行 harness 裡。

這個 harness 可能包含:

  • prompt 組裝邏輯
  • tool schema 管理
  • cacheable prefix 設計
  • retrieval 注入策略
  • compaction 觸發規則
  • session / memory 邊界
  • eval cases
  • latency 與 token metrics

這些事情很難被單純歸類成:

  • application code
  • infra code
  • model tuning

它更像是一層讓 agent 可被量測、可被回歸測試、可被持續優化的執行框架。

也就是說:

harness engineer 的工作,不只是把 agent 跑起來, 而是把 agent 的 prompt、tools、memory、cache、eval 與 observability 串成一個可優化閉環。

從這個角度看,context caching 就不是「一個可以順手打開的設定」。

它是 harness design 的一部分。


給 Coding Agent 團隊的實作建議

對使用 coding agent 的工程團隊來說,下面幾個設計原則最值得優先處理。

1. 先把 Prompt 結構切乾淨

把內容分成:

  • 穩定前綴
  • 會成長的 session 部分
  • 每次不同的動態輸入

如果這三者混在一起,你很難把 cache 做好。

2. Static Content 前置,Dynamic Content 後置

這幾乎是所有 provider 都反覆強調的原則。

把下列內容盡可能放前面:

  • system rules
  • coding style
  • review criteria
  • tool definitions
  • stable repo guidance

把下列內容放後面:

  • 當前任務
  • 最新 diff
  • 最新 tool result
  • 即時 retrieval snippets

3. 不要把 Compaction 當成免費午餐

Compaction 可以控制 session 成本,但它可能損失細節。

所以你需要 eval:

  • 長任務成功率
  • 多輪一致性
  • 關鍵資訊保留率

4. 不要把 Memory 當成「塞得比較聰明的 Prompt」

Memory 要做的是按需找回,不是把更多內容永遠附加在 request 前面。

如果 retrieval 沒設計好,memory 只會讓 prompt 更貴、更亂。

5. 量測的不是總 Token,而是結構化指標

至少應該觀測:

  • cache hit rate
  • cached tokens ratio
  • TTFT / latency
  • per-run total token cost
  • compaction frequency
  • retrieval hit quality

如果沒有這些指標,你就很難知道到底是:

  • cache 沒命中
  • compaction 太 aggressive
  • retrieval 注入太多垃圾
  • 還是 prompt prefix 根本不穩定

6. Side-agent 與 Background Agent 要特別注意 TTL

有些 agent 工作流不是連續高速互動,而是:

  • 先開一個 side-agent
  • 跑很久
  • 幾分鐘後才有下一輪

這種情況下,TTL 設定會直接影響 cache 還有沒有價值。

所以你的快取策略,必須和 workflow 節奏一起設計。


一個實用的心智模型

如果要把整篇濃縮成一個簡單模型,我會這樣拆:

問題適合機制核心目的
相同前綴反覆出現Context caching降低重複 prefill 成本
單一 session 不斷膨脹Context compaction控制 context 成長
需要跨 session 找回資訊Memory / retrieval提升 recall 能力

這三個問題常常同時存在。

真正成熟的 agent 系統,不會只選其中一個,而是會把三者組合起來。


結論

最近大家在談 agent cost,表面上像是在談模型價格。

但對工程團隊來說,更值得關心的其實是這個問題:

你的 agent,是否把 context 當成一種需要被架構化管理的資源?

ADK 的價值,在於它把幾種常被混淆的能力分開了:

  • caching
  • compaction
  • sessions / state
  • memory

這種切分方式,有助於用更清楚的架構視角理解 agent 成本與上下文管理。

而當你開始把這些東西放進同一個可觀測、可評估、可回歸優化的執行系統裡,這件事就已經不只是 prompt engineering。

它更像是 harness engineering。

在 agent 時代,成本優化不再只是 procurement 問題。

它是 prompt、runtime、memory、cache 與 eval 的共同設計問題。


參考資料