- Published on
第一次學 Kotlin Koog AI 就上手 Day 19:為 AI 裝上儀表板:OpenTelemetry 監控入門
經過前面的學習,我們已經建立了一個功能完整的 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 中您會看到什麼?
當您在 Jaeger UI 中查看追蹤結果時,會看到以下豐富的視覺化資訊
完整的追蹤時間軸
從用戶查詢開始到 Agent 回應結束的完整時間軸,清楚顯示每個操作的執行時間和先後順序
Koog AI 特有的 Span 類型
Koog 框架會自動建立以下類型的 Span
- InvokeAgentSpan:整個 Agent 執行的頂層 Span,包含完整的對話 ID
- NodeExecuteSpan:內部節點執行的 Span(如
nodeCallLLM
、nodeExecuteTool
) - 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
: 對話唯一 IDkoog.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 Kotr 的應用當中
參考文件
支持創作
如果這篇文章對您有幫助,歡迎透過 贊助連結 支持我持續創作優質內容。您的支持是我前進的動力!
圖片來源:AI 產生