从 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。 |
|
| 用户 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 质量评估的评分标准和评估维度。 |
去重策略 | 在 | 在精确去重基础上增加近似去重,进一步减少重复数据。 |
输出目标 | 修改 | 替换为实际的数据集输出目标。 |