Logo
Published on

第一次學 Kotlin Koog AI 就上手 Day 08:技術整合實戰:建立 AI 客服機器人

掌握了 Koog 框架的核心技術後,讓我們把這些技術整合起來,建立一個真正實用的 AI 客服機器人!

所需核心技術

基礎架構技術

  • AI Agent 建立:學會建立和配置基本的 AI Agent 程式
  • API 整合:掌握多個 LLM 提供商的基本整合方法

AI 對話技術

  • 提示工程:設計有效的系統提示來引導 AI 行為
  • 工具整合:了解基礎工具系統的使用方法

接下來我們要把這些技術組合起來,建立一個專注於對話功能的 AI 客服機器人

AI 客服機器人實作

現在讓我們把前面學到的技術組合起來,建立一個完整的 AI 客服機器人

/**
 * 對話紀錄資料結構 - 用來儲存對話歷史
 */
data class ConversationRecord(
    val userMessage: String,
    val assistantResponse: String,
    val timestamp: Long = System.currentTimeMillis()
)

/**
 * 客服工具集 - 提供實用的客服功能
 */
@LLMDescription("客服工具集,包含訂單查詢、營業時間查詢和常見問題搜尋")
class CustomerServiceToolSet : ToolSet {

    @Tool
    @LLMDescription("查詢訂單狀態和配送資訊")
    fun lookupOrder(
        @LLMDescription("訂單編號,格式如 ORD-20241201-001")
        orderId: String
    ): String {
        return try {
            // 模擬訂單查詢邏輯
            when {
                orderId.isBlank() -> "請提供有效的訂單編號"
                orderId.startsWith("ORD-") -> {
                    val randomStatus = listOf(
                        "已確認,預計 3-5 個工作天送達",
                        "已出貨,配送中,預計明天送達",
                        "已送達,感謝您的購買",
                        "處理中,我們正在準備您的商品"
                    ).random()
                    "訂單 $orderId 狀態:$randomStatus"
                }
                else -> "訂單編號格式不正確,請確認後重新輸入"
            }
        } catch (e: Exception) {
            "查詢訂單時發生錯誤:${e.message}"
        }
    }

    @Tool
    @LLMDescription("查詢指定門市或地區的營業時間")
    fun getBusinessHours(
        @LLMDescription("門市名稱或地區,如:台北、高雄、台中")
        location: String
    ): String {
        return try {
            val businessHours = mapOf(
                "台北" to "週一至週日 9:00-22:00",
                "台中" to "週一至週日 10:00-21:00",
                "高雄" to "週一至週日 9:30-21:30",
                "桃園" to "週一至週日 9:00-21:00",
                "台南" to "週一至週日 10:00-20:00"
            )

            val normalizedLocation = location.trim()
            businessHours[normalizedLocation]
                ?: "目前僅提供台北、台中、高雄、桃園、台南地區的營業時間查詢。一般門市營業時間為週一至週日 9:00-21:00"

        } catch (e: Exception) {
            "查詢營業時間時發生錯誤:${e.message}"
        }
    }

    @Tool
    @LLMDescription("搜尋常見問題的解答")
    fun searchFaq(
        @LLMDescription("問題關鍵字,如:退款、配送、會員")
        keyword: String
    ): String {
        return try {
            val faqDatabase = mapOf(
                "退款" to "退款政策:商品收到後 7 天內可申請退款,商品需保持原包裝。退款處理時間約 7-14 個工作天。",
                "配送" to "配送時間:一般商品 3-5 個工作天,急件可選擇隔日配送(需加收費用)。",
                "會員" to "會員權益:免費註冊即享 95 折優惠,消費滿額可累積點數兌換禮品。",
                "保固" to "保固服務:電子產品提供 1 年保固,非人為損壞免費維修。",
                "客服" to "客服時間:週一至週五 9:00-18:00,客服專線:0800-123-456"
            )

            val result = faqDatabase.entries.find {
                it.key.contains(keyword) || keyword.contains(it.key)
            }

            result?.value ?: "很抱歉,沒有找到相關的常見問題。您可以嘗試其他關鍵字,或直接聯絡客服人員協助。"

        } catch (e: Exception) {
            "搜尋常見問題時發生錯誤:${e.message}"
        }
    }
}

/**
 *  AI 客服機器人 - 整合前面學習的核心技術
 * 專注於對話功能,提供友善的客服體驗,並支援對話歷史和工具系統
 */
class SmartCustomerService {

    // 對話歷史儲存 - 使用簡單的 MutableList
    private val conversationHistory = mutableListOf<ConversationRecord>()

