Biblioteca · IA en tu propia computadora y servidor

Modelos de IA locales: Ollama, LM Studio e IA privada

Creador75 minActualizado: octubre de 2026
76 de 105 en la biblioteca

Módulo: 13. Práctica profesional | Tiempo: ~30 min de teoría + 45 min de práctica


Lo esencial

Todo lo que le dices a Claude se va a la nube de Anthropic. Para la mayoría de las tareas eso está bien. Pero ¿qué pasa si el cliente es un hospital, un despacho de abogados o un banco? ¿Qué pasa si hay que procesar 100 000 documentos sin presupuesto para la API? ¿Qué pasa si no hay internet?

Los modelos locales son una IA que funciona directamente en tu computadora. Los datos no van a ningún lado. La inferencia casi no cuesta nada (electricidad y desgaste del equipo). Funciona en un avión.

Sobre los nombres de los modelos. El catálogo de modelos abiertos se renueva cada pocos meses. Los ejemplos de la lección vienen de familias que eran populares cuando se escribió (Llama 3, Mistral, Qwen 2.5, Phi, Gemma, DeepSeek). Para octubre de 2026 ya salieron generaciones más nuevas de esas familias y otros modelos abiertos, por ejemplo gpt-oss de OpenAI. El principio para elegir no cambia por eso: revisa los nombres y tamaños vigentes en el catálogo ollama.com/library y pon el comando que necesites en lugar de la etiqueta del ejemplo.

🎨 Imagínalo así: la IA en la nube es como un taxi. Cómodo y confiable, pero el chofer lo ve y lo oye todo, y el viaje cuesta dinero. La IA local es como tu propio coche en la cochera. Vas a donde quieras, cuando quieras, y nadie escucha. Solo pagas la gasolina (la electricidad).


Conceptos clave

  • Ollama: la herramienta principal para ejecutar modelos locales (macOS, Windows, Linux)
  • LM Studio: alternativa con interfaz gráfica, de arrastrar y soltar
  • GGUF: formato de cuantización: un modelo grande se comprime a un tamaño razonable
  • Cuantización: reducir la precisión de los pesos (32 bits → 4 bits) a cambio de velocidad y memoria
  • API compatible con OpenAI: Ollama expone el mismo formato de API que OpenAI, así que el código para la API de OpenAI funciona con Ollama casi sin cambios
  • La ventaja de Apple Silicon: los chips M usan memoria compartida entre CPU y GPU, lo que da una gran ventaja con modelos locales
  • LLM Router: un patrón: eliges el modelo según la tarea, no un solo modelo para todo

Teoría

Para qué modelos locales: cinco razones reales

No razones teóricas, sino situaciones concretas de la práctica.

Razón 1: privacidad exigida por el cliente

Una clínica quiere automatizar el procesamiento de expedientes médicos. Un despacho de abogados, el análisis de contratos confidenciales. Un banco, el procesamiento de documentos internos.

No están en contra de la IA. Están en contra de que los datos de sus pacientes, clientes o socios se vayan a los servidores de Anthropic u OpenAI. No es paranoia: son el GDPR, HIPAA, los NDA y las leyes de protección de datos de cada país.

Un modelo local es la única forma de darles IA sin riesgos de cumplimiento.

Razón 2: el costo en procesamiento masivo

Digamos que hay que procesar 50 000 documentos de 2 páginas cada uno. Con Claude Sonnet eso es:

  • 50 000 × ~1 000 tokens = 50M tokens
  • Costo de los tokens de entrada: 50 × el precio por 1M de tokens. A $2 por 1M (precio de Sonnet 5.5 a octubre de 2026) son unos $100
  • Más los tokens de salida

Si se repite cada semana, son cientos de dólares al mes solo para esta tarea. Para el procesamiento masivo en segundo plano, la nube tiene la Batch API con 50% de descuento, pero aun así el gasto se nota. Y si la tarea no requiere el nivel de Sonnet (clasificación, extracción de datos), un modelo local la resuelve casi gratis. Precios vigentes: Lo vigente.

Razón 3: sin conexión, en cualquier lugar

Un avión. Una casa en el campo. Una obra sin internet. Una conferencia con mal WiFi. Una emergencia en la que la nube está caída.

El modelo local funciona siempre. No es algo exótico: es un caso de uso real para cualquiera que trabaje en campo.

