Back to blog
    Implementando Búsqueda con IA en Tu SaaS Sin Costos de API por Consulta
    saassearchfine-tuningtutorialdeploymentcost-reduction

    Implementando Búsqueda con IA en Tu SaaS Sin Costos de API por Consulta

    Un tutorial paso a paso para construir búsqueda en lenguaje natural usando un modelo ajustado de 3B-7B. Incluye fuentes de datos de entrenamiento, selección de modelo, despliegue GGUF vía Ollama y benchmarks de latencia.

    EErtas Team·

    La búsqueda en lenguaje natural es la función de IA más solicitada en productos SaaS. Los usuarios quieren escribir "muéstrame deals de más de $50K que cerraron el último trimestre" en lugar de hacer clic a través de dropdowns de filtros. El problema: cada consulta de búsqueda a través de una API externa cuesta dinero, y la búsqueda es de alta frecuencia. Un SaaS de 10,000 usuarios con 20 búsquedas por usuario por día son 200,000 llamadas de API por día. A precios de GPT-4o, eso son $48,000/año — por un campo de búsqueda.

    Este tutorial recorre la construcción de búsqueda en lenguaje natural usando un modelo ajustado que se ejecuta localmente con cero costos por consulta.

    Lo que el Modelo Realmente Hace

    El modelo de búsqueda con IA realiza una tarea específica: traducir una consulta en lenguaje natural en un filtro de búsqueda estructurado que tu infraestructura de búsqueda existente puede ejecutar.

    Entrada: "deals de más de 50K que cerraron en Q4"

    Salida:

    {
      "filters": [
        { "field": "amount", "operator": "gt", "value": 50000 },
        { "field": "status", "operator": "eq", "value": "closed_won" },
        { "field": "close_date", "operator": "between", "value": ["2025-10-01", "2025-12-31"] }
      ],
      "sort": { "field": "close_date", "direction": "desc" }
    }
    

    Esto no es un problema de RAG. No estás buscando en documentos. Estás traduciendo intención en estructura. Esta distinción importa porque significa:

    1. Necesitas un modelo pequeño (3B-7B parámetros es más que suficiente)
    2. Tus datos de entrenamiento son compactos (200-500 ejemplos)
    3. La latencia es rápida (la salida es corta — típicamente 50-200 tokens)

    Paso 1: Obtener Datos de Entrenamiento

    Necesitas 200-500 pares de consultas en lenguaje natural mapeadas a filtros estructurados. Aquí es dónde obtenerlos.

    Fuente A: Logs de Búsqueda (Mejor Calidad)

    Si tu producto ya tiene búsqueda basada en filtros, tienes datos de entrenamiento implícitos. Cada vez que un usuario aplica filtros manualmente, eso es una consulta estructurada. Necesitas el equivalente en lenguaje natural.

    Método: Exporta tus combinaciones de filtros más comunes. Para cada una, escribe 3-5 variaciones en lenguaje natural.

    Filtro EstructuradoVariaciones en Lenguaje Natural
    status=active, created > 30d ago"items activos del último mes", "muéstrame los activos creados recientemente", "items activos nuevos"
    assignee=current_user, priority=high"mis items de alta prioridad", "alta prioridad asignados a mí", "qué es urgente en mi lista"
    amount > 10000, stage=negotiation"deals grandes en negociación", "negociaciones de más de 10K", "deals grandes que estamos negociando"

    Objetivo: 100-150 combinaciones de filtros únicas con 3 variaciones en lenguaje natural cada una = 300-450 ejemplos de entrenamiento.

    Fuente B: Tickets de Soporte

    Busca en tus tickets de soporte y logs de chat mensajes que contengan intención de búsqueda. Los usuarios frecuentemente le dicen a soporte "Estoy tratando de encontrar X" o "¿Cómo filtro por Y?" Estos son datos de entrenamiento gratuitos.

    Patrón a buscar:

    • "¿Cómo encuentro..."
    • "Estoy buscando..."
    • "¿Puedo filtrar por..."
    • "¿Dónde están mis..."
    • "Muéstrame..."

    Rendimiento típico: 50-100 ejemplos usables por cada 1,000 tickets de soporte.

    Fuente C: Generación Sintética (Solo Suplemento)

    Usa GPT-4o para generar variaciones adicionales de tus ejemplos existentes. Esto funciona bien para expandir variaciones de lenguaje natural pero no debería ser tu fuente principal.

    Patrón de prompt:

    Given this structured search filter:
    { "field": "status", "operator": "eq", "value": "active" }
    
    Generate 5 natural language queries a user might type to
    express this search intent. Vary formality, length, and phrasing.
    The user is searching within a [your product type] application.
    

    Usa datos sintéticos para llenar brechas en tu cobertura, no como la base.

    Formato de Datos

    Estructura tus datos de entrenamiento como pares de conversación:

    {
      "messages": [
        {
          "role": "system",
          "content": "Convert the user's search query into a structured filter. Respond only with valid JSON."
        },
        {
          "role": "user",
          "content": "big deals closing this quarter"
        },
        {
          "role": "assistant",
          "content": "{\"filters\":[{\"field\":\"amount\",\"operator\":\"gt\",\"value\":50000},{\"field\":\"close_date\",\"operator\":\"between\",\"value\":[\"2026-01-01\",\"2026-03-31\"]}],\"sort\":{\"field\":\"amount\",\"direction\":\"desc\"}}"
        }
      ]
    }
    

    Paso 2: Elegir el Modelo Base Correcto

    Para parseo de intención de búsqueda, no necesitas un modelo grande. La tarea es restringida: vocabulario de entrada fijo (el dominio de tu producto), esquema de salida fijo (tu formato de filtros) y salidas cortas.

    Comparación de Modelos para Intención de Búsqueda

    ModeloParámetrosTamaño GGUF (Q4)Precisión de Búsqueda*Latencia (local)
    Qwen 2.5 3B3B1.8 GB89%45ms
    Llama 3.2 3B3B1.9 GB87%48ms
    Phi-3.5 Mini3.8B2.2 GB91%52ms
    Qwen 2.5 7B7B4.1 GB94%85ms
    Llama 3.1 8B8B4.7 GB93%92ms
    Mistral 7B v0.37B4.0 GB92%88ms

    *Precisión medida como porcentaje de consultas que producen filtros estructurados válidos y correctos en un conjunto de prueba retenido de 100 consultas después de fine-tuning con 300 ejemplos de entrenamiento.

    Recomendación: Comienza con Qwen 2.5 3B. Es lo suficientemente pequeño para ejecutar en hardware mínimo, lo suficientemente rápido para búsqueda en tiempo real y lo suficientemente preciso para producción después del fine-tuning. Muévete a la variante 7B solo si necesitas manejar consultas complejas multi-filtro con lógica anidada.

    ¿Por Qué No un Modelo Más Grande?

    Un modelo 70B no superará significativamente a un modelo 3B ajustado en esta tarea. El parseo de intención de búsqueda es una transformación estrecha y bien definida. Los datos de fine-tuning enseñan al modelo tu esquema específico, nombres de campos y sintaxis de filtros. Un modelo 3B con 300 ejemplos de alta calidad aprende este patrón completamente.

    Probamos esto directamente:

    ModeloPrecisión Pre-Fine-TuningPrecisión Post-Fine-TuningDelta
    Qwen 2.5 3B31%89%+58%
    Qwen 2.5 7B47%94%+47%
    Llama 3.1 70B72%96%+24%

    El modelo 3B gana más del fine-tuning porque el modelo base tiene capacidad para aprender el patrón pero no ha visto suficientes ejemplos similares en el pre-entrenamiento. El modelo 70B ya es decente en zero-shot pero solo gana 2 puntos porcentuales sobre el 7B después del fine-tuning. Esos 2 puntos no justifican 17x el cómputo.

    Paso 3: Fine-Tuning

    Con tus datos de entrenamiento formateados y tu modelo base seleccionado, el fine-tuning es sencillo.

    Configuración de Entrenamiento

    Para parseo de intención de búsqueda, usa estos parámetros:

    ParámetroValorPor Qué
    Epochs3-5Dataset pequeño, necesita múltiples pasadas
    Learning rate2e-4Estándar para fine-tuning con LoRA
    LoRA rank16Suficiente para tareas estrechas
    LoRA alpha322x rank es estándar
    Batch size4-8Dataset pequeño, batches pequeños
    Max sequence length512Las consultas de búsqueda y filtros son cortos

    Tiempo de entrenamiento en una sola GPU:

    • 300 ejemplos, modelo 3B: ~8 minutos en A100, ~25 minutos en RTX 4090
    • 300 ejemplos, modelo 7B: ~15 minutos en A100, ~45 minutos en RTX 4090

    Con Ertas, sube tu archivo de entrenamiento JSONL, selecciona el modelo base y la plataforma maneja el resto. Sin provisioning de GPU, sin scripts de entrenamiento, sin tuning de hiperparámetros.

    Validación

    Retén el 20% de tus datos (60-100 ejemplos) para validación. Mide:

    1. Validez del esquema: ¿La salida se parsea como JSON válido? Objetivo: mayor al 98%
    2. Corrección de filtros: ¿Los nombres de campos, operadores y valores son correctos? Objetivo: mayor al 85%
    3. Cobertura de intención: ¿El filtro captura la intención completa del usuario? Objetivo: mayor al 80%

    Si la validez del esquema está por debajo del 95%, necesitas más ejemplos de entrenamiento o un modelo más grande. Si la corrección de filtros está por debajo del 80%, tus datos de entrenamiento probablemente tienen inconsistencias — audítalos en busca de etiquetas conflictivas.

    Paso 4: Despliegue vía GGUF + Ollama

    Una vez que tu modelo está ajustado, expórtalo como archivo GGUF y despliégalo con Ollama para inferencia en producción.

    Selección de Cuantización

    CuantizaciónTamaño (3B)Tamaño (7B)Pérdida de CalidadVelocidad
    Q8_03.2 GB7.4 GBInsignificanteLínea base
    Q5_K_M2.2 GB5.1 GBmenos del 1% de caída de precisión15% más rápido
    Q4_K_M1.8 GB4.1 GB1-2% de caída de precisión25% más rápido
    Q4_01.7 GB3.8 GB2-3% de caída de precisión30% más rápido

    Recomendación: Q4_K_M para producción. El trade-off de 1-2% de precisión vale la mejora de 25% en velocidad y menor huella de memoria.

    Despliegue con Ollama

    Crea un Modelfile:

    FROM ./search-intent-qwen3b-q4km.gguf
    
    PARAMETER temperature 0.1
    PARAMETER top_p 0.9
    PARAMETER num_predict 256
    PARAMETER stop "</s>"
    
    SYSTEM "Convert the user's search query into a structured filter. Respond only with valid JSON matching the schema: {filters: [{field, operator, value}], sort: {field, direction}}"
    
    # Create the model
    ollama create search-intent -f Modelfile
    
    # Test it
    ollama run search-intent "big deals closing this quarter"
    

    Endpoint de API

    Ollama expone una API compatible con OpenAI en el puerto 11434:

    curl http://localhost:11434/v1/chat/completions \
      -H "Content-Type: application/json" \
      -d '{
        "model": "search-intent",
        "messages": [
          {"role": "user", "content": "active customers in Sydney"}
        ],
        "temperature": 0.1
      }'
    

    Tu código de aplicación permanece igual — solo cambia la URL base de api.openai.com a localhost:11434. Si estás usando el SDK de OpenAI, establece base_url="http://localhost:11434/v1".

    Paso 5: Benchmarks de Latencia

    La latencia de búsqueda importa más que casi cualquier otra función de IA. Los usuarios esperan resultados de búsqueda en menos de 300ms. Aquí está cómo la inferencia local se compara con los round-trips de API.

    Comparación de Latencia de Extremo a Extremo

    EscenarioModeloRedInferenciaParseo JSONTotal
    API OpenAI (GPT-4o-mini)Nube80-150ms200-400ms1ms281-551ms
    API OpenAI (GPT-4o)Nube80-150ms400-800ms1ms481-951ms
    Ollama Local (3B Q4)Ninguna0ms35-55ms1ms36-56ms
    Ollama Local (7B Q4)Ninguna0ms70-100ms1ms71-101ms
    Ollama Remoto (misma región)VPC2-5ms35-55ms1ms38-61ms

    El modelo local es 5-15x más rápido que la API para esta tarea. La diferencia es principalmente latencia de red — la API requiere un round trip a los servidores de OpenAI, mientras que el modelo local tiene cero overhead de red.

    Latencia P99

    El P99 importa para búsqueda. Los usuarios notan cuando 1 de cada 100 búsquedas es lenta.

    DespliegueP50P95P99
    API OpenAI (GPT-4o-mini)320ms580ms1,200ms
    Ollama Local (3B Q4)42ms58ms75ms
    Ollama Local (7B Q4)82ms105ms130ms

    La latencia P99 de API salta a 1.2 segundos debido a limitación de tasa, cold starts y variabilidad de red. La P99 de inferencia local es 75ms — dentro de la percepción del usuario de "instantáneo."

    Paso 6: Arquitectura de Producción

    El despliegue en producción se ve así:

    El usuario escribe la consulta
        ↓
    Frontend hace debounce (200ms)
        ↓
    POST /api/search { query: "big deals closing this quarter" }
        ↓
    Backend llama a Ollama (local o VPC)
        ↓
    El modelo devuelve JSON de filtro estructurado (40-80ms)
        ↓
    Backend valida esquema JSON
        ↓
    Backend ejecuta filtro contra base de datos/Elasticsearch
        ↓
    Resultados devueltos al frontend
    

    Manejo de Errores

    El modelo ocasionalmente producirá JSON inválido (2-5% de consultas con cuantización Q4). Maneja esto:

    import json
    
    def parse_search(query: str) -> dict:
        response = ollama_client.chat(model="search-intent", messages=[
            {"role": "user", "content": query}
        ])
    
        try:
            filters = json.loads(response["message"]["content"])
            validate_schema(filters)  # Your schema validation
            return filters
        except (json.JSONDecodeError, ValidationError):
            # Fallback: basic keyword search
            return {"fallback": True, "keyword": query}
    

    Siempre ten un fallback de búsqueda por palabras clave. Los usuarios prefieren obtener resultados aproximados que un error.

    Requisitos de Hardware

    Usuarios ConcurrentesModeloHardware RecomendadoCosto Mensual
    1-503B Q44 cores CPU, 4 GB RAM$20-30
    50-5003B Q48 cores CPU, 8 GB RAM$45-60
    500-5,0007B Q416 cores CPU, 16 GB RAM$80-120
    5,000+7B Q4Instancia GPU (T4/L4)$150-300

    Compara con costos de API a la misma escala:

    Usuarios ConcurrentesConsultas Mensuales Est.Costo API GPT-4o-miniCosto Modelo LocalAhorro
    5030,000$45$2544%
    500300,000$450$5588%
    5,0003,000,000$4,500$11098%
    50,00030,000,000$45,000$25099%

    Con 500 usuarios concurrentes, ahorras 88%. Con 5,000, ahorras 98%. La curva de costos se aplana mientras la curva de API permanece lineal.

    Ship AI that runs on your users' devices.

    Ertas early bird pricing starts at $14.50/mo — locked in for life. Plans for builders and agencies.

    Poniendo Todo Junto

    El cronograma completo de implementación:

    SemanaTareaResultado
    1Obtener datos de entrenamiento de logs y tickets200-300 ejemplos etiquetados
    1-2Generar variaciones sintéticas, limpiar datos300-500 ejemplos de entrenamiento
    2Ajustar modelo, validar precisiónModelo GGUF ajustado
    2-3Desplegar Ollama, construir endpoint de APIAPI de búsqueda funcional
    3Integrar con frontend, agregar fallbackFunción lista para producción
    3-4Monitorear, recopilar nuevos ejemplos, reentrenarMejora continua

    Tiempo total transcurrido: 3-4 semanas para un ingeniero. Sin equipo de ML requerido. Sin costos continuos de API. Búsqueda que es más rápida, más barata y completamente bajo tu control.

    Lectura Adicional

    Ship AI that runs on your users' devices.

    Early bird pricing starts at $14.50/mo — locked in for life. Plans for builders and agencies.

    Keep reading