iT邦幫忙

2025 iThome 鐵人賽

DAY 19
0
生成式 AI

第一次學 Kotlin Koog AI 就上手系列 第 19

第一次學 Kotlin Koog AI 就上手 Day 19:為 AI 裝上儀表板:OpenTelemetry 監控入門

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20250816/201219482KKLT8nZES.png

經過前面的學習,我們已經建立了一個功能完整的 AI Agent 系統,包含 LLM 整合、工具系統、記憶體機制等今天我們要學習一個全新的概念:可觀測性(Observability)。想像一下,當您的 AI 系統在生產環境中運行時,您能清楚看到每個請求如何流轉、每次 LLM 呼叫花費多少時間、工具執行是否正常嗎?

今天我們將透過 OpenTelemetry 為我們的 AI Agent 加上「儀表板」,讓它的執行過程變得透明可見

什麼是 OpenTelemetry?

簡單來說,OpenTelemetry 就像是為您的應用程式安裝了一套完整的監控系統。它能自動記錄應用程式中發生的每一個操作,並將這些資訊以視覺化的方式展現出來

核心概念簡介

在 OpenTelemetry 的世界中,有幾個重要概念

  • Trace(追蹤):一個完整的請求處理過程,就像追蹤一個包裹從寄出到收到的完整路徑
  • Span(跨度):請求中的單一操作,比如「呼叫 LLM」或「執行工具」
  • Metrics(指標):系統的效能數據,如回應時間、成功率等

對於 AI 應用來說,這特別有用,因為我們可以清楚看到

  • LLM 呼叫花費多少時間
  • 工具執行是否成功
  • 整個 Agent 的思考過程

最簡單的監控配置

讓我們從最基本的 OpenTelemetry 配置開始

class SimpleMonitoring {

    // 建立一個帶監控功能的 Agent
    private val monitoredAgent = AIAgent(
        executor = simpleOpenAIExecutor(ApiKeyManager.openAIApiKey!!),
        systemPrompt = """
            你是一個 AI 助手,請用正體中文回答問題
        """.trimIndent(),
        llmModel = OpenAIModels.CostOptimized.GPT4_1Mini,
        toolRegistry = ToolRegistry {
            tool(SayToUser)
            tool(FakeWeatherTool)
        }
    ) {
        // 安裝 OpenTelemetry 監控功能
        install(OpenTelemetry) {
            // 設定服務資訊:服務名稱和版本,用於識別和分組追蹤數據
            setServiceInfo("ai-agent-demo", "1.0.0")

            // 設定取樣速率
            setSampler(Sampler.traceIdRatioBased(0.5))

            // 開啟詳細模式,可以看到更多資訊
            setVerbose(true)

            // 新增自定的資源屬性
            addResourceAttributes(
                mapOf(
                    AttributeKey.stringKey("custom.attribute") to "custom-value",
                ),
            )
        }
    }

    suspend fun runWithMonitoring(query: String): String {
        println("開始執行查詢: $query")
        val result = monitoredAgent.run(query)
        println("查詢完成")
        return result
    }
}

這就是最基本的 OpenTelemetry 整合!當您執行這個程式時,會在控制台看到詳細的追蹤資訊

天氣查詢工具

建立一個假裝呼叫 API 的天氣查詢工具

object FakeWeatherTool: SimpleTool<FakeWeatherTool.Args>() {
    @Serializable
    data class Args(val city: String) : ToolArgs

    override val argsSerializer = Args.serializer()

    override val descriptor = ToolDescriptor(
        name = "get_weather",
        description = "查詢指定城市的天氣狀況",
        requiredParameters = listOf(
            ToolParameterDescriptor(
                name = "city",
                description = "要查詢天氣的城市名稱",
                type = ToolParameterType.String
            )
        )
    )

    override suspend fun doExecute(args: Args): String {
        // 模擬 API 呼叫延遲
        delay(2000)

        return when (args.city.lowercase()) {
            "台北", "taipei" -> "台北今天晴朗,溫度 25°C,濕度 60%"
            "高雄", "kaohsiung" -> "高雄今天多雲,溫度 28°C,濕度 70%"
            else -> "${args.city} 今天天氣良好,溫度適中"
        }
    }
}

監控 AI Agent 的執行範例

