Logo
Published on

Koog Kotlin AI 框架實戰 Day 35:Koog 0.0.4 版本重大更新 - 可觀測、可預測、可部署

前言

從我們開始這系列教學以來,Koog 框架持續快速發展,每個版本都帶來令人驚喜的新功能。今天要介紹的 0.0.4 版本可以說是一個里程碑,JetBrains 團隊以「Observable, Predictable, Deployable Anywhere You Build」為核心理念,為企業級 AI Agent 開發帶來了革命性的改進。這個版本的五大核心更新將徹底改變我們開發和部署 AI Agent 的方式

OpenTelemetry 可觀測性支援

什麼是可觀測性

可觀測性(Observability)是現代分散式系統的核心概念,對於 AI Agent 而言更是至關重要。想像一下,當你的 AI Agent 在生產環境中處理成千上萬個請求時,你需要知道:

  • 每個請求花費了多少時間
  • 哪些 LLM 模型被呼叫了多少次
  • Token 使用量和成本分析
  • 錯誤發生在哪個環節

Koog 0.0.4 透過整合 OpenTelemetry,讓這些問題都有了答案

整合 W&B Weave 和 Langfuse

新版本支援與業界領先的 AI 可觀測性平台整合:

  • W&B Weave:專為 LLM 應用設計的追蹤系統
  • Langfuse:開源的 LLM 工程平台
  • Jaeger:分散式追蹤系統

完整的實作範例

讓我們看看如何在實際專案中配置 OpenTelemetry:

// 檔案:src/main/kotlin/observability/ObservableAgent.kt
import ai.koog.agents.core.agent.AIAgent
import ai.koog.agents.features.opentelemetry.feature.OpenTelemetry
import ai.koog.agents.utils.use
import ai.koog.prompt.executor.clients.openai.OpenAIModels
import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor
import io.opentelemetry.exporter.logging.LoggingSpanExporter
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter
import io.opentelemetry.api.common.AttributeKey
import io.opentelemetry.sdk.trace.samplers.Sampler
import kotlinx.coroutines.runBlocking

const val openAIApiKey = "your-openai-api-key"

fun main() {
    runBlocking {
        // 創建具備完整可觀測性的 AI Agent
        val agent = AIAgent(
            executor = simpleOpenAIExecutor(openAIApiKey),
            llmModel = OpenAIModels.Chat.GPT4o,
            systemPrompt = "你是一個智能客服助手,專門處理技術支援問題"
        ) {
            install(OpenTelemetry) {
                // 設定服務資訊
                setServiceInfo("intelligent-customer-service", "1.0.0")

                // 添加控制台日誌輸出(開發環境)
                addSpanExporter(LoggingSpanExporter.create())

                // 連接到 Jaeger(本機開發環境)
                addSpanExporter(
                    OtlpGrpcSpanExporter.builder()
                        .setEndpoint("http://localhost:4317")
                        .build()
                )

                // 配置取樣策略(生產環境建議使用比例取樣)
                setSampler(Sampler.traceIdRatioBased(0.8))

                // 添加自訂資源屬性
                addResourceAttributes(mapOf(
                    AttributeKey.stringKey("deployment.environment") to "production",
                    AttributeKey.stringKey("service.version") to "1.0.0",
                    AttributeKey.stringKey("team") to "ai-platform"
                ))
            }
        }

        agent.use { agent ->
            println("啟動具備可觀測性的 AI Agent...")

            // 執行多個請求以產生追蹤資料
            val requests = listOf(
                "我的帳號無法登入,該怎麼辦?",
                "如何重設密碼?",
                "系統回應很慢,可能是什麼原因?"
            )

            requests.forEach { request ->
                val result = agent.run(request)
                println("處理請求:$request")
                println("回應:$result\n")
            }

            println("檢查 Jaeger UI:http://localhost:16686 查看追蹤資料")
        }
    }
}

追蹤巢狀事件的威力

Koog 的可觀測性不僅追蹤單一操作,還能完整記錄巢狀的操作階層:

CreateAgentSpan (建立代理)
    InvokeAgentSpan (呼叫代理)
        NodeExecuteSpan (執行節點)
            InferenceSpan (推理過程)
                - Token 使用量: 150 input tokens, 75 output tokens
                - 成本: $0.0034
                - 延遲: 1.2秒
        NodeExecuteSpan (執行工具)
            ExecuteToolSpan (執行工具)
                - 工具名稱: search_knowledge_base
                - 執行時間: 0.3秒
        NodeExecuteSpan (最終推理)
            InferenceSpan (推理過程)
                - 回應產生時間: 0.8秒

Ktor 框架整合

為什麼 Ktor 整合至關重要

