In high-throughput enterprise applications, standard Retrieval-Augmented Generation (RAG) implementations often fall short due to compounding latencies. When your architecture relies on naive chains—a sequential pass through vector search, prompt assembly, and standard LLM inference engines—Time-To-First-Token (TTFT) frequently breaches 1,500ms. For real-time applications such as copilots, live customer support agents, or high-frequency automated decision engines, this is unacceptable.
To achieve sub-100ms TTFT and sustain thousands of concurrent streams without running up an unmanageable AWS bill, you need to abandon legacy orchestrators and synchronous architectures. You must refactor your stack around zero-copy data passing, optimized memory management, concurrent pipeline stages, and dynamically scaled accelerator infrastructure.
This guide outlines the reference architecture for building an enterprise-grade, ultra-low-latency RAG pipeline on AWS using vLLM, Ray Serve, and Serverless Vector Databases.
Architectural Blueprint
The core philosophy of this architecture is concurrency and zero-copy parallelism. Instead of waiting for tasks to execute sequentially, execution is interleaved, vectorized, and streamed.
+---------------------------------------+
| Client Application |
+---------------------------------------+
| (HTTP/2 SSE or WebSockets)
v
+---------------------------------------+
| AWS Network Load Balancer |
+---------------------------------------+
|
v
+---------------------------------------+
| EKS Cluster (Ingress API) |
+---------------------------------------+
/ \
(Async Embedding) / \ (Prefetch Context)
v v
+---------------------------------+ +-----------------------------------+
| Local Embedding Service | | Serverless Vector DB |
| (TensorRT / ONNX Runtime) | | (Pinecone / Qdrant Serverless) |
+---------------------------------+ +-----------------------------------+
\ /
v v
+---------------------------------------+
| Ray Serve Router |
| (Ray Cluster on AWS) |
+---------------------------------------+
|
(IPC / Shared Memory / gRPC)
v
+---------------------------------------+
| vLLM Worker Instances |
| (PagedAttention + Continuous) |
| [AWS EC2 g5/p4d Instances] |
+---------------------------------------+
Request Lifecycle Flow
- Ingress Layer: AWS Network Load Balancer (NLB) terminates TLS and routes HTTP/2 streaming connections directly to an API gateway running in Amazon EKS.
- Asynchronous Vector Search: The gateway uses a lightweight, in-memory CPU embedding worker (e.g., ONNX Runtime initialized with
bge-micro-v2orall-MiniLM-L6-v2) to convert the query into a dense vector in <8ms. - Serverless Vector DB Query: The embedding vector is sent via gRPC to a Serverless Vector Database (e.g., Pinecone Serverless or Qdrant Cloud on AWS AWS Marketplace), returning top-$k$ document chunks over an warm connection pool in <25ms.
- Context Ingestion & Prompt Assembly: The Ray Serve head node receives metadata, formats the dynamic prompt, and dispatches the tensor state to the nearest vLLM worker using Ray’s zero-copy shared memory plasma store.
- vLLM Engine Processing: vLLM generates tokens using continuous batching and PagedAttention, firing the first token (TTFT) in <40ms.
- Streaming Back to Client: Tokens stream back via Server-Sent Events (SSE) through the NLB, delivering an end-to-end TTFT under the 100ms threshold.
Sub-100ms Subsystems Breakdown
1. The Embedding & Vector Retrieval Engine
To keep retrieval under 35ms total budget, eliminate external embedding API calls (like OpenAI embeddings) which introduce unpredictable multi-region network hops.
- Local On-Instance Embeddings: Run quantized embedding models (
FP16orINT8) directly in memory on your ingress nodes using ONNX Runtime or TensorRT. Keep these models co-located with the ingestion engine to avoid network calls entirely. - gRPC and Keep-Alive Connections: Use gRPC for connection pooling with your Serverless Vector DB. HTTP/1.1 TLS handshakes add 20–40ms per call; maintaining persistent HTTP/2 gRPC channels removes this tax.
- Metadata Filtering Optimization: Store pre-tokenized text or compressed context strings directly inside the vector database metadata payload. This eliminates the need to fetch raw document text from Amazon S3 or DynamoDB during the critical execution path.
2. vLLM: Eliminating Memory Bottlenecks with PagedAttention
Traditional LLM serving frameworks fail under scale because the Key-Value (KV) cache grows dynamically and requires contiguous GPU memory blocks. This causes severe memory fragmentation, limiting throughput and inflating latency.
vLLM solves this using PagedAttention:
- Paged KV Cache: Virtual memory management translates logical KV blocks into physical allocations, completely eliminating external fragmentation.
- Continuous Batching: Requests are batched at the iteration level rather than the sequence level. New requests enter the execution queue immediately on the next forward pass without waiting for previous requests to finish generation.
- Chunked Prefill: Long context windows (a common bottleneck in RAG) are split into chunks. vLLM computes prefill chunks alongside generation tokens from other requests in the same batch, preventing prefill operations from blocking ongoing streaming generations.
Standard Attn: [Req 1 Prefill................] [Req 1 Gen] [Req 1 Gen]
Continuous: [Req 1 Prefill Chunk 1] [Req 2 Gen] [Req 1 Prefill Chunk 2] [Req 1 Gen]
3. Ray Serve: Distributed Dynamic Orchestration
Ray Serve bridges the gap between client API routing and GPU worker nodes. It provides the low-overhead, asynchronous actor model needed to manage high-throughput vLLM deployments across multi-GPU nodes.
- Replicas & Actor Scaling: Ray creates long-lived Python worker processes on your GPU instances. This avoids process creation overhead and keeps the vLLM engine instance warm.
- Request Multiplexing: Ray Serve routes incoming streams to vLLM dynamic dynamic batching queues based on live replica workload telemetry, avoiding queue pileups on individual GPUs.
- Pipeline Parallelism: On larger models (e.g., Llama-3-70B), Ray orchestrates Tensor Parallelism ($TP$) across multiple GPUs on a single instance (e.g., 4x A10G on
g5.12xlarge) or Pipeline Parallelism ($PP$) across instances via AWS Elastic Fabric Adapter (EFA).
Implementation: High-Throughput Ray + vLLM Pipeline
Below is a production-ready implementation of a low-latency, streaming RAG deployment utilizing Ray Serve and vLLM's AsyncLLMEngine.
import asyncio
from typing import AsyncGenerator
import json
from ray import serve
from vllm.engine.arg_utils import AsyncEngineArgs
from vllm.engine.async_llm_engine import AsyncLLMEngine
from vllm.sampling_params import SamplingParams
from vllm.utils import random_uuid
@serve.deployment(
num_replicas=2,
ray_actor_options={"num_gpus": 1, "num_cpus": 4},
max_ongoing_requests=256
)
class LowLatencyRAGEngine:
def __init__(self, model_path: str):
# Configure vLLM engine for maximum KV-cache efficiency and chunked prefill
engine_args = AsyncEngineArgs(
model=model_path,
tensor_parallel_size=1,
gpu_memory_utilization=0.90,
max_num_batched_tokens=4096,
enable_chunked_prefill=True,
max_model_len=8192,
trust_remote_code=True,
dtype="float16"
)
self.engine = AsyncLLMEngine.from_engine_args(engine_args)
async def _retrieve_context(self, query: str) -> str:
"""
Simulated ultra-low latency serverless vector retrieval.
In production: Use gRPC client pool to Pinecone/Qdrant.
"""
# Execute retrieval asynchronously
await asyncio.sleep(0.018) # ~18ms lookup latency
return "AWS EFA provides up to 100 Gbps network bandwidth per OS instance for OS bypass scale."
@serve.batch(max_batch_size=16, batch_wait_timeout_s=0.005)
async def process_batch_retrieval(self, queries: list[str]) -> list[str]:
# High-density batch retrieval logic can be placed here if needed
pass
async def generate_stream(self, query: str) -> AsyncGenerator[str, None]:
# Step 1: Parallel retrieval phase
context_task = asyncio.create_task(self._retrieve_context(query))
# Step 2: Assemble prompt immediately when context returns
context = await context_task
prompt = (
f"<|system|>\nYou are an expert assistant. Context:\n{context}</s>\n"
f"<|user|>\n{query}</s>\n<|assistant|>\n"
)
# Step 3: Configure sampling for low-latency generation
sampling_params = SamplingParams(
temperature=0.1,
top_p=0.95,
max_tokens=256,
ignore_eos=False
)
request_id = f"rag-{random_uuid()}"
# Step 4: Stream tokens directly from vLLM AsyncLLMEngine
results_generator = self.engine.generate(
prompt,
sampling_params,
request_id
)
previous_text = ""
async for request_output in results_generator:
text_chunk = request_output.outputs[0].text
# Calculate stream diff
delta = text_chunk[len(previous_text):]
previous_text = text_chunk
# Form Server-Sent Event payload
payload = json.dumps({"token": delta, "finished": request_output.finished})
yield f"data: {payload}\n\n"
async def __call__(self, request) -> StreamingResponse:
# FastAPI / Starlette request wrapper for SSE stream
from starlette.responses import StreamingResponse
body = await request.json()
query = body.get("query", "")
return StreamingResponse(
self.generate_stream(query),
media_type="text/event-stream"
)
# Entry point for Ray application building
entrypoint = LowLatencyRAGEngine.bind(model_path="meta-llama/Meta-Llama-3-8B-Instruct")
AWS Infrastructure & Autoscaling Topology
Running high-performance models requires elastic infrastructure. Standard EC2 Auto Scaling Groups are too slow when provisioning GPU instances—pulling a 15GB model over standard networks can cause 10-minute cold starts.
EKS, KubeRay, and Karpenter Blueprint
We deploy our Ray cluster using the KubeRay Operator on Amazon EKS, paired with Karpenter for fast, dynamic NodePool provisioning using AWS instance types with fast local NVMe storage (g5.xlarge, g5.12xlarge, p4d.24xlarge).
apiVersion: karpenter.sh/v1beta1
kind: NodePool
metadata:
name: gpu-inference-nodepool
spec:
template:
spec:
requirements:
- key: karpenter.k8s.aws/instance-family
operator: In
values: ["g5"]
- key: karpenter.sh/capacity-type
operator: In
values: ["on-demand"]
- key: kubernetes.io/arch
operator: In
values: ["amd64"]
nodeClassRef:
name: gpu-node-class
limits:
resources:
gpu: "32"
disruption:
consolidationPolicy: WhenEmpty
consolidateAfter: 300s
---
apiVersion: karpenter.k8s.aws/v1beta1
kind: EC2NodeClass
metadata:
name: gpu-node-class
spec:
amiFamily: AL2
blockDeviceMappings:
- deviceName: /dev/xvda
ebs:
volumeSize: 200Gi
volumeType: gp3
iops: 10000
throughput: 500
subnetSelectorTerms:
- tags:
karpenter.sh/discovery: ecstaticloud-cluster
securityGroupSelectorTerms:
- tags:
karpenter.sh/discovery: ecstaticloud-cluster
Optimizing Provisioning Times
- Container Image Pre-warming: Build custom container images with pre-installed
vllm,ray,torch, and CUDA dependencies. Store these images in Amazon ECR with AWS Fargate/Container Read-Through Caching or use warm-node pools. - Model Cache on Instance Storage: Mount fast local instance store NVMe SSDs (
/dev/nvme0n1) for Hugging Face model weights. Avoid downloading weights directly over EBS at runtime. - AWS Elastic Fabric Adapter (EFA): Enable EFA network interfaces on
g5.12xlargeorp4d.24xlargenodes to allow low-latency, direct GPU-to-GPU communication across instances for large-scale multi-node tensor parallelism.
Latency Budget Breakdown (Target: <100ms TTFT)
Achieving sub-100ms TTFT requires setting strict performance boundaries for every step in the pipeline:
| Pipeline Stage | Strategy / Technology | Alloc. Target | Realized Latency | | :--- | :--- | :--- | :--- | | Ingress Overhead | AWS NLB + EKS Ingress Controller | < 5ms | 3ms | | Embedding Generation| Quantized local ONNX on CPU / GPU | < 12ms | 8ms | | Vector DB Search | Persistent gRPC connection to Serverless DB | < 30ms | 22ms | | Payload Assembly | Ray zero-copy plasma store & prompt format | < 5ms | 2ms | | vLLM Prefill & TTFT | Chunked prefill + PagedAttention (A10G/A100)| < 45ms | 38ms | | Total Pipeline TTFT| End-to-End Real-Time RAG | < 100ms | ~73ms |
Production Pitfalls & Architect's Checklist
Before pushing your streaming RAG architecture to production, run through this architectural verification checklist:
GPU Memory Tuning
- [ ] Avoid CUDA Out-Of-Memory (OOM) Errors: Set
gpu_memory_utilizationconservatively (e.g.,0.85to0.90) to reserve unallocated VRAM for dynamic allocation during execution spikes. - [ ] Monitor KV Cache Usage: Set up Prometheus metrics scraping vLLM's
vllm:num_requests_waitingandvllm:gpu_cache_usage_perc. If KV cache usage consistently exceeds 85%, scale out your Ray actors.
Network and I/O
- [ ] Disable Connection Buffering: Ensure all reverse proxies (e.g., NGINX, AWS ALB) have HTTP response buffering disabled (
proxy_buffering off;). Buffering breaks real-time token streaming and blocks SSE feeds. - [ ] Set Keep-Alive Parameters: Keep gRPC and HTTP/2 connections warm between Ray Serve and your Serverless Vector DB to avoid TLS renegotiation overhead on new requests.
High Availability
- [ ] Configure Health Check Endpoints: Implement specialized liveness and readiness probes in Ray Serve that verify GPU availability, model weight readiness, and remote Vector DB connectivity.
- [ ] Graceful Degradation Strategy: If your vector search latency spikes beyond a set timeout (e.g., >60ms), fall back to an internal cache or serve a response generated without context to preserve system responsiveness.
Summary
Sub-100ms RAG is no longer theoretical—it is an achievable production standard. By replacing heavy, synchronous orchestration layers with Ray Serve, bypassing traditional memory layout limits with vLLM PagedAttention, and connecting to high-speed Serverless Vector DBs over persistent channels on AWS, you can deliver fast, highly scalable enterprise AI applications.
Have questions about tuning vLLM parameters, configuring Karpenter NodePools, or optimizing Ray clusters on AWS? Drop a comment below or reach out to the Ecstaticloud architecture team.