Inicio Linux & Systems Cybersecurity Cloud & DevOps Networks & Infrastructure SIEM & Monitoring DFIR & Threat Intel Development & Other Todas las categorias Herramientas

Como montar un RAG desde cero: guia practica para Ops

Como montar un RAG desde cero: guia practica para Ops

Tabla de contenidos

Que es un RAG

RAG (Retrieval-Augmented Generation) es un patron de arquitectura que combina dos cosas:

  1. Retrieval (Recuperacion): Buscar informacion relevante en tus documentos usando busqueda semantica (vectores).
  2. Generation (Generacion): Enviar esa informacion como contexto a un LLM para que genere una respuesta fundamentada en datos reales.

¿Por que importa? Porque un LLM por si solo "alucina" (inventa datos). Con RAG, el modelo solo responde con informacion que realmente existe en tus documentos. Es la diferencia entre preguntarle a alguien que no sabe nada de tu infra vs. darle tus runbooks y que responda basandose en ellos.

Caso de uso: equipos de Ops

Imagina poder cargar toda tu documentacion interna (runbooks, configuraciones, playbooks, postmortems) y preguntar en lenguaje natural:

  • "¿Como reinicio el cluster de Kafka en produccion?"
  • "¿Que reglas de firewall tenemos para el segmento DMZ?"
  • "¿Cual fue el workaround del incidente INC-4523?"

Eso es exactamente lo que vamos a construir.

Que vamos a montar

  • ChromaDB como base de datos vectorial (ligero, persistente, en Docker)
  • Cualquier LLM compatible con formato OpenAI como motor de generacion
  • Ingesta de ficheros .md, .yaml, .conf, .sh de tus servidores
  • CLI simple: python rag.py -q "tu pregunta"

Para esta prueba de concepto usaremos las APIs gratuitas de Grok (xAI) y NVIDIA NIM, que no requieren pago ni desplegar infraestructura propia. El codigo esta preparado para que lo conectes a cualquier LLM (Azure OpenAI, AWS Bedrock, un modelo self-hosted, etc.) simplemente cambiando una variable de entorno.

Para entornos enterprise: Al final del post incluyo recomendaciones sobre que opciones existen para desplegar un LLM privado con las garantias de seguridad y compliance que necesitas.


Requisitos previos

ComponenteMinimoRecomendado
RAM4 GB8 GB+
Disco5 GB libres10 GB+
Docker24.x+27.x+
Docker Composev2.xv2.x
Python3.11+3.13+

Ademas necesitaras una API key de al menos uno de estos proveedores:

Ambos ofrecen tier gratuito suficiente para seguir este tutorial.


Arquitectura

TERRAFORM
┌─────────────────────────────────────────────────────┐
│                    Usuario                            │
│         python rag.py --provider nvidia              │
└──────────────────────┬──────────────────────────────┘
                       │
                       ▼
┌──────────────────────────────────────────────────────┐
│                  RAG Engine (Python)                   │
│                                                       │
│  1. Recibe pregunta                                   │
│  2. Genera embedding → consulta ChromaDB              │
│  3. Recupera documentos relevantes (top 5)            │
│  4. Construye prompt con contexto                     │
│  5. Envia al LLM via API                             │
│  6. Devuelve respuesta                                │
└───────┬──────────────────┬───────────────────────────┘
        │                  │
        ▼                  ▼
┌──────────────┐   ┌──────────────────────────┐
│   ChromaDB   │   │      LLM Provider        │
│  (Vectores)  │   │                          │
│  Docker      │   │ Grok API (xAI)           │
│              │   │ NVIDIA NIM               │
│              │   │ Azure OpenAI / Bedrock   │
└──────────────┘   └──────────────────────────┘

La clave de esta arquitectura es la separacion entre la logica de recuperacion (vectores) y la logica de generacion (LLM). Cambiar de proveedor LLM es solo cambiar una variable de entorno.


Paso 1: Estructura del proyecto

BASH
mkdir -p rag-ops/{data,chroma_data}
cd rag-ops

