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.
| Capacidad | Descripción |
|---|---|
| Text-to-SQL | Traduce preguntas en español a SQL de SQLite de solo lectura |
| Multi-candidato | Genera múltiples candidatos SQL y vota por el mejor |
| Auto-corrección | Si un SQL falla, lo reintenta con el error como contexto |
| Validación estática | Verifica que el SQL sea seguro (solo SELECT) antes de ejecutar |
| Retrieval híbrido | Recupera ejemplos relevantes usando embeddings + BM25 |
| Schema linking | Mapea lenguaje natural a columnas reales de la base de datos |
| Visualización | Genera gráficos de barras automáticos con Plotly |
| Explicación NL | Produce explicaciones en español de los resultados |
| Conversacional | Detecta si una pregunta es conversacional o de datos |
Datos reales anonimizados: enero 2023 – junio 2024.
| Capa | Tecnología | Archivo(s) |
|---|---|---|
| Interfaz | Streamlit 1.40+ | app.py |
| Motor | GenBI (genbi/) — multi-candidato + voting + validación | genbi/*.py |
| LLM | DeepSeek v4 (pro para generación, flash para auxiliares) | genbi/llm.py |
| Embeddings | MiniLM (via ChromaDB) | genbi/retrieval.py |
| BM25 | rank-bm25 | genbi/retrieval.py |
| Validación SQL | sqlglot (parseo AST) | genbi/validation.py |
| Datos | SQLite (solo lectura) | data/base_jac_real.db |
| Visualización | Pandas + Plotly | app.py |
| Contenedorización | Docker + Docker Compose | Dockerfile, docker-compose.yml |
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
┌─────────────────────────┐
│ 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 │
└─────────────────────────────────┘
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.schema_linking.py): identifica qué columnas, ciudades y valores relevantes menciona la pregunta.retrieval.py): recupera ejemplos few-shot y reglas de negocio relevantes usando embeddings + BM25 con RRF.generation.py): construye prompt completo y genera N candidatos SQL.validation.py): cada candidato se valida con sqlglot.sql_exec.py): cada candidato se ejecuta contra SQLite en modo solo lectura.engine.py): si falla, se reintentа pasando el error al LLM (hasta 2 rondas).engine.py): se agrupan candidatos por firma de result-set. Gana la firma con más votos.assistant.py): el LLM genera 1-2 oraciones explicando el resultado.app.py): se muestra tabla humanizada y gráfico de barras con Plotly.# 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
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
El archivo .env (nunca commitear) controla todo el comportamiento del sistema.
# 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
| Variable | Obligatoria | Default | Descripción |
|---|---|---|---|
DEEPSEEK_API_KEY | SÍ | — | API key de DeepSeek |
DEEPSEEK_BASE_URL | No | https://api.deepseek.com/v1 | Endpoint de la API |
DEEPSEEK_MODEL | No | deepseek-v4-pro | Modelo para generación SQL |
DEEPSEEK_MODEL_FLASH | No | deepseek-v4-flash | Modelo auxiliar |
SQLITE_PATH | No | data/base_jac_real.db | Ruta al SQLite |
CHROMA_PATH | No | ./chroma | Directorio de ChromaDB |
GENBI_INDEX_PATH | No | ./.genbi_index | Índice de recuperación |
streamlit run app.py
# Abre http://localhost:8501 en tu navegador
En la parte superior se muestran 5 indicadores clave:
El sistema genera SQL, ejecuta, y muestra:
El sistema responde directamente sin SQL:
| Sugerencia | Tipo 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 |
| Transformación | Ejemplo |
|---|---|
| Rutas legibles | tmco-conc → Temuco → Concepción |
| Montos CLP | 285057050 → $285.057.050 |
| Números formateados | 43882 → 43,882 |
| Nombres de columna | ingresos_totales → Ingresos |
| Ticket promedio | 6079 → $6,079 |
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
python ask.py
# Se inicia un REPL donde puedes escribir preguntas
# Escribe "salir", "exit", "quit" o presiona Ctrl-D para terminar
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)
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)
| Campo | Tipo | Descripción |
|---|---|---|
question | str | Pregunta original |
sql | str | None | SQL generado (None si falló) |
ok | bool | True si la consulta fue exitosa |
result | ExecResult | None | Resultado de la ejecución |
candidates | list | Todos los candidatos evaluados |
trace | list | Traza de diagnóstico |
n_votes | int | Votos del candidato ganador |
elapsed_s | float | Tiempo total en segundos |
error | str | Mensaje de error si falló |
| Parámetro | Default | Descripción |
|---|---|---|
model | deepseek-v4-pro | Modelo LLM para generación principal |
aux_model | deepseek-v4-flash | Modelo auxiliar (result check, intent detection) |
n_examples | 8 | Cantidad de ejemplos few-shot a inyectar |
n_docs | 12 | Cantidad de documentos de reglas de negocio |
use_embeddings | True | Usar embeddings (True=híbrido, False=solo BM25) |
n_candidates | 3 | Número de candidatos SQL a generar |
temperature_main | 0.0 | Temperatura para el candidato greedy |
temperature_sample | 0.6 | Temperatura para candidatos muestreados |
max_tokens | 1400 | Tokens máximos de respuesta del LLM |
use_schema_linking | True | Activar schema linking determinista |
use_validation | True | Activar validación estática con sqlglot |
use_execution_feedback | True | Activar auto-corrección por ejecución |
use_voting | True | Activar voting por ejecución |
use_result_check | False | Validación de coherencia del resultado con LLM |
max_repair_attempts | 2 | Intentos máximos de auto-corrección |
exec_timeout_s | 25.0 | Timeout de ejecución SQL en segundos |
row_limit_preview | 5000 | Límite de filas en el resultado |
max_workers | 5 | Workers paralelos para batch de candidatos |
ThreadPoolExecutor.Tres fuentes de grounding:
ventas vía PRAGMA table_info. Siempre sincronizado con los datos reales.diccionario_jac.py): DDL, reglas de negocio documentadas y 118+ pares pregunta→SQL validados.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.
Mapeo keyword→columna que evita los errores más comunes:
| Keywords | Columna |
|---|---|
| "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.
SELECT o WITH...SELECTSTRFTIME sobre enteros por la versión correctaEl prompt del sistema instruye al LLM para:
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
file:path?mode=ro (garantiza solo lectura a nivel de SQLite).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
);
FECHA_COMPRA y FECHA_SALIDA_SERVICIO son enteros YYYYMMDDSUBSTR(CAST(FECHA_COMPRA AS TEXT), 1, 4) → añoSTRFTIME() directamente sobre el entero| Tabla | Contenido |
|---|---|
kpis_mensuales | boletos, ingresos, ticket promedio por año/mes |
kpis_rutas | boletos, ingresos, ticket promedio por ruta |
kpis_canales | boletos, ingresos por canal/subcanal |
| Código | Ciudad | Código | Ciudad |
|---|---|---|---|
stgo | Santiago | lonq | Lonquimay |
pcon | Pucón | cnpe | Cañete |
tmco | Temuco | nsto | N. Sto. |
conc | Concepción | lsag | Los Sauces |
ango | Angol | lebu | Lebu |
chll | Chillán | mulc | Mulchén |
mott | Mottilla | ctin | Curanilahue |
vald | Valdivia | lonc | Loncoche |
vict | Victoria | vill | Villarrica |
crre | Corrales | rnco | Ranco |
apto | Apto. Temuco | lray | Los Ríos |
cauq | Cauquenes | clli | Collin |
Las rutas se almacenan como ORIGEN_DESTINO con formato codigoOrigen-codigoDestino en minúsculas:
pcon-tmco = Pucón → Temucotmco-conc = Temuco → Concepciónstgo-pcon = Santiago → PucónCuando 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')
El archivo diccionario_jac.py contiene toda la información de negocio que el motor inyecta al LLM.
La definición exacta de la tabla ventas (ver sección anterior).
100+ reglas documentadas incluyendo:
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.
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(),
},
Ejemplo completo: "¿Cuál fue la ruta con más ingresos contando ida y vuelta?"
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.
| Benchmark | Preguntas | Descripción |
|---|---|---|
| In-domain | 118 | Ejemplos de entrenamiento (mide techo) |
| Held-out | 45 | Preguntas nuevas no vistas (mide generalización) |
| Robustez | 30 | Coloquial, typos, ambiguas, multi-filtro |
| Hard | 30 | CTE, ventanas, cohortes, percentiles |
# 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
--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
python eval/ablations.py
| Configuración | Exactitud | % | Latencia | Tokens |
|---|---|---|---|---|
| 0_naive (1 cand, sin nada) | 38/45 | 84.4% | 13.49s | 134,803 |
| 1_+validacion | 38/45 | 84.4% | 3.04s | 0 |
| 2_+schema_linking | 39/45 | 86.7% | 3.34s | 14,906 |
| 3_+execution_feedback | 39/45 | 86.7% | 3.15s | 0 |
| 4_+voting (FULL, 5 cand) | 38/45 | 84.4% | 3.69s | 59,153 |
| 5_FULL_sin_embeddings | 36/45 | 80.0% | 11.33s | 732,105 |
| 6_FULL_flash_model | 39/45 | 86.7% | 8.85s | 677,064 |
python eval/showcase.py --explain
# Genera reports/MODEL_SHOWCASE.md
python tests/test_comparator.py
Batería de 21 casos adversariales que verifican que el comparador acepta variaciones legítimas y rechaza errores reales.
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"]
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
# Construir y levantar
docker compose up -d --build
# Ver logs
docker compose logs -f
# Detener
docker compose down
Los volúmenes montados aseguran que:
El sistema tiene tres capas de protección contra escritura:
validation.py): rechaza cualquier SQL que no empiece con SELECT o WITH...SELECT.file:path?mode=ro, garantía a nivel de SQLite de que no se puede escribir.DOCUMENTO_PASAJERO_ANONIMIZADO contiene documentos ya anonimizadosTIPO_DOCUMENTO_PASAJERO solo contiene "RUT" (tipo genérico)DEEPSEEK_API_KEY nunca se commitea (.env está en .gitignore)| # | Mejora | Estado | Impacto |
|---|---|---|---|
| 1 | Execution Feedback | Implementado | +5-10% accuracy |
| 2 | Multi-Candidate + Votación | Implementado | +3-5% accuracy |
| 3 | Schema Linking explícito | Implementado | +2-4% accuracy |
| 4 | Query Validation Layer | Implementado | Reduce errores repetitivos |
| 5 | Few-Shot dinámico | Implementado | +1-3% accuracy |
| # | Mejora | Prioridad | Impacto est. | Esfuerzo |
|---|---|---|---|---|
| 6 | Question Decomposition | Baja | +2-5% en complejas | Alto |
| 7 | Result Validation con LLM | Baja | +1-2% accuracy | Medio |
| 8 | Logging para Retraining | Baja | Mejora continua | Bajo |
diccionario_jac.py → EJEMPLOSpython eval/harness.py --out reports/after_additiondiccionario_jac.py → DOCUMENTACIONDEEPSEEK_MODEL en .envgenbi/llm.pymax_tokens y temperature en EngineConfiggenbi/sql_exec.py para usar psycopg2genbi/knowledge.py para introspeccionar esquema PostgreSQLSQLITE_PATH por connection stringPRAGMA → information_schema)app.py, agregar la columna a COL_DISPLAY_CLP_KW si es un monto en CLPdiccionario_jac.py, agregar la definición en DOCUMENTACIONcp .env.example .env
# Editar .env y agregar tu DEEPSEEK_API_KEY
.env tenga DEEPSEEK_API_KEY válidodata/base_jac_real.db existapip install streamlit
streamlit run app.py --logger.level=debug
El sistema aplica execution feedback automáticamente. Si después de 2 intentos sigue fallando, reescribir la pregunta de forma más específica:
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.
n_candidates en EngineConfig (default 3)DEEPSEEK_MODEL_FLASH para generación-- 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;
lsof -ti:8501 | xargs kill -9
# O usar otro puerto en docker-compose.yml: "8502:8501"
| Término | Definición |
|---|---|
| AURA | Asistente conversacional de JAC |
| GenBI | Motor Text-to-SQL v2 (reemplazo de Vanna AI) |
| Text-to-SQL | Traducción de lenguaje natural a consultas SQL |
| Execution Accuracy | Métrica estándar: dos SQL son iguales si sus result-sets coinciden |
| Multi-candidate | Generar N candidatos SQL y votar por el mejor |
| Execution feedback | Reintentar un SQL fallido pasando el error al LLM |
| Voting | Seleccionar el resultado más frecuente entre candidatos |
| Schema linking | Mapear lenguaje natural a columnas/valores del esquema |
| Retrieval híbrido | Combinar embeddings (semántico) + BM25 (léxico) |
| RRF | Reciprocal Rank Fusion — fusión de rankings |
| Few-shot | Ejemplos pregunta→SQL inyectados en el prompt |
| sqlglot | Librería de parseo/validación SQL con AST |
| Self-correction | Auto-corrección del LLM guiada por errores de ejecución |
| Corredor | Ruta normalizada bidireccional (ej: Temuco-Concepción = tmco-conc + conc-tmco) |
| Execution feedback loop | Ciclo: ejecutar → fallar → pasar error → regenerar → re-ejecutar |
| Adjudicación | Revisión manual de fallos para determinar si eran realmente correctos |
| Ablation study | Medir la contribución de cada componente activando/desactivando |
| CLP | Pesos chilenos (moneda) |
| PII | Personally Identifiable Information (información personal identificable) |
| mode=ro | Modo solo lectura de SQLite |
ARQUITECTURA.md — Diseño detallado del sistemaMEJORAS_TEXT_TO_SQL.md — Catálogo de mejoras con estado de implementaciónreports/EVALUACION_DETALLADA.md — Resultados completos de evaluaciónreports/ablations.md — Estudio de ablación