Manual de Usuario
AURA JAC
Asistente Conversacional de Inteligencia Analítica
Motor GenBI v2.0
Versión2.0 EmpresaJAC — Transporte Interurbano Año2026 ClasificaciónInterno

Tabla de Contenidos

01 — Qué es AURA JAC

AURA JAC es un asistente conversacional de inteligencia analítica diseñado para la empresa de buses interurbanos JAC (Jugos y Alimentos de Concepción, operador de transporte de pasajeros en Chile). Permite a ejecutivos y analistas realizar preguntas sobre ventas de boletos en lenguaje natural en español y obtener respuestas analíticas con datos, visualizaciones y explicaciones en texto.

Qué resuelve

1
Un ejecutivo pregunta: "¿Cuál fue la ruta con más ingresos en 2024?"
2
AURA traduce esa pregunta a una consulta SQL correcta sobre la base de datos de ventas.
3
Ejecuta la consulta de forma segura (solo lectura).
4
Devuelve una tabla con los resultados, un gráfico de barras y una explicación en lenguaje natural.

Capacidades principales

CapacidadDescripción
Text-to-SQLTraduce preguntas en español a SQL de SQLite de solo lectura
Multi-candidatoGenera múltiples candidatos SQL y vota por el mejor
Auto-correcciónSi un SQL falla, lo reintenta con el error como contexto
Validación estáticaVerifica que el SQL sea seguro (solo SELECT) antes de ejecutar
Retrieval híbridoRecupera ejemplos relevantes usando embeddings + BM25
Schema linkingMapea lenguaje natural a columnas reales de la base de datos
VisualizaciónGenera gráficos de barras automáticos con Plotly
Explicación NLProduce explicaciones en español de los resultados
ConversacionalDetecta si una pregunta es conversacional o de datos

Versión actual

v2.0
Versión
98.3%
Exactitud in-domain
84.4%
Exactitud nuevas
~98%
Con adjudicación

Datos reales anonimizados: enero 2023 – junio 2024.

02 — Stack Tecnológico

