Prueba analítica

Modelo de Capacidadpara la red comercial

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.

40.76% Métrica oficial x/y
= techo teórico
40.8% Techo teórico
predicho en EDA
27.115 Clientes asignados
de 34.145 totales
4 / 4 Enfoques convergen
al mismo óptimo

El problema en una imagen

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.

CAPA 1 · CLIENTES n = 34.145 B A C B A B C A B C CAPA 2 · EJECUTIVOS 392 asignables · 633 huérfanos E1 E2 E3 E4 huérfano (no en ecas) CAPA 3 · GERENTES DE INVERSIÓN 49 con equipo · T_g = 75.930 min/año Gerente 1 cap = 75.930 min Gerente 2 cap = 75.930 min Gerente 3 cap = 75.930 min ⚙ Restricciones: misma zona · un ejecutivo→un gerente · todo o nada · no exceder T_g · prioridad A>B>C
Insight

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.

La métrica oficial que tenemos que maximizar

$$ \text{score} = \frac{x}{y} = \frac{\#A_{\text{asignados}} + \#B_{\text{asignados}}}{N_{\text{clientes totales}}} $$

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 función realmente optimizada y su variable de decisión

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$:

$$ \max_{\boldsymbol{x}} \;\; \sum_{e \in E} \sum_{g \in G_e} V_e \cdot x_{e,g} $$

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:

$$ V_e = w_A \cdot n^A_e + w_B \cdot n^B_e + w_C \cdot n^C_e + \alpha \cdot n_e \cdot \bar{s}_e $$

¿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$).

Los datos disponibles

Recibimos 7 archivos CSV. Cada uno aporta una pieza del problema. Los tamaños van desde 50 filas (gerentes) hasta 247 mil (oportunidades).

👥

pcac_mac_gpi_clientes

34.145

Población de clientes Preferencial con categoría A/B/C, score, zona y asignación actual.

🔗

pcac_mac_gpi_ecas

392

Estructura de la red: relación gerente ↔ ejecutivo. Llave crítica para el modelo.

💼

pcac_oportunidades_comer

247.863

Oportunidades comerciales por cliente y producto. Insumo para calcular tiempo demandado.

📦

pcac_mac_gpi_tenencia_prod

66.802

Si el cliente ya tiene/usa cada producto. Define qué oportunidades requieren venta nueva.

🏢

pcac_planta_comercial2

50

Catálogo de Gerentes de Inversión con ciudad, región, banca y tipo.

⏱️

pcac_encuesta

1.804

Tiempos auto-reportados por (gerente, producto, etapa). Insumo para τ_c.

📅

pcac_capacidad_gerentes

50

Tiempo anual disponible T_g por gerente (75.930 min ≈ 1.265 horas).

Distribución por categoría comercial

El hallazgo dominante: los huérfanos

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.

EJECUTIVOS EN clientes 1.003 tienen al menos un cliente Preferencial EJECUTIVOS EN ecas 392 tienen un Gerente de Inversión asignable HUÉRFANOS en clientes pero no en ecas 633 63% del total ASIGNABLES intersección 370 SIN CLIENTES en ecas, sin cartera 22 ↓ Impacto cuantificado los 633 ejecutivos huérfanos arrastran 7.030 clientes (20,6%) fuera del modelo · entre ellos 1.579 A + 2.727 B

Impacto cuantificado en clientes

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

Los dos techos del problema

Diagnóstico

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í.

Validación de la conclusión

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.

Llaves consistentes

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.

Universo bien definido

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.

!

Matiz por segmento

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.

Composición por segmento de los 7.030 clientes huérfanos

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
Lectura

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”.

De lo simple a lo sofisticado

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.

Nivel 1 · Simple

🟢 Analítico (regla)

Ordena ejecutivos por A→B→C→score, asigna al primer gerente compatible con capacidad.

Tiempo
0.06s
Resultado
0.4076

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.

Nivel 2 · Heurística

🟡 Greedy densidad v/t

Prioriza ejecutivos por valor/tiempo (ρ = v_e / t_e), best-fit por capacidad.

Tiempo
0.06s
Resultado
0.4076

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.

Nivel 3 · Exacto

🟠 MILP (PuLP + CBC)

Programación entera mixta. Garantía de óptimo global por construcción.

Tiempo
0.49s
Estado
Optimal

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.

Nivel 4 · Físico

🔴 Simulated Annealing

Cadena MCMC sobre Hamiltoniano de Ising. Aporta diagnósticos físicos.

Pasos
80.000
Resultado
0.4076

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.

▶ Explorar las 4 metodologías de forma interactiva

simulacion-capacidad.netlify.app · simulaciones paso a paso: árbol de branch & bound, cooling curve, best-fit en vivo

El pipeline ejecutado