Estructura final:

CODE
rag-ops/
├── docker-compose.yaml
├── requirements.txt
├── rag.py
├── ingest.py
├── config.py
├── .env
├── .gitignore
└── data/
    └── (tus ficheros .md, .yaml, .conf, .sh)

Crea el .gitignore desde el principio:

BASH
echo -e ".env\n.venv/\n__pycache__/\nchroma_data/" > .gitignore

Paso 2: Docker Compose - ChromaDB

Solo necesitamos ChromaDB en Docker. El LLM se consume via API externa.

Crea docker-compose.yaml:

YAML
services:
  chromadb:
    image: chromadb/chroma:latest
    container_name: rag-chromadb
    ports:
      - "8000:8000"
    volumes:
      - ./chroma_data:/chroma/chroma
    environment:
      - ANONYMIZED_TELEMETRY=FALSE
    restart: unless-stopped

Nota: Si tienes el puerto 8000 ocupado (por ejemplo con otro servicio), cambia el mapeo a "8001:8000" y ajusta CHROMA_PORT en el .env a 8001. El puerto interno del contenedor siempre es 8000.

Levanta el servicio:

BASH
docker compose up -d

Verifica que esta corriendo:

BASH
curl http://localhost:8000/api/v2/heartbeat
# Respuesta esperada: {"nanosecond heartbeat":...}

Paso 3: Obtener API Keys (para nuestra PoC)

Para esta prueba de concepto usaremos dos proveedores gratuitos. En un entorno real, sustituirias estas APIs por la de tu LLM corporativo (ver seccion final).

Grok (xAI)

  1. Ve a console.x.ai
  2. Crea una cuenta o inicia sesion
  3. En el dashboard, genera una API key (empieza por xai-)
  4. El tier gratuito incluye suficientes tokens para pruebas

La API de Grok es compatible con el formato OpenAI, por lo que usaremos el mismo SDK con diferente base_url.

NVIDIA NIM

  1. Ve a build.nvidia.com
  2. Crea una cuenta NVIDIA
  3. Selecciona un modelo con "Free Endpoint" (recomendado: z-ai/glm4.7 o mistralai/mistral-nemotron)
  4. Haz clic en "Get API Key" (empieza por nvapi-)

Importante: Algunos modelos de NVIDIA NIM tienen restriccion geografica. Si ves "This NIM is unavailable in your location", prueba otro modelo que tenga "Free Endpoint".

NVIDIA NIM tambien usa formato OpenAI-compatible.


Paso 4: Dependencias Python

Crea requirements.txt:

TEXT
langchain>=0.3.25
langchain-huggingface>=1.0.0
langchain-openai>=0.3.12
langchain-text-splitters>=0.3.0
chromadb>=1.0.0
sentence-transformers>=4.0.0
python-dotenv>=1.0.0
rich>=14.0.0

Instala en un virtualenv:

BASH
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

Importante: Siempre activa el virtualenv (source .venv/bin/activate) antes de ejecutar los scripts. Si ves ModuleNotFoundError, es que no tienes el venv activo.


Paso 5: Configuracion

Crea el fichero .env:

BASH
# Proveedor LLM: "grok" o "nvidia"
LLM_PROVIDER=nvidia

# Grok (xAI) - Compatible con formato OpenAI
GROK_API_KEY=xai-tu-clave-aqui
GROK_BASE_URL=https://api.x.ai/v1
GROK_MODEL=grok-3-mini

# NVIDIA NIM - Compatible con formato OpenAI
NVIDIA_API_KEY=nvapi-tu-clave-aqui
NVIDIA_BASE_URL=https://integrate.api.nvidia.com/v1
NVIDIA_MODEL=z-ai/glm4.7

# ChromaDB
CHROMA_HOST=localhost
CHROMA_PORT=8000
CHROMA_COLLECTION=ops-docs

# Embeddings (se ejecutan localmente, no requieren API)
EMBEDDING_MODEL=all-MiniLM-L6-v2

