
AI 工作流的可观测性架构Trace、Metric 与 Log 的三维性能诊断体系一、可观测性在 AI 工作流中的必要性AI 工作流的复杂性与传统微服务有本质区别。一个典型的 Agent 调用链可能涉及用户意图解析 → RAG 检索 → 多轮 LLM 推理 → 工具调用 → 结果聚合 → 流式输出。这条链路上延迟的累积不是线性的——一个工具调用的超时可能引发后续节点的重试风暴一个 Prompt 的长度膨胀可能让 Token 成本翻倍。传统监控的痛点在于Metrics 能告诉你 P99 延迟变高了但不知道是哪个环节在拖后腿Log 记录了每个节点的输出却无法关联到具体的用户请求Trace 有父子 Span 关系但 LLM 调用的耗时不等于网络延迟 推理时间中间还有 Tokenization、KV Cache 查找等不可观测的步骤。三维可观测性体系的核心思路是Metrics 发现问题Trace 定位环节Log 还原现场。graph TD A[告警触发: P99 延迟 5s] -- B{Metrics 面板} B -- C[推理耗时占比 80%] B -- D[工具调用耗时占比 50%] C -- E[Trace 下钻到具体 Span] D -- E E -- F[定位到特定 Prompt 模板] E -- G[定位到特定 Tool API] F -- H[Log 查看完整 Prompt 内容] G -- I[Log 查看 API 返回] H -- J[优化 Prompt 长度 / 切换模型] I -- K[增加缓存 / 切换供应商]二、Trace 设计从链路追踪到语义追踪标准 OpenTelemetry Span 的属性是针对 HTTP/RPC 调用设计的对于 LLM 调用需要扩展自定义属性。建议按以下维度设计 Span// LLM 调用的自定义 Span 属性 type LLMSpanAttributes struct { Model string // gpt-4o-mini Provider string // openai PromptTokens int CompletionTokens int TotalTokens int Temperature float64 StopReason string // stop | length | tool_calls FirstTokenLatency time.Duration // TTFT StreamMode bool ToolCallsCount int CacheHit bool // 是否命中 Prompt Cache }关键设计原则Span 的父子关系要反映语义依赖而非物理调用时序。如果一个 Agent 先做查询改写再检索两个 LLM 调用应该是串行的父子 Span但如果做并行工具调用同时查天气、查日历它们应该是同一个父 Span 下的兄弟 Span。区分排队延迟和执行延迟。LLM API 调用中从发起请求到收到第一个 Token 的时间TTFT包含排队时间这往往是供应商侧的瓶颈。通过记录ttft和tokens_per_second可以判断P99 延迟高 TTFT 高 → 供应商排队拥塞考虑切换供应商或增加并发限制 P99 延迟高 tokens_per_second 低 → 模型推理慢考虑切换更快的模型 P99 延迟高 两者都正常 → 业务逻辑耗时多优化 Prompt 或减少 Tool 调用轮次三、Metrics 设计从基础设施指标到业务指标基础设施层面的 MetricsCPU、内存、GC 暂停是必要但不充分的。AI 工作流需要额外的业务视角// 业务 Metrics 的设计维度 const businessMetrics { // 质量指标 task_success_rate: { type: gauge, labels: [agent_type, workflow_id] }, tool_call_success_rate: { type: gauge, labels: [tool_name] }, // 成本指标 token_cost_per_task: { type: histogram, labels: [model, agent_type], buckets: [0.001, 0.01, 0.05, 0.1, 0.5] }, daily_token_cost: { type: counter, labels: [model] }, // 延迟指标 end_to_end_latency: { type: histogram, labels: [agent_type], buckets: [1, 5, 10, 30, 60, 120] }, ttft_latency: { type: histogram, labels: [model, provider] }, tool_execution_latency: { type: histogram, labels: [tool_name] }, // 缓存指标 semantic_cache_hit_rate: { type: gauge, labels: [cache_layer] }, cache_memory_mb: { type: gauge }, // 限流指标 rate_limit_hits: { type: counter, labels: [model, provider] } };PromQL 查询示例# 过去 1 小时 Token 成本突增 (sum(rate(token_cost_per_task_sum[5m])) / sum(rate(token_cost_per_task_count[5m]))) 2 * (sum(rate(token_cost_per_task_sum[1h])) / sum(rate(token_cost_per_task_count[1h]))) # 特定 Tool 调用的成功率下降 tool_call_success_rate{tool_namesearch_database} 0.95四、Log 设计结构化日志与采样策略AI 工作流的日志量可能远超传统服务一次 Agent 对话可能产生 20 轮 LLM 调用每轮都有完整的 Prompt 和 Response。全量记录所有 Prompt 内容极不经济。分层采样策略Level 1始终记录任务 ID、Agent 类型、总耗时、Token 总消耗、成功/失败 Level 2采样 10%每个 LLM 调用的 Prompt 前 500 字符 Response 前 500 字符 Level 3按错误采样 100%正常采样 1%完整 Prompt 完整 Response Level 4单条 Trace 按需开启所有中间状态、向量检索结果、RAG 召回内容实现示例type AIWorkflowLogger struct { base *slog.Logger sampler Sampler } func (l *AIWorkflowLogger) LogLLMCall(ctx context.Context, call LLMCallRecord) { // Level 1: 始终记录 l.base.InfoContext(ctx, llm_call, task_id, call.TaskID, model, call.Model, prompt_tokens, call.PromptTokens, completion_tokens, call.CompletionTokens, latency_ms, call.Latency.Milliseconds(), ) // Level 2: 采样记录摘要 if l.sampler.ShouldSample(call.TaskID, 0.1) { l.base.DebugContext(ctx, llm_call_detail, prompt_preview, truncate(call.Prompt, 500), response_preview, truncate(call.Response, 500), ) } // Level 3: 错误全量 正常采样 if call.Error ! nil || l.sampler.ShouldSample(call.TaskID, 0.01) { l.base.DebugContext(ctx, llm_call_full, prompt, call.Prompt, response, call.Response, error, call.Error, ) } }五、总结AI 工作流的可观测性不是监控工具的简单叠加需要针对 LLM 调用、Token 消费、语义缓存等新维度设计指标体系。核心原则Metrics 用于发现异常并触发告警Trace 用于定位到具体的 Span 节点Log 用于还原完整的上下文。Trace 设计要反映语义依赖而非物理时序Metrics 要覆盖业务指标成本、成功率、缓存命中率Log 要用分层采样避免存储爆炸。三者协同才能在 AI 工作流的性能迷宫中快速找到出口。