現在讓我們建立一個完整的範例,展示如何監控一個包含 LLM 呼叫和工具執行的請求

suspend fun main() {
    println("OpenTelemetry 監控演示")
    println("=".repeat(50))

    val simpleMonitoring = SimpleMonitoring()

    // 執行一個會觸發 LLM 呼叫和工具執行的查詢
    val query = "今天台北的天氣如何?"

    println("用戶查詢: $query")
    println()

    val result = simpleMonitoring.runWithMonitoring(query)

    println()
    println("Agent 回應: $result")
}

執行 AI 回應內容

在下面的 log 中,您可以看到

  • OpenTelemetry 自動追蹤了整個請求過程
  • 每個 Span (INFO: 開頭的) 代表一個操作(LLM 呼叫、工具執行等)
  • 包含操作時間、參數、結果等詳細資訊
OpenTelemetry 監控演示
==================================================
用戶查詢: 今天台北的天氣如何?

開始執行查詢: 今天台北的天氣如何?
Aug 16, 2025 4:38:01 PM io.opentelemetry.exporter.logging.LoggingSpanExporter export
INFO: 'node.__start__' : 26d38e0a1bd9e922d08638988e7debd5 70c9c6e048a95054 INTERNAL [tracer: ai-agent-demo:1.0.0] AttributesMap{data={gen_ai.conversation.id=c816322e-4e9e-4f1f-8e20-a256364daa83, koog.node.name=__start__}, capacity=128, totalAddedValues=2}
Aug 16, 2025 4:38:03 PM io.opentelemetry.exporter.logging.LoggingSpanExporter export
INFO: 'llm.chat' : 26d38e0a1bd9e922d08638988e7debd5 8a753c9d939d255a CLIENT [tracer: ai-agent-demo:1.0.0] AttributesMap{data={gen_ai.operation.name=chat, gen_ai.conversation.id=c816322e-4e9e-4f1f-8e20-a256364daa83, gen_ai.request.model=gpt-4.1-mini, gen_ai.system=openai, gen_ai.request.temperature=1.0}, capacity=128, totalAddedValues=5}
Aug 16, 2025 4:38:03 PM io.opentelemetry.exporter.logging.LoggingSpanExporter export
INFO: 'node.nodeCallLLM' : 26d38e0a1bd9e922d08638988e7debd5 a0e0d5d43019d92b INTERNAL [tracer: ai-agent-demo:1.0.0] AttributesMap{data={gen_ai.conversation.id=c816322e-4e9e-4f1f-8e20-a256364daa83, koog.node.name=nodeCallLLM}, capacity=128, totalAddedValues=2}
Aug 16, 2025 4:38:05 PM io.opentelemetry.exporter.logging.LoggingSpanExporter export
INFO: 'tool.get_weather' : 26d38e0a1bd9e922d08638988e7debd5 67373ce13efbe18f INTERNAL [tracer: ai-agent-demo:1.0.0] AttributesMap{data={gen_ai.tool.name=get_weather, gen_ai.tool.description=查詢指定城市的天氣狀況}, capacity=128, totalAddedValues=2}
Aug 16, 2025 4:38:05 PM io.opentelemetry.exporter.logging.LoggingSpanExporter export
INFO: 'node.nodeExecuteTool' : 26d38e0a1bd9e922d08638988e7debd5 5541a2cd155829bf INTERNAL [tracer: ai-agent-demo:1.0.0] AttributesMap{data={gen_ai.conversation.id=c816322e-4e9e-4f1f-8e20-a256364daa83, koog.node.name=nodeExecuteTool}, capacity=128, totalAddedValues=2}
Aug 16, 2025 4:38:06 PM io.opentelemetry.exporter.logging.LoggingSpanExporter export
INFO: 'llm.chat' : 26d38e0a1bd9e922d08638988e7debd5 d3e2d454d8f13499 CLIENT [tracer: ai-agent-demo:1.0.0] AttributesMap{data={gen_ai.operation.name=chat, gen_ai.conversation.id=c816322e-4e9e-4f1f-8e20-a256364daa83, gen_ai.request.model=gpt-4.1-mini, gen_ai.system=openai, gen_ai.request.temperature=1.0}, capacity=128, totalAddedValues=5}
Aug 16, 2025 4:38:06 PM io.opentelemetry.exporter.logging.LoggingSpanExporter export
INFO: 'node.nodeSendToolResult' : 26d38e0a1bd9e922d08638988e7debd5 77b76f7094784a6b INTERNAL [tracer: ai-agent-demo:1.0.0] AttributesMap{data={gen_ai.conversation.id=c816322e-4e9e-4f1f-8e20-a256364daa83, koog.node.name=nodeSendToolResult}, capacity=128, totalAddedValues=2}
Aug 16, 2025 4:38:06 PM io.opentelemetry.exporter.logging.LoggingSpanExporter export
INFO: 'tool.say_to_user' : 26d38e0a1bd9e922d08638988e7debd5 f7077755fab190df INTERNAL [tracer: ai-agent-demo:1.0.0] AttributesMap{data={gen_ai.tool.name=say_to_user, gen_ai.tool.description=Service tool, used by the agent to talk.}, capacity=128, totalAddedValues=2}
Aug 16, 2025 4:38:06 PM io.opentelemetry.exporter.logging.LoggingSpanExporter export
INFO: 'node.nodeExecuteTool' : 26d38e0a1bd9e922d08638988e7debd5 29b08c492f457db7 INTERNAL [tracer: ai-agent-demo:1.0.0] AttributesMap{data={gen_ai.conversation.id=c816322e-4e9e-4f1f-8e20-a256364daa83, koog.node.name=nodeExecuteTool}, capacity=128, totalAddedValues=2}
Agent says: 今天台北的天氣是晴朗,溫度約25°C,濕度60%。適合外出活動喔!
Aug 16, 2025 4:38:07 PM io.opentelemetry.exporter.logging.LoggingSpanExporter export
INFO: 'llm.chat' : 26d38e0a1bd9e922d08638988e7debd5 783464e28390eed5 CLIENT [tracer: ai-agent-demo:1.0.0] AttributesMap{data={gen_ai.operation.name=chat, gen_ai.conversation.id=c816322e-4e9e-4f1f-8e20-a256364daa83, gen_ai.request.model=gpt-4.1-mini, gen_ai.system=openai, gen_ai.request.temperature=1.0}, capacity=128, totalAddedValues=5}
Aug 16, 2025 4:38:07 PM io.opentelemetry.exporter.logging.LoggingSpanExporter export
INFO: 'node.nodeSendToolResult' : 26d38e0a1bd9e922d08638988e7debd5 38551d875c620dee INTERNAL [tracer: ai-agent-demo:1.0.0] AttributesMap{data={gen_ai.conversation.id=c816322e-4e9e-4f1f-8e20-a256364daa83, koog.node.name=nodeSendToolResult}, capacity=128, totalAddedValues=2}
Aug 16, 2025 4:38:07 PM io.opentelemetry.exporter.logging.LoggingSpanExporter export
INFO: 'run.c816322e-4e9e-4f1f-8e20-a256364daa83' : 26d38e0a1bd9e922d08638988e7debd5 1e1042a24e3975f9 CLIENT [tracer: ai-agent-demo:1.0.0] AttributesMap{data={gen_ai.operation.name=invoke_agent, gen_ai.conversation.id=c816322e-4e9e-4f1f-8e20-a256364daa83, koog.agent.strategy.name=single_run, gen_ai.system=openai, gen_ai.agent.id=17069dfe-31cd-44d6-90e9-6caf59f34b6a}, capacity=128, totalAddedValues=5}
查詢完成

