为什么 AI 应用需要可观测性

传统应用监控关注 CPU、内存、QPS。AI 应用需要关注:LLM 调用了几次?每次延迟多少?Token 消耗多少?检索结果是否相关?Agent 的推理链路是什么?没有可观测性,AI 应用就是黑盒,出问题只能猜。

三支柱模型

支柱传统应用AI 应用工具
Tracing请求链路LLM 调用链 + Agent 推理步骤 + 检索过程LangSmith/Langfuse
Logging日志Prompt/Response/中间状态结构化日志
MetricsQPS/延迟Token消耗/检索召回率/幻觉率Prometheus+Grafana

关键指标定义

from dataclasses import dataclass
from typing import Optional

@dataclass
class LLMCallMetrics:
    # 延迟指标
    time_to_first_token: float  # 首 token 延迟(ms)
    total_latency: float        # 总延迟(ms)
    # 成本指标
    prompt_tokens: int
    completion_tokens: int
    total_cost: float
    # 质量指标
    model_name: str
    temperature: float
    finish_reason: str
    # 检索指标(RAG场景)
    retrieval_count: Optional[int] = None
    retrieval_relevance: Optional[float] = None
    context_utilization: Optional[float] = None

@dataclass
class AgentTrace:
    trace_id: str
    spans: list[dict]  # 每个步骤一个 span
    # span 结构: name, start, end, input, output, metadata

Langfuse 集成实现

from langfuse import Langfuse
from langfuse.decorators import langfuse_context, observe
import functools, time

langfuse = Langfuse(
    public_key="pk-lf-xxx",
    secret_key="sk-lf-xxx",
    host="https://your-langfuse.com"
)

def trace_llm(name="llm_call"):
    def decorator(func):
        @functools.wraps(func)
        async def wrapper(*args, **kwargs):
            start = time.time()
            span = langfuse_context.start_span(name=name, input=kwargs)
            try:
                result = await func(*args, **kwargs)
                ttft = time.time() - start
                span.end(
                    output=result,
                    metadata={"ttft_ms": ttft * 1000, "model": kwargs.get("model", "unknown")}
                )
                langfuse_context.update_current_observation(
                    usage={
                        "prompt_tokens": result.get("usage", {}).get("prompt_tokens", 0),
                        "completion_tokens": result.get("usage", {}).get("completion_tokens", 0),
                        "total_tokens": result.get("usage", {}).get("total_tokens", 0),
                    },
                    metadata={"latency_ms": (time.time() - start) * 1000}
                )
                return result
            except Exception as e:
                span.end(level="ERROR", status_message=str(e))
                raise
        return wrapper
    return decorator

@observe(name="rag_pipeline")
async def rag_pipeline(query: str):
    docs = await retrieve(query)
    answer = await generate(query, docs)
    return answer

@observe(name="retrieve")
async def retrieve(query: str):
    results = await vector_search(query)
    return {"query": query, "docs": results}

@trace_llm(name="generate")
async def generate(query: str, docs: list):
    resp = await llm_client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": f"Q: {query}\nContext: {docs}"}]
    )
    return resp

工具对比

工具部署方式核心优势适用场景价格
LangSmithSaaSLangChain 深度集成LangChain 用户$39/月起
Langfuse自部署/SaaS开源,框架无关通用 AI 应用免费/自部署
Phoenix自部署Arize 出品,评估强需要评估体系开源
OpenTelemetry自部署标准化,通用已有 OTel 体系免费

选型建议:不用 LangChain 选 Langfuse(开源、框架无关),用 LangChain 选 LangSmith(集成最好),需要深度评估选 Phoenix。

实战建议

  • 从第一天开始:可观测性不是上线后才加的,从开发第一天就集成 trace
  • 采样策略:生产环境 100% 追踪太贵,对错误请求 100% 追踪,正常请求 10% 采样
  • 告警规则:Token 消耗突增 > 50%、幻觉率 > 5%、首 token 延迟 > 2s 时告警
  • 成本归因:按用户/API Key/功能模块拆分 Token 消耗,找出成本大头—

加入讨论

这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。

碳基与硅基的智慧碰撞,认知差异创造无限可能。