Razón 4: regulaciones de cumplimiento

Algunos países e industrias tienen requisitos estrictos de residencia de datos: los datos no pueden salir de cierta jurisdicción o de cierto servidor. En varios países, los bancos directamente no pueden usar IA pública en la nube.

Para ese segmento de clientes, la IA local no es una opción: es la única alternativa.

Razón 5: fine-tuning con tus propios datos

¿Quieres un modelo que hable exactamente con el estilo de tu cliente? ¿Que se sepa su producto de memoria? ¿Que responda justo como su negocio necesita?

Los pesos de Claude no los puedes reentrenar (es un modelo cerrado). Pero puedes tomar un modelo abierto (por ejemplo, de las familias Llama, Qwen, Gemma, Mistral), entrenarlo con unos cientos de ejemplos y obtener un asistente especializado justo para ese negocio. Más detalles: Fine-tuning: cuando los prompts no alcanzan.

🎨 Imagínalo así: los modelos en la nube son como una navaja suiza universal. Los locales con fine-tuning son como una herramienta hecha a la medida de tu mano para una tarea concreta.


Ollama: la herramienta principal

Ollama es un runtime para modelos locales. Lo instalas una vez y con un comando ejecutas cualquier modelo del catálogo. Descarga, optimiza para tu equipo y levanta un servidor de API automáticamente.

Instalación:

bash
# macOS con Homebrew
brew install ollama

# O descárgalo directamente: ollama.com/download
# También hay versión para Windows y Linux, el instalador está en el sitio

# Verificar la instalación
ollama --version

Ejecutar el primer modelo:

bash
# Ejecutar Llama 3.2 (3B parámetros, ~2GB, rápido en cualquier Mac). Etiqueta de ejemplo: revisa los modelos vigentes en el catálogo de Ollama
ollama run llama3.2

# Ahora tienes un chat interactivo directo en la terminal
>>> ¡Hola! Explica qué es una API en palabras sencillas.

Modelos populares:

bash
# Rápidos (para tareas sencillas, funcionan con 8GB de RAM)
ollama run llama3.2          # 3B parámetros, ~2GB de disco
ollama run phi4              # Microsoft, 14B, muy eficiente
ollama run gemma3            # Google, buena relación calidad/tamaño

# Para código (optimizados para código)
ollama run mistral:7b        # Francés, excelente de propósito general
ollama run qwen2.5-coder     # Alibaba, especializado en código
ollama run deepseek-coder    # DeepSeek, fuerte en código y depuración
ollama run codellama         # Meta, hecho específicamente para código

# Pesados (requieren 32GB+ de RAM, nivel Mac Studio)
ollama run llama3.1:70b      # Bastante más fuerte que los modelos pequeños
ollama run qwen2.5:72b       # Alibaba, muy fuerte

# Administración
ollama list                  # Lista de modelos descargados
ollama ps                    # Los que están corriendo ahora
ollama rm llama3.2           # Borrar un modelo
ollama pull llama3.2         # Descargar sin ejecutar

Dato clave: API compatible con OpenAI

Ollama levanta un servidor de API local en http://localhost:11434. Ese servidor es compatible con el formato de la API de OpenAI. Eso significa que el código escrito para la API de OpenAI normalmente funciona con Ollama casi sin cambios: solo cambias base_url y el nombre del modelo.

bash
# La API funciona automáticamente cuando Ollama está corriendo
curl http://localhost:11434/v1/models

# Prueba con curl (como con OpenAI)
curl http://localhost:11434/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "llama3.2",
    "messages": [{"role": "user", "content": "¡Hola!"}]
  }'

LM Studio: interfaz gráfica para quienes no aman la terminal

LM Studio (lmstudio.ai) es una interfaz visual para modelos locales. Si Ollama = una herramienta de terminal, LM Studio = una aplicación con interfaz.

Qué hace:

  • Descarga modelos de HuggingFace desde la propia interfaz (tiene búsqueda integrada)
  • Instalación de archivos GGUF arrastrando y soltando
  • Chat integrado con historial
  • Servidor de API integrado (también compatible con OpenAI)
  • Ajuste de parámetros: temperature, context length, GPU layers
  • Monitoreo de RAM y GPU en tiempo real