    // 工具註冊 - 包含客服相關工具
    private val toolRegistry = ToolRegistry {
        tools(CustomerServiceToolSet())
    }

    // AI Agent - 整合執行器、工具和提示
    private val agent = AIAgent(
        executor = simpleOpenAIExecutor(ApiKeyManager.openAIApiKey!!),
        systemPrompt = createCustomerServicePrompt(),
        toolRegistry = toolRegistry,
        llmModel = OpenAIModels.CostOptimized.GPT4_1Mini,
        temperature = 0.7
    )

    init {
        println("🤖  AI 客服機器人啟動完成!")
        println("💭  對話歷史功能已啟用")
        println("🔧  工具系統已載入:訂單查詢、營業時間、FAQ 搜尋")
    }

    // 專業客服提示系統設計
    private fun createCustomerServicePrompt(): String {
        return """
            你是一個專業、友善的 AI 客服助手,名字叫「9527」

            你的服務原則:
            1. 用正體中文回答,語調親切專業
            2. 耐心解答客戶的各種問題
            3. 提供清楚、有用的資訊
            4. 記住之前的對話內容,提供連貫的服務
            5. 遇到複雜問題時,建議客戶聯絡真人客服

            你有以下工具可以使用:
            - lookupOrder:查詢訂單狀態,需要客戶提供訂單編號
            - getBusinessHours:查詢門市營業時間,可以指定地區
            - searchFaq:搜尋常見問題解答,根據關鍵字查找

            使用工具的時機:
            - 客戶詢問訂單狀態時,使用 lookupOrder 工具
            - 客戶詢問營業時間時,使用 getBusinessHours 工具
            - 客戶有一般性問題時,使用 searchFaq 工具搜尋相關解答

            請記住,你的目標是讓每位客戶都感受到溫暖的服務體驗
        """.trimIndent()
    }

    /**
    * 處理客戶問題 - 核心對話功能,支援對話歷史和工具系統
    */
    suspend fun chat(customerMessage: String): String {
        return try {
            // 建立包含對話歷史的完整輸入
            val fullInput = buildString {
                // 加入對話歷史作為上下文
                if (conversationHistory.isNotEmpty()) {
                    appendLine("=== 對話歷史 ===")
                    conversationHistory.takeLast(5).forEach { record -> // 只取最近 5 輪對話
                        appendLine("客戶:${record.userMessage}")
                        appendLine("9527:${record.assistantResponse}")
                    }
                    appendLine("=== 當前問題 ===")
                }
                append(customerMessage)
            }

            // 使用 AIAgent 處理客戶問題(自動包含工具使用)
            val assistantResponse = agent.run(fullInput)

            // 儲存到對話歷史
            conversationHistory.add(
                ConversationRecord(
                    userMessage = customerMessage,
                    assistantResponse = assistantResponse
                )
            )

            assistantResponse

        } catch (e: Exception) {
            // 健全的錯誤處理機制
            println("處理對話時發生錯誤:${e.message}")
            "很抱歉,我現在遇到一些技術問題。請稍後再試,或直接撥打客服專線。"
        }
    }

    /**
    * 取得對話歷史統計資訊
    */
    fun getConversationStats(): String {
        return "📊 目前已進行 ${conversationHistory.size} 輪對話"
    }

    /**
    * 清除對話歷史
    */
    fun clearHistory() {
        conversationHistory.clear()
        println("🗑️ 對話歷史已清除")
    }

    /**
    * 開始互動式對話 - 使用 while 迴圈實現連續對話
    */
    suspend fun startInteractiveChat() {
        println("🎉  歡迎使用 AI 客服系統")
        println("💬  您可以開始提問,輸入 'exit' 結束對話,輸入 'stats' 查看統計")
        println("=".repeat(50))

        while (true) {
            print("\n💬 您的問題:")
            val input = readlnOrNull()?.trim()

            when {
                input.isNullOrEmpty() -> continue

                input.lowercase() == "exit" -> {
                    println("👋 感謝使用 AI 客服系統,祝您有美好的一天!")
                    println(getConversationStats())
                    break
                }

                input.lowercase() == "stats" -> {
                    println(getConversationStats())
                    continue
                }

                input.lowercase() == "clear" -> {
                    clearHistory()
                    continue
                }

                else -> {
                    try {
                        print("🤖 9527 回應中...")
                        val response = chat(input)
                        print("\r🤖 9527:$response\n")
                        println("-".repeat(50))
                    } catch (e: Exception) {
                        println("⚠️ 系統錯誤:${e.message}")
                    }
                }
            }
        }
    }
}

