Proyecto: SubseBot — Piloto de Cursos Obligatorios, Gobierno de la Ciudad de Buenos Aires Fecha: 25 de agosto de 2026 Preparado por: Jota (con asistencia de Claude) Fuente primaria: Especificación de Requerimientos EARS - SubseBot (documento completo, leído el 2026-08-25) Limitación declarada: el prototipo actual en Google Apps Script + Sheets (link provisto) está protegido por login de Google del usuario propietario; no fue accesible para este análisis. Todo lo que sigue sobre el prototipo actual es inferencia razonable a partir del nombre "Apps Script + Sheets", no una auditoría del código real.
SubseBot es un asistente conversacional que responde, para el piloto de Cursos Obligatorios de GCBA, dos tipos de consulta: (a) un colaborador pregunta qué cursos le faltan y recibe los enlaces a ICBA; (b) RRHH consulta estados por equipo/curso y exporta reportes en Excel. La especificación EARS ya vigente exige dos cosas que son, en el fondo, requisitos de arquitectura: que el sistema trabaje solo sobre una copia anonimizada de los datos (nunca el original), y que exista una distinción de comportamiento por rol (colaborador vs. RRHH) atada a un identificador único (el legajo).
Este informe traduce esa spec funcional a una arquitectura técnica, usando el patrón MCP (Model Context Protocol) para separar la orquestación conversacional (a cargo de un LLM) de la ejecución de la lógica de negocio y el acceso a datos (a cargo de código determinístico). Se presentan dos propuestas —A, evolución liviana del prototipo actual, y B, arquitectura desacoplada con base de datos— pensadas como una decisión Now/Next: A para el piloto, B como horizonte si el proyecto escala a más áreas de GCBA.
.../exec).| Actor | Qué necesita | Dato sensible que toca |
|---|---|---|
| Colaborador | Saber qué cursos obligatorios tiene pendientes y cómo hacerlos | Su propio legajo y estado de cursos — nada de otros |
| RRHH (lector/operador) | Filtrar pendientes por equipo o curso, exportar a Excel | Legajos y estados de todo un equipo o de toda la organización |
| RRHH (administrador de datos) | Ejecutar/supervisar el proceso de anonimización y sync | Acceso transitorio a la fuente original para generar la copia anonimizada |
| Administrador del sistema (implícito, no está en la spec) | Mantener el servicio, revisar logs de error | Metadatos de sistema, no debería necesitar ver datos personales |
Explícitamente fuera: saldo de días de vacaciones y "cualquier información sensible" no relacionada a cursos obligatorios. Esto importa para el diseño: el sistema no debe convertirse en un punto de acceso genérico a RRHH, sino mantenerse acotado a un dominio de datos (cursos + legajo + equipo).
Esta tabla es el corazón del informe: conecta cada bloque de requerimientos con la decisión técnica que le corresponde.
| Requerimiento EARS | Qué exige | Decisión de arquitectura |
|---|---|---|
| REQ-UBI-01 — solo copia anonimizada | Nunca tocar el original | Pipeline de anonimización/ETL separado del servicio de consulta; el servicio de consulta ni siquiera tiene credenciales hacia el original |
| REQ-UBI-02 — legajo como ID único | Cruce determinístico | El legajo nunca se toma de texto libre del usuario final; se resuelve server-side a partir de la identidad autenticada (OAuth) |
| REQ-EVD-01/02 — consulta individual + links | Respuesta personalizada con enlaces | Tool MCP consultar_estado(legajo) — determinístico; el LLM solo decide cuándo llamarlo |
| REQ-EVD-03/04 — filtros y exportación RRHH | Acceso agregado, solo para RRHH | Tools separadas (listar_por_equipo, exportar_excel) con scope de autorización distinto al de colaborador |
| REQ-UNW-01/02/03 — errores | Legajo no encontrado, fuera de alcance, falla de origen | Manejo de excepciones explícito en el servidor MCP, no delegado al LLM |
| REQ-STD-01 — 100% cumplido | No mostrar links si ya está todo aprobado | Lógica condicional determinística en la tool, no en el prompt |
| REQ-STD-02 — bloqueo durante sync | No servir datos a mitad de actualización | Un flag de estado ("sincronizando") que el servidor chequea antes de responder cualquier tool |
| REQ-OPT-01/02 — roadmap futuro | Carga de certificados, FAQ de RRHH | Diseñado como tools adicionales, sin tocar el core — el MCP server es extensible por diseño |
| REQ-CPX-01 — reporte para líderes bajo permiso admin | Combinación de rol + evento | Autorización de grano fino: no alcanza con "es RRHH", hace falta un scope específico (rrhh_admin vs rrhh_lector) |
Un enfoque ingenuo sería darle al LLM acceso directo a la Google Sheet y dejar que arme las queries. Esto es exactamente lo que la spec pide evitar, por dos motivos concretos:
MCP resuelve esto poniendo una frontera dura entre el modelo (que interpreta lenguaje y decide qué herramienta llamar) y el servidor (que ejecuta funciones puras, con su propia autenticación, autorización y logging, independientes del prompt). El modelo nunca ve el dataset completo — solo ve lo que la tool decide devolverle, ya filtrado por el rol de quien preguntó.
flowchart TB
subgraph Usuarios
COL[Colaborador]
RRHH[RRHH]
end
subgraph Canal
BOT[Canal de chat: WhatsApp / Google Chat / Web]
end
subgraph Identidad
OAUTH[Google OAuth 2.0 - Workspace GCBA]
end
subgraph MCPLayer[Servidor MCP]
RBAC[Middleware: valida token, resuelve rol y legajo]
T1[tool: consultar_estado]
T2[tool: listar_por_equipo]
T3[tool: exportar_excel]
end
subgraph Datos
SHEET[(Google Sheet anonimizada)]
SYNC[Apps Script: job de sync + anonimización]
ORIG[(Base RRHH original)]
end
COL --> BOT
RRHH --> BOT
BOT --> OAUTH
OAUTH -->|id_token con email| RBAC
RBAC --> T1
RBAC --> T2
RBAC --> T3
T1 --> SHEET
T2 --> SHEET
T3 --> SHEET
ORIG -->|solo lectura, ventana programada| SYNC
SYNC -->|escribe copia anonimizada| SHEET
RBAC extrae el email verificado y lo cruza contra una tabla chica (Sheet o archivo de config) de mapeo email → legajo y email → rol.subsebot-rrhh@gcba...) o un allowlist explícito — no por heurística de dominio de email.A favor: tiempo de entrega corto (días), reutiliza el prototipo existente casi entero, costo de infraestructura mínimo, encaja con el marco temporal de un piloto.
En contra: Google Sheets como fuente de verdad tiene límites de cuota de API, no ofrece row-level security nativo (todo el control de acceso vive en el código del servidor, no en el almacenamiento), y la auditoría de accesos requiere instrumentarse a mano (no viene gratis).
flowchart TB
subgraph Usuarios
COL2[Colaborador]
RRHHL[RRHH lector]
RRHHA[RRHH admin]
end
subgraph Canales
WA[WhatsApp]
WEBP[Portal web]
SLK[Chat interno]
end
subgraph IdP[Identity Provider OIDC]
OAUTH2[Google Workspace / IdP GCBA]
end
subgraph MCP[Servidor MCP - stateless]
GATE[Middleware: valida JWT + scope]
TOOLA[tool: consultar_estado]
TOOLB[tool: reporte_equipo]
TOOLC[tool: exportar_excel]
TOOLD[tool: resumen_lideres]
end
subgraph DB[Datastore]
ANON[(Vista anonimizada - Postgres, RLS por rol)]
AUDIT[(Log de auditoría: acceso por legajo)]
end
JOB[Job ETL determinístico: extrae + pseudonimiza]
ORIGDB[(Base RRHH original - sistema de origen)]
COL2 --> WA
RRHHL --> WEBP
RRHHA --> SLK
WA --> OAUTH2
WEBP --> OAUTH2
SLK --> OAUTH2
OAUTH2 -->|JWT con rol y sub| GATE
GATE --> TOOLA
GATE --> TOOLB
GATE --> TOOLC
GATE --> TOOLD
GATE --> AUDIT
TOOLA --> ANON
TOOLB --> ANON
TOOLC --> ANON
TOOLD --> ANON
ORIGDB -->|solo lectura, ventana programada| JOB
JOB -->|escribe| ANON
quién, qué legajo o equipo, cuándo, qué tool) — algo que un organismo público típicamente necesita poder mostrar.colaborador, rrhh_lector, rrhh_admin — necesario para diferenciar REQ-CPX-01 (que exige permisos administrativos específicos) de una consulta RRHH común.A favor: escala a más secretarías/cursos, auditoría real desde el día uno, los permisos no dependen de que cada nueva tool "se acuerde" de chequear el rol — la base los impone estructuralmente.
En contra: más superficie para construir y operar (base de datos, ETL, hosting del servidor) — desproporcionado para un piloto de una sola área, salvo que ya exista experiencia de DevOps disponible en el equipo.
| Dimensión | A — Sheets + MCP liviano | B — Postgres + MCP desacoplado |
|---|---|---|
| Tiempo estimado a producción | Días | Semanas |
| Fuente de datos | Google Sheet anonimizada | Postgres con RLS |
| Dónde se impone el permiso | Código del servidor MCP | Base de datos (RLS) + servidor MCP |
| Auditoría de accesos | Requiere instrumentación manual | Tabla de auditoría nativa |
| Costo de infraestructura | Casi nulo | Bajo-medio |
| Reutiliza el prototipo actual | Sí, casi entero | Solo el criterio de anonimización |
| Mejor para | El piloto tal como está planteado | Adopción en más áreas de GCBA |
Recomendación: arrancar con A para la demo del Idiatón. B queda documentado como el paso siguiente natural si RRHH decide extender SubseBot más allá del piloto — no se justifica construir RLS en Postgres para validar un flujo con una sola área.
Principio guía: separar "decisión de negocio y acceso a datos" (determinístico, en código) de "comprensión del lenguaje y redacción de la respuesta" (no determinístico, en el LLM). Confianza alta en el principio general; confianza media en el mapeo específico a cada requerimiento, porque depende de decisiones de implementación que el equipo todavía no tomó.
curso → URL fija en la base, no un link que el modelo "recuerda" o completa de memoria.Si un error en un punto del flujo implica mostrarle a alguien un dato que no le corresponde, ese punto tiene que ser código determinístico, sin excepción. Si el error implica, en el peor caso, una respuesta mal redactada o con un tono poco claro, puede quedar del lado del LLM.
Nivel de certeza: especulación fundamentada — depende de qué stack ya tiene instalado o preferido el equipo de GCBA, información que no forma parte de la spec revisada.
| Componente | Recomendación | Motivo |
|---|---|---|
| Servidor MCP | Node.js + TypeScript (SDK oficial de MCP) o Python | Ninguno de los dos tiene ventaja técnica clara acá; conviene el que el equipo ya domine |
| Identidad | Google OAuth 2.0 / OIDC contra el Workspace de GCBA | El prototipo ya vive en Google Apps Script — es la integración más barata para empezar |
| Identidad (a futuro) | IdP intermedio (Keycloak / Auth0) si hay que federar con otros organismos | Solo se justifica si aparece un segundo organismo o sistema externo |
| Datos — Propuesta A | Google Sheets | Continuidad con el prototipo actual |
| Datos — Propuesta B | Postgres (Supabase como opción rápida de levantar con RLS nativo) | RLS de fábrica, sin reinventar control de acceso a mano |
| Canal de chat | El que ya esté acordado con RRHH (WhatsApp Business API, Google Chat, webchat) | Es independiente de la arquitectura de datos — no condiciona A ni B |
| Generación de Excel | Librería determinística del lado del servidor (openpyxl, exceljs) |
Nunca generado por el LLM |
| Riesgo | Impacto | Mitigación propuesta |
|---|---|---|
| El LLM interpreta mal una intención y llama la tool equivocada | Bajo si las tools están bien acotadas — en el peor caso, una respuesta irrelevante, no una fuga de datos | Diseñar tools con alcance estrecho y validación de parámetros en el servidor |
| Falla o demora en el job de anonimización | Servicio sirve datos desactualizados o inconsistentes | REQ-STD-02 ya contempla esto: bloquear consultas mientras el flag de sync esté activo |
| Suplantación de rol (alguien se hace pasar por RRHH) | Alto — expone legajos de terceros | El rol nunca se autodeclara por el usuario; se resuelve desde el token OAuth y una tabla de permisos controlada por el administrador del sistema |
| Cuota de la API de Google Sheets se agota con uso alto (Propuesta A) | Servicio degradado en momentos de pico | Cachear resultados de lectura con TTL corto, o migrar a Propuesta B si el volumen lo justifica |
| Falta de auditoría en A ante un pedido de organismo de control | Dificultad para demostrar cumplimiento de protección de datos | Igual en A conviene loguear accesos a un archivo/hoja separada desde el día uno, aunque sea manual |
Now (piloto, Propuesta A):
sincronizando / listo).consultar_estado, listar_por_equipo, exportar_excel.Next (si RRHH pide extender el piloto):
6. Sumar logging de auditoría básico sobre la Propuesta A (sin migrar aún de almacenamiento).
7. Sumar la tool resumen_lideres (REQ-CPX-01) con su scope de permiso específico.
Later (si se adopta en más áreas — Propuesta B): 8. Migrar el almacenamiento a Postgres con RLS. 9. Formalizar el ETL como job versionado y monitoreado. 10. Evaluar si hace falta un IdP intermedio para federar con otros organismos.
Estas preguntas no están respondidas por la spec EARS ni por este informe — son decisiones que le corresponden al equipo, no a la arquitectura. Agruparlas ayuda a no perder ninguna en la reunión.