Agent 回應: 今天台北的天氣是晴朗,溫度約25°C,濕度60%。適合外出活動喔!

在 Jaeger UI 中查看追蹤結果

雖然日誌輸出很有用,但視覺化的追蹤介面更直觀。Jaeger 是一個開源的分散式追蹤系統,能夠提供強大的視覺化功能來分析 AI Agent 的執行過程

啟動 Jaeger

使用 Docker 啟動 Jaeger 服務

# 啟動 Jaeger 全功能服務
docker run --rm --name jaeger \
  -p 16686:16686 \
  -p 4317:4317 \
  -p 4318:4318 \
  -p 5778:5778 \
  -p 9411:9411 \
  cr.jaegertracing.io/jaegertracing/jaeger:2.9.0

啟動後,可以在 http://localhost:16686 訪問 Jaeger UI

安裝 OTLP Exporter 套件

在您的 build.gradle.kts 中加入

dependencies {
    // 其他依賴...

    // 不同通訊協定所使用的套件不同,請參考相關 Github 說明
    implementation("io.opentelemetry:opentelemetry-exporter-otlp:1.53.0")
}

修改程式碼

將日誌 exporter 替換為 OTLP exporter

install(OpenTelemetry) {

    // 其它程式碼 ...

    // 使用 OTLP gRPC exporter 將數據發送到 Jaeger
    addSpanExporter(
        OtlpGrpcSpanExporter.builder()
            .setEndpoint("http://localhost:4317") // Jaeger 的 OTLP gRPC 接收端點
            .build()
    )
}

