Logo
Published on

第一次學 Kotlin Koog AI 就上手 Day 07:打造更穩健的 AI:錯誤處理基礎

在前一篇文章中,我們學習了如何調校 AIAgent 的配置參數。但是,即使配置得再完美的 AI 系統,在真實世界中也難免會遇到各種問題:API 金鑰失效、網路連線中斷、服務暫停等。今天我們將學習如何建立基礎的錯誤處理機制,讓你的 AI 應用能夠優雅地應對這些常見問題

為什麼需要錯誤處理?

想像一下以下場景

  • 用戶正在與智慧客服機器人對話,突然 API 金鑰過期,系統直接崩潰顯示技術錯誤
  • AI 助手正在處理問題,網路連線中斷導致程式拋出異常

如果沒有適當的錯誤處理,這些情況會造成糟糕的用戶體驗。一個具備基礎錯誤處理的 AI 應用應該能夠

預期常見的錯誤情況,提供友善的錯誤訊息,避免程式直接崩潰

Koog 應用中的兩種常見錯誤

API 相關錯誤

最常見的錯誤是 API 金鑰問題或模型不支援

suspend fun main() {

    try {
        val agent = AIAgent(
            // executor = simpleOpenAIExecutor(ApiKeyManager.openAIApiKey!!),
            // 使用假的 api key 來模擬錯誤
            executor = simpleOpenAIExecutor("fake api key"),
            systemPrompt = "你是一個友善的 AI 助手",
            llmModel = OpenAIModels.CostOptimized.GPT4_1Mini
        )

        // 測試是否能正常運作
        val result = agent.run("你好")
        println("✅ Agent 建立成功:$result")
    } catch (e: Exception) {
        when {
            // API 金鑰相關錯誤
            e.message?.contains("api", ignoreCase = true) == true ||
                    e.message?.contains("key", ignoreCase = true) == true ||
                    e.message?.contains("auth", ignoreCase = true) == true -> {
                println("❌ API 金鑰問題:請檢查您的 API 金鑰是否正確且有效")
            }

            // 配額相關錯誤
            e.message?.contains("quota", ignoreCase = true) == true ||
                    e.message?.contains("limit", ignoreCase = true) == true -> {
                println("⏱️ API 配額已滿:請稍後再試或檢查您的使用配額")
            }

            // 其他 API 錯誤
            else -> {
                println("原始錯誤訊息:${e.message}")
                println("❓ 無法連接到 AI 服務,請稍後再試")
            }
        }
    }
}

相關的錯誤判斷這裡只是大概的示意,但實際上可能還會有更多細節需要考慮,例如錯誤代碼、錯誤訊息的格式等。請根據實體的情況進行調整。

執行 AI 回應內容

正常情況

Agent 建立成功:你好!很高興見到你,有什麼我可以幫忙的嗎?

使用一個假的 API Key 來模擬錯誤的情況

API 金鑰問題:請檢查您的 API 金鑰是否正確且有效

網路連線錯誤

網路問題也是常見的錯誤來源

suspend fun main() {

    val agent = AIAgent(
        executor = simpleOpenAIExecutor(ApiKeyManager.openAIApiKey!!),
        systemPrompt = "你是一個友善的 AI 助手",
        llmModel = OpenAIModels.CostOptimized.GPT4_1Mini
    )

    try {
        // 設定 5 秒超時
        withTimeout(5000) {

            val result = agent.run("你好")
            // delay 6 秒來模擬錯誤的情況
            delay(6000)
            println("✅ Agent 建立成功:$result")
        }
    } catch (e: TimeoutCancellationException) {
        println("⏰ 回應時間過長,請檢查網路連線後再試")
    } catch (e: Exception) {
        when {
            // 網路連線問題
            e.message?.contains("network", ignoreCase = true) == true ||
                    e.message?.contains("connection", ignoreCase = true) == true ||
                    e.message?.contains("timeout", ignoreCase = true) == true -> {
                println("🌐 網路連線問題,請檢查網路設定後再試")
            }

            // 服務不可用
            e.message?.contains("service", ignoreCase = true) == true ||
                    e.message?.contains("unavailable", ignoreCase = true) == true -> {
                 println("🚫 AI 服務暫時不可用,請稍後再試")
            }

            else -> {
                println("網路錯誤詳情:${e.message}") // 開發時用於除錯
                println("❓ 處理請求時發生問題,請稍後再試")
            }
        }
    }
}

