Anchoring AI — Post 10

Trust, but Verify

Detecting RAG degradation before your users do

Status: Structured draft. This post has a detailed outline and will be written after Posts 02–07 are complete.


Thesis

RAG systems degrade silently. Embeddings drift as language evolves, retrieval quality erodes as the corpus grows, and LLM behavior shifts between model versions. By the time users complain, the damage is done. A 200 OK response with a confidently stated wrong answer is worse than an error — at least an error is visible.

This post shows how to use the telemetry from Post 7 to detect degradation early — and how the CSAT scores we already collect become a lagging ground-truth signal for answer quality. The goal is to move from “we hope it’s working” to “we know when it’s not.”


Key Concepts


Planned Outline

  1. The silent failure problem — a wrong answer delivered confidently is worse than no answer; why 200 OK means nothing for AI systems

  2. Three failure modes, diagnosed

    • Data drift: detecting new categories that weren’t in the corpus at ingest time; how to surface this from query logs
    • Concept drift: embedding the same phrase from 2023 vs. 2026 produces different vectors; when to re-embed the corpus
    • Retrieval drift: how ANN index quality degrades as a pgvector collection grows; HNSW vs. IVFFlat behavior; when to rebuild the index
  3. The telemetry we have (from Post 7) — what rag_retrieval_score, rag_documents_retrieved_total, and llm_tokens_prompt_total tell us; what’s still missing

  4. Adding the missing retrieval quality signal

    • Emit max cosine similarity from retrieve() as a Prometheus histogram
    • A Grafana alert rule: fire when p50 retrieval score drops below 0.70 for 30 minutes
  5. CSAT as ground truth

    • Revisit query_csat() — correlating low CSAT weeks with MCP query patterns from Post 7 telemetry
    • New Grafana panel: CSAT score distribution vs. RAG query volume by category (time series overlay)
    • What a correlated drop looks like; how to investigate
  6. LLM-as-judge for answer quality

    • A lightweight scripts/eval_sample.py: sample N recent query/answer pairs from logs, ask a cheap model to rate 1–5, emit scores as a metric
    • How to automate this as a nightly job (cron or GitHub Actions)
    • The limits of LLM-as-judge: what it catches and what it misses
  7. Drift detection in practice — Grafana alert rules for:

    • Retrieval score p50 degradation
    • Embedding latency spikes (OpenAI model API changes)
    • New category labels not seen at original ingest time
  8. SLOs for this system — proposed thresholds that make sense for the Northgate Federal use case:

    SLO Threshold Window
    Retrieval score p50 > 0.70 Rolling 7-day
    Query latency p95 < 4 seconds Rolling 24-hour
    CSAT trend ≥ 4.0 average Rolling 30-day
    LLM-as-judge score p50 > 3.5 / 5 Rolling 7-day
  9. Runbook sketch: what to do when alerts fire

    • Retrieval score drops → check for corpus skew; consider re-ingest or index rebuild
    • Latency spike → check OpenAI status; consider Ollama fallback
    • CSAT drop → trigger LLM-as-judge eval; review flagged query/answer pairs with a human
    • New category detected → add representative calls; re-embed corpus; update retrieval tests
  10. Shadow mode evaluation — running a new embedding model (text-embedding-3-large or a future Ollama model) in a shadow collection; comparing retrieval scores before promoting


Code Changes for This Post


Outstanding Questions / TBD