# Qdrant

> **Base de datos vectorial de alto rendimiento para búsqueda semántica y aplicaciones RAG: indexación acelerada por GPU**

Qdrant es una base de datos vectorial de código abierto y lista para producción escrita en Rust. Ofrece búsqueda aproximada de vecinos más cercanos (ANN) rápida en miles de millones de vectores con filtrado avanzado, indexación de payload y soporte multivector. Es la columna vertebral de muchos pipelines RAG (Generación Aumentada por Recuperación) y aplicaciones de búsqueda semántica en producción.

**GitHub:** [qdrant/qdrant](https://github.com/qdrant/qdrant) — 22K+ ⭐

***

## ¿Por qué Qdrant?

| Característica                 | Qdrant     | Pinecone          | Weaviate | Chroma   |
| ------------------------------ | ---------- | ----------------- | -------- | -------- |
| Código abierto                 | ✅          | ❌                 | ✅        | ✅        |
| Rendimiento de Rust            | ✅          | —                 | ❌ Go     | ❌ Python |
| Filtrado en tiempo de consulta | ✅ Avanzado | ✅ Básico          | ✅        | ✅ Básico |
| Multivector                    | ✅          | ❌                 | ✅        | ❌        |
| HNSW en disco                  | ✅          | ✅                 | ✅        | ❌        |
| Indexación de payload          | ✅          | Limitado          | ✅        | Limitado |
| gRPC + REST                    | ✅ Ambos    | ✅ REST            | ✅        | REST     |
| Autoalojado                    | ✅          | ❌ Solo en la nube | ✅        | ✅        |

{% hint style="success" %}
**Qdrant está escrito en Rust** — ofreciendo rendimiento a nivel de C con seguridad de memoria. Las pruebas de benchmark muestran que Qdrant es consistentemente **1.5–3x más rápido** que alternativas basadas en Python como Chroma en escenarios de alta carga.
{% endhint %}

***

## Casos de uso clave

* **RAG (Generación Aumentada por Recuperación)** — encontrar contexto relevante para prompts de LLM
* **Búsqueda semántica** — buscar por significado, no solo por palabras clave
* **Sistemas de recomendación** — encontrar ítems similares por similitud de embeddings
* **Detección de duplicados** — identificar contenido casi duplicado
* **Detección de anomalías** — encontrar vectores alejados de los centros de los clústeres
* **Búsqueda de similitud de imagen/audio** — recuperación multimodal

***

## Prerrequisitos

* Cuenta en Clore.ai con alquiler de GPU
* Familiaridad básica con APIs REST o Python
* Su modelo de embeddings preferido (OpenAI, SentenceTransformers, etc.)

***

## Paso 1 — Alquilar un servidor en Clore.ai

Qdrant está principalmente limitado por CPU/RAM para el servicio, pero se beneficia de GPU cuando:

* Se generan embeddings junto con el servicio (modelo de embeddings en el mismo servidor)
* Operaciones de indexación por lotes a gran escala

1. Ve a [clore.ai](https://clore.ai) → **Marketplace**
2. Para **combinación de embeddings + servicio:** RTX 3090/4090 con 32GB+ de RAM
3. Para **solo servicio:** Servidor optimizado para CPU con almacenamiento NVMe rápido

{% hint style="info" %}
**Planificación de memoria:**

* Cada vector float32 con 1536 dimensiones = 6KB
* 1 millón de vectores = \~6GB de RAM
* 10 millones de vectores = \~60GB de RAM
* Habilite almacenamiento en disco para colecciones muy grandes
  {% endhint %}

***

## Paso 2 — Desplegar el contenedor Qdrant

**Imagen Docker:**

```
qdrant/qdrant:latest
```

**Puertos:**

```
22
6333
6334
```

* **Puerto 6333:** API REST (HTTP)
* **Puerto 6334:** API gRPC (mayor rendimiento para operaciones en lote)

**Variables de entorno:**

```
QDRANT__SERVICE__HTTP_PORT=6333
QDRANT__SERVICE__GRPC_PORT=6334
QDRANT__LOG_LEVEL=INFO
QDRANT__STORAGE__STORAGE_PATH=/qdrant/storage
```

**Volumen/Almacenamiento persistente:** Montar `/qdrant/storage` para la persistencia de datos. Sin esto, los datos se pierden al reiniciar el contenedor.

***

## Paso 3 — Verificar que Qdrant está en ejecución

```bash
ssh root@<ip-del-servidor> -p <puerto-ssh>

# Comprobar que Qdrant está en ejecución
curl http://localhost:6333/

# Respuesta esperada:
# {"title":"qdrant - vector search engine","version":"..."}

# Comprobar estado de salud
curl http://localhost:6333/healthz

# Comprobar información del clúster
curl http://localhost:6333/cluster
```

***

## Paso 4 — Instalar el cliente de Python

```bash
# Instalar el cliente Qdrant para Python y herramientas de embeddings
pip install qdrant-client sentence-transformers openai numpy

# Verificar conexión
python3 << 'EOF'
from qdrant_client import QdrantClient

client = QdrantClient("localhost", port=6333)
print(f"Qdrant conectado: {client.get_collections()}")
EOF
```

***

## Paso 5 — Crear una colección

Una colección es un grupo nombrado de vectores con una dimensionalidad fija.

```python
from qdrant_client import QdrantClient
from qdrant_client.models import (
    Distance,
    VectorParams,
    HnswConfigDiff,
    OptimizersConfigDiff,
    QuantizationConfig,
    ScalarQuantizationConfig,
    ScalarType
)

client = QdrantClient("localhost", port=6333)

# Crear colección para OpenAI text-embedding-3-small (1536 dims)
client.create_collection(
    collection_name="documents",
    vectors_config=VectorParams(
        size=1536,           # Dimensión del vector (coincida con su modelo de embeddings)
        distance=Distance.COSINE,  # Opciones: COSINE, EUCLID, DOT
        on_disk=False        # Poner True para colecciones muy grandes
    ),
    hnsw_config=HnswConfigDiff(
        m=16,                # Conectividad del grafo HNSW (más alto = mejor recall, más RAM)
        ef_construct=100,    # Profundidad de búsqueda en tiempo de construcción (más alto = mejor calidad, indexación más lenta)
        full_scan_threshold=10000  # Usar fuerza bruta por debajo de este recuento
    ),
    optimizers_config=OptimizersConfigDiff(
        indexing_threshold=20000  # Iniciar la indexación HNSW después de esta cantidad de vectores
    ),
    quantization_config=QuantizationConfig(
        scalar=ScalarQuantizationConfig(
            type=ScalarType.INT8,  # Comprimir vectores a INT8 (reducción de memoria 4x)
            quantile=0.99,
            always_ram=True        # Mantener el índice cuantizado en RAM
        )
    )
)

print("¡Colección creada!")
print(client.get_collection("documents"))
```

### Colección para SentenceTransformers (384 dims)

```python
client.create_collection(
    collection_name="embeddings_384",
    vectors_config=VectorParams(
        size=384,              # tamaño de salida de all-MiniLM-L6-v2
        distance=Distance.COSINE
    )
)
```

***

## Paso 6 — Indexar documentos

### Con OpenAI Embeddings

```python
from qdrant_client import QdrantClient
from qdrant_client.models import PointStruct
from openai import OpenAI
import uuid

client = QdrantClient("localhost", port=6333)
openai_client = OpenAI(api_key="tu-clave-openai")

def get_embeddings(texts: list[str], batch_size: int = 100) -> list[list[float]]:
    """Generar embeddings por lotes."""
    all_embeddings = []
    for i in range(0, len(texts), batch_size):
        batch = texts[i:i + batch_size]
        response = openai_client.embeddings.create(
            model="text-embedding-3-small",
            input=batch
        )
        all_embeddings.extend([e.embedding for e in response.data])
    return all_embeddings

# Documentos de ejemplo
documents = [
    {
        "id": str(uuid.uuid4()),
        "text": "Qdrant es una base de datos vectorial construida en Rust para alto rendimiento.",
        "source": "documentation",
        "category": "database",
        "year": 2024
    },
    {
        "id": str(uuid.uuid4()),
        "text": "Los modelos de aprendizaje automático convierten el texto en representaciones vectoriales densas.",
        "source": "article",
        "category": "ml",
        "year": 2023
    },
    # Añadir más documentos...
]

# Generar embeddings
texts = [doc["text"] for doc in documents]
embeddings = get_embeddings(texts)

# Upsert en Qdrant
points = [
    PointStruct(
        id=str(uuid.uuid4()),
        vector=embedding,
        payload={
            "text": doc["text"],
            "source": doc["source"],
            "category": doc["category"],
            "year": doc["year"]
        }
    )
    for doc, embedding in zip(documents, embeddings)
]

client.upsert(
    collection_name="documents",
    points=points,
    wait=True  # Esperar a que la indexación se complete
)

print(f"¡Indexados {len(points)} documentos!")
```

### Con SentenceTransformers (Local, acelerado por GPU)

```python
from sentence_transformers import SentenceTransformer
from qdrant_client import QdrantClient
from qdrant_client.models import PointStruct
import torch
import uuid

# Cargar modelo de embeddings en GPU
model = SentenceTransformer("all-MiniLM-L6-v2", device="cuda")

client = QdrantClient("localhost", port=6333)

documents = [
    {"text": "¿Cómo configuro Qdrant en un servidor con GPU?", "tag": "setup"},
    {"text": "Las bases de datos vectoriales almacenan embeddings de alta dimensión para búsqueda por similitud.", "tag": "concept"},
    {"text": "El algoritmo HNSW proporciona búsqueda aproximada de vecinos más cercanos.", "tag": "algorithm"},
    # ... más documentos
]

# Codificación por lotes acelerada por GPU
texts = [doc["text"] for doc in documents]
embeddings = model.encode(
    texts,
    batch_size=256,       # Tamaño de lote grande para eficiencia en GPU
    show_progress_bar=True,
    normalize_embeddings=True  # Normalizar para similitud coseno
)

# Indexar en Qdrant
points = [
    PointStruct(
        id=str(uuid.uuid4()),
        vector=embedding.tolist(),
        payload=doc
    )
    for doc, embedding in zip(documents, embeddings)
]

# Upsert por lotes (más eficiente)
BATCH_SIZE = 1000
for i in range(0, len(points), BATCH_SIZE):
    batch = points[i:i + BATCH_SIZE]
    client.upsert(collection_name="embeddings_384", points=batch)
    print(f"Indexados {min(i + BATCH_SIZE, len(points))}/{len(points)}")
```

***

## Paso 7 — Buscar y consultar

### Búsqueda semántica básica

```python
from qdrant_client import QdrantClient
from sentence_transformers import SentenceTransformer

client = QdrantClient("localhost", port=6333)
model = SentenceTransformer("all-MiniLM-L6-v2", device="cuda")

def search(query: str, limit: int = 5, collection: str = "embeddings_384"):
    # Generar embedding de la consulta
    query_vector = model.encode(query, normalize_embeddings=True).tolist()
    
    # Buscar
    results = client.search(
        collection_name=collection,
        query_vector=query_vector,
        limit=limit,
        with_payload=True,
        with_vectors=False    # No devolver vectores (ahorra ancho de banda)
    )
    
    return results

# Probar búsqueda
results = search("rendimiento de bases de datos vectoriales")
for r in results:
    print(f"Score: {r.score:.4f} | {r.payload['text'][:100]}")
```

### Búsqueda filtrada (Metadatos + Vector)

```python
from qdrant_client.models import Filter, FieldCondition, MatchValue, Range

# Búsqueda con filtros de metadatos
results = client.search(
    collection_name="documents",
    query_vector=query_vector,
    query_filter=Filter(
        must=[
            FieldCondition(
                key="category",
                match=MatchValue(value="database")
            ),
            FieldCondition(
                key="year",
                range=Range(gte=2023)  # Año >= 2023
            )
        ]
    ),
    limit=10,
    with_payload=True
)
```

### Búsqueda por lotes/múltiples consultas

```python
from qdrant_client.models import SearchRequest

queries = [
    "cómo instalar una base de datos vectorial",
    "optimizaciÃ³n de inferencia en aprendizaje automático",
    "arquitectura de pipeline RAG"
]

query_vectors = model.encode(queries, normalize_embeddings=True)

# Búsqueda por lotes (una llamada API para todas las consultas)
results = client.search_batch(
    collection_name="embeddings_384",
    requests=[
        SearchRequest(
            vector=vec.tolist(),
            limit=5,
            with_payload=True
        )
        for vec in query_vectors
    ]
)

for query, res in zip(queries, results):
    print(f"\nConsulta: {query}")
    for r in res:
        print(f"  {r.score:.3f}: {r.payload['text'][:80]}")
```

***

## Paso 8 — Construir un pipeline RAG

```python
from qdrant_client import QdrantClient
from sentence_transformers import SentenceTransformer
from openai import OpenAI

# Inicializar clientes
qdrant = QdrantClient("localhost", port=6333)
embedder = SentenceTransformer("all-MiniLM-L6-v2", device="cuda")
llm = OpenAI(api_key="tu-clave-openai")

def rag_query(question: str, n_context: int = 5) -> str:
    # Paso 1: Embedding de la pregunta
    query_vector = embedder.encode(question, normalize_embeddings=True).tolist()
    
    # Paso 2: Recuperar contexto relevante de Qdrant
    search_results = qdrant.search(
        collection_name="documents",
        query_vector=query_vector,
        limit=n_context,
        with_payload=True
    )
    
    # Paso 3: Construir cadena de contexto
    context = "\n\n".join([
        f"[Fuente: {r.payload.get('source', 'desconocida')}]\n{r.payload['text']}"
        for r in search_results
        if r.score > 0.5  # Filtrar resultados de baja confianza
    ])
    
    # Paso 4: Generar respuesta con LLM
    response = llm.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {
                "role": "system",
                "content": "Responde preguntas basándote en el contexto proporcionado. Sé conciso y preciso."
            },
            {
                "role": "user",
                "content": f"Contexto:\n{context}\n\nPregunta: {question}"
            }
        ],
        temperature=0.1
    )
    
    return response.choices[0].message.content

# Probar pipeline RAG
answer = rag_query("¿Qué es Qdrant y cómo funciona?")
print(answer)
```

***

## Paso 9 — Monitorizar y gestionar colecciones

```python
# Estadísticas de la colección
info = client.get_collection("documents")
print(f"Recuento de vectores: {info.vectors_count:,}")
print(f"Recuento de puntos: {info.points_count:,}")
print(f"Vectores indexados: {info.indexed_vectors_count:,}")
print(f"Estado: {info.status}")
print(f"Uso de disco: {info.disk_data_size / 1024 / 1024:.1f} MB")

# Listar todas las colecciones
collections = client.get_collections()
for c in collections.collections:
    print(f" - {c.name}")

# Eliminar puntos por filtro
client.delete(
    collection_name="documents",
    points_selector=Filter(
        must=[FieldCondition(key="source", match=MatchValue(value="old_source"))]
    )
)

# Optimizar colección (forzar construcción del índice)
client.update_collection(
    collection_name="documents",
    optimizer_config=OptimizersConfigDiff(indexing_threshold=0)  # Forzar indexación inmediata
)
```

***

## Solución de problemas

### Conexión rechazada

```bash
# Comprobar que Qdrant está en ejecución
docker ps | grep qdrant
# O comprobar el proceso
ps aux | grep qdrant

# Comprobar que los puertos están abiertos
curl http://localhost:6333/
netstat -tlnp | grep 6333
```

### Rendimiento de búsqueda lento

```python
# Optimizar parámetros HNSW para mejor recall
client.update_collection(
    collection_name="documents",
    hnsw_config=HnswConfigDiff(ef=128)  # Aumentar ef en tiempo de búsqueda (por defecto 100)
)

# Usar cuantización INT8 para ajustar más vectores en RAM
```

### Alto uso de memoria

```python
# Habilitar almacenamiento en disco para colecciones grandes
client.create_collection(
    collection_name="large_collection",
    vectors_config=VectorParams(
        size=1536,
        distance=Distance.COSINE,
        on_disk=True  # Almacenar vectores en disco en lugar de RAM
    )
)
```

***

## Referencia rápida de la API REST

```bash
# Listar colecciones
curl http://localhost:6333/collections

# Crear colección
curl -X PUT http://localhost:6333/collections/my_collection \
    -H "Content-Type: application/json" \
    -d '{"vectors": {"size": 384, "distance": "Cosine"}}'

# Contar puntos
curl http://localhost:6333/collections/my_collection/points/count

# Buscar
curl -X POST http://localhost:6333/collections/my_collection/points/search \
    -H "Content-Type: application/json" \
    -d '{
        "vector": [0.1, 0.2, ...],
        "limit": 5,
        "with_payload": true
    }'

# Eliminar colección
curl -X DELETE http://localhost:6333/collections/my_collection
```

***

## Estimación de costos en Clore.ai

| Configuración    | Servidor           | Coste mensual | Capacidad      |
| ---------------- | ------------------ | ------------- | -------------- |
| RAG pequeño      | RTX 3090, 32GB RAM | \~$60–80      | \~5M vectores  |
| Búsqueda mediana | RTX 4090, 64GB RAM | \~$120–150    | \~15M vectores |
| Gran escala      | A100, 128GB RAM    | \~$250–350    | \~30M vectores |

***

## Recursos adicionales

* [Documentación de Qdrant](https://qdrant.tech/documentation/)
* [Qdrant en GitHub](https://github.com/qdrant/qdrant)
* [Cliente de Python de Qdrant](https://github.com/qdrant/qdrant-client)
* [Ejemplos de Qdrant](https://github.com/qdrant/examples)
* [Benchmarks de bases de datos vectoriales](https://qdrant.tech/benchmarks/)
* [Sentence Transformers](https://www.sbert.net/)

***

*Qdrant en Clore.ai te ofrece una base de datos vectorial autoalojada y de alto rendimiento sin los costes por consulta de Pinecone o Weaviate Cloud. Perfecto para pipelines RAG que procesan millones de documentos.*

***

## Recomendaciones de GPU en Clore.ai

| Caso de uso                      | GPU recomendada | Coste estimado en Clore.ai |
| -------------------------------- | --------------- | -------------------------- |
| Desarrollo/Pruebas               | RTX 3090 (24GB) | \~$0.12/gpu/hr             |
| Búsqueda vectorial en producción | RTX 3090 (24GB) | \~$0.12/gpu/hr             |
| Embeddings de alto rendimiento   | RTX 4090 (24GB) | \~$0.70/gpu/hr             |

> 💡 Todos los ejemplos en esta guía pueden desplegarse en [Clore.ai](https://clore.ai/marketplace) servidores GPU. Navega las GPUs disponibles y alquila por hora — sin compromisos, acceso root completo.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.clore.ai/guides/guides_v2-es/rag-y-bases-de-datos-vectoriales/qdrant.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