這個 SmartCustomerService 實作雖然簡潔,但整合了 AI 客服系統的所有核心技術

  • API 管理:使用 ApiKeyManager 統一管理 API 金鑰
  • 提示工程:設計專業的客服提示系統,塑造友善的服務語調
  • 對話歷史:使用 MutableList<ConversationRecord> 儲存完整對話記錄
  • 上下文管理:透過結構化字串將歷史對話傳遞給 AI,實現記憶功能
  • 工具系統:整合客服專用工具 CustomerServiceToolSet,提供訂單查詢、營業時間查詢、FAQ 搜尋
  • 自動工具選擇:AI 會根據客戶問題自動選擇適合的工具
  • 模型選擇:選擇適合的 GPT-4.1 mini 模型,平衡性能與成本 (實際要用什麼模型,自己可以根據需求和成本來調整)
  • 互動式介面:使用 while 迴圈實現連續對話,支援多種指令

對話歷史功能特色

記憶能力展示

從執行結果可以看到,AI 客服現在具備了真正的記憶能力

  • 上下文引用:AI 能準確回答「我剛才問的是什麼問題?」
  • 情境延續:當客戶說「剛才我提到的訂單」時,AI 能理解並連結之前的對話
  • 個人化服務:記住客戶是「新客戶」,在後續回應中提供相應的服務

技術實作要點

資料結構設計

data class ConversationRecord(
    val userMessage: String,
    val assistantResponse: String,
    val timestamp: Long = System.currentTimeMillis()
)

使用簡單的 data class 和 MutableList,避免過度複雜的設計

對話歷史整合

val fullInput = buildString {
    if (conversationHistory.isNotEmpty()) {
        appendLine("=== 對話歷史 ===")
        conversationHistory.takeLast(5).forEach { record ->
            appendLine("客戶:${record.userMessage}")
            appendLine("9527:${record.assistantResponse}")
        }
        appendLine("=== 當前問題 ===")
    }
    append(customerMessage)
}

val response = agent.run(fullInput)

將對話歷史以結構化方式整合到輸入中,配合 AIAgent 的工具系統,讓 AI 能「看到」完整的對話脈絡並自動選擇工具

要注意的是,這裡暫時不考慮歷史對話遺失的問題,這裡只拿最後 5筆

重要技術限制

prompt DSL 與工具系統的選擇

在 Koog 框架中,有兩種主要的對話管理方式,但它們不能直接混用

prompt DSL

val conversationPrompt = prompt("conversation") {
    system("你是友善的助手")
    user("使用者問題")
}
val response = executor.execute(conversationPrompt, model)

適用場景:純對話、複雜提示結構、需要精確控制對話格式

AIAgent + 工具系統

val agent = AIAgent(
    executor = executor,
    systemPrompt = "你是友善的助手",
    toolRegistry = toolRegistry,
    llmModel = model
)
val response = agent.run("使用者輸入")

適用場景:需要工具功能、自動工具選擇、企業級應用

為什麼不能混用?

  • API 設計限制AIAgent 只有 run() 方法,沒有 chat() 方法
  • 架構差異:prompt DSL 直接操作 executor,AIAgent 封裝了工具處理邏輯
  • 控制層級:prompt DSL 提供低階控制,AIAgent 提供高階封裝

實際選擇建議

  • 選擇 prompt DSL

    • 需要精確控制對話格式
    • 實作複雜的多輪對話邏輯
    • 不需要工具功能
    • 追求最大彈性
  • 選擇 AIAgent + 工具系統

    • 需要 AI 自動使用工具
    • 建立實用的應用系統
    • 追求開發效率
    • 需要企業級功能

本文採用 AIAgent 方式,因為客服機器人需要訂單查詢、營業時間查詢等實用工具功能

工具系統整合特色

實用工具展示

我們為客服機器人整合了三個實用的工具

  • 訂單查詢工具 (lookupOrder)

    • 模擬查詢真實訂單狀態
    • 支援訂單編號格式驗證
    • 提供配送進度資訊
  • 營業時間查詢工具 (getBusinessHours)

    • 支援多地區門市查詢
    • 涵蓋台北、台中、高雄、桃園、台南
    • 提供詳細營業時間資訊
  • FAQ 搜尋工具 (searchFaq)

    • 內建常見問題資料庫
    • 支援關鍵字模糊搜尋
    • 涵蓋退款、配送、會員、保固等主題

技術實作要點

Annotation-based 工具設計

@Tool
@LLMDescription("查詢訂單狀態和配送資訊")
fun lookupOrder(
    @LLMDescription("訂單編號,格式如 ORD-20241201-001")
    orderId: String
): String

