Classical ML monitoring watches distributions; LLM systems additionally need traces — the full tree of prompts, retrievals, tool calls, and completions behind each response — because that is where failures hide.
Tier: Advanced · Workflow area: Serving & LLMOps
This playbook instruments an existing LLM app (your RAG service or agent from earlier playbooks) with OpenTelemetry/OpenInference tracing via self-hosted Arize Phoenix (or MLflow 3 tracing), builds the token-cost dashboard, and adds quality-drift alerting by running evals on sampled production traces.
pip install arize-phoenix openinference-instrumentation-<your-framework> — Phoenix self-hosts with no account; or MLflow ≥3 for its tracing UI if MLflow is already your shop standard.Start Phoenix and instrument the app. phoenix serve gives a local UI and an OTLP collector endpoint. Auto-instrument your framework (OpenInference instrumentors exist for LangChain/LangGraph, LlamaIndex, OpenAI-compatible clients, Anthropic) so every request emits a trace with nested spans: retrieval, each LLM call with prompt/completion/token counts, tool executions, latency per span.
Verify trace completeness on one request. Open a trace in the UI and check you can see: the user input, retrieved chunks with scores, the exact final prompt, the completion, token usage, and total latency. If a span is missing (custom code paths), add manual spans — an incomplete trace is a false sense of coverage.
Build the cost view. Token counts per span × your price table (or gateway-reported cost from pb-48) → cost per request, aggregated by route/feature/day. Phoenix surfaces token stats; for stakeholder dashboards, export to Prometheus/Grafana or a scheduled notebook. The two charts that matter: daily spend by feature, and cost-per-request distribution (p50/p95 — the p95 tail is where prompt bloat and runaway agents show up).
Add online eval sampling. A scheduled job (cron, every hour/day) pulls a sample of recent traces (e.g. 5–10%), runs your scorers on (input, retrieved context, output) — faithfulness and answer-relevance for RAG, task-specific rubrics otherwise — and logs scores back as trace annotations. Use a cheap-tier model for judge calls; sampling exists precisely so evaluation cost stays a rounding error.
Alert on drift, not single failures. Compute a rolling baseline (e.g. 7-day mean faithfulness); alert when the daily sampled mean drops by more than a threshold (say 5 points) or p95 cost/latency breaches SLO. Route alerts to Slack. Single bad samples are noise; sustained shifts are retrieval rot, upstream model changes, or new traffic mixes.
Close the loop with a triage drill. Inject a regression (swap in a worse retriever setting, or truncate context) and practice the diagnosis path: alert → dashboard → filtered traces → offending span → root cause. Time it; that duration is your real MTTR.
Traces are the unit of debuggability, evals-on-samples are the unit of quality monitoring — dashboards of averages without drill-down traces tell you that something broke, never what.
Phoenix, Langfuse, MLflow, and Grafana all self-host — the full stack runs inside a client VPC with no data egress, which is exactly why the OSS path is taught here. Point judge calls at your self-hosted vLLM model. If even trace storage is sensitive, redact prompt/document payloads at the instrumentation layer and keep only structure + metrics; you lose drill-down depth but keep drift detection.
The cost dashboard is a leadership artifact, not just an ops tool — the same numbers feed your executive reporting (pb-52); build them once, consistently.