- Published on
第一次學 Kotlin Koog AI 就上手 Day 13:省錢又高效:學會使用提示快取
在前幾篇文章中,我們探索了 Koog 的多模態內容處理能力。隨著我們的 AI 應用功能越來越強大,一個新的挑戰浮現:當用戶詢問相同問題時(例如「如何重設密碼」、「支援哪些付款方式」),系統仍會重複調用昂貴的 LLM API,造成不必要的成本和延遲
今天我們將學習 提示快取機制,這是實現效能優化和成本控制的關鍵技術,讓系統能夠瞬間回應常見問題並大幅節省成本
什麼是提示快取?
提示快取是一種將 AI 回應結果暫存起來的機制。當遇到相同的提示時,系統直接返回快取的結果,而不需要重新呼叫 LLM API
主要效益:
- 回應速度:從秒級降至毫秒級
- 成本節省:避免重複的 API 付費請求
- 系統穩定性:減少對外部 API 的依賴
簡單對比:
- 沒有快取:每次問「如何重設密碼」可能都需要花 1~2 秒 + API 費用
- 有快取:第一次 1~2 秒,之後可能都是毫秒的回應且免費
記憶體快取:最簡單的開始
記憶體快取將結果直接存在記憶體中,是最快速的快取方案
基本使用
suspend fun main() {
// 建立記憶體快取,限制最大 100 筆記錄
val cache = InMemoryPromptCache(maxEntries = 100)
val executor = simpleOpenAIExecutor(ApiKeyManager.openAIApiKey!!)
val prompt = prompt("memory") {
system {
text("""
你是一個友善的 AI 助手
請使用正體中文回答
""".trimIndent())
}
user {
text("什麼是 Kotlin 協程?請簡單的說明")
}
}
val promptRequest = PromptCache.Request.create(prompt, emptyList())
println("=== 第一次詢問(會呼叫 API)===")
var cachedResponse = cache.get(promptRequest)
if (cachedResponse != null) {
println("AI (使用快取回應): ${cachedResponse.first().content}")
} else {
val response = executor.execute(prompt, OpenAIModels.CostOptimized.GPT4_1Mini)
// 將回應存入快取
cache.put(promptRequest, listOf(response))
println("AI : ${response.content}")
}
println("\n=== 第二次詢問相同問題(使用快取)===")
cachedResponse = cache.get(promptRequest)
if (cachedResponse != null) {
println("AI (使用快取回應): ${cachedResponse.first().content}")
} else {
val response = executor.execute(prompt, OpenAIModels.CostOptimized.GPT4_1Mini)
// 將回應存入快取
cache.put(promptRequest, listOf(response))
println("AI : ${response.content}")
}
}
- 優點: 速度最快、設定簡單
- 缺點: 應用重啟後會消失
執行 AI 回應內容
=== 第一次詢問(會呼叫 API)===
AI : Kotlin 協程(Coroutine)是一種輕量級的非同步程式設計工具,可以幫助你用更簡單、直覺的方式寫出非同步、並行的程式碼。它讓你可以暫停函式的執行(不會阻塞線程),等候耗時操作完成後再繼續,寫起來像同步程式碼一樣清晰易懂。簡單來說,協程讓非同步程式設計變得更簡單、更有效率。
=== 第二次詢問相同問題(使用快取)===
AI (使用快取回應): Kotlin 協程(Coroutine)是一種輕量級的非同步程式設計工具,可以幫助你用更簡單、直覺的方式寫出非同步、並行的程式碼。它讓你可以暫停函式的執行(不會阻塞線程),等候耗時操作完成後再繼續,寫起來像同步程式碼一樣清晰易懂。簡單來說,協程讓非同步程式設計變得更簡單、更有效率。
檔案快取:持久化的選擇
檔案快取將結果存在磁碟上,重啟應用後仍然保留
基本使用
suspend fun main() {
// 建立檔案快取,指定快取目錄和最大檔案數量
val cache = FilePromptCache(
Path.of("cache/prompts"),
50
)
val executor = simpleOpenAIExecutor(ApiKeyManager.openAIApiKey!!)
val prompt = prompt("file") {
system {
text("""
你是一個友善的 AI 助手
請使用正體中文回答
""".trimIndent())
}
user {
text("什麼是 Kotlin 協程?請簡單的說明")
}
}
val promptRequest = PromptCache.Request.create(prompt, emptyList())
println("=== 第一次詢問(會呼叫 API)===")
var cachedResponse = cache.get(promptRequest)
if (cachedResponse != null) {
println("AI (從檔案快取載入): ${cachedResponse.first().content}")
} else {
val response = executor.execute(prompt, OpenAIModels.CostOptimized.GPT4_1Mini)
// 將回應存入檔案快取
cache.put(promptRequest, listOf(response))
println("AI : ${response.content}")
println("(回應已存入檔案快取)")
}
println("\n=== 第二次詢問相同問題(使用檔案快取)===")
cachedResponse = cache.get(promptRequest)
if (cachedResponse != null) {
println("AI (從檔案快取載入): ${cachedResponse.first().content}")
} else {
val response = executor.execute(prompt, OpenAIModels.CostOptimized.GPT4_1Mini)
// 將回應存入檔案快取
cache.put(promptRequest, listOf(response))
println("AI : ${response.content}")
println("(回應已存入檔案快取)")
}
}
- 優點: 持久化存儲、重啟後仍可用
- 缺點: 相對較慢、需要磁碟空間
執行 AI 回應內容
=== 第一次詢問(會呼叫 API)===
AI : Kotlin 協程是一種用來寫非同步程式的輕量級工具。它可以讓你用同步的寫法來寫非同步程式,讓程式碼更簡潔且容易理解。協程可以暫停和恢復執行,幫助你有效率地處理耗時的操作,比如網路請求或資料庫讀取,而不會阻塞主執行緒。簡單來說,Kotlin 協程讓非同步程式寫起來像同步程式一樣方便。
(回應已存入檔案快取)
=== 第二次詢問相同問題(使用檔案快取)===
AI (使用快取回應): Kotlin 協程是一種用來寫非同步程式的輕量級工具。它可以讓你用同步的寫法來寫非同步程式,讓程式碼更簡潔且容易理解。協程可以暫停和恢復執行,幫助你有效率地處理耗時的操作,比如網路請求或資料庫讀取,而不會阻塞主執行緒。簡單來說,Kotlin 協程讓非同步程式寫起來像同步程式一樣方便。
檔案快取內容
這是儲存在檔案裡的快取內容
{
"response": [
{
"type": "ai.koog.prompt.message.Message.Assistant",
"content": "Kotlin 協程是一種用來寫非同步程式的輕量級工具。它可以讓你用同步的寫法來寫非同步程式,讓程式碼更簡潔且容易理解。協程可以暫停和恢復執行,幫助你有效率地處理耗時的操作,比如網路請求或資料庫讀取,而不會阻塞主執行緒。簡單來說,Kotlin 協程讓非同步程式寫起來像同步程式一樣方便。",
"metaInfo": {
"timestamp": "2025-08-14T07:09:06.782301Z",
"totalTokensCount": 163,
"inputTokensCount": 42,
"outputTokensCount": 121
},
"finishReason": "stop"
}
],
"request": {
"prompt": {
"messages": [
{
"type": "ai.koog.prompt.message.Message.System",
"content": "你是一個友善的 AI 助手\n請使用正體中文回答",
"metaInfo": {
"timestamp": "2025-08-14T07:09:03.958772Z"
}
},
{
"type": "ai.koog.prompt.message.Message.User",
"content": "什麼是 Kotlin 協程?請簡單的說明",
"metaInfo": {
"timestamp": "2025-08-14T07:09:03.959661Z"
}
}
],
"id": "memory"
}
}
}
Redis 快取:企業級選擇
Redis 快取適合生產環境,支援分散式部署
基本配置
suspend fun main() {
// 建立 Redis 客戶端連線
val client = RedisClient.create("redis://localhost:6379")
// 建立 Redis 快取,設定鍵前綴和 TTL
val cache = RedisPromptCache(
client = client,
keyPrefix = "ai-app-cache:",
ttl = 7.days // 快取保存 7 天
)
val executor = simpleOpenAIExecutor(ApiKeyManager.openAIApiKey!!)
val prompt = prompt("redis") {
system {
text("""
你是一個友善的 AI 助手
請使用正體中文回答
""".trimIndent())
}
user {
text("什麼是 Kotlin 協程?請簡單的說明")
}
}
val promptRequest = PromptCache.Request.create(prompt, emptyList())
try {
println("=== 第一次詢問(會呼叫 API)===")
var cachedResponse = cache.get(promptRequest)
if (cachedResponse != null) {
println("AI (從 Redis 快取載入): ${cachedResponse.first().content}")
} else {
val response = executor.execute(prompt, OpenAIModels.CostOptimized.GPT4_1Mini)
// 將回應存入 Redis 快取
cache.put(promptRequest, listOf(response))
println("AI : ${response.content}")
println("(回應已存入 Redis 快取)")
}
println("\n=== 第二次詢問相同問題(使用 Redis 快取)===")
cachedResponse = cache.get(promptRequest)
if (cachedResponse != null) {
println("AI (從 Redis 快取載入): ${cachedResponse.first().content}")
} else {
val response = executor.execute(prompt, OpenAIModels.CostOptimized.GPT4_1Mini)
// 將回應存入 Redis 快取
cache.put(promptRequest, listOf(response))
println("AI : ${response.content}")
println("(回應已存入 Redis 快取)")
}
} finally {
cache.close()
}
}
執行 AI 回應內容
=== 第一次詢問(會呼叫 API)===
AI : Kotlin 協程(Coroutine)是一種輕量級的非同步程式設計工具,可以讓你用順序的寫法來處理非同步任務。它能夠暫停和恢復程式執行,達到高效管理多個任務的效果,比傳統的線程使用更少資源,讓程式碼更簡潔易讀。簡單來說,協程就是幫助你寫出「看起來像同步,實際上是非同步」的程式碼。
(回應已存入 Redis 快取)
=== 第二次詢問相同問題(使用 Redis 快取)===
AI (從 Redis 快取載入): Kotlin 協程(Coroutine)是一種輕量級的非同步程式設計工具,可以讓你用順序的寫法來處理非同步任務。它能夠暫停和恢復程式執行,達到高效管理多個任務的效果,比傳統的線程使用更少資源,讓程式碼更簡潔易讀。簡單來說,協程就是幫助你寫出「看起來像同步,實際上是非同步」的程式碼。
快取策略選擇
比較一下三種不同快取之間,相關的優點和缺點
快取類型 | 適用場景 | 優勢 | 限制 |
---|---|---|---|
記憶體快取 | 開發測試、小型應用 | 速度最快、設定簡單 | 重啟後遺失、記憶體限制 |
檔案快取 | 單機部署、中型應用 | 持久化、成本低 | 相對較慢、磁碟空間限制 |
Redis 快取 | 生產環境、分散式系統 | 高可用、支援集群 | 需要維護 Redis、網路開銷 |
使用 CachedPromptExecutor
Koog 提供了 CachedPromptExecutor
來簡化快取整合
suspend fun main() {
// 建立快取
val cache = InMemoryPromptCache(maxEntries = 100)
// 執行器
val executor = simpleOpenAIExecutor(ApiKeyManager.openAIApiKey!!)
// 包裝成快取執行器
val cachedExecutor = CachedPromptExecutor(
cache,
executor
)
val prompt = prompt("memory") {
system {
text(
"""
你是一個友善的 AI 助手
請使用正體中文回答
""".trimIndent()
)
}
user {
text("什麼是 Kotlin 協程?請簡單的說明")
}
}
println("=== 第一次詢問(會呼叫 API)===")
var response = cachedExecutor.execute(prompt, OpenAIModels.CostOptimized.GPT4_1Mini)
println("$response")
println("\nAI : ${response.content}")
println("\n=== 第二次詢問相同問題(使用 memory 快取)===")
response = cachedExecutor.execute(prompt, OpenAIModels.CostOptimized.GPT4_1Mini)
println("\n$response")
println("\nAI : ${response.content}")
}
這個設計的好處是
- 簡單方便:不需要大量的修改/增加現有程式碼
- 自動處理:快取的存取完全自動
- 易於切換:可以輕鬆開關快取功能
執行 AI 回應內容
如果我們把 response
完整的印出來看的話,因為是從 cache 中讀取,所以第二次的所有 token count
都會是 null
=== 第一次詢問(會呼叫 API)===
Assistant(content=Kotlin 協程(Coroutine)是一種用來處理非同步編程的輕量級工具。它讓你可以寫出看起來像同步的程式碼,但實際上可以非同步執行,避免阻塞主執行緒。協程能簡化異步任務的寫法,提高程式的可讀性和效率。簡單來說,協程就是用來方便管理並發和異步操作的機制。, metaInfo=ResponseMetaInfo(timestamp=2025-08-14T07:32:19.366384Z, totalTokensCount=146, inputTokensCount=42, outputTokensCount=104, additionalInfo={}), attachments=[], finishReason=stop)
AI : Kotlin 協程(Coroutine)是一種用來處理非同步編程的輕量級工具。它讓你可以寫出看起來像同步的程式碼,但實際上可以非同步執行,避免阻塞主執行緒。協程能簡化異步任務的寫法,提高程式的可讀性和效率。簡單來說,協程就是用來方便管理並發和異步操作的機制。
=== 第二次詢問相同問題(使用 memory 快取)===
Assistant(content=Kotlin 協程(Coroutine)是一種用來處理非同步編程的輕量級工具。它讓你可以寫出看起來像同步的程式碼,但實際上可以非同步執行,避免阻塞主執行緒。協程能簡化異步任務的寫法,提高程式的可讀性和效率。簡單來說,協程就是用來方便管理並發和異步操作的機制。, metaInfo=ResponseMetaInfo(timestamp=2025-08-14T07:32:19.372485Z, totalTokensCount=null, inputTokensCount=null, outputTokensCount=null, additionalInfo={}), attachments=[], finishReason=stop)
AI : Kotlin 協程(Coroutine)是一種用來處理非同步編程的輕量級工具。它讓你可以寫出看起來像同步的程式碼,但實際上可以非同步執行,避免阻塞主執行緒。協程能簡化異步任務的寫法,提高程式的可讀性和效率。簡單來說,協程就是用來方便管理並發和異步操作的機制。
總結
在本篇文章中,我們學會了
- 提示快取概念:透過暫存回應避免重複 API 呼叫
- 記憶體快取:最快速的快取方案,適合開發和測試
- 檔案快取:持久化選擇,適合單機部署
- Redis 快取:企業級方案,支援分散式部署
- CachedPromptExecutor:簡化快取整合的透明化工具
快取機制是 AI 應用優化的重要基石。透過適當的快取策略,我們可以
- 大幅提升回應速度:從秒級降至毫秒級
- 顯著降低 API 成本:避免重複的付費請求
- 改善用戶體驗:更快的回應讓用戶更滿意
在使用快取時,最麻煩的是官方的文件寫的設定方式全部都是錯的方式,所以在寫這篇時,研究了很久..
下一篇文章(Day 14),我們將學習 Agent 記憶體系統,這與快取不同,記憶體系統能讓 AI 記住用戶的個人資訊和偏好,實現真正的個人化服務
參考資料
- Koog 官方文件
- Koog prompt-cache-model API
- Koog prompt-cache-files API
- Koog prompt-cache-redis API
- Koog prompt-executor-cached API
支持創作
如果這篇文章對您有幫助,歡迎透過 贊助連結 支持我持續創作優質內容。您的支持是我前進的動力!
圖片來源:AI 產生