Seguridad: Nunca subas .env a git. Ya lo tenemos en .gitignore.

Crea config.py:

PYTHON
import os
from dotenv import load_dotenv

load_dotenv()


class Config:
    # LLM Provider
    LLM_PROVIDER = os.getenv("LLM_PROVIDER", "grok")

    # Grok (xAI)
    GROK_API_KEY = os.getenv("GROK_API_KEY", "")
    GROK_BASE_URL = os.getenv("GROK_BASE_URL", "https://api.x.ai/v1")
    GROK_MODEL = os.getenv("GROK_MODEL", "grok-3-mini")

    # NVIDIA NIM
    NVIDIA_API_KEY = os.getenv("NVIDIA_API_KEY", "")
    NVIDIA_BASE_URL = os.getenv("NVIDIA_BASE_URL", "https://integrate.api.nvidia.com/v1")
    NVIDIA_MODEL = os.getenv("NVIDIA_MODEL", "z-ai/glm4.7")

    # ChromaDB
    CHROMA_HOST = os.getenv("CHROMA_HOST", "localhost")
    CHROMA_PORT = int(os.getenv("CHROMA_PORT", "8000"))
    CHROMA_COLLECTION = os.getenv("CHROMA_COLLECTION", "ops-docs")

    # Embeddings
    EMBEDDING_MODEL = os.getenv("EMBEDDING_MODEL", "all-MiniLM-L6-v2")

Paso 6: Script de ingesta

Este script lee tus documentos, los divide en chunks, genera embeddings y los almacena en ChromaDB.

Crea ingest.py:

PYTHON
#!/usr/bin/env python3
"""Ingesta de documentos en ChromaDB para el RAG."""

import sys
from pathlib import Path

import chromadb
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_huggingface import HuggingFaceEmbeddings
from rich.console import Console
from rich.progress import track

from config import Config

console = Console()


def load_documents(data_dir: str) -> list[dict]:
    """Carga todos los ficheros soportados del directorio."""
    extensions = {".md", ".yaml", ".yml", ".conf", ".sh", ".txt", ".json", ".toml"}
    documents = []

    data_path = Path(data_dir)
    if not data_path.exists():
        console.print(f"[red]Error: directorio '{data_dir}' no existe[/red]")
        sys.exit(1)

    for filepath in data_path.rglob("*"):
        if filepath.suffix.lower() in extensions and filepath.is_file():
            try:
                content = filepath.read_text(encoding="utf-8")
                documents.append({
                    "content": content,
                    "metadata": {
                        "source": str(filepath.relative_to(data_path)),
                        "extension": filepath.suffix,
                        "filename": filepath.name,
                    }
                })
            except Exception as e:
                console.print(f"[yellow]Aviso: no se pudo leer {filepath}: {e}[/yellow]")

    return documents


def chunk_documents(documents: list[dict]) -> list[dict]:
    """Divide documentos en chunks manejables."""
    splitter = RecursiveCharacterTextSplitter(
        chunk_size=1000,
        chunk_overlap=200,
        separators=["\n## ", "\n### ", "\n\n", "\n", " "],
    )

    chunks = []
    for doc in documents:
        splits = splitter.split_text(doc["content"])
        for i, split in enumerate(splits):
            chunks.append({
                "content": split,
                "metadata": {
                    **doc["metadata"],
                    "chunk_index": i,
                }
            })

    return chunks