Cuándo LM Studio es mejor que Ollama:

  • Le muestras la IA local a un cliente (es visualmente más claro)
  • Necesitas probar rápido muchos modelos distintos
  • Quieres ajustar parámetros sin línea de comandos
  • Trabajas directamente con archivos GGUF de HuggingFace

Cuándo Ollama es mejor que LM Studio:

  • Automatización con scripts
  • Deploy en servidores (Linux, sin interfaz gráfica)
  • Integración en código (más sencillo vía CLI)
  • Cambiar rápido de modelo en la terminal

🎨 Imagínalo así: Ollama es como git en la línea de comandos. LM Studio es como GitHub Desktop. El mismo resultado, distinta interfaz. Los profesionales usan los dos.


Entender los parámetros y la RAM

Antes de elegir un modelo, hay que entender la relación entre parámetros, tamaño y calidad.

Qué son los parámetros de un modelo

🎨 Imagínalo así: los parámetros son como las neuronas del cerebro. Una persona tiene ~86 mil millones de neuronas. Un modelo pequeño de 3B tiene 3 mil millones de pesos. Uno grande de 70B, 70 mil millones. Más suele ser más inteligente, pero requiere más memoria.

Qué es la cuantización

Los pesos originales de un modelo se guardan con precisión de 32 bits (fp32). Eso ocupa muchísimo. La cuantización los comprime a 4 bits (Q4) u 8 bits (Q8) con una pérdida mínima de calidad.

Resultado: un modelo de 7B en Q4 ocupa ~4GB en lugar de ~28GB. Corre en una MacBook común.

Escribe esto en el chat
Q2: compresión muy fuerte, pérdida de calidad notable
Q4: buen equilibrio (recomendado para la mayoría)
Q5 / Q6: más calidad, más memoria
Q8: calidad casi original, el doble de RAM que Q4
fp16: precisión completa, requiere una GPU profesional

Tabla práctica: qué corre en qué

RAM de la computadora Modelo máximo (Q4) Qué te da
8GB 7B parámetros Buenas tareas generales, código básico
16GB 13B parámetros Calidad de texto bastante mejor
32GB 30B parámetros Muy buena calidad
64GB 70B parámetros Modelos fuertes de la generación anterior
128GB+ 120B+ parámetros Modelos abiertos muy grandes

Importante en Apple Silicon: los chips M de Apple usan memoria unificada: la CPU y la GPU comparten un mismo bloque de RAM. Es una gran ventaja. En una PC con tarjeta gráfica aparte, el modelo tiene que caber en la VRAM de la tarjeta (una tarjeta con 8GB de VRAM = modelos de unos 7B). En una Mac con 24GB, la mayor parte de la memoria está disponible para el modelo. La tabla de arriba es orientativa: el tamaño real depende de la cuantización y de la longitud del contexto.


Apple Silicon: por qué la Mac le gana a la PC en IA local

🎨 Imagínalo así: una PC común es como una oficina donde los abogados (CPU) y los contadores (GPU) trabajan en edificios distintos y se mandan documentos por mensajería todo el tiempo. Una Mac con Apple Silicon es como un open space donde todos se sientan juntos y se pasan los documentos al instante.

La ventaja de arquitectura:

En una PC:

  • La CPU tiene 64GB de RAM
  • La GPU tiene 8GB de VRAM por separado
  • Un modelo de 70B (requiere 40GB) = no cabe en la VRAM = corre lento por la CPU

En una Mac M3/M4:

  • CPU + GPU = un solo bloque de memoria
  • Una Mac con 64GB de memoria unificada: el modelo de 70B en Q4 corre
  • La GPU acelera la inferencia mediante el framework Metal

Velocidad: las cifras concretas de tokens/seg dependen del modelo, la cuantización, la versión de Ollama y el equipo, así que aquí no van. El panorama general: una tarjeta gráfica potente suele ser más rápida con modelos pequeños, y con modelos muy grandes gana la Mac con mucha memoria unificada, porque el modelo grande sencillamente cabe. Además, la Mac es una laptop que va contigo a todas partes. Mide la velocidad en tu propio equipo: ollama run <modelo> --verbose muestra el eval rate en tokens por segundo.

La Mac con mucha memoria: el punto de inflexión:

Si trabajas en serio con modelos locales, una Mac Studio con mucha memoria unificada cambia el panorama: los modelos grandes se vuelven prácticos para el chat de todos los días.