CapaTecnologíaArchivo(s)
InterfazStreamlit 1.40+app.py
MotorGenBI (genbi/) — multi-candidato + voting + validacióngenbi/*.py
LLMDeepSeek v4 (pro para generación, flash para auxiliares)genbi/llm.py
EmbeddingsMiniLM (via ChromaDB)genbi/retrieval.py
BM25rank-bm25genbi/retrieval.py
Validación SQLsqlglot (parseo AST)genbi/validation.py
DatosSQLite (solo lectura)data/base_jac_real.db
VisualizaciónPandas + Plotlyapp.py
ContenedorizaciónDocker + Docker ComposeDockerfile, docker-compose.yml

Dependencias (requirements.txt)

openai>=1.0.0           # Cliente DeepSeek (compatible con OpenAI API)
chromadb>=1.5.0         # Embeddings MiniLM para retrieval híbrido
rank-bm25>=0.2.2        # Retrieval léxico (BM25)
sqlglot>=25.0.0         # Validación/parseo de SQL
numpy>=1.26.0           # Álgebra para fusión de retrieval
pandas>=2.2.0
plotly>=5.24.0
streamlit>=1.40.0       # Interfaz web
python-dotenv>=1.0.1    # Variables de entorno

03 — Arquitectura General

Diagrama de alto nivel

                        ┌─────────────────────────┐
                        │      Usuario humano      │
                        │  (ejecutivo / analista)  │
                        └────────────┬────────────┘
                                     │
                    ┌────────────────▼────────────────┐
                    │     Interfaz Streamlit (app.py)  │
                    │  · Chat web con historial        │
                    │  · Detección de intención (LLM)  │
                    │  · Gráficos Plotly               │
                    └────────────────┬────────────────┘
                                     │
                    ┌────────────────▼────────────────┐
                    │   Asistente (assistant.py)       │
                    │  · Analiza intención (CONV/DATA) │
                    │  · Traduce a formato Vanna       │
                    │  · Genera explicación NL          │
                    └────────────────┬────────────────┘
                                     │
                    ┌────────────────▼────────────────┐
                    │   Motor GenBI (engine.py)        │
                    │  · Orquesta todo el pipeline     │
                    │  · Self-correction               │
                    │  · Voting por ejecución          │
                    └────────────────┬────────────────┘
                                     │
          ┌──────────────────────────┼──────────────────────────┐
          │                          │                          │
  ┌───────▼────────┐  ┌─────────────▼─────────────┐  ┌────────▼────────┐
  │ Schema Linking  │  │   Retrieval Híbrido        │  │   Knowledge     │
  │ (determinista)  │  │   (embeddings + BM25)      │  │   (DDL + catálogo│
  │                 │  │                             │  │    de valores)   │
  └───────┬────────┘  └─────────────┬─────────────┘  └────────┬────────┘
          │                          │                          │
          └──────────────────────────┼──────────────────────────┘
                                     │
                    ┌────────────────▼────────────────┐
                    │   Generación (generation.py)     │
                    │  · N candidatos (1 greedy + N-1  │
                    │    muestreados a T=0.6)          │
                    │  · Batch paralelo                │
                    └────────────────┬────────────────┘
                                     │
                    ┌────────────────▼────────────────┐
                    │   Validación (validation.py)     │
                    │  · sqlglot (AST check)           │
                    │  · Solo SELECT, 1 sentencia      │
                    │  · Auto-repair de STRFTIME       │
                    └────────────────┬────────────────┘
                                     │
                    ┌────────────────▼────────────────┐
                    │   Ejecución (sql_exec.py)        │
                    │  · Read-only mode=ro             │
                    │  · Timeout 25s                   │
                    │  · Execution feedback loop       │
                    └────────────────┬────────────────┘
                                     │
                    ┌────────────────▼────────────────┐
                    │   Voting (engine.py)             │
                    │  · Firma por result-set          │
                    │  · Majority voting ponderado     │
                    │  · Desempata por SQL más simple  │
                    └────────────────┬────────────────┘
                                     │
                    ┌────────────────▼────────────────┐
                    │   Respuesta                      │
                    │  · SQL + resultado + traza       │
                    │  · Explicación NL + gráfico      │
                    └─────────────────────────────────┘
    

Flujo de datos completo

  1. El usuario escribe una pregunta en el chat.
  2. Detección de intención (analizar_pregunta en app.py): un LLM clasifica si es conversacional (CONV) o de datos (DATA). Si es conversacional, responde directamente sin ir a la base de datos.
  3. Si es DATA, reescribe la pregunta como autónoma incorporando contexto de la conversación previa.
  4. Schema linking (schema_linking.py): identifica qué columnas, ciudades y valores relevantes menciona la pregunta.
  5. Retrieval híbrido (retrieval.py): recupera ejemplos few-shot y reglas de negocio relevantes usando embeddings + BM25 con RRF.
  6. Generación de candidatos (generation.py): construye prompt completo y genera N candidatos SQL.
  7. Validación estática (validation.py): cada candidato se valida con sqlglot.
  8. Ejecución read-only (sql_exec.py): cada candidato se ejecuta contra SQLite en modo solo lectura.
  9. Execution feedback (engine.py): si falla, se reintentа pasando el error al LLM (hasta 2 rondas).
  10. Voting (engine.py): se agrupan candidatos por firma de result-set. Gana la firma con más votos.
  11. Explicación NL (assistant.py): el LLM genera 1-2 oraciones explicando el resultado.
  12. Visualización (app.py): se muestra tabla humanizada y gráfico de barras con Plotly.

04 — Primeros Pasos — Instalación

Requisitos previos

Instalación local

# 1. Clonar el repositorio
git clone <url-del-repositorio>
cd aura-genbi

# 2. Crear entorno virtual (recomendado)
python -m venv .venv
source .venv/bin/activate   # Linux/Mac
# .venv\Scripts\activate    # Windows

# 3. Instalar dependencias
pip install -r requirements.txt

# 4. Configurar variables de entorno
cp .env.example .env
# Editar .env y agregar tu DEEPSEEK_API_KEY

# 5. Generar datos de ejemplo (opcional, para pruebas)
python generate_data.py

# 6. Cargar datos reales (si tienes los CSVs anonimizados de JAC)
python load_real_data.py

# 7. Lanzar la interfaz web
streamlit run app.py
# Se abre en http://localhost:8501

Usando Make

El proyecto incluye un Makefile con atajos:

make install          # pip install -r requirements.txt
make app              # streamlit run app.py
make test             # python tests/test_comparator.py
make cli Q='¿Cuánto se vendió en 2024?'
make eval-heldout     # Evaluación held-out (45 preguntas)
make eval-robustness  # Evaluación robustez (coloquial/typos)
make eval-hard        # Evaluación hard (CTE/ventanas)
make ablations        # Estudio de ablación
make showcase         # Genera reports/MODEL_SHOWCASE.md

05 — Variables de Entorno

El archivo .env (nunca commitear) controla todo el comportamiento del sistema.

Archivo .env.example

# DeepSeek API (OBLIGATORIO)
DEEPSEEK_API_KEY=tu_api_key_aqui
DEEPSEEK_BASE_URL=https://api.deepseek.com/v1

# Modelos
DEEPSEEK_MODEL=deepseek-v4-pro          # Modelo principal (generación SQL)
DEEPSEEK_MODEL_FLASH=deepseek-v4-flash  # Modelo auxiliar (barato, tareas simples)

# Base de datos
SQLITE_PATH=data/base_jac_real.db       # Ruta al archivo SQLite
CHROMA_PATH=./chroma                    # Directorio de ChromaDB (embeddings)

# Motor GenBI
GENBI_INDEX_PATH=./.genbi_index         # Índice de recuperación persistente

Tabla de variables

VariableObligatoriaDefaultDescripción
DEEPSEEK_API_KEYAPI key de DeepSeek
DEEPSEEK_BASE_URLNohttps://api.deepseek.com/v1Endpoint de la API
DEEPSEEK_MODELNodeepseek-v4-proModelo para generación SQL
DEEPSEEK_MODEL_FLASHNodeepseek-v4-flashModelo auxiliar
SQLITE_PATHNodata/base_jac_real.dbRuta al SQLite
CHROMA_PATHNo./chromaDirectorio de ChromaDB
GENBI_INDEX_PATHNo./.genbi_indexÍndice de recuperación

06 — Uso de la Interfaz Web (Streamlit)

Acceso

streamlit run app.py
# Abre http://localhost:8501 en tu navegador

Elementos de la interfaz

Header

Barra de KPIs

En la parte superior se muestran 5 indicadores clave:

Chat

Tipos de interacción

Preguntas de datos

El sistema genera SQL, ejecuta, y muestra:

  1. Tabla de resultados con columnas humanizadas
  2. Gráfico de barras automático (si hay ≥2 filas con columna numérica + textual)
  3. Explicación en texto del resultado clave

Preguntas conversacionales

El sistema responde directamente sin SQL:

Sugerencias predefinidas

SugerenciaTipo de análisis
"¿Cuál fue la ruta con más ingresos?"Ranking
"Ventas por mes en 2023"Temporal
"Top 5 rutas con más boletos"Ranking top-N
"¿Cuánto se vendió por Kupos?"Subcanal específico
"Ticket promedio por tipo de asiento"Comparativa por dimensión
"¿Cuántos pasajeros únicos viajaron?"Métrica simple
"Enero 2023 vs enero 2024"Comparativa interanual
"Ventas por subcanal de venta"Ranking por subcanal

Humanización de resultados

TransformaciónEjemplo
Rutas legiblestmco-concTemuco → Concepción
Montos CLP285057050$285.057.050
Números formateados4388243,882
Nombres de columnaingresos_totalesIngresos
Ticket promedio6079$6,079

07 — Uso por Línea de Comandos (CLI)

Pregunta única

python ask.py "¿Cuál fue la ruta con más ingresos?"

Salida típica:

── SQL ──────────────────────────────────────────
SELECT ORIGEN_DESTINO, SUM(VALOR) AS ingresos_totales
FROM ventas GROUP BY ORIGEN_DESTINO ORDER BY ingresos_totales DESC LIMIT 1

── Resultado ────────────────────────────────────
Ruta | Ingresos
Temuco → Concepción | $285,057,050

── AURA ─────────────────────────────────────────
La ruta Temuco → Concepción generó los mayores ingresos con un total de $285 millones.

[diag] candidatos=3 votos=3 filas=1 t=4.2s

Modo interactivo (REPL)

python ask.py
# Se inicia un REPL donde puedes escribir preguntas
# Escribe "salir", "exit", "quit" o presiona Ctrl-D para terminar

Opciones de CLI

python ask.py --sql-only "¿Cuántos boletos hay?"  # Solo imprime el SQL generado
python ask.py --candidates 5 "Pregunta aquí"       # Usa 5 candidatos (default: 3)

08 — Uso Programático (API Python)

from genbi import Engine, EngineConfig
from genbi.assistant import Assistant

# Crear el motor con configuración personalizada
cfg = EngineConfig(n_candidates=5, use_voting=True)
eng = Engine(cfg)

# Pregunta directa
ans = eng.ask("¿Cuál fue la ruta con más ingresos contando ida y vuelta?")

# Resultados
print(ans.sql)            # SQL final generado
print(ans.result.rows)    # Filas del resultado
print(ans.result.columns) # Nombres de columnas
print(ans.n_votes)        # Votos que ganó
print(ans.trace)          # Traza de diagnóstico
print(ans.elapsed_s)      # Tiempo total en segundos
print(ans.candidates)     # Todos los candidatos evaluados

# Con asistente (incluye explicación NL)
asst = Assistant(eng)
ans = asst.ask("Top 5 rutas con más boletos")
df = DataFrame(ans.result.rows, columns=ans.result.columns)
explanation = asst.explain("Top 5 rutas con más boletos", df, [])
print(explanation)

Objeto Answer

CampoTipoDescripción
questionstrPregunta original
sqlstr | NoneSQL generado (None si falló)
okboolTrue si la consulta fue exitosa
resultExecResult | NoneResultado de la ejecución
candidateslistTodos los candidatos evaluados
tracelistTraza de diagnóstico
n_votesintVotos del candidato ganador
elapsed_sfloatTiempo total en segundos
errorstrMensaje de error si falló

09 — Motor GenBI — Explicación Detallada

Estructura del paquete genbi/

genbi/ ├── __init__.py # Expone Engine, EngineConfig, Answer ├── config.py # Configuración central (hiperparámetros, flags) ├── llm.py # Cliente DeepSeek con caché, reintentos, batch ├── knowledge.py # Introspección del esquema + catálogo de valores ├── retrieval.py # Retrieval híbrido (embeddings + BM25 + RRF) ├── schema_linking.py # Grounding determinista NL→esquema ├── validation.py # Validación estática + auto-repair con sqlglot ├── generation.py # Construcción de prompts + muestreo de candidatos ├── engine.py # Orquestador principal del pipeline ├── assistant.py # Capa conversacional + adaptador VannaCompat └── sql_exec.py # Ejecución read-only + comparación de result-sets

config.py — Hiperparámetros

ParámetroDefaultDescripción
modeldeepseek-v4-proModelo LLM para generación principal
aux_modeldeepseek-v4-flashModelo auxiliar (result check, intent detection)
n_examples8Cantidad de ejemplos few-shot a inyectar
n_docs12Cantidad de documentos de reglas de negocio
use_embeddingsTrueUsar embeddings (True=híbrido, False=solo BM25)
n_candidates3Número de candidatos SQL a generar
temperature_main0.0Temperatura para el candidato greedy
temperature_sample0.6Temperatura para candidatos muestreados
max_tokens1400Tokens máximos de respuesta del LLM
use_schema_linkingTrueActivar schema linking determinista
use_validationTrueActivar validación estática con sqlglot
use_execution_feedbackTrueActivar auto-corrección por ejecución
use_votingTrueActivar voting por ejecución
use_result_checkFalseValidación de coherencia del resultado con LLM
max_repair_attempts2Intentos máximos de auto-corrección
exec_timeout_s25.0Timeout de ejecución SQL en segundos
row_limit_preview5000Límite de filas en el resultado
max_workers5Workers paralelos para batch de candidatos

llm.py — Cliente LLM

knowledge.py — Capa de Conocimiento

Tres fuentes de grounding:

  1. Esquema vivo: introspección directa de la tabla ventas vía PRAGMA table_info. Siempre sincronizado con los datos reales.
  2. Catálogo de valores: valores distintos de columnas categóricas, códigos de rutas, rango de fechas, meses disponibles.
  3. Diccionario de negocio (diccionario_jac.py): DDL, reglas de negocio documentadas y 118+ pares pregunta→SQL validados.

retrieval.py — Retrieval Híbrido

Combinación de dos señales:

Fusión con Reciprocal Rank Fusion (RRF): combina los rankings de ambas señales de forma robusta y sin parámetros.

schema_linking.py — Grounding Determinista

Mapeo keyword→columna que evita los errores más comunes:

KeywordsColumna
"precio"/"costo"/"ingresos"VALOR
"ruta"/"corredor"ORIGEN_DESTINO
"canal"/"online"CANAL_VENTA
"asiento"/"cama"TIPO_ASIENTO
"pasajero"DOCUMENTO_PASAJERO_ANONIMIZADO

Además detecta ciudades y mapea a códigos (ej: "Temuco" → "tmco"), muestra rutas candidatas y recuerda convenciones de fechas.

validation.py — Validación Estática

generation.py — Construcción de Prompts

El prompt del sistema instruye al LLM para:

engine.py — Orquestador

para cada candidato:
    1. validar estáticamente (sqlglot)
    2. auto-reparar errores conocidos
    3. ejecutar read-only (con timeout)
    4. si falla o vacío → execution feedback (hasta 2 rondas):
        - pasar error al LLM
        - generar SQL corregido
        - re-validar
        - re-ejecutar
    5. si devolvió datos → candidato exitoso

voting:
    - agrupar candidatos exitosos por firma del result-set
    - ganador = firma con más votos (ponderados)
    - desempate: SQL más simple
    - (opcional) result_check con LLM si hay empate

retornar Answer con SQL, resultado, traza y diagnóstico

sql_exec.py — Ejecución y Comparación

10 — Base de Datos y Esquema

Tabla principal: ventas

CREATE TABLE ventas (
    NRO_BOLETO                      INTEGER,  -- ID único del boleto/transacción
    TIPO_DOCUMENTO_PASAJERO         TEXT,     -- Tipo de documento (ej: 'RUT')
    DOCUMENTO_PASAJERO_ANONIMIZADO  INTEGER,  -- Documento anonimizado (PII protegida)
    LINEA                           INTEGER,  -- Línea de servicio (5, 9)
    FECHA_COMPRA                    INTEGER,  -- Fecha compra, YYYYMMDD (ej: 20230122)
    HORA_COMPRA                     INTEGER,  -- Hora compra, HHMM (ej: 2033)
    VALOR                           REAL,     -- Monto del boleto en CLP
    ORIGEN_DESTINO                  TEXT,     -- Trayecto: 'codigoOrigen-codigoDestino'
    FECHA_SALIDA_SERVICIO           INTEGER,  -- Fecha salida, YYYYMMDD
    HORA_SALIDA_SERVICIO            INTEGER,  -- Hora salida, HHMM
    TIPO_ASIENTO                    TEXT,     -- Categoría de asiento
    CANAL_VENTA                     TEXT,     -- 'Presencial' o 'Internet'
    SUBCANAL_VENTA                  TEXT      -- Subcanal específico
);

Convenciones de fechas

Tablas auxiliares (pre-calculadas)

TablaContenido
kpis_mensualesboletos, ingresos, ticket promedio por año/mes
kpis_rutasboletos, ingresos, ticket promedio por ruta
kpis_canalesboletos, ingresos por canal/subcanal

Códigos de ciudad

CódigoCiudadCódigoCiudad
stgoSantiagolonqLonquimay
pconPucóncnpeCañete
tmcoTemuconstoN. Sto.
concConcepciónlsagLos Sauces
angoAngollebuLebu
chllChillánmulcMulchén
mottMottillactinCuranilahue
valdValdivialoncLoncoche
victVictoriavillVillarrica
crreCorralesrncoRanco
aptoApto. TemucolrayLos Ríos
cauqCauquenesclliCollin

Formato de rutas

Las rutas se almacenan como ORIGEN_DESTINO con formato codigoOrigen-codigoDestino en minúsculas:

Corredores bidireccionales

Cuando el usuario pide datos de "ida y vuelta", hay que normalizar los dos sentidos:

-- Normalización del corredor (alfabéticamente primero el menor)
CASE
  WHEN SUBSTR(ORIGEN_DESTINO,1,INSTR(ORIGEN_DESTINO,'-')-1)
       < SUBSTR(ORIGEN_DESTINO,INSTR(ORIGEN_DESTINO,'-')+1)
  THEN ORIGEN_DESTINO
  ELSE SUBSTR(ORIGEN_DESTINO,INSTR(ORIGEN_DESTINO,'-')+1)
       ||'-'||SUBSTR(ORIGEN_DESTINO,1,INSTR(ORIGEN_DESTINO,'-')-1)
END AS corredor

O más simple: WHERE ORIGEN_DESTINO IN ('tmco-conc', 'conc-tmco')

11 — Diccionario de Negocio JAC

El archivo diccionario_jac.py contiene toda la información de negocio que el motor inyecta al LLM.

1. DDL

La definición exacta de la tabla ventas (ver sección anterior).

2. Reglas de negocio (DOCUMENTACION)

100+ reglas documentadas incluyendo:

3. Ejemplos validados (EJEMPLOS)

118+ pares pregunta→SQL validados sobre datos reales, cubriendo: rankings, análisis temporales, corredores bidireccionales, canales, tipos de asiento, pasajeros, geografía, horarios, distribución de precios, KPIs, líneas de servicio, concentración (Pareto), preguntas combinadas y window functions.

Cómo extender el diccionario

Tip Para agregar nuevos ejemplos validados, agregar al final de la lista EJEMPLOS en diccionario_jac.py:
# En diccionario_jac.py, agregar al final de la lista EJEMPLOS:
{
    "question": "Tu nueva pregunta en español",
    "sql": "SELECT ... FROM ventas WHERE ...".strip(),
},
Importante El SQL debe ser correcto y ejecutarse sin errores sobre la base de datos.

12 — Pipeline de Generación de SQL

Ejemplo completo: "¿Cuál fue la ruta con más ingresos contando ida y vuelta?"

1
Detección de intención — LLM clasifica: DATA (requiere consulta de datos). Reescribe como pregunta autónoma.
2
Schema linking — Columnas detectadas: VALOR (ingresos), ORIGEN_DESTINO (ruta). Nota: pregunta de corredor/bidireccional → normalizar.
3
Retrieval híbrido — Recupera ejemplos de ingresos por ruta, corredores bidireccionales y reglas de convención.
4
Generación de candidatos — Candidato 1 (greedy, T=0): SQL con CASE de normalización. Candidato 2 (T=0.6): SQL con IN(...). Candidato 3 (T=0.6): variante con alias diferente.
5
Validación — Los 3 candidatos pasan: solo SELECT, tablas conocidas.
6
Ejecución — Candidato 1: 72 filas (normalizado). Candidato 2: 144 filas (sin normalizar). Candidato 3: 72 filas.
7
Voting — Firma 1 (72 filas): 2 votos. Firma 2 (144 filas): 1 voto. Ganador: Firma 1 con 2/3 votos.
8
Explicación — "La ruta con más ingresos considerando ambos sentidos es Temuco → Concepción, con un total de $285 millones en el período analizado."

13 — Sistema de Evaluación

Métrica: Execution Accuracy (EX)

Estándar de Spider/BIRD: dos consultas son equivalentes si sus result-sets coinciden. El comparador es robusto a orden de filas/columnas, columnas auxiliares extra, redondeo (±0.5 CLP) y formato de etiquetas temporales.

Benchmarks

BenchmarkPreguntasDescripción
In-domain118Ejemplos de entrenamiento (mide techo)
Held-out45Preguntas nuevas no vistas (mide generalización)
Robustez30Coloquial, typos, ambiguas, multi-filtro
Hard30CTE, ventanas, cohortes, percentiles

Ejecutar evaluación

# Held-out (generalización)
python eval/harness.py \
    --benchmark eval/benchmark_heldout.json \
    --out reports/heldout --label heldout

# Robustez
python eval/harness.py \
    --benchmark eval/benchmark_robustness.json \
    --out reports/robustness --label robustness

# Hard
python eval/harness.py \
    --benchmark eval/benchmark_hard.json \
    --out reports/hard --label hard

# In-domain (default)
python eval/harness.py --out reports/indomain --label indomain

Opciones de evaluación

--candidates N     # Número de candidatos (default: 5)
--workers N        # Workers paralelos (default: 4)
--sample N         # Muestrear N preguntas aleatorias
--no-voting        # Desactivar voting
--no-feedback      # Desactivar execution feedback
--no-linking       # Desactivar schema linking
--no-embeddings    # Solo BM25 (sin embeddings)
--model NAME       # Cambiar el modelo LLM

Estudio de ablación

python eval/ablations.py
ConfiguraciónExactitud%LatenciaTokens
0_naive (1 cand, sin nada)38/4584.4%13.49s134,803
1_+validacion38/4584.4%3.04s0
2_+schema_linking39/4586.7%3.34s14,906
3_+execution_feedback39/4586.7%3.15s0
4_+voting (FULL, 5 cand)38/4584.4%3.69s59,153
5_FULL_sin_embeddings36/4580.0%11.33s732,105
6_FULL_flash_model39/4586.7%8.85s677,064

Showcase cualitativo

python eval/showcase.py --explain
# Genera reports/MODEL_SHOWCASE.md

Tests del comparador

python tests/test_comparator.py

Batería de 21 casos adversariales que verifican que el comparador acepta variaciones legítimas y rechaza errores reales.

14 — Despliegue con Docker

Dockerfile

FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8501
CMD ["sh", "-c", "streamlit run app.py --server.port=8501 --server.address=0.0.0.0"]

docker-compose.yml

services:
  jac-genbi:
    build: .
    container_name: jac-genbi
    ports:
      - "8501:8501"
    env_file:
      - .env
    volumes:
      - ./chroma:/app/chroma     # memoria vectorial persistente
      - ./data:/app/data         # base de datos

Desplegar

# Construir y levantar
docker compose up -d --build

# Ver logs
docker compose logs -f

# Detener
docker compose down

Persistencia

Los volúmenes montados aseguran que:

Producción

Recomendaciones Para producción considerar: (1) PostgreSQL en vez de SQLite, (2) reverse proxy (nginx) con HTTPS, (3) autenticación (Streamlit auth o proxy con auth básica), (4) monitoreo de uso y latencia, (5) alertas de errores del LLM.

15 — Seguridad

Garantías de solo lectura

El sistema tiene tres capas de protección contra escritura:

Capa 1
Validación estática
Capa 2
Parsing AST
Capa 3
SQLite mode=ro
  1. Validación estática (validation.py): rechaza cualquier SQL que no empiece con SELECT o WITH...SELECT.
  2. Parsing AST (sqlglot): verifica que la consulta sea un único statement SELECT sobre tablas conocidas.
  3. Ejecución SQLite mode=ro: la conexión se abre con file:path?mode=ro, garantía a nivel de SQLite de que no se puede escribir.

Protección de datos personales

Variables sensibles

16 — Cómo Mejorar el Sistema

Mejoras implementadas (v2)

#MejoraEstadoImpacto
1Execution FeedbackImplementado+5-10% accuracy
2Multi-Candidate + VotaciónImplementado+3-5% accuracy
3Schema Linking explícitoImplementado+2-4% accuracy
4Query Validation LayerImplementadoReduce errores repetitivos
5Few-Shot dinámicoImplementado+1-3% accuracy

Mejoras pendientes

#MejoraPrioridadImpacto est.Esfuerzo
6Question DecompositionBaja+2-5% en complejasAlto
7Result Validation con LLMBaja+1-2% accuracyMedio
8Logging para RetrainingBajaMejora continuaBajo

Guías de extensión

Agregar nuevas preguntas de entrenamiento

  1. Agregar la pregunta y SQL validado a diccionario_jac.pyEJEMPLOS
  2. Re-ejecutar el benchmark: python eval/harness.py --out reports/after_addition
  3. El retrieval híbrido automáticamente incorporará los nuevos ejemplos

Agregar nuevas reglas de negocio

  1. Agregar la regla a diccionario_jac.pyDOCUMENTACION
  2. La regla se inyectará automáticamente a los prompts relevantes

Cambiar de modelo LLM

  1. Si es compatible con OpenAI API: solo cambiar DEEPSEEK_MODEL en .env
  2. Si usa otro formato: modificar genbi/llm.py
  3. Ajustar max_tokens y temperature en EngineConfig

Escalar a PostgreSQL

  1. Modificar genbi/sql_exec.py para usar psycopg2
  2. Modificar genbi/knowledge.py para introspeccionar esquema PostgreSQL
  3. Cambiar SQLITE_PATH por connection string
  4. Ajustar consultas de introspección (PRAGMAinformation_schema)

Agregar nuevas métricas

  1. En app.py, agregar la columna a COL_DISPLAY
  2. Agregar keywords a _CLP_KW si es un monto en CLP
  3. En diccionario_jac.py, agregar la definición en DOCUMENTACION

17 — Solución de Problemas

"Falta DEEPSEEK_API_KEY en el archivo .env"

Causa No se configuró la API key de DeepSeek.
cp .env.example .env
# Editar .env y agregar tu DEEPSEEK_API_KEY

"No pude inicializar el agente"

Causa Error al crear el motor GenBI (falta API key, modelo no disponible, etc.).
  1. Verificar que .env tenga DEEPSEEK_API_KEY válido
  2. Verificar que data/base_jac_real.db exista
  3. Revisar logs del error específico

La interfaz no carga / queda en blanco

pip install streamlit
streamlit run app.py --logger.level=debug

El LLM devuelve SQL incorrecto

El sistema aplica execution feedback automáticamente. Si después de 2 intentos sigue fallando, reescribir la pregunta de forma más específica:

El gráfico no aparece

Plotly solo genera gráfico si hay ≥2 filas con al menos una columna numérica y una textual. Hacer una pregunta que retorne múltiples filas.

Lentitud en respuestas

  1. Reducir n_candidates en EngineConfig (default 3)
  2. Usar DEEPSEEK_MODEL_FLASH para generación
  3. Verificar conexión a internet (latencia a la API de DeepSeek)

Los datos muestran "0" o vacío

-- Verificar qué meses tienen datos
SELECT DISTINCT SUBSTR(CAST(FECHA_COMPRA AS TEXT),1,6) FROM ventas;

-- Verificar rango de fechas
SELECT MIN(FECHA_COMPRA), MAX(FECHA_COMPRA) FROM ventas;

Error de Docker: "port already in use"

lsof -ti:8501 | xargs kill -9
# O usar otro puerto en docker-compose.yml: "8502:8501"

18 — Glosario

TérminoDefinición
AURAAsistente conversacional de JAC
GenBIMotor Text-to-SQL v2 (reemplazo de Vanna AI)
Text-to-SQLTraducción de lenguaje natural a consultas SQL
Execution AccuracyMétrica estándar: dos SQL son iguales si sus result-sets coinciden
Multi-candidateGenerar N candidatos SQL y votar por el mejor
Execution feedbackReintentar un SQL fallido pasando el error al LLM
VotingSeleccionar el resultado más frecuente entre candidatos
Schema linkingMapear lenguaje natural a columnas/valores del esquema
Retrieval híbridoCombinar embeddings (semántico) + BM25 (léxico)
RRFReciprocal Rank Fusion — fusión de rankings
Few-shotEjemplos pregunta→SQL inyectados en el prompt
sqlglotLibrería de parseo/validación SQL con AST
Self-correctionAuto-corrección del LLM guiada por errores de ejecución
CorredorRuta normalizada bidireccional (ej: Temuco-Concepción = tmco-conc + conc-tmco)
Execution feedback loopCiclo: ejecutar → fallar → pasar error → regenerar → re-ejecutar
AdjudicaciónRevisión manual de fallos para determinar si eran realmente correctos
Ablation studyMedir la contribución de cada componente activando/desactivando
CLPPesos chilenos (moneda)
PIIPersonally Identifiable Information (información personal identificable)
mode=roModo solo lectura de SQLite

19 — Referencias

Paper del proyecto

Investigación académica

Métricas de evaluación

20 — Estructura Completa del Proyecto

aura-genbi/ ├── app.py # Interfaz web Streamlit ├── ask.py # CLI interactivo ├── diccionario_jac.py # Diccionario de negocio JAC ├── generate_data.py # Genera base de datos de ejemplo ├── load_real_data.py # Carga CSVs anonimizados reales ├── genbi/ # Motor Text-to-SQL GenBI │ ├── __init__.py # API pública │ ├── config.py # Configuración central │ ├── llm.py # Cliente LLM con caché y batch │ ├── knowledge.py # Introspección del esquema + catálogo │ ├── retrieval.py # Retrieval híbrido (embeddings + BM25) │ ├── schema_linking.py # Grounding determinista NL→esquema │ ├── validation.py # Validación SQL + auto-repair │ ├── generation.py # Construcción de prompts + muestreo │ ├── engine.py # Orquestador del pipeline │ ├── assistant.py # Capa conversacional + VannaCompat │ └── sql_exec.py # Ejecución read-only + comparación ├── eval/ # Herramientas de evaluación │ ├── harness.py # Harness de evaluación + reportes │ ├── ablations.py # Estudio de ablación │ ├── showcase.py # Showcase cualitativo del modelo │ ├── build_report.py # Generador de reportes │ ├── rescore.py # Re-escorar resultados existentes │ ├── benchmark_heldout.json # 45 preguntas │ ├── benchmark_robustness.json # 30 preguntas │ ├── benchmark_hard.json # 30 preguntas │ └── CONTEXT_FOR_BENCHMARK.md # Contexto para benchmarks ├── tests/ │ └── test_comparator.py # Tests del comparador de result-sets ├── reports/ # Reportes de evaluación ├── data/ # Datos (regenerables) ├── chroma/ # ChromaDB (memoria vectorial) ├── legacy/ # Código legado (Vanna AI, Chainlit) ├── requirements.txt ├── Dockerfile ├── docker-compose.yml ├── Makefile ├── .env.example ├── .gitignore ├── README.md ├── ARQUITECTURA.md ├── MEJORAS_TEXT_TO_SQL.md └── eval_report.md