在企業環境中,AI Agent 通常需要透過 HTTP API 提供服務。Koog 0.0.4 的 Ktor 整合讓這個過程變得非常簡單,你可以:

  • 快速將 AI Agent 包裝成 RESTful API
  • 透過 YAML 設定檔管理多個 LLM 供應商
  • 享受 Ktor 的高效能和豐富生態系

透過 YAML 配置的靈活性

# 檔案:src/main/resources/application.yaml
koog:
  openai:
    apikey: 'your-openai-api-key'
    baseUrl: 'https://api.openai.com'
    timeout:
      requestTimeoutMillis: 30000
      connectTimeoutMillis: 10000
      socketTimeoutMillis: 30000

  anthropic:
    apikey: 'your-anthropic-api-key'
    baseUrl: 'https://api.anthropic.com'
    timeout:
      requestTimeoutMillis: 30000

  google:
    apikey: 'your-google-api-key'
    baseUrl: 'https://generativelanguage.googleapis.com'

  openrouter:
    apikey: 'your-openrouter-api-key'
    baseUrl: 'https://openrouter.ai'

  ollama:
    baseUrl: 'http://localhost:11434'
    timeout:
      requestTimeoutMillis: 60000

完整的 RESTful API 實作

// 檔案:src/main/kotlin/deployment/KtorIntegration.kt
import ai.koog.agents.core.agent.AIAgent
import ai.koog.ktor.singleRunAgent
import ai.koog.prompt.executor.clients.openai.OpenAIModels
import io.ktor.server.application.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.http.*
import kotlinx.serialization.Serializable

@Serializable
data class ChatRequest(
    val message: String,
    val sessionId: String? = null
)

@Serializable
data class ChatResponse(
    val response: String,
    val sessionId: String,
    val timestamp: Long = System.currentTimeMillis()
)

fun Application.configureKoog() {
    // 安裝 Koog 外掛
    install(Koog) {
        llm {
            // 從 application.yaml 自動載入配置
            openAI(apiKey = "your-openai-api-key")
            anthropic(apiKey = "your-anthropic-api-key")
        }

        // 配置 Agent 行為
        agent {
            // 設定預設模型
            model = OpenAIModels.GPT4.Turbo

            // 設定最大迭代次數
            maxAgentIterations = 10

            // 註冊工具
            registerTools {
                tool(::searchTool)
                tool(::calculatorTool)
            }

            // 配置系統提示
            prompt {
                system("你是一個專業的技術支援助手,具備豐富的問題解決經驗")
            }
        }
    }

    // 定義 API 路由
    routing {
        route("/api/v1") {
            // 單次對話端點
            post("/chat") {
                try {
                    val request = call.receive<ChatRequest>()
                    val response = singleRunAgent(request.message)

                    call.respond(HttpStatusCode.OK, ChatResponse(
                        response = response,
                        sessionId = request.sessionId ?: generateSessionId()
                    ))
                } catch (e: Exception) {
                    call.respond(HttpStatusCode.InternalServerError, mapOf(
                        "error" to "處理請求時發生錯誤",
                        "details" to e.message
                    ))
                }
            }

            // 健康檢查端點
            get("/health") {
                call.respond(HttpStatusCode.OK, mapOf(
                    "status" to "healthy",
                    "timestamp" to System.currentTimeMillis()
                ))
            }

            // Agent 狀態端點
            get("/agent/status") {
                call.respond(HttpStatusCode.OK, mapOf(
                    "agent" to "active",
                    "models" to listOf("gpt-4", "claude-3"),
                    "tools" to listOf("search", "calculator")
                ))
            }
        }
    }
}

// 輔助函數
private fun generateSessionId(): String =
    "session-${System.currentTimeMillis()}-${(1000..9999).random()}"

private fun searchTool(query: String): String =
    "搜尋結果:$query"

private fun calculatorTool(expression: String): String =
    "計算結果:$expression"

結構化輸出功能

為什麼結構化輸出很重要

在企業應用中,我們經常需要從 AI 回應中提取結構化資料。傳統的文字處理方法不僅不可靠,還容易出錯。Koog 0.0.4 的結構化輸出功能徹底解決了這個問題

自動錯誤修正和重試機制

新版本最令人驚艷的是內建的錯誤修正功能。當 LLM 回傳的結構化資料格式不正確時,系統會自動使用另一個模型進行修正:

// 檔案:src/main/kotlin/structured/StructuredDataExample.kt
import ai.koog.prompt.structure.*
import ai.koog.prompt.executor.clients.openai.OpenAIModels
import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.Serializable

@Serializable
data class CustomerInfo(
    val name: String,
    val email: String,
    val issue: String,
    val priority: String, // "low", "medium", "high", "urgent"
    val category: String, // "technical", "billing", "account", "general"
    val estimatedResolutionTime: Int // 預估解決時間(小時)
)