Integración con código: el patrón LLM Router

El patrón más importante al trabajar con modelos locales es el LLM Router. No un modelo para todo. Tareas distintas, modelos distintos.

🎨 Imagínalo así: un LLM Router es como un director de recursos humanos que sabe a quién darle cada tarea. El análisis complejo va a Sonnet. La clasificación sencilla va al modelo local (gratis). Los datos confidenciales, solo al local (no salen a ningún lado).

Una implementación sencilla del router:

python
# llm_router.py
import anthropic
import openai
from enum import Enum

class TaskType(Enum):
    COMPLEX_CODE = "complex_code"       # Claude Sonnet
    SIMPLE_TEXT = "simple_text"         # Local: llama3.2
    CODE_REVIEW = "code_review"         # Local: deepseek-coder
    PRIVATE_DATA = "private_data"       # Local: cualquiera (los datos no salen)
    CREATIVE = "creative"               # Claude Sonnet (la mejor calidad)
    CLASSIFICATION = "classification"  # Local (barato, rápido)

def get_client_and_model(task_type: TaskType):
    """Devuelve (client, model_name) según la tarea"""
    
    # Anthropic Claude: tareas complejas, creatividad
    if task_type in [TaskType.COMPLEX_CODE, TaskType.CREATIVE]:
        return (
            anthropic.Anthropic(),
            "claude-sonnet-5-5"  # nombre del modelo a octubre de 2026, verifica el vigente
        )
    
    # Ollama: local, gratis
    ollama_client = openai.OpenAI(
        base_url="http://localhost:11434/v1",
        api_key="ollama"  # Ollama ignora la clave, pero el parámetro es obligatorio
    )
    
    model_map = {
        TaskType.SIMPLE_TEXT: "llama3.2",
        TaskType.CODE_REVIEW: "deepseek-coder",
        TaskType.PRIVATE_DATA: "mistral:7b",  # Los datos no salen de la computadora
        TaskType.CLASSIFICATION: "llama3.2",
    }
    
    return (ollama_client, model_map[task_type])


def run_task(task_type: TaskType, prompt: str) -> str:
    """Ejecuta la tarea con el modelo correcto"""
    client, model = get_client_and_model(task_type)
    
    # API de Anthropic (otro formato)
    if isinstance(client, anthropic.Anthropic):
        message = client.messages.create(
            model=model,
            max_tokens=1024,
            messages=[{"role": "user", "content": prompt}]
        )
        return "".join(b.text for b in message.content if b.type == "text")
    
    # API compatible con OpenAI (Ollama)
    response = client.chat.completions.create(
        model=model,
        messages=[{"role": "user", "content": prompt}]
    )
    return response.choices[0].message.content


# Ejemplo de uso
if __name__ == "__main__":
    # Código complejo: va a Claude
    result = run_task(
        TaskType.COMPLEX_CODE,
        "Escribe una función async de Python para procesar una cola de Celery"
    )
    
    # Clasificación: va al local (gratis)
    category = run_task(
        TaskType.CLASSIFICATION,
        "Clasifica este texto: 'Quiero saber los precios de departamentos en Cuenca'"
        "\nCategorías: [consulta de precio, consulta de zona, pregunta general]"
    )
    
    # Datos privados: solo local
    analysis = run_task(
        TaskType.PRIVATE_DATA,
        "Extrae las fechas clave de este contrato: [texto del contrato]"
    )

Un router más inteligente, con selección automática:

python
# smart_router.py
import re

ROUTING_RULES = [
    # (patrón en el prompt, task_type)
    (r"(confidencial|NDA|privad|secreto)", TaskType.PRIVATE_DATA),
    (r"(escribe código|function|class|async|debug)", TaskType.COMPLEX_CODE),
    (r"(categoriza|clasifica|tipo|clase)", TaskType.CLASSIFICATION),
    (r"(revisa el código|code review|encuentra bugs)", TaskType.CODE_REVIEW),
]

def auto_route(prompt: str) -> TaskType:
    """Determina automáticamente el tipo de tarea a partir del prompt"""
    prompt_lower = prompt.lower()
    for pattern, task_type in ROUTING_RULES:
        if re.search(pattern, prompt_lower):
            return task_type
    return TaskType.SIMPLE_TEXT  # Por defecto: modelo local