def ingest(data_dir: str = "data"):
    """Pipeline principal de ingesta."""
    console.print("[bold blue]RAG Ops - Ingesta de documentos[/bold blue]\n")

    # 1. Cargar documentos
    console.print(f"Cargando documentos de [green]{data_dir}/[/green]...")
    documents = load_documents(data_dir)
    console.print(f"  Encontrados: [bold]{len(documents)}[/bold] ficheros\n")

    if not documents:
        console.print("[red]No se encontraron documentos para ingestar.[/red]")
        sys.exit(1)

    # 2. Dividir en chunks
    console.print("Dividiendo en chunks...")
    chunks = chunk_documents(documents)
    console.print(f"  Total chunks: [bold]{len(chunks)}[/bold]\n")

    # 3. Generar embeddings
    console.print(f"Generando embeddings con [green]{Config.EMBEDDING_MODEL}[/green]...")
    console.print("  (primera vez descarga el modelo ~80MB, luego es instantaneo)\n")
    embeddings = HuggingFaceEmbeddings(model_name=Config.EMBEDDING_MODEL)

    # 4. Almacenar en ChromaDB
    console.print(f"Conectando a ChromaDB en {Config.CHROMA_HOST}:{Config.CHROMA_PORT}...")
    client = chromadb.HttpClient(host=Config.CHROMA_HOST, port=Config.CHROMA_PORT)

    # Borrar coleccion existente si hay (re-ingesta limpia)
    try:
        client.delete_collection(Config.CHROMA_COLLECTION)
        console.print("  Coleccion anterior eliminada.")
    except Exception:
        pass

    collection = client.create_collection(
        name=Config.CHROMA_COLLECTION,
        metadata={"hnsw:space": "cosine"},
    )

    # 5. Insertar chunks
    console.print("\nInsertando chunks en ChromaDB...")
    batch_size = 100
    for i in track(range(0, len(chunks), batch_size), description="Ingesta"):
        batch = chunks[i:i + batch_size]
        texts = [c["content"] for c in batch]
        metadatas = [c["metadata"] for c in batch]
        ids = [f"chunk_{i + j}" for j in range(len(batch))]

        embs = embeddings.embed_documents(texts)

        collection.add(
            documents=texts,
            embeddings=embs,
            metadatas=metadatas,
            ids=ids,
        )

    console.print(f"\n[bold green]Ingesta completada![/bold green]")
    console.print(f"  Documentos: {len(documents)}")
    console.print(f"  Chunks almacenados: {len(chunks)}")
    console.print(f"  Coleccion: {Config.CHROMA_COLLECTION}")


if __name__ == "__main__":
    data_dir = sys.argv[1] if len(sys.argv) > 1 else "data"
    ingest(data_dir)

Paso 7: El motor RAG

Crea rag.py:

PYTHON
#!/usr/bin/env python3
"""RAG Ops - Asistente de infraestructura con soporte multi-proveedor LLM."""

import argparse
import sys

import chromadb
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from rich.console import Console
from rich.markdown import Markdown
from rich.panel import Panel

from config import Config

console = Console()

SYSTEM_PROMPT = """Eres un asistente tecnico de infraestructura y operaciones.
Responde usando la informacion del contexto proporcionado.
Si no encuentras la respuesta en el contexto, di "No tengo informacion sobre eso en los documentos cargados."
Da respuestas detalladas y completas. Incluye ejemplos de configuracion, comandos y nombres de ficheros cuando sea relevante.
Estructura la respuesta con secciones si es necesario.
Responde en el mismo idioma en que te preguntan.

CONTEXTO:
{context}
"""


def get_llm(provider: str):
    """Factory que devuelve el LLM segun el proveedor.

    Tanto Grok como NVIDIA NIM son compatibles con el formato OpenAI,
    por lo que usamos ChatOpenAI con diferente base_url y api_key.
    Para anadir cualquier otro proveedor OpenAI-compatible, solo necesitas
    una nueva entrada con su base_url, api_key y model.
    """
    if provider == "grok":
        if not Config.GROK_API_KEY:
            console.print("[red]Error: GROK_API_KEY no configurada en .env[/red]")
            sys.exit(1)
        console.print(f"  LLM: Grok ({Config.GROK_MODEL})")
        return ChatOpenAI(
            api_key=Config.GROK_API_KEY,
            base_url=Config.GROK_BASE_URL,
            model=Config.GROK_MODEL,
            temperature=0.1,
            max_tokens=4096,
        )
    elif provider == "nvidia":
        if not Config.NVIDIA_API_KEY:
            console.print("[red]Error: NVIDIA_API_KEY no configurada en .env[/red]")
            sys.exit(1)
        console.print(f"  LLM: NVIDIA NIM ({Config.NVIDIA_MODEL})")
        return ChatOpenAI(
            api_key=Config.NVIDIA_API_KEY,
            base_url=Config.NVIDIA_BASE_URL,
            model=Config.NVIDIA_MODEL,
            temperature=0.1,
            max_tokens=4096,
            extra_body={
                "chat_template_kwargs": {
                    "enable_thinking": False,
                }
            },
        )
    else:
        console.print(f"[red]Proveedor '{provider}' no soportado. Usa 'grok' o 'nvidia'.[/red]")
        sys.exit(1)