@Serializable
data class ProductFeedback(
    val productName: String,
    val rating: Int, // 1-5
    val positiveAspects: List<String>,
    val negativeAspects: List<String>,
    val recommendationScore: Int, // 0-10
    val improvementSuggestions: List<String>
)

suspend fun extractCustomerInfo(userInput: String): CustomerInfo {
    val executor = simpleOpenAIExecutor(System.getenv("OPENAI_KEY"))

    return executor.executeStructured(
        prompt = prompt("customer-service-analysis") {
            system("""
                你是一個專業的客服分析師。
                請分析客戶訊息,提取關鍵資訊並分類問題的優先級和類別。
                優先級判斷標準:
                - urgent:影響業務運作的嚴重問題
                - high:重要功能無法使用
                - medium:一般功能問題
                - low:詢問或建議
            """.trimIndent())

            user("客戶訊息:$userInput")
        },
        model = OpenAIModels.CostOptimized.GPT4oMini,
        config = StructuredOutputConfig(
            default = StructuredOutput.Manual(CustomerInfo.serializer()),
            // 當資料格式錯誤時,使用 GPT-4 進行修正
            fixingParser = StructureFixingParser(
                fixingModel = OpenAIModels.Chat.GPT4o,
                retries = 3
            )
        )
    )
}

suspend fun analyzeProductFeedback(feedback: String): ProductFeedback {
    val executor = simpleOpenAIExecutor(System.getenv("OPENAI_KEY"))

    return executor.executeStructured(
        prompt = prompt("product-feedback-analysis") {
            system("""
                你是一個產品反饋分析專家。
                請分析用戶反饋,提取產品評價的各個維度。
                評分標準:
                - rating: 1-5 (1=非常不滿意, 5=非常滿意)
                - recommendationScore: 0-10 (0=絕不推薦, 10=強烈推薦)
            """.trimIndent())

            user("用戶反饋:$feedback")
        },
        model = OpenAIModels.Chat.GPT4o,
        config = StructuredOutputConfig(
            default = StructuredOutput.Manual(ProductFeedback.serializer()),
            fixingParser = StructureFixingParser(
                fixingModel = OpenAIModels.Chat.GPT4o,
                retries = 2
            )
        )
    )
}

fun main() {
    runBlocking {
        // 測試客戶資訊提取
        val customerMessage = """
            你好,我是張小明,email 是 [email protected]            我的帳號無法登入,已經嘗試重設密碼但還是不行。
            這個問題影響到我無法處理重要的工作,請盡快協助解決。
        """.trimIndent()

        val customerInfo = extractCustomerInfo(customerMessage)
        println("客戶資訊:$customerInfo")

        // 測試產品反饋分析
        val productFeedback = """
            我使用這個軟體已經三個月了,整體來說還不錯。
            UI 介面很直觀,功能也很完整,特別是自動備份功能很實用。
            但是載入速度有點慢,希望能改進。另外報表功能可以更豐富一些。
            我會推薦給同事使用。
        """.trimIndent()

        val feedback = analyzeProductFeedback(productFeedback)
        println("產品反饋分析:$feedback")
    }
}

在會話中使用結構化輸出

// 在 LLM 會話中使用結構化輸出
llm.writeSession {
    // 發送一般訊息
    requestLLM("請分析這位客戶的問題")

    // 請求結構化回應
    val structuredResponse = requestLLMStructured(
        config = StructuredOutputConfig(
            default = StructuredOutput.Manual(CustomerInfo.serializer()),
            fixingParser = StructureFixingParser(
                fixingModel = OpenAIModels.Chat.GPT4o,
                retries = 3
            )
        )
    )

    // 繼續處理結構化資料
    println("提取的客戶資訊:$structuredResponse")
}

新平台和模型支援

iOS 平台支援

Koog 0.0.4 正式支援 iOS 平台,這意味著你可以:

  • 在 iOS 應用中直接整合 AI Agent
  • 開發離線優先的智能應用
  • 利用 Kotlin Multiplatform 的優勢跨平台開發

新的 LLM 模型支援

新版本新增了多個業界領先的模型:

// 支援 GPT-5(當可用時)
OpenAIModels.Chat.GPT5

// 支援 DeepSeek 系列模型
DeepSeekModels.Chat.V2
DeepSeekModels.Code.V2

// 支援 Qwen 系列模型
QwenModels.Chat.QwenMax
QwenModels.Code.QwenCoder

// 如何選擇適合的模型
when (task) {
    is CodeGeneration -> QwenModels.Code.QwenCoder
    is MathProblem -> DeepSeekModels.Chat.V2
    is GeneralChat -> OpenAIModels.Chat.GPT4o
    is Translation -> OpenAIModels.Chat.GPT4oMini // 成本考量
}