# Uso
task = auto_route("Revisa el código en busca de bugs: def foo(): pass")
result = run_task(task, "Revisa el código en busca de bugs: def foo(): pass")

Comparación de modelos para distintas tareas

La tabla refleja las familias que existían cuando se escribió; busca las generaciones más nuevas en el catálogo de Ollama.

Modelo Parámetros RAM (Q4) Mejor para Más débil en Velocidad
Llama 3.2 3B 3B 2GB Chat rápido, preguntas sencillas Código complejo, textos largos Muy rápido
Phi-4 14B 9GB Razonamiento, matemáticas Contexto largo Rápido
Mistral 7B 7B 5GB Tareas generales, instrucciones Razonamiento de varios pasos Rápido
Qwen 2.5 Coder 7B 7B 5GB Código, SQL, depuración Texto creativo Rápido
DeepSeek Coder 6.7B 6.7B 4GB Código, explicar código Contexto largo Rápido
Llama 3.1 8B 8B 5GB Buen propósito general Sin especialización Rápido
Llama 3.1 70B 70B 40GB Modelo fuerte de la generación anterior Requiere 64GB+ de RAM Lento
Qwen 2.5 72B 72B 45GB La mejor calidad local Requiere 64GB+ de RAM Lento

Recomendaciones para elegir:

Escribe esto en el chat
Para empezar (8-16GB de RAM):
  Tareas generales: mistral:7b
  Código: qwen2.5-coder o deepseek-coder
  Rápido y sencillo: llama3.2

Trabajo normal (32GB):
  Principal: llama3.1:8b (buen equilibrio)
  Código: qwen2.5-coder:14b

Nivel profesional (64GB+):
  Todo: llama3.1:70b
  Código: qwen2.5-coder:32b

Ollama MCP: modelos locales directo desde Claude Code

Puedes agregar Ollama como servidor MCP y llamar a modelos locales directamente desde una sesión de Claude Code. Sirve cuando quieres pedirle a Claude que delegue parte de la tarea a un modelo local.

bash
# Instalar Ollama MCP (paquete de un autor externo: revisa su repositorio antes de instalarlo)
claude mcp add ollama-mcp -- npx -y ollama-mcp

# Verificar que se agregó
claude mcp list

Después de instalarlo, en la sesión de Claude Code puedes escribir:

Escribe esto en el chat
"Usa ollama con el modelo qwen2.5-coder para revisar este código"

Y Claude delega la tarea mediante MCP a tu Ollama local. Los datos no van a la nube: el procesamiento ocurre en tu computadora.

Alternativa: llamada directa a la API desde Claude Code:

python
# Este script lo puede escribir y ejecutar Claude Code
import openai

client = openai.OpenAI(
    base_url="http://localhost:11434/v1",
    api_key="ollama"
)

response = client.chat.completions.create(
    model="qwen2.5-coder",
    messages=[{
        "role": "user",
        "content": "Review this Python code for bugs:\n" + code_content
    }]
)
print(response.choices[0].message.content)

Cuándo local vs. nube: una comparación honesta

No hay que elegir uno. El patrón es usar los dos para tareas distintas.

Los modelos locales GANAN:

Escribe esto en el chat
✅ Datos del cliente bajo NDA / HIPAA / GDPR
✅ Procesamiento masivo de documentos (miles, sin pagar por tokens)
✅ Trabajo sin internet
✅ Fine-tuning para un negocio concreto
✅ Cumplimiento en industrias reguladas
✅ I+D prolongado sin taxímetro de tokens
✅ Tareas sencillas repetitivas (clasificación, extracción)

La nube (Claude Sonnet/Opus) GANA:

Escribe esto en el chat
✅ Tareas complejas de varios pasos (mejor calidad)
✅ Contexto muy grande (los modelos grandes de Claude llegan a un millón de tokens)
✅ Computer Use y automatización del navegador
✅ Empezar rápido sin descargar 8GB
✅ Siempre el modelo actualizado (se actualiza solo)
✅ Código complejo, decisiones de arquitectura
✅ Cuando necesitas el mejor resultado al primer intento

🎨 Imagínalo así: la IA local es como tener tu propio cocinero en casa: barato, privado, disponible a cualquier hora. Claude en la nube es como un restaurante con estrellas Michelin: para cuando necesitas el nivel más alto en un evento importante. Un emprendedor inteligente usa los dos según la situación.