執行 AI 回應內容

正常情況

Agent 建立成功:你好!很高興見到你,有什麼我可以幫忙的嗎?

使用 delay 來模擬回應時間過長的情況

⏰ 回應時間過長,請檢查網路連線後再試

基礎重試機制

有時候錯誤是暫時的,我們可以實作簡單的重試機制

suspend fun <T> simpleRetry(
    maxAttempts: Int = 3,
    delayMs: Long = 1000,
    operation: suspend () -> T
): T {
    repeat(maxAttempts) { attempt ->
        try {
            return operation()
        } catch (e: Exception) {
            println("嘗試 ${attempt + 1} 失敗:${e.message}")
            delay(delayMs)
        }
    }

    // 最後一次嘗試,如果失敗就讓異常拋出
    return operation()
}

// 使用範例
suspend fun main() {

    val agent = AIAgent(
        executor = simpleOpenAIExecutor(ApiKeyManager.openAIApiKey!!),
        systemPrompt = "你是一個友善的 AI 助手",
        llmModel = OpenAIModels.CostOptimized.GPT4_1Mini
    )

    try {
        simpleRetry(maxAttempts = 3, delayMs = 2000) {
            val result= agent.run("你好")
            // 直接丟出 error 來模擬未知的錯誤
            throw Exception("unknown error")
            println("✅ Agent 建立成功:$result")
        }
    } catch (e: Exception) {
        println("❌ 經過多次嘗試後仍無法處理您的請求,請稍後再試")
    }
}

執行 AI 回應內容

正常情況

Agent 建立成功:你好!很高興見到你,有什麼我可以幫忙的嗎?

直接 throw Exception 來模擬未知錯誤的發生情況

嘗試 1 失敗:unknown error
嘗試 2 失敗:unknown error
嘗試 3 失敗:unknown error
❌ 經過多次嘗試後仍無法處理您的請求,請稍後再試

多供應商 Fallback 機制

除了基本的重試機制,我們還可以建立多供應商的 Fallback 機制。當主要的 LLM 供應商服務中斷或發生錯誤時,系統可以自動切換到備用供應商,確保服務的持續性

多供應商 Fallback 機制提供了以下優勢

  • 提高可靠性:單一供應商故障時仍能維持服務
  • 改善使用者體驗:無縫切換,使用者感受不到服務中斷
  • 成本優化:可優先使用成本較低的供應商
  • 負載分散:避免過度依賴單一服務商

💡 延伸學習:這裡只是簡單介紹多供應商 Fallback 的概念。如果您想了解完整的多 LLM 供應商整合實作,可以參考 Day 04:多 LLM 供應商整合 文章,裡面有詳細的程式碼範例和實作說明

整合範例:SafeAIHelper

現在讓我們把前面學到的錯誤處理技術整合起來,建立一個安全的 AI 助手

/**
 * 安全的 AI 助手 - 整合錯誤處理技術
 */
class SafeAIHelper {

    fun createSafeAIAgent(): AIAgent<String, String>? {
        return try {

            AIAgent(
                executor = simpleOpenAIExecutor(ApiKeyManager.openAIApiKey!!),
                systemPrompt = "你是一個友善的 AI 助手,用正體中文回答問題",
                llmModel = OpenAIModels.CostOptimized.GPT4_1Mini
            )
        } catch (e: Exception) {
            println("建立 Agent 時發生錯誤:${e.message}")
            null
        }
    }

    suspend fun askAI(question: String): String {
        val agent = createSafeAIAgent()

        // 如果 Agent 建立失敗
        if (agent == null) {
            return "❌ AI 助手暫時無法使用,請稍後再試"
        }

        return try {
            // 使用重試機制 + 超時處理
            simpleRetry() {
                // 5秒超時
                withTimeout(5000) {
                    // delay 6 秒來模擬錯誤的情況
                    delay(6000)
                    agent.run(question)
                }
            }
        } catch (e: TimeoutCancellationException) {
            "⏰ 回應時間過長,請稍後再試"
        } catch (e: Exception) {
            "❓ 處理問題時發生錯誤,請稍後再試"
        }
    }