使用註解方式快速建立工具,程式碼簡潔易懂

工具選擇

private val toolRegistry = ToolRegistry {
    tools(CustomerServiceToolSet())
}

註冊相關的工具後,AI 會根據客戶問題自動選擇合適的工具,無需手動指定

使用示範

suspend fun main() {
    println("🎉  AI 客服機器人技術展示")
    println("=".repeat(35))

    // 建立客服機器人實例
    val chatbot = SmartCustomerService()

    // 模擬一些客戶對話 - 現在有上下文記憶和工具使用
    val conversations = listOf(
        "你好!我想了解你們的服務",
        "請問台北門市的營業時間?", // 測試營業時間查詢工具
        "我想查詢訂單 ORD-20241201-001 的狀態", // 測試訂單查詢工具
        "請問你們的退款政策是什麼?", // 測試 FAQ 搜尋工具
        "我剛才查詢的訂單如果要退款該怎麼辦?", // 測試記憶功能 + FAQ
        "謝謝你的協助!"
    )

    // 逐一進行對話
    conversations.forEachIndexed { index, message ->
        println("\n💬 客戶:$message")

        val response = chatbot.chat(message)
        println("🤖 9527:$response")

        // 顯示對話統計
        if (index == conversations.size - 1) {
            println("\n${chatbot.getConversationStats()}")
        }

        // 模擬對話間隔
        delay(1000)
    }

    println("\n🎊 對話測試完成!")
}

執行 AI 回應內容

🎉  AI 客服機器人技術展示
===================================
🤖  AI 客服機器人啟動完成!
💭  對話歷史功能已啟用
🔧  工具系統已載入:訂單查詢、營業時間、FAQ 搜尋

💬 客戶:你好!我想了解你們的服務
🤖 9527:您好!很高興為您服務。我是9527,您的專屬AI客服助理。請問您想了解我們哪方面的服務呢?像是訂單查詢、商品資訊、退款政策、門市營業時間等,我都可以為您說明喔!如果您有具體問題,也歡迎隨時告訴我。

💬 客戶:請問台北門市的營業時間?
🤖 9527:台北門市的營業時間是週一至週日的上午9點到晚上10點。如果您還有其他問題,隨時告訴我喔!

💬 客戶:我想查詢訂單 ORD-20241201-001 的狀態
🤖 9527:您的訂單 ORD-20241201-001 目前狀態是「處理中」,我們正在準備您的商品。如果您需要進一步協助,隨時告訴我喔!

💬 客戶:請問你們的退款政策是什麼?
🤖 9527:您好,我們的退款政策是:商品收到後 7 天內可申請退款,且商品需保持原包裝。退款處理時間約為 7-14 個工作天。如果您有其他問題,或需要協助申請退款,請隨時告訴我喔!

💬 客戶:我剛才查詢的訂單如果要退款該怎麼辦?
🤖 9527:您好!如果您想對訂單 ORD-20241201-001 申請退款,請您確認商品是在收到後7天內,且商品包裝保持完整。接著您可以透過我們的官方客服管道提交退款申請,通常需要提供訂單編號和退款原因。我可以幫您提供退款申請的詳細流程或協助聯繫真人客服,請問您需要嗎?

💬 客戶:謝謝你的協助!
🤖 9527:不客氣!很高興能幫助到您。如果之後還有任何問題或需要協助,隨時歡迎找我喔。祝您有美好的一天!😊

📊 目前已進行 6 輪對話

🎊 對話測試完成!

總結

通過建立這個 AI 客服機器人,我們成功示範了如何整合 Koog 框架的核心技術

  • API 管理與安全

    • 建立統一的 ApiKeyManager 來管理 API 金鑰
    • 避免在程式碼中散布敏感資訊,提升系統安全性
  • 提示工程與對話設計

    • 學會撰寫清楚、專業的系統提示
    • 掌握如何透過提示引導 AI 的行為和語調
  • 工具系統整合

    • 建立實用的客服工具集(訂單查詢、營業時間、FAQ 搜尋)
    • 使用 Annotation-based 方式快速開發工具
    • 讓 AI 智能選擇合適的工具解決客戶問題
  • 架構設計選擇

    • 理解 prompt DSL 與 AIAgent 工具系統的差異和限制
    • 學會根據需求選擇合適的實作方式
    • 掌握工具系統的優勢和適用場景

下一篇文章中,我們將學習如何開發非同步工具,讓 AI Agent 能夠與外部 API 服務互動

參考資料


支持創作

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


圖片來源:AI 產生