El contexto
Una empresa SaaS con base de datos PostgreSQL de 80+ tablas y un equipo de datos desbordado. Los product managers, el equipo comercial, y los ejecutivos dependían de los analistas para cualquier consulta: “¿cuántos usuarios activos tuvimos en enero?”, “¿cuál es el churn por segmento?”, “¿qué features usan más los clientes enterprise?”
Cada request tardaba entre 2 días y 2 semanas. El backlog no paraba de crecer.
El brief
La solicitud inicial era ambiciosa: un sistema donde cualquier persona del negocio pudiera hacer preguntas en español y obtener respuestas basadas en datos reales, sin saber SQL.
Lo que suena simple tiene complejidades reales en producción:
- 80+ tablas con nombres no intuitivos (heredados de versiones antiguas)
- Joins complejos — las métricas de negocio requerían cruzar 5-6 tablas
- Seguridad — no todos los usuarios podían ver todos los datos
- Corrección — una query equivocada da números incorrectos. Un número incorrecto en una reunión de board tiene consecuencias.
Text-to-SQL mal implementado es peor que no tener Text-to-SQL. Genera confianza en números incorrectos.
La arquitectura
Capa de contexto — el schema semántico
El mayor trabajo no fue el LLM. Fue construir la capa de contexto que le permite al modelo entender el negocio.
Tradujimos el schema de la base de datos a lenguaje de negocio: qué significa cada tabla, qué columnas son relevantes, qué joins son válidos, qué métricas se calculan cómo.
-- Lo que existe en la DB:
SELECT u.id, s.plan_id, SUM(e.value)
FROM users u
JOIN subscriptions s ON s.user_id = u.id
JOIN events e ON e.user_id = u.id
WHERE e.type = 'feature_used'
GROUP BY u.id, s.plan_id
-- Lo que el sistema entiende cuando le preguntan:
-- "¿cuánto usan el producto los clientes enterprise?"
Generación + validación
El flujo por query:
- El usuario escribe en lenguaje natural
- El LLM genera SQL usando el schema semántico como contexto
- Un validador revisa la query antes de ejecutarla (previene queries peligrosas, valida tablas y columnas)
- Si pasa validación: ejecuta y devuelve resultado
- Si falla: el LLM intenta corregir (hasta 2 reintentos)
- Si sigue fallando: mensaje de error claro, sin ejecutar nada
Control de acceso
Cada usuario tiene un “scope” de tablas accesibles. El sistema inyecta condiciones WHERE automáticamente para respetar los permisos sin que el usuario lo sepa ni pueda evadirlo.
Resultados
El 13% de queries rechazadas son mayoritariamente preguntas ambiguas o que cruzan permisos. El sistema comunica el motivo claramente.
Tres meses post-lanzamiento, el backlog del equipo de datos se redujo en un 65%. Los analistas ahora trabajan en análisis profundos, no en responder “¿cuántos usuarios activos?” por décima vez.
Las decisiones difíciles
Por qué no usamos un agente autónomo
La tentación era hacer un agente que pudiera encadenar múltiples queries, generar gráficos, y armar reportes completos. Lo descartamos en la fase de diseño.
Un agente autónomo sobre datos de producción es un vector de riesgo. Preferimos un sistema más acotado pero confiable: una pregunta, una query, un resultado. El scope claro hace que sea predecible y auditable.
El umbral de confianza
El sistema tiene un score interno de confianza en la query generada. Bajo cierto umbral, en vez de ejecutar, le pregunta al usuario si eso es lo que quería consultar. Más lento, más seguro.
Este tradeoff lo decidimos con el cliente en semana 2. Es el tipo de decisión que no puede tomar el equipo técnico solo.
¿Tu equipo de datos está desbordado con requests internos? Conversemos.