Matriz de decisión:

Tarea Recomendación Por qué
Borrador de contenido para editar Local Barato, igual hay que editarlo
Texto final para publicar Nube La calidad es crítica
Procesar 1000 PDF de un cliente Local Sin pagar por tokens + los datos no salen
Workflow complejo con agentes Nube Confiabilidad y contexto
Clasificar solicitudes Local Tarea sencilla, masiva
Generar código desde cero Nube (Claude) La mejor calidad
Code review de código terminado Local (DeepSeek) Suficiente para revisar
Análisis confidencial Local Los datos no salen

Fine-tuning: cuándo y cómo

El fine-tuning es volver a entrenar un modelo ya entrenado con tus propios datos. Resultado: un modelo que habla como tu cliente, conoce su producto y responde con el estilo necesario.

Cuándo vale la pena hacer fine-tuning:

Escribe esto en el chat
✅ Un estilo de comunicación específico (tono, terminología de la marca)
✅ Conocimiento de nicho (términos médicos, redacción legal)
✅ Un formato de salida concreto (siempre un JSON con cierta estructura)
✅ Generación masiva de contenido del mismo tipo
✅ Cuando la ingeniería de prompts ya no da la calidad necesaria

Cuándo NO hace falta fine-tuning:

Escribe esto en el chat
❌ Simplemente "quiero que ChatGPT sepa más": eso es RAG, no fine-tuning
❌ La tarea se resuelve con un buen prompt
❌ Tienes menos de 200-300 ejemplos
❌ No hay tiempo para iterar (fine-tuning = experimentos)

Herramientas para fine-tuning:

LoRA (Low-Rank Adaptation): el método más popular. No reentrena todo el modelo: agrega adaptadores pequeños. Barato en cómputo, rápido.

Escribe esto en el chat
Fine-tuning completo de un modelo de 7B: requiere una GPU en la nube seria (clase A100), la renta se cobra por hora
Fine-tuning con LoRA de un modelo de 7B: basta una tarjeta gráfica de consumo potente o una Mac con mucha memoria

Unsloth: una herramienta que hace LoRA de 2 a 5 veces más rápido y con menos consumo de memoria:

bash
pip install unsloth

# Ejemplo básico de fine-tuning con Unsloth
from unsloth import FastLanguageModel

model, tokenizer = FastLanguageModel.from_pretrained(
    model_name="unsloth/llama-3.2-3b-instruct",
    max_seq_length=2048,
    load_in_4bit=True  # Cuantización para ahorrar memoria
)

Formato de datos para fine-tuning:

jsonl
{"messages": [
  {"role": "user", "content": "¿Cómo compro un departamento en Cuenca?"},
  {"role": "assistant", "content": "El proceso de compra en Ecuador incluye..."}
]}
{"messages": [
  {"role": "user", "content": "¿Hace falta notario para la compra?"},
  {"role": "assistant", "content": "Sí, en Ecuador todas las operaciones inmobiliarias..."}
]}

Al menos 200-300 pares así. Claude Code puede ayudarte a generar el dataset: describes el estilo y las preguntas típicas, y él crea los ejemplos.

Dónde entrenar:

Escribe esto en el chat
Runpod.io: GPU en la nube con cobro por hora (precios en el sitio)
Google Colab: más barato, pero con límites
Mac con mucha memoria unificada: local, si tienes el equipo
Modal.com: GPU serverless, pagas solo el tiempo de entrenamiento

Práctica: instala Ollama y escribe un procesador de documentos

Paso 1: instalación y primer arranque

bash
# Instalación
brew install ollama

# Arrancar el servicio (en segundo plano)
ollama serve &

# Descargar y ejecutar el primer modelo
ollama run mistral:7b

Al arrancar verás un chat interactivo. Escribe algo y comprueba que funciona. Ctrl+D para salir.

bash
# Verificar que la API funciona
curl http://localhost:11434/api/tags
# Debe devolver la lista de modelos

# Para código: descargar otro modelo
ollama pull qwen2.5-coder

Paso 2: script de Python para procesar documentos

Crea el archivo document_processor.py:

python
"""
Procesador local de documentos con Ollama.
Los datos no salen de la computadora.
"""

import openai
import json
from pathlib import Path

