Asignación óptima de 34.145 clientes Preferenciales a Gerentes de Inversión. Cuatro enfoques (analítico, greedy, MILP exacto y simulated annealing con MCMC) convergen al mismo óptimo. Este informe explica el problema, los datos, los caminos y, sobre todo, lo que se aprende cuando los métodos sofisticados no mejoran el resultado.
Bancolombia tiene una red comercial jerárquica de tres niveles. Hay que decidir cómo conectar el nivel intermedio (ejecutivos) con el nivel superior (gerentes), sabiendo que los clientes están atados a su ejecutivo y que cada gerente tiene un tiempo limitado al año.
Como todos los clientes del ejecutivo se asignan en bloque, la unidad de decisión no es el cliente, es el ejecutivo. Esto transforma el problema de 34.145 variables binarias en solo 392, lo que lo hace tratable con MILP exacto.
El denominador es fijo (34.145 clientes). El numerador suma solo A y B asignados (los C ya asignados no aportan). Por eso el modelo prioriza A>B>C aunque la asignación es por ejecutivo.
La métrica oficial $x/y$ no es lineal en las decisiones por ejecutivo (depende del conteo total de A y B asignados). Por eso el modelo no maximiza $x/y$ directamente, sino una función subrogada lineal diseñada para alinearse con $x/y$:
donde $x_{e,g} \in \{0,1\}$ es la variable de decisión (asignar o no al ejecutivo $e$ al gerente $g$), y $V_e$ es el valor agregado del ejecutivo:
¿De dónde salen los pesos? Se eligen tales que cada cliente A pese 10× más que un B y un B pese 100× más que un C: $w_A = 1000$, $w_B = 100$, $w_C = 1$. Con esa razón, asignar un solo ejecutivo con un cliente A siempre es preferible a asignar un ejecutivo con 9 clientes B (los cuales a su vez son preferibles a 99 C). Esto reproduce la prioridad del PDF (A>B>C). $\alpha = 0{,}1$ es un peso pequeño para que el score promedio $\bar{s}_e$ solo desempate.
Esta función se obtuvo por análisis directo de la métrica: se descompuso $x/y$ en aportes por ejecutivo asumiendo que asignamos al ejecutivo completo, y se eligieron pesos que conservaran la prioridad relativa del PDF. La validación se hace observando que el MILP con esta función subrogada produce la misma solución que el SA con Hamiltoniano físico (ambos llegan a $x/y = 0{,}4076$).
Recibimos 7 archivos CSV. Cada uno aporta una pieza del problema. Los tamaños van desde 50 filas (gerentes) hasta 247 mil (oportunidades).
Población de clientes Preferencial con categoría A/B/C, score, zona y asignación actual.
Estructura de la red: relación gerente ↔ ejecutivo. Llave crítica para el modelo.
Oportunidades comerciales por cliente y producto. Insumo para calcular tiempo demandado.
Si el cliente ya tiene/usa cada producto. Define qué oportunidades requieren venta nueva.
Catálogo de Gerentes de Inversión con ciudad, región, banca y tipo.
Tiempos auto-reportados por (gerente, producto, etapa). Insumo para τ_c.
Tiempo anual disponible T_g por gerente (75.930 min ≈ 1.265 horas).
Lo que parece un problema de optimización resulta ser un problema de calidad de datos. Antes de modelar, descubrimos que el techo del problema está fijo: no por capacidad, sino por integridad referencial.
| Grupo | Clientes | % | A | B | C |
|---|---|---|---|---|---|
| Asignables | 27.115 | 79.4% | 3.916 | 10.000 | 13.199 |
| Huérfanos | 7.030 | 20.6% | 1.579 | 2.727 | 2.724 |
Aunque tuviéramos un modelo perfecto, la métrica jamás superará 40.8%. Los 4.306 clientes A+B huérfanos son estructuralmente no asignables: sus ejecutivos no aparecen en la tabla ecas que define la jerarquía. Mejorar el modelo no resuelve esto. Mejorar los datos sí.
Una conclusión tan fuerte (“la métrica está acotada por integridad de datos”) merece auditoría. Verifiqué tres aspectos: tipos de dato, filtros de universo y composición por segmento. La conclusión se sostiene, con un matiz importante.
cod_ejec_bco es string en ambas tablas (clientes y ecas). Sin pérdida de precisión en enteros de 19 dígitos. Los IDs son textualmente idénticos cuando coinciden.
Los 34.145 clientes tienen marca_mac_inv no nula (A/B/C 100%). El 99,94% tiene estado_gte_inv = ACTIVO. El universo está limpio.
De los 7.030 huérfanos: 4.446 son Preferencial/Plus (scope estricto del MAC GPI). Los 2.584 restantes son PYMES, Personal, Empresarial. Su ejecutivo es de otra banca.
| Segmento del cliente | A | B | C | Total | Lectura |
|---|---|---|---|---|---|
| PREFERENCIAL | 433 | 1.181 | 1.610 | 3.224 | Scope natural; ejec aún sin GPI |
| PLUS | 257 | 443 | 522 | 1.222 | Banca premium, mismo caso |
| PYMES | 458 | 353 | 129 | 940 | Ejec. de banca PYME, no de Inversión |
| PERSONAL | 106 | 347 | 327 | 780 | Persona natural sin gerente de inversión |
| NEGOCIOS & INDEP. | 67 | 121 | 74 | 262 | Ejec. de banca corporativa pequeña |
| EMPRESARIAL | 129 | 164 | 6 | 299 | Ejec. de banca empresarial |
| Otros | 3 | 10 | 10 | 23 | Corporativa, gobierno, internacional |
El hallazgo es real: hay 633 ejecutivos cuyos clientes están marcados para el MAC de Inversión, pero ellos pertenecen a otras bancas (PYME, Personal, Empresarial) sin Gerente de Inversión asociado. No es un error de merge ni un problema de tipos: las llaves coinciden cuando deben coincidir. Es un hecho operativo: el marcado del cliente en marca_mac_inv va por delante de la reasignación del ejecutivo a la jerarquía de inversión, lo cual la prueba prohíbe explícitamente con la regla “los clientes no pueden moverse ni reasignarse a otros ejecutivos”.
Implementamos cuatro enfoques con complejidad creciente, no para "asegurar" el resultado, sino para triangular y diagnosticar. Si los cuatro convergen → la solución es robusta. Si difieren → hay decisión real.
Ordena ejecutivos por A→B→C→score, asigna al primer gerente compatible con capacidad.
Cómo se llega: una sola pasada por la lista ordenada, primer gerente compatible que aún tenga capacidad libre se queda con el ejecutivo. Sin búsqueda, sin retroceso.
Prioriza ejecutivos por valor/tiempo (ρ = v_e / t_e), best-fit por capacidad.
Cómo se llega: ordena por ρ = v_e / t_e descendente y elige el gerente compatible con la menor capacidad libre suficiente (best-fit), reduciendo fragmentación.
Programación entera mixta. Garantía de óptimo global por construcción.
Cómo se llega: el solver CBC explora un árbol de configuraciones binarias usando ramificación y poda; descarta ramas cuya cota superior ya está debajo del mejor encontrado.
Cadena MCMC sobre Hamiltoniano de Ising. Aporta diagnósticos físicos.
Cómo se llega: cadena de Markov con regla de Metropolis: propone movimientos aleatorios (flip o swap) y los acepta con probabilidad e^(−ΔH/T). Al enfriar T, converge al estado fundamental.
simulacion-capacidad.netlify.app · simulaciones paso a paso: árbol de branch & bound, cooling curve, best-fit en vivo
7 CSVs en data/raw/
τ_c y t_e por ejecutivo
Analytical, greedy, MILP, SA
5 reglas duras del PDF
resultado_prueba.csv
Los cuatro enfoques producen exactamente la misma asignación. No es coincidencia: es evidencia empírica de que el problema no tiene mínimos locales malos. El paisaje energético es trivial → el modelo más simple basta para producción.
No es analogía pedagógica. Es equivalencia matemática exacta: podemos formular el problema en lenguaje del banco o en lenguaje de física, y cada formulación produce los mismos resultados óptimos. Lo interesante es lo que se gana al ver el problema desde cada lado.
Para cada par (ejecutivo $e$, gerente $g$), el banco decide $x_{e,g} \in \{0,1\}$: asignar al ejecutivo a ese gerente (1) o no (0). Maximiza el valor comercial total:
donde $V_e = 1000\cdot n^A_e + 100\cdot n^B_e + 1\cdot n^C_e + 0{,}1\cdot n_e\bar{s}_e$ pondera los clientes A/B/C que el ejecutivo trae consigo, según prioridad oficial.
Sujeto a tres restricciones de negocio:
Cambiamos $x_{e,g}$ por un spin de Ising $\sigma_{e,g} \in \{0,1\}$, y reformulamos el negocio como minimización de la energía $H(\sigma)$:
Equivalencia exacta: el estado fundamental $\arg\min H$ con $\lambda_g \to \infty$ es idéntico a la solución óptima del problema del banco. Las restricciones duras (zona, único gerente) son reglas que filtran configuraciones permitidas, no van en $H$.
| Concepto del banco | Concepto físico | Significado del mapeo |
|---|---|---|
| Decisión binaria $x_{e,g}$ | Spin $\sigma_{e,g} \in \{0,1\}$ | Cada par ejecutivo-gerente es una variable binaria del sistema |
| Valor del ejecutivo $V_e$ | Campo externo $h_{e,g} = -v_e$ | Cuanto más valioso, más fuerte tira al spin hacia 1 (asignar) |
| Tiempo demandado $t_e$ | Masa del spin (en restricción) | Lo que el ejecutivo “pesa” en la capacidad del gerente |
| Capacidad anual $T_g$ | Profundidad del pozo de potencial | Hasta cuánta masa cabe antes de que el sistema rechace más |
| Sobrepaso de capacidad | Resorte unilateral cuadrático | Penalización suave si overshoot pequeño, brutal si grande |
| Restricción de zona | Espacio de configuraciones reducido | Vínculos prohibidos; los pares cross-zona ni siquiera existen |
| Único gerente por ejecutivo | Exclusión tipo Pauli | El ejecutivo solo puede ocupar un “estado de asignación” |
| Solución óptima del MILP | Estado fundamental ($T \to 0$) | Configuración de mínima energía es la asignación de máximo valor |
| Pesos $w_A, w_B, w_C$ | Magnitud del campo externo | Tunable: cambia la “preferencia” del sistema sin alterar las reglas |
| Heterogeneidad de $t_e$ por ejec | Glassiness (vidrioso, no cristalino) | Sistema desordenado por construcción; no admite simplificación trivial |
Cuando los dos lenguajes describen la misma matemática, podemos elegir el método según el objetivo. Para encontrar la solución óptima exacta: MILP. Para entender cómo de robusta es esa solución ante cambios o cómo escalaría con datos más grandes: la termodinámica del sistema.
En física, un campo externo (ej. magnético) actúa sobre cada spin individualmente, intentando alinearlo. Aquí, $V_e$ es el campo: cuanto más valor trae el ejecutivo, más fuerte tira hacia “asignar”. La capacidad es el contrapeso que impide que todos se asignen.
El término $(\sum t_e \sigma_{e,g} - T_g)_+^2$ es cero si la capacidad NO se sobrepasa, y crece cuadráticamente si sí. Esto modela bien la realidad operativa: estar al 105% es aceptable; estar al 200% es desastroso.
Los siguientes tres diagnósticos solo emergen del enfoque físico (MILP no los produce). Cada uno se explica en doble lenguaje: qué le dice al físico y qué le dice al banco.
Qué es: energía promedio del sistema en función de la temperatura, observada al lo largo de la simulación.
📊 Para el físico: trayectoria del sistema desde alta temperatura (estado caótico, exploración) hacia $T \to 0$ (estado fundamental). Plateau final $=$ convergencia. Saltos abruptos $=$ transiciones de fase: el sistema reorganizó masivamente su estructura.
🏦 Para el banco: ¿qué tan rápido y suave el modelo encuentra la asignación óptima cuando arranca desde una configuración aleatoria? Si baja monótona y se aplana, el modelo está bien afinado. Si tiene saltos, hay puntos donde “reordenar” ejecutivos cambia mucho el valor total: zonas de inestabilidad operativa.
Qué es: varianza de la energía sobre $T^2$. Mide la sensibilidad del sistema a cambios de temperatura.
📊 Para el físico: un pico afilado a $T_c$ marca una transición de fase. $C$ residual alto a $T \to 0$ indica frustración: el sistema no puede satisfacer todas las restricciones simultáneamente. Sin pico = sin transición, problema fácil.
🏦 Para el banco: ¿hay un “umbral” en los pesos a partir del cual el modelo empieza a comportarse de forma diferente? Si SÍ, los parámetros del modelo son frágiles cerca de ese umbral; si calibras los pesos cerca, pequeños cambios producen reasignaciones masivas. Si NO, el modelo es robusto, el negocio puede confiar.
Qué es: varianza del número total de ejecutivos asignados $M$ sobre $T$. Mide cómo responde la “magnetización total” ante el campo externo.
📊 Para el físico: equivalente a la susceptibilidad magnética. Una $\chi$ alta significa que el sistema responde fuertemente al campo, una $\chi$ baja significa rigidez ante perturbaciones.
🏦 Para el banco: si subo $w_A$ de 1000 a 1500, ¿cuántos ejecutivos cambian de gerente? Susceptibilidad baja $\Rightarrow$ la asignación es estable, no hace falta calibrar finamente los pesos. Susceptibilidad alta $\Rightarrow$ la elección de pesos importa mucho, y ahí el negocio debe definir explícitamente sus prioridades.
Para nuestro problema concreto: $C$ residual bajo, $\chi$ residual baja, curva de enfriamiento sin saltos. Los tres diagnósticos coinciden: el problema no tiene frustración estructural y la solución es robusta. Esto es coherente con que los cuatro métodos de optimización converjan al mismo valor 0.4076.
| Método | Lo que el banco gana | Lo que el físico gana | Cuándo elegirlo |
|---|---|---|---|
| Analítico (regla) | Solución explicable a no-técnicos | Aproximación de campo medio “a mano” | Demos rápidas, baseline, comunicación a comité |
| Greedy densidad | Tiempo de cómputo < 1 s, escalable | Aproximación de Bethe / mean-field | Producción diaria, pipelines automatizados |
| MILP exacto | Garantía formal de optimalidad | Diagonalización exacta del Hamiltoniano | Cierre mensual, reportes regulatorios |
| Simulated Annealing | Diagnóstico de robustez ante cambios | Termodinámica completa: $C$, $\chi$, transiciones | Auditoría del modelo, análisis de sensibilidad |
resultado_prueba.csvEsquema y tipos consistentes con la plantilla del PDF. 27.115 filas, una por cada cliente asignado. Los clientes no asignados se omiten del archivo (regla del enunciado).
| Columna | Tipo | Descripción |
|---|---|---|
num_doc_cli | string (19 dígitos) | Documento del cliente asignado |
cod_tipo_doc_cli | integer | Tipo de documento (1, 2, ...) |
cod_ejec_bco | string (19 dígitos) | Ejecutivo del cliente |
num_doc_gte_inv | string (19 dígitos) | Documento del Gerente de Inversión asignado |
cod_gte_inv | string (19 dígitos) | Código del Gerente de Inversión asignado |
Cada cod_ejec_bco aparece con un solo cod_gte_inv.
100% de las asignaciones cumplen zona(e) = zona(g).
Ningún gerente excede su tiempo_restante anual.
Todos los clientes de un ejecutivo van al mismo gerente, sin parciales.
El archivo resultado_prueba.csv se genera de forma reproducible con un solo comando. A continuación, los cuatro módulos clave que lo producen, con el código real del repositorio.
# 1. Carga las 7 tablas (loader con dtype=string para IDs de 19 dígitos) clientes, ecas, capacidad, encuesta, oport, tenencia, planta = load_all() # 2. Calcula tiempo demandado por cliente (τ_c) y por ejecutivo (t_e) tau = calcular_tau_cliente(oport, tenencia, encuesta) df_ejec = construir_df_ejecutivos(clientes, tau, ecas) df_ger = construir_df_gerentes(capacidad, ecas) # 3. Resuelve con MILP exacto (CBC) - garantía de óptimo global res = asignar_milp(df_ejec, df_ger, time_limit=300, gap_rel=0.001) # 4. Valida las 5 reglas duras del PDF y exporta el CSV resultado, metricas = validar_y_exportar( res.asignaciones, df_ejec, df_ger, clientes, planta, out_path="resultado_prueba.csv" ) print(f"x/y = {metricas['metrica_x_y']:.4f}") # 0.4076
El tiempo demandado por cliente sale de cruzar oportunidades, tenencia y la encuesta de tiempos. Tres casos:
def calcular_tau_cliente(oportunidades, tenencia, encuesta): # Mediana de minutos por (producto, etapa) - robusta a outliers tabla = construir_tabla_tiempos(encuesta) df = oportunidades.merge(tenencia, on=["num_doc_cli", "cod_producto"]) df["producto_norm"] = df["producto"].replace(PRODUCTO_MAP) # Vectorizado con np.where: 3 casos según tenencia/uso no_tiene = df["tenencia"].fillna(0) == 0 tiene_no_usa = (df["tenencia"] == 1) & (df["usa_producto"].fillna(0) == 0) minutos = np.where(no_tiene, t_venta_nueva, # Caso 1: venta nueva np.where(tiene_no_usa, t_postventa, # Caso 2: activación 0.0)) # Caso 3: tiene y usa df["minutos"] = minutos return df.groupby("num_doc_cli")["minutos"].sum().reset_index(name="tau")
Programación entera con PuLP/CBC. Optimal en 0.49 s para 370 ejecutivos × 49 gerentes con filtro de zona.
def asignar_milp(df_ejec, df_ger, time_limit=300, gap_rel=0.001): # Pares válidos (ejecutivo, gerente) con misma zona pares = [(e, g) for e in df_ejec["cod_ejec_bco"] for g in df_ger["cod_gte_inv"] if zona[e] == zona[g]] model = pl.LpProblem("asignacion", pl.LpMaximize) x = pl.LpVariable.dicts("x", pares, cat=pl.LpBinary) # Objetivo: max Σ v_e · x_eg model += pl.lpSum(valor[e] * x[(e, g)] for (e, g) in pares) # R1: a lo más un gerente por ejecutivo for e in df_ejec["cod_ejec_bco"]: model += pl.lpSum(x[p] for p in pares if p[0] == e) <= 1 # R2: capacidad anual por gerente for g in df_ger["cod_gte_inv"]: model += pl.lpSum(tiempo[e] * x[(e, g)] for (e, gg) in pares if gg == g) <= cap[g] model.solve(pl.PULP_CBC_CMD(timeLimit=time_limit, gapRel=gap_rel)) return [(e, g) for (e, g) in pares if x[(e, g)].value() > 0.5]
Antes de escribir el archivo se verifican las 5 reglas duras del PDF. Si alguna falla, el pipeline detiene la publicación.
def validar_y_exportar(asignaciones_ejec, df_ejec, df_ger, clientes, planta, out_path): # Las 5 validaciones duras del PDF validar_unico_gerente(asignaciones_ejec) # 1 ejec → 1 gerente validar_misma_zona(asignaciones_ejec, df_ejec, df_ger) uso = validar_capacidad(asignaciones_ejec, df_ejec, df_ger) # Expande (ejec, gerente) → filas a nivel cliente (regla todo-o-nada) resultado = expandir_a_clientes(asignaciones_ejec, clientes, planta) # Calcula la métrica oficial x/y metricas = metrica_oficial(resultado, clientes) # Exporta con esquema EXACTO de la plantilla del PDF resultado[["num_doc_cli", "cod_tipo_doc_cli", "cod_ejec_bco", "num_doc_gte_inv", "cod_gte_inv"]].to_csv(out_path, index=False, encoding="utf-8") return resultado, metricas # 27,115 filas, x/y = 0.4076
El pipeline completo se ejecuta de extremo a extremo en una sola línea:
uv run python -m modelo_capacidad.run_all
Tiempo total ~3 segundos. Genera artefactos intermedios en data/processed/ y el archivo final resultado_prueba.csv en la raíz del repo. Los 19 tests pytest validan que cada función respeta su contrato.
Cobertura de los cuatro entregables del PDF + extras: pipeline reproducible, tests automáticos y subagentes especializados.
| # | Entregable PDF | Archivo / ubicación | Estado |
|---|---|---|---|
| 1 | Documento explicativo | docs/documento_final.md + este HTML | ✓ |
| 2 | Archivo de asignación | resultado_prueba.csv (27.115 filas) | ✓ |
| 3 | Códigos documentados | src/modelo_capacidad/ + tests/ | ✓ |
| 4 | Bosquejo arquitectura | Sección anterior + docs/arquitectura.md | ✓ |
| + | Variables adicionales (opcional) | docs/variables_adicionales.md + sección siguiente | ✓ |
Recomendaciones organizadas por origen y costo. Detalle completo en docs/variables_adicionales.md.
Notación: 🟢 bajo, 🟡 medio, 🔴 alto.
| Origen | Variables clave | Por qué | Costo |
|---|---|---|---|
| Internas (data warehouse) | Volumen y monto transaccional, recencia, ticket promedio, churn score, tasa histórica de respuesta a campañas, tasa de cierre del ejecutivo, antigüedad del cliente | Refina v_e y τ_c; permite priorizar retención sobre venta cuando aplica |
🟢 |
| Telemetría CRM | Tiempo real medido por actividad (reemplaza la encuesta auto-reportada) | Reduce el error de medición de τ_c de >50% a <10% |
🟢 |
| Externas · buró | Score DataCrédito / TransUnion, endeudamiento, reportes negativos | Mejor identificación de A reales (clientes solventes con capacidad de inversión) | 🟡 |
| Externas · sociodemografía | Estrato (DANE), CIIU enriquecido (Asobancaria), composición del hogar | Segmentación adicional para el score upstream | 🟡 |
| Externas · macro | Tasa USD/COP, IBR, DTF, IPC (APIs Banco de la República y DANE) | Ajustar pesos según ciclo económico | 🟢 |
| Scraping / proveedores | LinkedIn Sales Navigator, RUES (Cámara de Comercio), Catastro, EMIS | Patrimonio visible y actividad empresarial; sólo si los anteriores plateauean | 🟡 / 🔴 |
Antes de buscar más datos afuera, hay que aprovechar bien los que ya hay adentro. Reconciliar los 633 ejecutivos huérfanos en la tabla ecas sube el techo de la métrica de 40,8% a 53,4% (+12,6 puntos), sin tocar el algoritmo ni traer datos nuevos. Esa es la inversión con mayor ROI inmediato.
Bosquejo conceptual que permite servir los resultados del modelo a páginas web, app móvil, CRM y servicios externos. La idea es separar el cómputo (batch) del consumo (consultas instantáneas): el modelo corre periódicamente, escribe sus resultados versionados en una tabla, y una API REST expone esos resultados con baja latencia.
| Endpoint | Para qué | Consumidor típico |
|---|---|---|
GET /asignacion/cliente/{num_doc_cli} |
Saber a qué gerente y ejecutivo está asignado un cliente | CRM, app móvil del gerente |
GET /asignacion/gerente/{cod_gte_inv} |
Cartera completa de un gerente + carga de tiempo acumulada | Dashboard del gerente |
GET /asignacion/ejecutivo/{cod_ejec_bco} |
Reporte del ejecutivo: clientes, mix A/B/C, gerente al que reporta | Reporte interno |
GET /metricas/run/{run_id} |
KPIs de una corrida: x/y, %A, %B, utilización media | Comité, área analítica |
GET /metricas/historico |
Evolución mensual de los KPIs para detectar drift | Power BI ejecutivo |
La arquitectura incluye además una capa de agentes IA sobre MCP (Model Context Protocol): un servidor MCP en Lambda expone los endpoints como herramientas, y cualquier asistente IA (Claude en Bedrock, copilots, agentes propios) las consume sin integraciones ad-hoc.
Container image desde ECR con uv; corre en < 1 s, sin servidores.
Trigger mensual y orquestación extract → modelo → validators con reintentos.
Tabla de asignaciones particionada por run_id; histórico replicado a S3.
REST con WAF, autenticación Cognito y FastAPI sobre Mangum.
OAuth2/SAML federado, least-privilege por servicio y cifrado en reposo.
Logs, traceo distribuido y alertas si métricas hacen drift > 15% MoM.
Versiona imágenes con SemVer; build, test y deploy automáticos al hacer push.
Dashboards ejecutivos conectados directo a Redshift, sin SQL para el usuario.
Expone endpoints como tools MCP para cualquier asistente IA (Claude, copilots).
Amazon Lex + Chime SDK en Teams/Slack/móvil que consulta el MCP.
Genera la razón en lenguaje natural de cada asignación (cumplimiento).
Notebook + tool MCP para simulaciones de escenarios sin escribir código.
EventBridge → Lambda → SNS con alerta proactiva si hay drift.
Roles IAM por agente, logs cifrados y política de no-fabricación.
El modelo en sí es lo más barato del sistema: corre en menos de 1 segundo. Lo que cuesta dinero y tiempo de implementación es la infraestructura de servicio: API, cache, autenticación, observabilidad y gobernanza. Por eso el bosquejo se concentra en separar las capas y dejar el cómputo desacoplado: cualquier mejora futura del modelo (cambiar pesos, agregar SA, escalar el problema) se publica reemplazando el job batch sin tocar la API ni los consumidores.
40.76% ≈ 40.8% predicho. Esto valida que el modelo es óptimo y que el techo no es artefacto del algoritmo.
Sobre-oferta (ratio 0.70). El MILP no encuentra ganancia porque no hay decisión real que tomar.
El CSV se podría producir con regla determinista en 0.06 s. Lo valioso es el techo cuantificado antes de optimizar.
Aceptar que es proyecto de gobernanza, no de algoritmos, demuestra madurez analítica.
Aunque innecesaria para este caso concreto, pueden existir muchos problemas del banco donde los análogos físicos (spin glass, mean-field, transiciones de fase) permitan entender y explicar mejor el negocio.
Reportar 40.76% sin contexto omite que el techo del problema está en 40.8%. Por eso este informe empieza por el techo.
ecasRecupera los 633 ejecutivos que están en clientes pero no en ecas. Sin tocar algoritmo.
Vocabulario unificado entre oportunidades y encuesta. Hoy mitigado con mapeo manual.
Reemplazar la encuesta auto-reportada con tiempos medidos directamente del CRM.
Histórico de campañas, tasa de respuesta, ticket promedio, churn, todas desde el data warehouse.
Buró, estrato, sector económico. APIs ya existentes. Solo si los pasos previos plateauean.