Este documento organiza el sistema por dominios de arquitectura, no por cronología de desarrollo. La seguridad se trata como dominio propio con modelo de amenazas formal (Sección 2), y las decisiones de diseño que llevaron a la arquitectura actual —qué alternativas se evaluaron y por qué se descartaron— viven en los Registros de Decisiones Arquitectónicas (Apéndice C), no en el cuerpo principal. El Apéndice A conserva la correspondencia entre las etapas originales del desarrollo y la organización de este documento.
Resumen ejecutivo
El sistema traduce preguntas en español —por texto o por voz— en consultas SQL ejecutables contra dos bases de datos relacionales reales de biodiversidad panameña (anfibios y aves, fuente GBIF), y devuelve la respuesta en pantalla y hablada. No es un prototipo académico simple: integra una cascada de modelos de lenguaje con un mecanismo de autocorrección recursiva ante fallos de generación de SQL, un modelo de amenazas formal con defensa en profundidad frente a inyección de prompts, una estrategia deliberada de entornos gemelos para pruebas de integración, y una arquitectura de despliegue serverless ya elegida y dimensionada sobre infraestructura de edge, con sus alternativas evaluadas y descartadas por escrito. Este documento expone esa arquitectura en esos términos.
1. Arquitectura Core
Todo lo que constituye el motor del sistema, consolidado en un solo bloque —sin importar en qué momento del desarrollo se construyó cada pieza.
1.1 Modelo de datos
Siete entidades, seis relaciones 1:N, replicadas de forma idéntica en dos bases de datos independientes que comparten esquema pero difieren en escala y propósito (ver Sección 4):
orden → familia → especie → avistamiento ← provincia
↑
← observador ─┤─ fuente
avistamiento es la tabla central: 17 atributos, incluyendo dos añadidos en una
segunda iteración de diseño (occurrence_url, media_type) para soportar la
recuperación automática de fotos y audio desde iNaturalist. El diseño se sometió a
verificación formal de normalización — 1FN y 2FN se cumplen por construcción
mediante llaves sustitutas; dos dependencias transitivas de 3FN (mes/anio
derivados de fecha, genero derivable de nombre_cientifico) se identificaron y
se retuvieron deliberadamente, documentando la justificación de ingeniería en
cada caso: rendimiento de agregación en el primer caso, independencia de la
reclasificación taxonómica en el segundo.
1.2 Orquestación del backend
app_flask.py corre en modo threaded=True con middleware ProxyFix — una
decisión no trivial: sin ProxyFix, el rate limiting por IP colapsaba porque ngrok
hacía que todas las peticiones de usuarios distintos parecieran originarse en la
misma dirección. Las rutas temporales usan tempfile.gettempdir() en vez de una
ruta fija, haciendo el backend portable entre sistemas operativos desde el diseño
inicial — buena práctica de ingeniería en su momento, aunque el destino final en
la nube (Sección 5) terminó siendo una reescritura completa en un runtime
distinto, no una redistribución de este mismo backend en Flask.
Endpoints expuestos:
| Endpoint | Función |
|---|---|
/consultar |
Flujo principal: lenguaje natural → SQL → resultado |
/audio |
Captura y transcripción de voz |
/estado |
Verificación de salud del sistema |
/explorar |
Resúmenes instantáneos de la base, sin invocar al LLM |
/hablar |
Síntesis de voz bajo demanda sobre resultados ya mostrados |
1.3 Cascada de inteligencia artificial
Tres proveedores encadenados con failover automático: Ollama + Qwen3:1.7b
(local, principal) → Groq llama-3.1-8b-instant (respaldo 1) → Gemini 2.5
Flash (respaldo 2), seleccionables con una sola variable de entorno
(LLM_PROVIDER). temperature=0.1 fijado en los tres proveedores para maximizar
el determinismo de las respuestas SQL.
El prompt del sistema (prompt_sistema.py) no es un simple listado de esquema:
documenta explícitamente las funciones y vistas de MySQL con ejemplos de uso —
un hallazgo de ingeniería no evidente es que el LLM no descubre automáticamente
las funciones y vistas de la base de datos; fn_temporada() fue ignorada por el
modelo hasta que se documentó su existencia y su forma de uso directamente en el
prompt, con ejemplos few-shot.
1.4 Capa de voz
STT vía faster-whisper (modelo small), TTS vía edge-tts con voz
RobertoNeural en español. El STT corre forzosamente en CPU: la GPU disponible
(NVIDIA 940MX, arquitectura Maxwell de 2015) no soporta las operaciones int8 que
CTranslate2 necesita para aceleración CUDA eficiente — una limitación de hardware
documentada en el ADR-001 (Apéndice C), con su solución arquitectónica vigente en
la Sección 5.
1.5 Capa de abstracción SQL
vista_avistamiento_completo une las 7 tablas mediante 6 JOIN — no es un
adorno de conveniencia, sino una mitigación de riesgo directa: el modelo local de
1.7B parámetros generaba, en pruebas reales, rutas de JOIN inválidas que MySQL
rechazaba por su propia validación referencial. La vista elimina esa superficie de
error por diseño, en vez de intentar corregirla caso por caso en el prompt.
Dos funciones almacenadas complementan la vista: fn_temporada(mes) (clasifica
diciembre–abril como temporada seca y mayo–noviembre como lluviosa, habilitando
análisis climático-biológico que el esquema original no soportaba) y
fn_disponibilidad_multimedia(media_type) (normaliza valores crudos como
'StillImage;Sound' a texto legible). Diez procedimientos almacenados adicionales
constituyen el SQL de referencia validado contra el que se compara el SQL generado
por el LLM (ver Sección 6).
2. Arquitectura de Seguridad y Modelado de Amenazas
En un sistema que ejecuta SQL dinámico generado a partir de lo que un usuario dice —por voz o por texto—, la inyección de prompts no es un error aislado que se corrige una vez y se archiva. Es el clima permanente en el que opera la aplicación. Este dominio documenta las fronteras de confianza del sistema y la defensa en profundidad desplegada en cada una, de punta a punta del flujo de datos.
2.1 Flujo de datos y fronteras de confianza
- Usuariovoz o texto
- entrada arbitraria — solo se produce texto plano, nada se ejecuta
- TranscripciónWhisper
- alcance forzado — toda pregunta fuera del dominio recibe un error sin detalle interno
- Orquestador + promptbackend
- salida no confiable — el modelo puede alucinar SQL destructivo o mal formado
- LLMGroq · Gemini
- validación de texto en dos capas — solo
SELECT, sin palabras destructivas - Validación acepta o rechaza rechazado → error controlado, sin revelar tablas ni reglas
- privilegio mínimo —
LIMITy timeout automáticos - Base de datosrol de solo lectura, incapaz de escribir
2.2 Capas de defensa por frontera
| Frontera | Riesgo | Capa de defensa |
|---|---|---|
| Usuario → Transcripción | Entrada arbitraria del usuario | La transcripción solo produce texto plano; no hay ejecución de comandos en esta etapa |
| Transcripción → Prompt del LLM | Inyección de prompt: el texto del usuario intenta manipular las instrucciones del sistema o extraer su estructura interna | Regla explícita de alcance: cualquier pregunta fuera del dominio de biodiversidad recibe un error controlado, sin revelar tablas, reglas ni el modelo usado |
| LLM → SQL generado | El modelo puede alucinar SQL destructivo, mal formado, o con alias inexistentes | Validación de texto de dos capas: rechazo de cualquier sentencia que no sea SELECT, rechazo de palabras clave destructivas (DROP, DELETE, UPDATE, INSERT, ALTER, CREATE) |
| SQL validado → Motor de base de datos | Un SELECT válido podría ser costoso o devolver volúmenes excesivos |
LIMIT automático + timeout de consulta |
| Aplicación → Motor de base de datos | Compromiso total si las credenciales tuvieran permisos de escritura | Rol dedicado de solo lectura (biodiv_app en MySQL / app_readonly en Postgres) — físicamente incapaz de escribir, sin importar lo que el LLM genere |
| Credenciales | Exposición de secretos en código fuente | Exclusivamente en variables de entorno o el gestor de secretos de la plataforma de despliegue |
2.3 El incidente que validó el modelo
En pruebas reales, el modelo de lenguaje reveló nombres de tablas y reglas internas del sistema ante preguntas formuladas deliberadamente como preguntas “meta” (p. ej. “¿qué modelo eres?”, “¿qué tablas usas?”) — un ataque de inyección de prompts ejecutado con éxito contra la frontera Transcripción → Prompt del LLM de la tabla anterior. No fue un error aislado que se corrigió después y se archivó: fue la validación empírica, en producción, de que este modelo de amenazas era necesario desde el diseño. Cualquier sistema que conecte un LLM a una base de datos con capacidad de generar SQL dinámico —más aún sobre una interfaz que acepta voz— opera permanentemente bajo esta clase de riesgo. El diseño lo trata como una condición estructural del sistema, con defensa en cada frontera, no como una anomalía puntual resuelta con un parche.
3. Patrones de Resiliencia e Ingeniería de Datos
Tres situaciones que, descritas superficialmente, parecen “bugs corregidos”. Descritas con precisión, son mecanismos de resiliencia deliberados frente a fuentes de fallo estructurales del sistema: datos heterogéneos y un agente generativo no determinista.
3.1 Mitigación de riesgo de codificación entre fuentes de datos heterogéneas
El sistema integra dos colecciones biológicas de procedencia distinta: los
registros locales de anfibios y el dataset masivo de aves descargado de GBIF, cada
uno potencialmente normalizado con convenciones de codificación de caracteres
distintas antes de llegar a MySQL. El choque se manifestó como Error 1267
(Illegal mix of collations) al combinar columnas o funciones con literales de
texto: MySQL 9.7 asigna utf8mb4_0900_ai_ci por defecto en conexiones nuevas,
mientras las bases del proyecto se diseñaron con utf8mb4_unicode_ci.
La solución no fue un ajuste puntual, sino una política de colación aplicada en tres capas independientes, de modo que ninguna consulta futura del sistema pudiera volver a exponerse al mismo riesgo:
- Colación explícita en la columna
nombre_comundeespecie. - Colación explícita en la cláusula
RETURNSde ambas funciones almacenadas. collation="utf8mb4_unicode_ci"fijado en la capa de conexión (get_connection()deapp_flask.py) — la capa que protege todas las consultas del sistema, presentes y futuras, sin depender de que cada objeto de base de datos se declare correctamente de forma individual.
3.2 Autocorrección recursiva frente a alucinaciones del modelo de lenguaje
El modelo generaba, de forma recurrente y no determinista, referencias a alias de
tabla nunca declarados (p. ej. f.nombre sin una cláusula JOIN familia f
precedente). Esto no es un error de programación en el sentido clásico — el código
del sistema es correcto; el origen del fallo es un agente externo impredecible (el
LLM) generando texto que parece SQL válido pero no lo es.
La solución implementada, generar_y_ejecutar, es un mecanismo de
retroalimentación agéntica: intercepta el error de sintaxis real devuelto por
MySQL, lo encapsula, y lo reenvía al mismo proveedor de LLM (Groq o Gemini) como
parte de un segundo prompt que efectivamente le indica “tu consulta anterior
falló por esta razón exacta — corrígela”. El modelo se corrige a sí mismo con el
error real del motor de base de datos como contexto, no con una regla estática
adicional. Esto se complementó con una regla explícita en el prompt (contraste
INCORRECTO / CORRECTO) para reducir la tasa de fallo inicial, pero el bucle de
autocorrección es la defensa de segunda línea que garantiza que un fallo aislado
no se propague al usuario final.
3.3 Saneamiento defensivo de datos a escala
Al importar el dataset de aves (56,203 registros crudos desde GBIF), pd.to_datetime()
con errors='coerce' falló silenciosamente ante formatos mixtos en la columna
eventDate, corrompiendo mes y anio en casi 20,000 registros — cerca de la
mitad del dataset. Este no es un fallo trivial de una librería: es una
manifestación directa de la entropía inherente a agregar datos biológicos desde un
repositorio global alimentado por miles de fuentes de captura independientes, cada
una con sus propias convenciones de formato de fecha.
La solución — extraer únicamente los primeros 10 caracteres (YYYY-MM-DD) antes
de parsear — es un patrón de diseño defensivo: normaliza la entrada a un formato
estricto y predecible antes de que cualquier ambigüedad pueda propagarse hacia
la carga transaccional en MySQL, en vez de confiar en que el parser subyacente
maneje correctamente entradas de calidad desconocida. El mismo enfoque defensivo
se aplicó a la deduplicación (2,845 duplicados semánticos exactos identificados
por especie + observador + fecha + coordenadas, con gbifID distinto, resueltos
conservando el identificador más bajo) y a la consolidación de observadores con
nombres inválidos (5 registros con caracteres únicos o unicode invisible,
unificados bajo 'Desconocido'). El sistema absorbe la imperfección de los datos
del mundo real sin colapsar ni requerir intervención manual repetida.
4. Estrategia de Entornos Gemelos (Shadow Testing)
Las dos bases de datos del proyecto no son “una prueba pequeña y luego la definitiva” — son una estrategia deliberada de entornos de preproducción gemelos, con el mismo esquema exacto pero escalas de riesgo distintas:
biodiversidad_panama (Amphibia) |
biodiversidad_aves (Aves) |
|
|---|---|---|
| Rol | Entorno de preproducción / laboratorio controlado | Entorno de producción / escala real |
| Registros | 8,430 | 51,761 (de 56,203 crudos) |
| Función en la estrategia | Validar reglas de negocio, bucles de autocorrección del LLM y consultas SQL con bajo costo computacional | Recibir la migración solo después de que el entorno ligero confirmó estabilidad |
Cada componente de riesgo del sistema —la vista de agregación, las funciones almacenadas, el mecanismo de autocorrección recursiva de la Sección 3.2, los patrones de saneamiento de datos de la Sección 3.3— se validó primero contra el dataset de anfibios, de menor escala y menor costo de iteración, antes de arriesgar su comportamiento contra el volumen y la heterogeneidad reales del dataset de aves. Esto es, en términos de ingeniería de software, una prueba de integración en un entorno reducido antes de un despliegue de mayor riesgo — no un ejercicio de calentamiento incidental.
5. Arquitectura en la Nube — Stack C
Esta es la arquitectura de destino: elegida, dimensionada y con sus alternativas descartadas por escrito, pendiente de ejecutar el plan por fases de la Sección 5.5. Las alternativas evaluadas y descartadas antes de llegar a esta configuración —junto con el contexto y las consecuencias de cada decisión— están documentadas en el Apéndice C (ADR-001 y ADR-002); esta sección describe únicamente cómo funciona el sistema hoy.
5.1 Topología
Pages (frontend) → Workers + Hono → Neon Postgres (driver serverless HTTP)
↓
Groq (LLM + STT) + Worker edge-tts (RobertoNeural)
| Costo | $0/mes |
| Cold start | ~300–500ms solo en la primera consulta tras inactividad (Neon escala a cero); ~5ms en las siguientes |
| Voz | RobertoNeural, vía Worker |
| Procedimientos almacenados | Preservados — Postgres los soporta de forma nativa |
| Plataformas a administrar | 2 (Cloudflare + Neon) |
| Almacenamiento usado | ~15 MB de los 500 MB del tier gratuito de Neon por rama |
El tier de Neon da 190 horas de cómputo al mes — una aplicación always-on las agotaría en apenas 8 días, así que escalar a cero no es una limitación sino el mecanismo que permite que el costo se mantenga en $0.
5.2 Componentes
- Frontend: Cloudflare Pages.
- Backend: Cloudflare Workers + Hono (TypeScript) — reemplaza
app_flask.pypor completo. - Base de datos: Neon Postgres, con driver basado en HTTP que funciona en runtimes de edge sin necesitar una conexión TCP persistente — compatible con Drizzle, Prisma, Kysely o cualquier cliente Postgres estándar, con procedimientos almacenados nativos.
- LLM y STT de producción: Groq (
llama-3.1-8b-instant+ endpoint de Whisper). Ollama se mantiene únicamente como modo de desarrollo local. - TTS:
edge-tts(voz RobertoNeural) servido desde un Worker de Cloudflare (DIYgod/cloudflare-edge-tts) — funciona porque la APIfetchde Cloudflare Workers soporta conexiones WebSocket vía HTTP Upgrade con encabezados personalizados, algo que el navegador no puede hacer directamente al no poder fijar el encabezadoOriginque Microsoft valida.
5.3 Riesgo y mitigación: la dependencia de edge-tts
edge-tts consume un servicio de Microsoft de forma no oficial, y Microsoft ya
endureció el acceso una vez — es la razón por la que el Worker descrito en 5.2 es
necesario en primer lugar. Puede volver a romperse sin aviso. Además, la
implementación en JavaScript (@edge-tts/universal) está bajo licencia
AGPL-3.0-or-later, relevante si el repositorio se publica en algún momento.
Mitigación: cachear el audio generado en R2 o KV — las respuestas se repiten (“El resultado es: 784”), así que la mayoría de reproducciones se resolverían desde caché sin tocar el servicio de Microsoft — y dejar la Web Speech API del navegador como respaldo automático si el servicio falla. Esto convierte una dependencia frágil en un sistema con degradación controlada, en vez de un punto único de fallo silencioso.
5.4 Migración de datos: extensión de la estrategia de entornos gemelos
El principio de la Sección 4 no termina en MySQL. La base de datos de anfibios —de menor escala y menor costo de reintento— es el candidato natural para validar primero el port completo a Postgres (esquema, vista, funciones almacenadas, política de colación equivalente) antes de arriesgar la migración de los más de 50,000 registros de aves a Neon. Ventaja adicional del cambio de motor: las funciones y procedimientos ganan capacidades en Postgres que MySQL no ofrecía directamente, en vez de simplemente trasladarse sin cambios.
5.5 Plan de implementación por fases
Cuentas (Cloudflare + Neon) → port del esquema y los objetos de MySQL a
Postgres, validado primero contra el dataset de anfibios (Sección 5.4) →
construcción del backend en Workers + Hono (TypeScript), reemplazando la lógica
de app_flask.py → integración del Worker de edge-tts con la mitigación de
caché de la Sección 5.3 → conexión de Groq para LLM y STT → frontend en
Cloudflare Pages → validación de las 10 consultas de la rúbrica contra el nuevo
stack antes de migrar el dataset de aves a producción.
6. Validación Empírica
Las 10 consultas exigidas por la rúbrica, cubriendo los 4 tipos requeridos
(simples, con filtros, con JOIN, con agregación), se validaron manualmente
contra ambas colecciones el 11 de julio de 2026:
Amphibia: familia con más especies = Hylidae (28) · avistamientos en 2025 = 1,925 · provincia de mayor diversidad = Panamá (72 especies) · especie más avistada = Rhinella horribilis (1,364 registros).
Aves: familia con más especies = Tyrannidae (80) · avistamientos en 2025 = 21,346 · provincia de mayor diversidad = Chiriquí (495 especies).
La consulta de mayor complejidad estructural probada — “¿Qué familia tuvo más especies distintas en Chiriquí en 2025?”, que cruza cuatro tablas filtrando por año y provincia simultáneamente — se confirmó consistente en múltiples corridas y con múltiples usuarios simultáneos el día de la demostración.
7. Estado de la Entrega
Sistema demostrado en funcionamiento completo el 13 de julio de 2026: acceso multi-usuario simultáneo vía ngrok, pipeline de voz de extremo a extremo, ambas bases de datos respondiendo de forma independiente. Informe académico completo (31 páginas) entregado con las 10 consultas validadas y evidencia visual del sistema en funcionamiento. Proyecto entregado y aprobado.
Evaluación del curso
Calificación final 100,00 (A), registrada el 14 de julio de 2026 por el Prof. Rafael Vejarano en la plataforma del curso. Retroalimentación textual:
Sustentación oral — 100/100. Excelente implementación.
La sustentación evidenció un excelente dominio técnico del proyecto. Los estudiantes realizaron una demostración funcional del sistema en tiempo real, validando las respuestas generadas por la inteligencia artificial contra los resultados obtenidos directamente desde la base de datos. Asimismo, demostraron comprensión sobre la integración entre la interfaz, el modelo de IA y MySQL, explicando las limitaciones encontradas durante el desarrollo y las decisiones técnicas adoptadas para optimizar el desempeño de la aplicación. La exposición fue clara, organizada y permitió verificar que el sistema se encuentra implementado y operando adecuadamente.
Documentación — 100/100. Este proyecto sobresale por su alcance, originalidad y nivel técnico. Integra bases de datos relacionales, inteligencia artificial, procesamiento de voz, datos científicos reales y mecanismos avanzados de validación y seguridad. La calidad de la documentación, la cantidad de pruebas realizadas y la complejidad de la arquitectura lo posicionan entre los mejores trabajos desarrollados en el curso.
Apéndice A — Cronología completa de desarrollo (línea de tiempo condensada)
La bitácora cronológica completa —sesión por sesión, con el mismo nivel de detalle— se mantiene fuera de esta publicación. Esta tabla indica dónde vive ahora cada etapa original dentro de este documento:
| Etapa original | Contenido | Dónde vive ahora |
|---|---|---|
| 0 | Planificación y decisiones iniciales | Resumen ejecutivo / Sección 1 |
| 1 | Modelo ER y DDL | Sección 1.1 |
| 2–3 | Importación Amphibia y Aves | Sección 3.3 / Sección 4 |
| 4 | Seguridad | Sección 2 (dominio propio) |
| 5 | Backend Flask y cascada IA | Sección 1.2 / 1.3 / 3.2 |
| 6 | Vista, funciones, procedimientos | Sección 1.5 |
| 7 | Capa de voz | Sección 1.4 |
| 8 | Frontend e identidad visual | (sin cambios de framing — ver documento original) |
| 9 | Bugs críticos | Sección 3 (resiliencia) + Sección 2 (seguridad) |
| 10 | Pruebas y validación | Sección 6 |
| 11 | Informe académico | Extraído de este documento (ver nota abajo) |
| 12–13 | Presentación y demo day | Sección 7 |
| 14 | Migración a la nube | Sección 5 + Apéndice C (ADRs) |
La metodología de generación programática del informe académico —manipulación directa del XML de un
.docxvía Node.js— se retiró de este documento para mantener el enfoque exclusivo en el sistema de biodiversidad e IA. Es una herramienta administrativa, no parte de la arquitectura que aquí se describe.
Apéndice B — Referencia rápida: stack y cifras
| Categoría | Dato |
|---|---|
| Backend | Python 3.14 · Flask 3.1.3 · MySQL 9.7 |
| LLM (producción original) | Ollama+Qwen3:1.7b → Groq llama-3.1-8b → Gemini 2.5 Flash |
| LLM (producción en la nube) | Groq llama-3.1-8b-instant → Gemini 2.5 Flash |
| STT / TTS | Groq Whisper endpoint (nube) / faster-whisper local (desarrollo) · edge-tts RobertoNeural |
| BD Amphibia | 8,430 registros · DOI 10.15468/dl.zcy9ys |
| BD Aves | 51,761 registros (de 56,203 crudos) · DOI 10.15468/dl.qpx3vw |
| Vista central | vista_avistamiento_completo (6 JOIN) |
| Funciones almacenadas | fn_temporada(), fn_disponibilidad_multimedia() |
| Procedimientos almacenados | 10, como SQL de referencia de la rúbrica |
| Destino en la nube | Cloudflare (Pages+Workers/Hono) + Neon Postgres + Groq — $0/mes |
| Entrega | 13 de julio de 2026 — completada con éxito |
Apéndice C — Registros de Decisiones Arquitectónicas (ADRs)
ADR-001 — Arquitectura de despliegue en la nube
Contexto. El sistema, en su forma entregada, solo corre de manera confiable
sobre una laptop con procesador Intel i7-7500U y GPU NVIDIA 940MX (arquitectura
Maxwell, 2015) — hardware que obligó a forzar faster-whisper a ejecutar en CPU.
Al revisar la migración, se cuestionaron dos supuestos de diseño: (1) la
arquitectura relacional original se dimensionó como si el sistema fuera a escribir
y crecer constantemente, pero la aplicación solo lee; (2) Python nunca fue un
requisito estricto — fue una consecuencia de una sola dependencia (edge-tts), y
se confirmó la existencia de un Worker de Cloudflare de código abierto
(DIYgod/cloudflare-edge-tts) capaz de servir la misma voz sin Python.
Opciones consideradas.
- Stack A — Todo Cloudflare (Pages + Workers/Hono + D1 + Groq + Worker edge-tts). Un solo proveedor, cero cold start. D1 es SQLite: sin procedimientos almacenados, los 10 del sistema desaparecerían.
- Stack B — Python conservador (Pages + FastAPI en Northflank + MySQL/Postgres
- Groq +
edge-ttsnativo). Conserva todo, cero aprendizaje nuevo. El tier gratuito de Northflank está pensado para experimentación, no para producción con garantías de disponibilidad.
- Groq +
- Stack C — Híbrido edge + Postgres (Pages + Workers/Hono + Neon Postgres + Groq + Worker edge-tts).
Decisión. Stack C.
Consecuencias. Cold start de ~300–500ms solo en la primera consulta tras inactividad (Neon escala a cero). Dos plataformas a administrar (Cloudflare + Neon), costo $0/mes. Los procedimientos almacenados se preservan y ganan capacidades al portarse a Postgres. Ollama sale de la cascada de producción y queda solo como modo de desarrollo local — no existe GPU gratuita equivalente en la nube.
ADR-002 — Motor de Postgres serverless: Neon vs. Supabase
Contexto. Stack C requiere Postgres accesible vía HTTP desde runtimes de edge, sin conexión TCP persistente, y sin mantenimiento manual dado que el proyecto no tiene presupuesto operativo.
Opciones consideradas.
- Neon — escala a cero automáticamente, despierta sola en la siguiente consulta.
- Supabase — también ofrece Postgres gratuito, pero el tier gratuito pausa proyectos con poca actividad tras 7 días, con reactivación manual desde el panel, sin respaldos en el tier gratuito, y con riesgo de borrado permanente si el proyecto queda pausado un periodo extendido. Trae además funciones que el sistema no usa hoy (Auth, Storage, Realtime, Edge Functions).
Decisión. Neon.
Consecuencias. Sin mantenimiento — no hace falta un cron de “keep-alive” como el que exigiría evitar la pausa de Supabase. Se renuncia a las funciones extra de Supabase, irrelevantes mientras el sistema no tenga autenticación de usuarios ni carga de archivos; si eso cambia en el futuro, Supabase pasaría a ser la opción con sentido y este ADR debería revisarse.