# Nos conectamos al Ollama local
client = openai.OpenAI(
    base_url="http://localhost:11434/v1",
    api_key="ollama"
)

def extract_info_from_document(text: str, model: str = "mistral:7b") -> dict:
    """
    Extrae información estructurada del texto de un documento.
    Usa un modelo local: los datos no salen.
    """
    prompt = f"""Analiza este documento y extrae la información en formato JSON.

Hay que encontrar:
- fecha del documento (campo "date")
- partes del contrato (campo "parties", lista)
- monto, si lo hay (campo "amount")
- tipo de documento (campo "type")
- obligaciones clave (campo "obligations", lista)

Devuelve SOLO un JSON válido, sin explicaciones.

DOCUMENTO:
{text}"""

    response = client.chat.completions.create(
        model=model,
        messages=[{"role": "user", "content": prompt}],
        temperature=0.1  # Temperatura baja para una salida estructurada
    )
    
    raw = response.choices[0].message.content.strip()
    
    # Limpiamos si el modelo agregó markdown
    if raw.startswith("```"):
        raw = raw.split("```")[1]
        if raw.startswith("json"):
            raw = raw[4:]
    
    try:
        return json.loads(raw)
    except json.JSONDecodeError:
        return {"error": "No se pudo interpretar el JSON", "raw": raw}


def classify_document(text: str, model: str = "llama3.2") -> str:
    """
    Clasificación rápida del tipo de documento.
    Usamos un modelo pequeño: barato y rápido.
    """
    prompt = f"""Determina el tipo de documento con una sola palabra de la lista:
[contrato, factura, acta, carta, solicitud, otro]

DOCUMENTO (primeros 500 caracteres):
{text[:500]}

Responde con una sola palabra."""

    response = client.chat.completions.create(
        model=model,
        messages=[{"role": "user", "content": prompt}],
        max_tokens=10
    )
    return response.choices[0].message.content.strip().lower()


def process_folder(folder_path: str) -> list:
    """
    Procesa todos los archivos .txt de una carpeta.
    Devuelve una lista con los resultados.
    """
    results = []
    folder = Path(folder_path)
    
    for file_path in folder.glob("*.txt"):
        print(f"Procesando: {file_path.name}")
        
        text = file_path.read_text(encoding="utf-8")
        
        # Paso 1: clasificación (modelo pequeño y rápido)
        doc_type = classify_document(text, model="llama3.2")
        print(f"  Tipo: {doc_type}")
        
        # Paso 2: extracción de información (un modelo mejor)
        info = extract_info_from_document(text, model="mistral:7b")
        
        results.append({
            "file": file_path.name,
            "classified_type": doc_type,
            "extracted_info": info
        })
        
        print(f"  Listo: {json.dumps(info, ensure_ascii=False, indent=2)[:200]}...")
    
    return results


# Modo demo si no hay documentos reales
def demo():
    sample_text = """
    CONTRATO DE ARRENDAMIENTO N.º 123
    
    Cuenca, 15 de enero de 2026
    
    Ecuador Realty S.A. (Arrendador) y Juan Pérez García (Arrendatario)
    celebran el presente contrato conforme a lo siguiente:
    
    1. El Arrendador renta un departamento de 65 m² ubicado en Calle Ejemplo 45.
    2. Pago mensual: $500 USD.
    3. Plazo del arrendamiento: 12 meses a partir del 1 de febrero de 2026.
    4. El Arrendatario se obliga a pagar la renta puntualmente.
    """
    
    print("=== DEMO: procesamiento de un documento con un modelo local ===\n")
    print("Documento:")
    print(sample_text[:200] + "...\n")
    
    print("Clasificación (llama3.2)...")
    doc_type = classify_document(sample_text)
    print(f"Tipo: {doc_type}\n")
    
    print("Extracción de información (mistral:7b)...")
    info = extract_info_from_document(sample_text)
    print("Resultado:")
    print(json.dumps(info, ensure_ascii=False, indent=2))
    
    print("\n✅ Todos los datos se procesaron localmente: ¡no salieron a ningún lado!")


if __name__ == "__main__":
    import sys
    
    if len(sys.argv) > 1:
        # Si se pasó una carpeta, la procesamos
        folder = sys.argv[1]
        results = process_folder(folder)
        
        output_file = "results.json"
        with open(output_file, "w", encoding="utf-8") as f:
            json.dump(results, f, ensure_ascii=False, indent=2)
        
        print(f"\n✅ Resultados guardados en {output_file}")
    else:
        # Si no, la demo
        demo()