def search_context(query: str, n_results: int = 5) -> str:
    """Busca documentos relevantes en ChromaDB."""
    embeddings = HuggingFaceEmbeddings(model_name=Config.EMBEDDING_MODEL)
    client = chromadb.HttpClient(host=Config.CHROMA_HOST, port=Config.CHROMA_PORT)

    collection = client.get_collection(Config.CHROMA_COLLECTION)
    query_embedding = embeddings.embed_query(query)

    results = collection.query(
        query_embeddings=[query_embedding],
        n_results=n_results,
    )

    # Construir contexto con fuentes
    context_parts = []
    for doc, metadata in zip(results["documents"][0], results["metadatas"][0]):
        source = metadata.get("source", "desconocido")
        context_parts.append(f"[Fuente: {source}]\n{doc}")

    return "\n\n---\n\n".join(context_parts)


def ask(question: str, provider: str):
    """Pipeline principal: buscar contexto -> generar respuesta."""
    # 1. Buscar contexto relevante
    console.print("Buscando en documentos...")
    context = search_context(question)

    if not context:
        console.print("[yellow]No se encontraron documentos relevantes.[/yellow]")
        return

    # 2. Construir prompt
    prompt = ChatPromptTemplate.from_messages([
        ("system", SYSTEM_PROMPT),
        ("human", "{question}"),
    ])

    # 3. Obtener LLM
    llm = get_llm(provider)

    # 4. Ejecutar chain
    chain = prompt | llm | StrOutputParser()

    console.print("Generando respuesta...\n")
    response = chain.invoke({
        "context": context,
        "question": question,
    })

    # 5. Mostrar resultado
    console.print(Panel(Markdown(response), title="Respuesta", border_style="green"))


def interactive_mode(provider: str):
    """Modo interactivo: pregunta tras pregunta."""
    console.print(Panel(
        f"[bold]RAG Ops - Modo interactivo[/bold]\n"
        f"Proveedor: [green]{provider}[/green]\n"
        f"Escribe 'salir' o 'exit' para terminar.",
        border_style="blue",
    ))

    while True:
        try:
            question = console.input("\n[bold blue]Pregunta>[/bold blue] ").strip()
            if question.lower() in ("salir", "exit", "quit", "q"):
                console.print("[dim]Hasta luego.[/dim]")
                break
            if not question:
                continue
            ask(question, provider)
        except KeyboardInterrupt:
            console.print("\n[dim]Interrumpido.[/dim]")
            break


def main():
    parser = argparse.ArgumentParser(description="RAG Ops - Asistente de infraestructura")
    parser.add_argument(
        "--provider", "-p",
        choices=["grok", "nvidia"],
        default=Config.LLM_PROVIDER,
        help="Proveedor LLM (default: valor de .env)",
    )
    parser.add_argument(
        "--question", "-q",
        type=str,
        help="Pregunta directa (sin modo interactivo)",
    )
    args = parser.parse_args()

    if args.question:
        ask(args.question, args.provider)
    else:
        interactive_mode(args.provider)


if __name__ == "__main__":
    main()

Paso 8: Probar el sistema

8.1 Anadir documentos

Copia ficheros de configuracion o documentacion a data/. Cuanta mas documentacion metas, mejores seran las respuestas:

