Streaming RAG Isn't a Flag — It's a Full-Stack Latency Problem
Perceived latency — the gap between sending a query and seeing the first character — determines whether users trust a RAG system or abandon it. The techniques here (parallel retrieval with soft timeouts, conditional rewrites, SSE progress) turn a 3–6 second blank-screen wait into sub-second first-token delivery without replacing infrastructure, but they demand careful threshold calibration and production hardening that most `stream=True`-only setups ignore.
A RAG pipeline that adds `stream=True` to the LLM call still makes users wait 3–6 seconds for the first token because the entire retrieval chain — query rewriting, vector search, BM25, Rerank — runs synchronously before generation begins. The fix is a systematic overhaul of the pre-generation pipeline. Query rewriting can be skipped for 70–80% of simple queries or run in parallel with raw-query retrieval, with a similarity-score threshold deciding which results to use. Vector and BM25 retrieval can run concurrently with a soft timeout (e.g., 800ms) to cap worst-case latency, though this doubles downstream load and requires semaphore-based concurrency limits. Rerank can be skipped entirely when the top retrieval score already exceeds a calibrated threshold, saving another 200–500ms.
On the user-facing side, Server-Sent Events push progress through each stage (rewriting, retrieving, reranking) so the wait feels shorter even when it isn't. Three production traps trip up most SSE implementations: Nginx response buffering silently breaks real-time delivery, missing `Last-Event-ID` handling causes duplicate tokens on reconnect, and round-robin load balancing routes reconnects to instances that have no session state. For inline citations during streaming, LLMs routinely hallucinate source numbers or produce inconsistent formatting, requiring a post-processing regex pipeline that validates, normalizes, and falls back to appended citations when the output is too messy.
The gap between 'we do streaming' and actually delivering sub-second first tokens is almost entirely in the retrieval pipeline, not the LLM generation call — yet most RAG tutorials stop at the generation flag.
Parallel retrieval with a soft timeout is a cheap latency win, but it quietly doubles peak load on vector stores and Elasticsearch; teams that skip concurrency limits often find parallel execution slower than serial under real traffic.
Every threshold in this optimization stack — the similarity delta for choosing rewritten vs. raw results, the score for skipping Rerank — is model-specific and dataset-specific. Borrowing numbers from a blog post guarantees misconfiguration.
SSE's production failures are almost never in the application code; they're in Nginx config, missing `Last-Event-ID` handling, and load-balancer session routing — infrastructure details that local testing completely hides.
LLM inline citation during streaming is a genuinely hard problem because it asks a non-deterministic system to produce machine-parseable output in a single pass; the post-processing regex pipeline is an admission that the model can't be trusted here, not a temporary workaround.