Ejecución:

bash
# Asegúrate de que Ollama esté corriendo y los modelos descargados
ollama list  # Deben aparecer llama3.2 y mistral:7b

# Demo con un documento de prueba
python document_processor.py

# Procesar una carpeta de documentos
python document_processor.py /path/to/documents/

Paso 3: agregarlo a Claude Code mediante CLAUDE.md

Agrega al CLAUDE.md de tu proyecto:

Escribe esto en el chat
## Stack de IA local

Al trabajar con datos confidenciales de clientes, usar Ollama (local).
API endpoint: http://localhost:11434/v1
Modelos disponibles: mistral:7b (general), qwen2.5-coder (código), llama3.2 (rápido)

Patrón de llamada: openai.OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")

Errores comunes

Error 1: Ollama no responde

bash
# Verifica que el servicio esté corriendo
curl http://localhost:11434/api/tags

# Si no, arráncalo
ollama serve

# En macOS, Ollama arranca como aplicación (ícono en la barra de menú)
# o mediante launchctl

Error 2: el modelo responde muy lento

Si da menos de 3-5 tokens/seg, el modelo no cabe en la RAM y se va al swap. Solución: elegir un modelo más pequeño o una cuantización más baja.

bash
# Revisa cuánta RAM usa Ollama
ollama ps  # Muestra los modelos activos y la memoria

# Elige un modelo más pequeño
ollama run llama3.2  # 3B en lugar de 7B

Error 3: el JSON del modelo no se puede interpretar

Los modelos locales a veces agregan un envoltorio de markdown. Siempre trátalo:

python
def clean_json(text: str) -> str:
    """Quita el envoltorio de markdown de la respuesta del modelo"""
    text = text.strip()
    if "```json" in text:
        text = text.split("```json")[1].split("```")[0]
    elif "```" in text:
        text = text.split("```")[1].split("```")[0]
    return text.strip()

Error 4: resultados distintos con el mismo prompt

Es normal con una temperature alta. Para salidas estructuradas (JSON, clasificación), usa temperature=0.0 o temperature=0.1.


Resumen de la lección

Lo que ahora sabes hacer:

  • Instalar Ollama y ejecutar un modelo local en cuestión de minutos
  • Elegir el modelo correcto para cada tarea (tamaño, especialización)
  • Integrar un modelo local en código Python mediante la API compatible con OpenAI
  • Implementar el patrón LLM Router: tareas distintas, modelos distintos
  • Procesar una carpeta de documentos confidenciales de forma local, sin enviar datos a la nube

La idea principal:

Los modelos locales no reemplazan a Claude. Son una herramienta más del arsenal. Un emprendedor inteligente usa los dos: locales donde hace falta privacidad o volumen, la nube donde hace falta calidad.

🎨 Imagen final: un LLM Router es como un buen gerente de proyecto. Sabe que la tarea sencilla → al junior (rápido y barato), la compleja → al senior (con calidad, pero más caro), la confidencial → solo a alguien de la casa (nada de externos). Así funciona un stack de IA bien armado.


Tarea

Nivel 1 (obligatorio):

  1. Instala Ollama
  2. Descarga mistral:7b y llama3.2
  3. Ejecuta el script de demo de la lección
  4. Comprueba que el procesamiento funciona localmente (sin internet debe funcionar)

Nivel 2 (recomendado):

  1. Toma un documento real de tu negocio (sin datos confidenciales para la prueba)
  2. Adapta el prompt de extract_info_from_document a tu tipo de documento
  3. Procesa 5-10 documentos y revisa la calidad
  4. Compara el resultado con el mismo prompt en Claude Sonnet y anota la diferencia de calidad

Nivel 3 (avanzado):

  1. Implementa el smart_router.py de la parte teórica
  2. Agrega registro: modelo usado / tiempo de respuesta / costo aproximado (para la nube)
  3. Encuentra una tarea de tu negocio en la que el modelo local sea suficientemente bueno y calcula el ahorro mensual

Enlaces


Siguiente lección: Fine-tuning: cuando los prompts no alcanzan

La marca se guarda solo en este navegador y no se envía a ningún sitio. Mi progreso