    suspend fun <T> simpleRetry(
        maxAttempts: Int = 3,
        delayMs: Long = 1000,
        operation: suspend () -> T
    ): T {
        repeat(maxAttempts) { attempt ->
            try {
                return operation()
            } catch (e: Exception) {
                println("嘗試 ${attempt + 1} 失敗:${e.message}")
                delay(delayMs)
            }
        }

        // 最後一次嘗試,如果失敗就讓異常拋出
        return operation()
    }
}

這個範例整合了我們這次學到的幾種錯誤處理技術

  • API 錯誤處理:安全建立 Agent
  • 網路超時處理:設定 5 秒超時限制
  • 重試機制:失敗時自動重試 3 次

為了版面的程式碼不要過長,這裡並沒有把所有的錯誤的情況都放到這個範例中判斷,有興趣的可以寫一個更加完整的錯誤處理元件

使用範例

讓我們測試一下這個安全的 AI 助手

suspend fun main() {
    val aiHelper = SafeAIHelper()

    println("🤖 測試安全 AI 助手")
    println("=".repeat(30))

    val questions = listOf(
        "你好",
        "什麼是 Kotlin 協程? 請簡單回答"
    )

    questions.forEach { question ->
        println("\n💬 問題:$question")
        val answer = aiHelper.askAI(question)
        println("🤖 回答:$answer")
        println("-".repeat(30))
    }
}

執行 AI 回應內容

正常情況

🤖 測試安全 AI 助手
==============================

💬 問題:你好
🤖 回答:你好!有什麼我可以幫忙的嗎?
------------------------------

💬 問題:什麼是 Kotlin 協程? 請簡單回答
🤖 回答:Kotlin 協程是一種輕量級的非同步程式設計工具,讓你可以用類似同步的方式撰寫非同步代碼,簡化異步操作和多線程管理。

使用 delay 來模擬回應時間過長的情況

🤖 測試安全 AI 助手
==============================

💬 問題:你好
嘗試 1 失敗:Timed out waiting for 5000 ms
嘗試 2 失敗:Timed out waiting for 5000 ms
嘗試 3 失敗:Timed out waiting for 5000 ms
🤖 回答:⏰ 回應時間過長,請稍後再試
------------------------------

💬 問題:什麼是 Kotlin 協程? 請簡單回答
嘗試 1 失敗:Timed out waiting for 5000 ms
嘗試 2 失敗:Timed out waiting for 5000 ms
嘗試 3 失敗:Timed out waiting for 5000 ms
🤖 回答:⏰ 回應時間過長,請稍後再試
------------------------------

錯誤處理最佳實踐

友善的錯誤訊息

  • ❌ 不好:Exception: OpenAI API key is invalid
  • ✅ 好:API 金鑰問題:請檢查您的 API 金鑰是否正確且有效

提供解決方案

fun createHelpfulErrorMessage(error: Exception): String {
    return when {
        error.message?.contains("key") == true ->
            "🔑 API 金鑰問題:請到 OpenAI 官網檢查您的金鑰是否有效"

        error.message?.contains("network") == true ->
            "🌐 網路問題:請檢查網路連線,或稍後再試"

        else -> "❓ 遇到問題了,請稍後再試或聯繫客服"
    }
}

記錄重要資訊

fun logErrorForDebugging(error: Exception, context: String) {
    // 開發環境:詳細記錄
    if (System.getProperty("environment") == "development") {
        println("錯誤詳情 [$context]: ${error.message}")
        error.printStackTrace()
    }

    // 生產環境:簡化記錄
    println("錯誤 [$context]: ${error.javaClass.simpleName}")
}

總結

今天我們學習了 Koog 應用的基礎錯誤處理技巧

  • API 金鑰問題和網路連線錯誤
  • 將技術錯誤轉換為用戶易懂的說明
  • 簡單重試機制處理暫時性的問題
  • 建立安全的 AI 助手,整合相關的錯誤處理技術

下一篇文章中,我們將探討實戰專案的開發,建立一個完整的 AI 聊天機器人

參考資料


支持創作

如果這篇文章對您有幫助,歡迎透過 贊助連結 支持我持續創作優質內容。您的支持是我前進的動力!


圖片來源:AI 產生