在 Jaeger UI 中您會看到什麼?

https://ithelp.ithome.com.tw/upload/images/20250816/20121948AuzLjRlEae.png

https://ithelp.ithome.com.tw/upload/images/20250816/201219487BnCNfsyJu.png

當您在 Jaeger UI 中查看追蹤結果時,會看到以下豐富的視覺化資訊

完整的追蹤時間軸

從用戶查詢開始到 Agent 回應結束的完整時間軸,清楚顯示每個操作的執行時間和先後順序

Koog AI 特有的 Span 類型

Koog 框架會自動建立以下類型的 Span

  • InvokeAgentSpan:整個 Agent 執行的頂層 Span,包含完整的對話 ID
  • NodeExecuteSpan:內部節點執行的 Span(如 nodeCallLLMnodeExecuteTool
  • InferenceSpan:LLM 推論呼叫,包含模型資訊、溫度設定、token 使用量
  • ExecuteToolSpan:工具執行的 Span,包含工具名稱、參數、執行結果
  • CreateAgentSpan:Agent 建立時的初始化 Span

詳細的屬性和標籤

每個 Span 都包含豐富的元數據

LLM 相關屬性

  • gen_ai.system: LLM 提供商(如 "openai")
  • gen_ai.request.model: 使用的模型(如 "gpt-4-mini")
  • gen_ai.request.temperature: 溫度設定
  • gen_ai.operation.name: 操作類型(如 "chat")

工具相關屬性

  • gen_ai.tool.name: 工具名稱
  • gen_ai.tool.description: 工具描述
  • 工具參數和執行結果

Koog 特有屬性

  • gen_ai.conversation.id: 對話唯一 ID
  • koog.node.name: 內部節點名稱
  • koog.agent.strategy.name: Agent 執行策略
  • gen_ai.agent.id: Agent 唯一 ID

服務依賴圖

展示 AI Agent 如何與外部服務(LLM API、工具服務等)互動的完整依賴關係

視覺化的實際價值

透過 Jaeger UI,您可以

  • 效能分析:快速識別哪個 LLM 呼叫或工具執行最耗時
  • 錯誤追蹤:當 Agent 行為異常時,可以精確定位問題發生在哪個步驟
  • 決策理解:清楚看到 Agent 的思考流程,包括何時呼叫 LLM、何時使用工具
  • 成本監控:追蹤 token 使用量和 API 呼叫次數,有效控制成本
  • 對話分析:使用 conversation.id 追蹤完整的多輪對話過程

總結

今天我們學習了如何為 AI Agent 添加 OpenTelemetry 監控功能

透過這個簡單的入門範例,我們了解到

  • 監控的重要性:AI 應用需要可觀測性來了解執行過程
  • 簡單的整合:只需要 install(OpenTelemetry) 就能啟用監控
  • 自動追蹤:Koog 框架會自動追蹤 LLM 呼叫和工具執行
  • 視覺化工具:可以使用 Jaeger 等工具來查看追蹤結果

在下一篇文章,我們將學習如何將 Koog AI 整合到 Kotlin Ktor 的 Web 應用當中

參考文件


圖片來源:AI 產生

同步刊登於 Blog 第一次學 Kotlin Koog AI 就上手 Day 19:為 AI 裝上儀表板:OpenTelemetry 監控入門'


上一篇
第一次學 Kotlin Koog AI 就上手 Day 18:服務不中斷:建立智慧路由與容錯機制
系列文
第一次學 Kotlin Koog AI 就上手19
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言