生產級重試機制

為什麼重試機制至關重要

在生產環境中,網路問題、API 限制、暫時性錯誤都是常見的情況。一個穩健的重試機制能夠:

  • 提高系統可用性
  • 減少因暫時性問題導致的失敗
  • 提供更好的使用者體驗

可配置重試策略

// 檔案:src/main/kotlin/retry/RetryConfiguration.kt
import ai.koog.agents.core.agent.AIAgent
import ai.koog.agents.features.retry.RetryConfig
import ai.koog.prompt.executor.clients.openai.OpenAIModels
import kotlinx.coroutines.delay
import kotlin.time.Duration.Companion.seconds

suspend fun createResilientAgent(): AIAgent {
    return AIAgent(
        executor = simpleOpenAIExecutor(apiKey),
        llmModel = OpenAIModels.Chat.GPT4o,
        systemPrompt = "你是一個高可用性的 AI 助手"
    ) {
        // 配置重試策略
        install(RetryFeature) {
            // 最大重試次數
            maxRetries = 3

            // 指數退避策略
            backoffStrategy = ExponentialBackoff(
                initialDelay = 1.seconds,
                maxDelay = 10.seconds,
                multiplier = 2.0
            )

            // 可重試的錯誤類型
            retryableExceptions = setOf(
                NetworkException::class,
                RateLimitException::class,
                TemporaryServiceException::class
            )

            // 自訂重試條件
            retryCondition = { exception, attemptNumber ->
                when {
                    exception is RateLimitException -> {
                        // 對於限制錯誤,等待更長時間
                        delay(30.seconds)
                        attemptNumber < 2
                    }
                    exception is NetworkException -> {
                        // 網路錯誤快速重試
                        attemptNumber < 3
                    }
                    else -> false
                }
            }
        }
    }
}

子圖重試與程式化回饋

在複雜的工作流程中,Koog 支援對特定子圖進行重試:

// 子圖級別的重試配置
val workflow = createWorkflow {
    node("data-extraction") {
        retry(maxAttempts = 2) {
            extractDataFromDocument(input)
        }
    }

    node("data-validation") {
        retry(
            maxAttempts = 3,
            backoff = LinearBackoff(2.seconds)
        ) {
            validateExtractedData(data)
        }
    }

    node("data-processing") {
        // 程式化回饋:根據前一步結果調整重試策略
        val retryCount = if (previousStepFailed) 1 else 2
        retry(maxAttempts = retryCount) {
            processValidatedData(data)
        }
    }
}

升級指南

從舊版本升級的注意事項

如果你正在使用 Koog 0.0.3 或更早版本,請注意以下變更:

  1. 依賴更新
// 舊版本
implementation("ai.koog.agents:core:0.0.3")

// 新版本
implementation("ai.koog.agents:core:0.0.4")
implementation("ai.koog.agents:agents-features-opentelemetry:0.0.4")
  1. API 變更
  • requestStructured 已更名為 requestLLMStructured
  • OpenTelemetry 配置需要明確安裝 feature
  1. 相容性檢查
// 檢查版本相容性
if (KoogVersion.current >= KoogVersion.v0_0_4) {
    // 使用新的結構化輸出功能
    val structured = llm.writeSession {
        requestLLMStructured<DataClass>()
    }
} else {
    // 使用舊的方法
    val response = llm.writeSession {
        requestLLM()
    }
}

最佳實踐建議

  1. 逐步升級:建議先在開發環境測試新功能
  2. 可觀測性優先:立即啟用 OpenTelemetry 追蹤
  3. 結構化輸出:重構現有的資料提取邏輯
  4. 重試策略:根據實際使用情況調整重試配置

總結

Koog 0.0.4 版本帶來的五大核心更新讓 AI Agent 開發邁入了新的階段:

  1. OpenTelemetry 可觀測性:讓 AI 系統的運作透明化,便於監控和最佳化
  2. Ktor 整合:簡化 API 服務的建置和部署流程
  3. 結構化輸出:提供可靠的資料提取和自動錯誤修正機制
  4. 新平台支援:iOS 支援和新 LLM 模型擴展了應用場景
  5. 生產級重試:增強系統的穩定性和可用性

這些更新不僅提升了開發效率,更重要的是為企業級 AI Agent 應用奠定了堅實的基礎。隨著 AI 技術的快速發展,Koog 框架展現出了跟上時代步伐的能力和決心

下一步,建議你開始實驗這些新功能,特別是可觀測性和結構化輸出,它們將大幅改善你的 AI Agent 開發體驗

支持創作

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


圖片來源:AI 產生