1
Carga

7 CSVs en data/raw/

2
Feature engineering

τ_c y t_e por ejecutivo

3
4 modelos

Analytical, greedy, MILP, SA

4
Validación

5 reglas duras del PDF

5
Export

resultado_prueba.csv

Comparación visual de los 4 enfoques

Lectura

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.

El problema es un Ising spin glass

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.

Versión 1: el problema en lenguaje del banco

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:

$$ \max \sum_{e,g} \underbrace{V_e}_{\text{valor del ejecutivo}} \cdot \; x_{e,g} $$

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:

Versión 2: el mismo problema como Ising spin glass

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)$:

$$ \min \, H(\boldsymbol{\sigma}) = \underbrace{-\sum_{e,g} v_e \, \sigma_{e,g}}_{\text{campo externo (tira a 1)}} \;+\; \underbrace{\sum_g \lambda_g \Big(\sum_e t_e \sigma_{e,g} - T_g\Big)_+^2}_{\text{muro de capacidad}} $$

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$.

Equivalencia: misma matemática, dos lenguajes

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
Insight

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.

¿Qué es un campo externo?

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.

¿Por qué un resorte unilateral?

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.

Estado de spines (configuración σ) σ_eg = 1 (asignado) σ_eg = 0 (no asignado) El estado fundamental ≡ asignación óptima

Tres diagnósticos físicos: qué gana el banco, qué gana el físico

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.

1. Curva de enfriamiento $\langle H \rangle(T)$

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.

2. Calor específico $C(T) = \dfrac{\langle H^2 \rangle - \langle H \rangle^2}{T^2}$

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.

3. Susceptibilidad $\chi(T) = \dfrac{\langle M^2 \rangle - \langle M \rangle^2}{T}$

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.

Lectura

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.

¿Cuándo conviene cada método?

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

Lo que el modelo entrega

27.115
Clientes asignados
de 34.145
71.3%
Clientes A asignados
de los asignables
78.6%
Clientes B asignados
de los asignables
92.3%
Utilización media
de los gerentes

Estructura del archivo resultado_prueba.csv

Esquema 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_clistring (19 dígitos)Documento del cliente asignado
cod_tipo_doc_cliintegerTipo de documento (1, 2, ...)
cod_ejec_bcostring (19 dígitos)Ejecutivo del cliente
num_doc_gte_invstring (19 dígitos)Documento del Gerente de Inversión asignado
cod_gte_invstring (19 dígitos)Código del Gerente de Inversión asignado

Validaciones que pasa el resultado

Único gerente por ejecutivo

Cada cod_ejec_bco aparece con un solo cod_gte_inv.

Misma zona

100% de las asignaciones cumplen zona(e) = zona(g).

Capacidad respetada

Ningún gerente excede su tiempo_restante anual.

Asignación todo-o-nada

Todos los clientes de un ejecutivo van al mismo gerente, sin parciales.

La solución completa, paso a paso

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.

Pipeline en 5 pasos

src/modelo_capacidad/run_all.py Orquestador
# 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

Feature engineering: cálculo de τc

El tiempo demandado por cliente sale de cruzar oportunidades, tenencia y la encuesta de tiempos. Tres casos:

src/modelo_capacidad/features/tiempo_demanda.py Feature engineering
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")

Modelo MILP exacto

Programación entera con PuLP/CBC. Optimal en 0.49 s para 370 ejecutivos × 49 gerentes con filtro de zona.

src/modelo_capacidad/models/milp.py Optimización exacta
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]

Validación y exportación del CSV

Antes de escribir el archivo se verifican las 5 reglas duras del PDF. Si alguna falla, el pipeline detiene la publicación.

src/modelo_capacidad/utils/validators.py Validación + export
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
Reproducibilidad

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.

Entregables del proyecto

Cobertura de los cuatro entregables del PDF + extras: pipeline reproducible, tests automáticos y subagentes especializados.

# Entregable PDF Archivo / ubicación Estado
1Documento explicativodocs/documento_final.md + este HTML
2Archivo de asignaciónresultado_prueba.csv (27.115 filas)
3Códigos documentadossrc/modelo_capacidad/ + tests/
4Bosquejo arquitecturaSección anterior + docs/arquitectura.md
+Variables adicionales (opcional)docs/variables_adicionales.md + sección siguiente

Variables adicionales propuestas

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 🟡 / 🔴
Insight

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.

Arquitectura del sistema

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.