BASH
# Ejemplos: configs, scripts, manuales, runbooks
cp /etc/nginx/nginx.conf data/
cp /etc/nginx/sites-available/* data/
cp /path/to/runbooks/*.md data/
cp /path/to/ansible/playbooks/*.yaml data/

Importante: El RAG solo es tan bueno como la documentacion que le metas. Con un solo fichero de configuracion por defecto, las respuestas seran pobres. Mete documentacion real y detallada para obtener respuestas utiles.

8.2 Ejecutar ingesta

BASH
source .venv/bin/activate
python ingest.py data/

Salida esperada:

CODE
RAG Ops - Ingesta de documentos

Cargando documentos de data/...
  Encontrados: 45 ficheros

Dividiendo en chunks...
  Total chunks: 892

Generando embeddings con all-MiniLM-L6-v2...
  (primera vez descarga el modelo ~80MB, luego es instantaneo)

Conectando a ChromaDB en localhost:8000...

Insertando chunks en ChromaDB...
Ingesta ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100%

Ingesta completada!
  Documentos: 45
  Chunks almacenados: 892
  Coleccion: ops-docs

La primera ejecucion descarga el modelo de embeddings (~80MB). Las siguientes son instantaneas porque se cachea en ~/.cache/huggingface/.

8.3 Consultar

BASH
# Pregunta directa
python rag.py --provider nvidia -q "Que reglas protegen contra SQL injection"

# Modo interactivo
python rag.py --provider nvidia

8.4 Cambiar de proveedor

BASH
# Con Grok
python rag.py --provider grok -q "Como configuro SSL en nginx"

# Con NVIDIA NIM
python rag.py --provider nvidia -q "Como configuro SSL en nginx"

Paso 9: Anadir un nuevo proveedor LLM

La arquitectura esta preparada para anadir cualquier proveedor compatible con el formato OpenAI. El patron es siempre el mismo: ChatOpenAI + base_url + api_key + model.

Ejemplo: Azure OpenAI

  1. Anade las variables al .env:
BASH
AZURE_OPENAI_API_KEY=tu-clave
AZURE_OPENAI_ENDPOINT=https://tu-recurso.openai.azure.com
AZURE_OPENAI_MODEL=gpt-4o
AZURE_API_VERSION=2024-10-21
  1. Anade la configuracion en config.py:
PYTHON
    # Azure OpenAI
    AZURE_OPENAI_API_KEY = os.getenv("AZURE_OPENAI_API_KEY", "")
    AZURE_OPENAI_ENDPOINT = os.getenv("AZURE_OPENAI_ENDPOINT", "")
    AZURE_OPENAI_MODEL = os.getenv("AZURE_OPENAI_MODEL", "gpt-4o")
    AZURE_API_VERSION = os.getenv("AZURE_API_VERSION", "2024-10-21")
  1. Anade el caso en get_llm() de rag.py:
PYTHON
elif provider == "azure":
    from langchain_openai import AzureChatOpenAI
    return AzureChatOpenAI(
        api_key=Config.AZURE_OPENAI_API_KEY,
        azure_endpoint=Config.AZURE_OPENAI_ENDPOINT,
        azure_deployment=Config.AZURE_OPENAI_MODEL,
        api_version=Config.AZURE_API_VERSION,
        temperature=0.1,
        max_tokens=4096,
    )
  1. Anade "azure" a choices en el argparser.

El mismo patron aplica para AWS Bedrock, GCP Vertex AI, o cualquier LLM self-hosted que exponga una API OpenAI-compatible (vLLM, Ollama, LiteLLM, etc.).


Paso 10: Recomendaciones para entornos enterprise

En un entorno empresarial, no deberias usar APIs publicas con datos internos sensibles. Las opciones recomendadas:

Opcion A: LLM gestionado en cloud privado

ProveedorServicioVentaja
AzureAzure OpenAI ServiceDatos no salen de tu tenant, SLA 99.9%, compliance SOC2/ISO
AzureAzure AI FoundryCatalogo multi-modelo (OpenAI, Llama, Mistral), MLOps integrado
AWSAmazon BedrockMulti-modelo (Claude, Llama, Mistral), VPC endpoints
GCPVertex AIGemini, modelos open-source, integracion con GKE

Estos servicios garantizan que tus datos no se usan para entrenar modelos y ofrecen aislamiento de red (Private Endpoints / VPC).

Opcion B: LLM self-hosted en tu infraestructura

Para maxima privacidad, despliega el modelo en tu propio cluster:

  • vLLM en Kubernetes con GPUs (A100/H100) — expone API OpenAI-compatible
  • NVIDIA NIM containers en tu infraestructura on-premise
  • Ollama en un servidor dedicado con GPU para equipos pequenos

Opcion C: Hibrido

  • Embeddings: Siempre locales (no envian datos a ningun sitio)
  • LLM para datos sensibles: Azure OpenAI / Bedrock con Private Endpoint
  • LLM para datos publicos: API directa (Grok, NVIDIA NIM) por coste/velocidad

Seguridad minima recomendada

  1. Nunca envies credenciales, tokens o secrets al LLM — filtra antes de la ingesta
  2. Usa variables de entorno o un secret manager (Vault, AWS Secrets Manager)
  3. Audita los logs de que se envia a la API
  4. Implementa RBAC si multiples equipos usan el RAG
  5. Cifra los datos en reposo en ChromaDB (volume encryption)

Troubleshooting

ChromaDB no conecta

BASH
# Verificar que esta corriendo
docker logs rag-chromadb

# Probar conexion (ChromaDB v2+)
curl http://localhost:8000/api/v2/heartbeat

Si ves Connection reset by peer, verifica que el mapeo de puertos es correcto en docker-compose.yaml. El contenedor siempre escucha en 8000 internamente. Si mapeas a otro puerto externo (ej. 8001:8000), ajusta CHROMA_PORT en .env.

Error de autenticacion en Grok

CODE
openai.AuthenticationError: Incorrect API key provided

Verifica que la key en .env empieza por xai- y esta activa en console.x.ai.

Error de autenticacion o geolocalizacion en NVIDIA NIM

CODE
openai.AuthenticationError: ...
# o
This NIM is unavailable in your location
  • Verifica que la key empieza por nvapi-
  • Si hay bloqueo geografico, cambia a otro modelo con "Free Endpoint" en build.nvidia.com
  • Modelos recomendados sin restriccion: z-ai/glm4.7, mistralai/mistral-nemotron

Respuestas vacias o muy cortas

Si el LLM devuelve respuestas vacias, puede ser que el modelo use "thinking mode" que pone el contenido en un campo separado. La solucion es desactivar el thinking en extra_body:

PYTHON
extra_body={
    "chat_template_kwargs": {
        "enable_thinking": False,
    }
}

Si las respuestas son demasiado cortas, aumenta max_tokens en la configuracion del LLM (recomendado: 4096).

Respuestas pobres o genericas

El RAG solo es tan bueno como la documentacion que le metas. Si las respuestas son pobres:

  1. Mete mas documentacion relevante en data/ — no solo configs, tambien manuales, runbooks, postmortems
  2. Re-ejecuta la ingesta (python ingest.py data) despues de anadir documentos
  3. Verifica cuantos chunks tienes: pocos chunks = poco contexto = respuestas pobres

ModuleNotFoundError

TERRAFORM
ModuleNotFoundError: No module named 'langchain_huggingface'

Asegurate de tener el virtualenv activo:

BASH
source .venv/bin/activate
python rag.py ...

Proximos pasos

  • Interfaz web: Anadir Streamlit o Gradio como frontend visual
  • Ingesta automatica: Watcher que re-ingeste cuando cambian ficheros
  • Multi-coleccion: Separar documentos por proyecto o entorno
  • Evaluacion: Medir calidad de respuestas con RAGAS
  • Historial: Implementar memoria conversacional para preguntas encadenadas

Recursos

:wq!

Comentarios