← Volver al blog Técnicas IA

RAG con Ollama: LLM Local, Datos Privados, Cero API Externa

2 jul. 20267 min

RAG (Retrieval-Augmented Generation) permite que un LLM responda preguntas a partir de tus propios documentos. La mayoría de los tutoriales envían todo a OpenAI o Anthropic. Aquí está cómo hacer lo mismo completamente en local con Ollama.

Por Qué Local?

Tres razones prácticas:

  • RGPD: los datos sensibles nunca salen de tu infraestructura
  • Coste: cero llamadas API, cero facturación por token
  • Offline: funciona sin conexión a internet

La contrapartida: necesitas una máquina suficientemente potente (mínimo 8 GB VRAM para modelos 7B).

Stack

Ollama         → servidor LLM local (Llama 3.1, Mistral, Phi-3...)
nomic-embed    → modelo de embedding local (vía Ollama)
ChromaDB       → vector store en memoria o persistente
Python         → pegamento

Pipeline en 4 Pasos

1. Instalar Ollama y los Modelos

# Instalar Ollama (Linux/Mac)
curl -fsSL https://ollama.com/install.sh | sh

# Descargar el LLM y el modelo de embedding
ollama pull llama3.1:8b
ollama pull nomic-embed-text

2. Indexar Documentos

import chromadb
import ollama

def embed(text: str) -> list[float]:
    resp = ollama.embeddings(model="nomic-embed-text", prompt=text)
    return resp["embedding"]

client = chromadb.Client()
collection = client.create_collection("docs")

documents = [
    {"id": "doc1", "text": "La política RGPD de la empresa establece que..."},
    {"id": "doc2", "text": "Los datos de clientes se almacenan en..."},
]

for doc in documents:
    collection.add(
        ids=[doc["id"]],
        embeddings=[embed(doc["text"])],
        documents=[doc["text"]],
    )

3. Retrieval — Encontrar Pasajes Relevantes

def retrieve(query: str, n: int = 3) -> list[str]:
    results = collection.query(
        query_embeddings=[embed(query)],
        n_results=n,
    )
    return results["documents"][0]

4. Generación con Contexto

def rag(question: str) -> str:
    passages = retrieve(question)
    context = "\n\n".join(passages)

    response = ollama.chat(
        model="llama3.1:8b",
        messages=[{
            "role": "user",
            "content": f"""Responde la pregunta usando ÚNICAMENTE el contexto proporcionado.
Si la respuesta no está en el contexto, dilo claramente.

Contexto:
{context}

Pregunta: {question}"""
        }]
    )
    return response["message"]["content"]

Lo Que Cambia vs RAG en la Nube

Embedding local vs cloud: nomic-embed-text es notablemente menos preciso que text-embedding-3-large de OpenAI en corpus complejos. En documentos de dominio específico, la diferencia es real — prueba siempre con tus datos reales antes de elegir.

Latencia: en un MacBook M2 o una máquina Linux con GPU de gama media, espera 2-5 segundos por respuesta 7B. Aceptable para herramientas internas, no para tiempo real.

ChromaDB en producción: usa el modo persistente o reemplaza con pgvector (PostgreSQL) para volúmenes de datos mayores.

# ChromaDB persistente
client = chromadb.PersistentClient(path="/data/chroma")

Casos de Uso Ideales

  • Base de conocimiento RRHH interna (contratos, políticas)
  • Soporte al cliente sobre documentación propietaria
  • Asistente sobre datos médicos o jurídicos
  • Cualquier contexto donde los datos no puedan salir de la red interna
SC

Stéphanie Caumont

Product Owner de IA · Saber más