☁ AWS Cloud VPC privada · IAM least-privilege · KMS PIPELINE BATCH (mensual) FUENTES S3 (raw lake) + Aurora Glue Data Catalog ORQUESTADOR EventBridge Scheduler + Step Functions ⚡ AWS LAMBDA job de modelado container image · uv · <1 s VALIDATORS Lambda separada 5 reglas duras PERSISTENCIA Amazon Redshift Serverless tabla asignaciones · particionada por run_id + S3 export para histórico inmutable OBSERVABILIDAD CloudWatch + X-Ray SNS para alertas SERVICIO (consumo en línea) API Gateway REST · throttling · WAF Cognito authorizer ⚡ Lambda (FastAPI) Mangum adapter · Pydantic /asignacion/cliente/{id} Web interna · QuickSight dashboard gerentes App móvil cartera del gerente CRM (Salesforce) vista del cliente

Endpoints clave de la API

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.

☁ AWS Cloud · Capa de agentes API EXISTENTE API GW + Lambda FastAPI ⚡ SERVIDOR MCP · Lambda expone endpoints como tools de IA get_asignacion · get_metricas · what_if Amazon Bedrock Claude · Llama · Titan ⚠ Watchdog agent EventBridge → Lambda → SNS 💬 Asistente del gerente Amazon Lex / Chime SDK Teams · Slack · app móvil 🧠 Explainer agent Lambda + Bedrock Agents razón de cada asignación 🔁 Copilot what-if SageMaker Notebook + MCP simulaciones de escenarios

Componentes AWS y decisiones técnicas

AWS Lambda · job de modelado

Container image desde ECR con uv; corre en < 1 s, sin servidores.

📅

EventBridge + Step Functions

Trigger mensual y orquestación extract → modelo → validators con reintentos.

🗄

Amazon Redshift Serverless

Tabla de asignaciones particionada por run_id; histórico replicado a S3.

🌐

API Gateway + Lambda (FastAPI)

REST con WAF, autenticación Cognito y FastAPI sobre Mangum.

🔐

Cognito + IAM + KMS

OAuth2/SAML federado, least-privilege por servicio y cifrado en reposo.

📊

CloudWatch + X-Ray + SNS

Logs, traceo distribuido y alertas si métricas hacen drift > 15% MoM.

🐳

ECR + CodePipeline

Versiona imágenes con SemVer; build, test y deploy automáticos al hacer push.

📈

QuickSight

Dashboards ejecutivos conectados directo a Redshift, sin SQL para el usuario.

🔌

Servidor MCP · Lambda

Expone endpoints como tools MCP para cualquier asistente IA (Claude, copilots).

💬

Asistente del gerente

Amazon Lex + Chime SDK en Teams/Slack/móvil que consulta el MCP.

🧠

Explainer · Bedrock Agents

Genera la razón en lenguaje natural de cada asignación (cumplimiento).

🔁

Copilot what-if · SageMaker

Notebook + tool MCP para simulaciones de escenarios sin escribir código.

Watchdog agent

EventBridge → Lambda → SNS con alerta proactiva si hay drift.

🛡

Gobernanza de agentes

Roles IAM por agente, logs cifrados y política de no-fabricación.

Insight

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.

Conclusiones

1

La métrica obtenida coincide con el techo teórico

40.76% ≈ 40.8% predicho. Esto valida que el modelo es óptimo y que el techo no es artefacto del algoritmo.

2

Convergen porque no compiten por capacidad

Sobre-oferta (ratio 0.70). El MILP no encuentra ganancia porque no hay decisión real que tomar.

4

El verdadero entregable es el diagnóstico

El CSV se podría producir con regla determinista en 0.06 s. Lo valioso es el techo cuantificado antes de optimizar.

5

Frontera tenue entre optimización y datos

Aceptar que es proyecto de gobernanza, no de algoritmos, demuestra madurez analítica.

5

El análogo físico vale más allá de este problema

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.

7

La métrica oficial puede ser injusta

Reportar 40.76% sin contexto omite que el techo del problema está en 40.8%. Por eso este informe empieza por el techo.

Oportunidades de mejora

Oport.

Reconciliar huérfanos en ecas

Recupera los 633 ejecutivos que están en clientes pero no en ecas. Sin tocar algoritmo.

↑ Métrica: 40.8% → 53.4% (+12.6 puntos)
Oport.

Tabla maestra de productos

Vocabulario unificado entre oportunidades y encuesta. Hoy mitigado con mapeo manual.

↑ τ_c con error reducido del 50% al 10%
Oport.

Telemetría real de actividad

Reemplazar la encuesta auto-reportada con tiempos medidos directamente del CRM.

↑ Validez del modelo en producción
Oport.

Variables internas adicionales

Histórico de campañas, tasa de respuesta, ticket promedio, churn, todas desde el data warehouse.

↑ Mejor priorización dentro de A/B
Oport.

Variables externas (DataCrédito, DANE)

Buró, estrato, sector económico. APIs ya existentes. Solo si los pasos previos plateauean.

↑ Refinamiento del score upstream