RAG 3 min read

Measure RAG Retrieval Latency with Stage Traces

Separate search, reading, reranking and LLM first-token timing. Calculate parallel wall time with a reproducible trace and preserve per-stage failure records.

(Updated: ) • 485 words

To diagnose RAG latency, time each stage with the same monotonic clock and retain the dependencies between stages. Separate external search, page reading, vector retrieval, reranking and LLM first-token time. Parallel stage durations cannot simply be added to obtain user-visible elapsed time.

Start from one trace, not an ideal latency target

Record a request ID, stage start/end, outcome, attempts, input version and cache-hit decision. LLM first-token time ends when the first token is observed, not when generation finishes. A failed retrieval should remain in the trace. Do not compare a warm-cache success with an uncached request without labeling the difference.

Calculate wall time with parallel reads

from math import isfinite

def retrieval_trace(spans):
    if not spans:
        raise ValueError("Trace is empty")
    for name, start, end in spans:
        if not isinstance(start, (int,float)) or not isinstance(end, (int,float)) or not isfinite(start) or not isfinite(end) or end < start:
            raise ValueError("Invalid span: " + name)
    if len({s[0] for s in spans}) != len(spans):
        raise ValueError("Stage names must be unique")
    start, end = min(s[1] for s in spans), max(s[2] for s in spans)
    return {"wall_clock_ms":end-start,
            "stage_ms":{name:b-a for name,a,b in spans},
            "summed_stage_ms":sum(b-a for _,a,b in spans)}

if __name__ == "__main__":
    # Fictional elapsed timestamps from one clock, not measured service latency.
    spans = [("search",0,20),("read_A",20,50),("read_B",20,60),
             ("rerank",60,70),("llm_first_token",70,100)]
    print(retrieval_trace(spans))

The synthetic trace takes one hundred milliseconds from its first start to last end. Stage durations sum to one hundred and thirty because the two reads overlap. Those values illustrate the arithmetic; they do not establish a target or measured SearchCans performance. An end before a start is rejected. The timestamps must come from one clock domain.

Locate the dependency that delays the answer

After search completes, reads may execute in parallel subject to the account and application limits. If the answer needs both sources, the slower required read delays reranking. If one optional read can be omitted, make that a documented evidence policy rather than silently dropping it to improve a latency number. Vector retrieval may be independent or depend on newly extracted text; record the actual graph.

A cache can reduce repeated work, but its age and invalidation policy affect freshness. Record the source version and decide when the task requires revalidation. For an experiment, hold the question set, market, cache condition and evidence acceptance rules constant, then compare per-request traces and failure rates. No universal “ideal milliseconds” follows from this method.

Use the external search benchmark for source freshness and the real-time RAG architecture for pipeline design. This page owns timing attribution rather than duplicating the ingestion architecture.

Sources and reproduction

Tags:

RAG LLM Tutorial Python AI Agent
SearchCans Team

SearchCans Team

SERP API & Reader API Experts

The SearchCans engineering team builds high-performance search APIs serving developers worldwide. We share practical tutorials, best practices, and insights on SERP data, web scraping, RAG pipelines, and AI integration.

Ready to build with SearchCans?

Test SERP API and Reader API with 100 free credits. No credit card required.