從 OT-Trace 資料中提取每次 LLM 調用的完整快照,經去重採樣後自動完成 Prompt 與響應的品質評估及 Token 統計分析。
業務情境
AI Agent 應用中 LLM 調用是核心組件。本模板聚焦單次 LLM 調用(Span-LLM)粒度,支援以下分析情境:
發現 Prompt 設計缺陷,例如 System Prompt 缺失、輸入訊息冗餘等。
識別異常響應,例如幻覺輸出、格式錯誤、工具調用異常。
分析 Token 使用效率和推理效能,例如輸入輸出 Token 比、每秒產生速率。
Pipeline 流程
本模板的 Pipeline 分為以下 4 個階段,從原始 Trace 資料到 AI 品質評估依次處理。
階段 | 節點 | 說明 |
Phase 1:資料群組裝 | extend(extract) | 從 OT-Trace Span 原始欄位中提取 LLM 調用相關屬性,包括 |
按 | ||
extend(derive) | 派生計算指標,包括 | |
where(filter) | 過濾無效記錄,僅保留模型名稱非空且輸入 Token 大於 0 的調用。 | |
Phase 2:資料清洗 | 按 | |
Phase 3:特徵計算 | 對 | |
Phase 4:採樣 + AI 處理 | 隨機採樣 50 條記錄,控制後續 AI 評估的調用量。 | |
llm-call(quality_eval) | 調用 AI 模型對每條 LLM 調用進行品質評估,輸出結構化評估結果。 |
完整配置
本模板提供 JSON 配置格式。關於 Pipeline 配置的基本概念和使用方式,請參見資料處理(Pipeline)。
以下為完整的 Pipeline JSON 配置,可直接通過 API 建立。
{
"name": "ot_llm_quality_analysis",
"description": "OT-Trace Span-LLM 粒度:LLM 調用品質分析 — 從 Trace 資料提取所有 LLM 調用快照,去重採樣後進行 Prompt/響應品質評估與文本統計",
"source": {
"type": "logstore",
"logstore": {
"project": "ali-pub-cn-hangzhou-staging-sls-admin",
"logstore": "logstore-tracing",
"query": "\"attributes.gen_ai.span.kind\": LLM"
}
},
"pipeline": {
"nodes": [
{
"id": "extract",
"type": "extend",
"parameters": {
"session_id": "\"attributes.gen_ai.session.id\"",
"user_id": "\"attributes.gen_ai.user.id\"",
"framework": "\"attributes.gen_ai.framework\"",
"operation_name": "\"attributes.gen_ai.operation.name\"",
"provider": "\"attributes.gen_ai.provider.name\"",
"request_model": "CASE WHEN \"attributes.gen_ai.request.model\" IS NOT NULL AND \"attributes.gen_ai.request.model\" != '' THEN \"attributes.gen_ai.request.model\" WHEN substr(spanname, 1, 4) = 'llm:' THEN substr(spanname, 5) WHEN substr(spanname, 1, 14) = 'llm_streaming_' THEN substr(spanname, 15) WHEN substr(spanname, 1, 9) = 'llm_call_' THEN substr(spanname, 10) WHEN substr(spanname, 1, 5) = 'chat ' THEN substr(spanname, 6) WHEN strpos(spanname, '.invoke') > 0 THEN substr(spanname, 1, strpos(spanname, '.invoke') - 1) ELSE spanname END",
"system_prompt": "\"attributes.gen_ai.system_instructions\"",
"input_messages": "\"attributes.gen_ai.input.messages\"",
"output_messages": "\"attributes.gen_ai.output.messages\"",
"input_tokens": "CAST(\"attributes.gen_ai.usage.input_tokens\" AS BIGINT)",
"output_tokens": "CAST(\"attributes.gen_ai.usage.output_tokens\" AS BIGINT)",
"finish_reasons": "\"attributes.gen_ai.response.finish_reasons\""
}
},
{
"id": "assemble",
"type": "make-instance",
"parameters": {
"by": "spanid",
"trace_id": "any(traceid)",
"service_name": "any(servicename)",
"span_name": "any(spanname)",
"duration_ns": "max(duration)",
"status_code": "max(statuscode)",
"span_time": "first(__time__)",
"session_id": "any(session_id)",
"user_id": "any(user_id)",
"framework": "any(framework)",
"operation_name": "any(operation_name)",
"provider": "any(provider)",
"request_model": "any(request_model)",
"system_prompt": "first(system_prompt)",
"input_messages": "first(input_messages)",
"output_messages": "last(output_messages)",
"input_tokens": "sum(input_tokens)",
"output_tokens": "sum(output_tokens)",
"finish_reasons": "any(finish_reasons)"
}
},
{
"id": "derive",
"type": "extend",
"parameters": {
"duration_ms": "CAST(duration_ns AS DOUBLE) / 1000000.0",
"tokens_per_sec": "CASE WHEN CAST(duration_ns AS DOUBLE) > 0 THEN CAST(output_tokens AS DOUBLE) / (CAST(duration_ns AS DOUBLE) / 1000000000.0) ELSE 0.0 END",
"input_output_ratio":"CASE WHEN CAST(output_tokens AS DOUBLE) > 0 THEN CAST(input_tokens AS DOUBLE) / CAST(output_tokens AS DOUBLE) ELSE 0.0 END",
"has_tool_call": "CASE WHEN finish_reasons LIKE '%tool_calls%' THEN 'true' ELSE 'false' END",
"has_system_prompt": "CASE WHEN system_prompt IS NOT NULL AND system_prompt != '' THEN 'true' ELSE 'false' END",
"full_text": "concat('[', coalesce(request_model, '?'), '] ', coalesce(operation_name, 'chat'), chr(10), 'Tokens: ', coalesce(CAST(input_tokens AS VARCHAR), '?'), '→', coalesce(CAST(output_tokens AS VARCHAR), '?'), chr(10), 'Input: ', substr(coalesce(input_messages, ''), 1, 500), chr(10), 'Output: ', substr(coalesce(output_messages, ''), 1, 500))"
}
},
{
"id": "filter_valid",
"type": "where",
"parameters": {
"filter": "(request_model IS NOT NULL AND request_model != '') AND (input_tokens IS NOT NULL AND input_tokens > 0)"
}
},
{
"id": "exact_dedup",
"type": "dedup-exact",
"parameters": {
"field": "full_text"
}
},
{
"id": "text_stats",
"type": "doc-stats",
"parameters": {
"field": "full_text"
}
},
{
"id": "downsample",
"type": "sample",
"parameters": {
"n": 50
}
},
{
"id": "quality_eval",
"type": "llm-call",
"parameters": {
"prompt": "@eval/llm-call-eval.md",
"fields": "request_model,input_tokens,output_tokens,tokens_per_sec,duration_ms,has_tool_call,full_text",
"format": "json",
"as": "eval"
}
}
]
},
"sink": {
"type": "dataset",
"dataset": {
"workspace": "inner-playground",
"dataset": "ot_llm_quality_analysis"
}
},
"executePolicy": {
"mode": "run_once",
"run_once": { "fromTime": 0, "toTime": 0 }
}
}欄位說明
下表列出本模板涉及的主要欄位及其來源。
extract 節點提取欄位
欄位名 | 原始屬性 | 說明 |
|
| 會話 ID。 |
|
| 使用者識別碼。 |
|
| AI 架構名稱(如 LangChain、Semantic Kernel 等)。 |
|
| 操作類型(如 chat、completion 等)。 |
|
| 模型供應商名稱。 |
|
| 請求的模型名稱。支援從 Span 名稱中自動提取(當屬性欄位為空白時)。 |
|
| 系統指令(System Prompt)。 |
|
| 輸入訊息內容。 |
|
| 輸出訊息內容。 |
|
| 輸入 Token 數量。 |
|
| 輸出 Token 數量。 |
|
| 模型響應的結束原因。 |
derive 節點派生欄位
欄位名 | 類型 | 說明 |
| DOUBLE | LLM 調用耗時(毫秒)。由 |
| DOUBLE | 每秒產生 Token 數。計算公式: |
| DOUBLE | 輸入/輸出 Token 比值。比值過高可能表示輸入訊息冗餘。 |
| VARCHAR | 是否包含工具調用。當 |
| VARCHAR | 是否包含 System Prompt。 |
| VARCHAR | 拼接的完整文本摘要,用於去重和文本統計。格式為 |
定製建議
可以基於本模板進行以下定製調整。
定製項 | 修改方式 | 說明 |
資料來源 | 修改 | 替換為實際的 OT-Trace 資料來源。 |
採樣數量 | 修改 | 控制 AI 評估的樣本量。增大採樣數會增加 AI 調用成本。 |
評估維度 | 自訂 | 調整 AI 品質評估的評分標準和評估維度。 |
去重策略 | 在 | 在精確去重基礎上增加近似去重,進一步減少重複資料。 |
輸出目標 | 修改 | 替換為實際的資料集輸出目標。 |