# Leads Optimizer — Documentación técnica ## Qué hace Agente automatizado que cada día: 1. Sincroniza el catálogo de campañas de Google Ads → Airtable 2. Lee las métricas del mes en curso (coste, conversiones, leads) 3. Analiza el rendimiento de cada campaña 4. Toma una decisión con IA (Claude) sobre qué hacer con el presupuesto 5. Aplica la decisión en Google Ads (o la simula en modo DRY_RUN) 6. Guarda el resultado en Airtable --- ## Modelo de negocio ``` Ingreso = leads_entregados × PPL Margen = (Ingreso - Gasto Google Ads) / Ingreso CPA max = PPL × 0.70 → margen mínimo aceptable del 30% ``` - **PPL** (Precio Por Lead): fijo por curso, ponderado por el % de invalidación del centro. - **Capping mensual**: máximo de leads a entregar por campaña ese mes. Si = 0, sin límite. --- ## Flujo de ejecución (`run.py`) ``` 1. Sincronizar campañas Google Ads → Google Ads Campaigns (Airtable) 2. Sincronizar GACampaignMes (estado mensual: coste, conversiones, PPL, cap) 3. Leer campañas activas del mes desde GACampaignMes 4. Detección anticipada: - courses_with_both: cursos con Search Y PMX simultáneos - courses_with_leadform: cursos con campaña _leadform activa 5. PRIMERA PASADA: para cada campaña - Contar leads Airtable (get_leads_this_month_gads) - Obtener métricas Google Ads (get_campaign_metrics) - Calcular leads_grupo (para PMX con companion: usa conversiones Google) - Analizar (analyzer.py) → urgencia, CPA, margen, ritmo - Decidir (agent.py / Claude) → acción, presupuesto, consejo 6. Actualizar Status en ambas tablas Airtable 7. SEGUNDA PASADA (ordenada por criticidad): - Imprimir resultado por consola - Aplicar decisión en Google Ads - Construir log_text con notas de atribución PMX/leadform 8. Guardar en GACampaignMes: Consejo, Criticidad, Log 9. Guardar ConvLeadsLakeMesFinal + ConvLeadsLakeMesGrupo ``` --- ## Módulos ### `analyzer.py` Calcula métricas derivadas y asigna la **urgencia**. Entradas: `campaign_config`, `leads_entregados`, `ads_metrics` | Métrica | Cálculo | |---------|---------| | `ratio_leads` | leads / capping | | `ratio_mes` | día actual / días del mes | | `ritmo` | ratio_leads − ratio_mes (>0 adelantado, <0 atrasado) | | `cpa_actual` | gasto / leads | | `margen` | (leads × PPL − gasto) / (leads × PPL) | **Urgencia:** | Valor | Condición | |-------|-----------| | `PAUSAR` | ratio_leads ≥ 1.0 (capping lleno) | | `SPRINT` | Atrasado >15% **y** quedan ≤5 días para fin de mes | | `ACELERAR` | ritmo < −0.15 | | `FRENAR` | ritmo > +0.15 | | `EN_RITMO` | diferencia ≤ 15% | --- ### `agent.py` Llama a Claude (claude-sonnet-4-20250514) con el resultado del análisis y devuelve una decisión en JSON. **Reglas de decisión:** | Urgencia | Rentable | Acción | Parámetro | |----------|----------|--------|-----------| | PAUSAR | — | `PAUSAR` | — | | SPRINT | — | `AUMENTAR_PRESUPUESTO` | ×1.3–1.5 | | ACELERAR | ✅ | `AUMENTAR_PRESUPUESTO` | ×1.1–1.25 | | ACELERAR | ❌ | `MANTENER` | — | | FRENAR | — | `REDUCIR_PRESUPUESTO` | ×0.75–0.9 | | EN_RITMO | ✅ | `MANTENER` | — | | EN_RITMO | ❌ | `REDUCIR_PRESUPUESTO` | ×0.85 | Devuelve: `accion`, `parametro`, `nuevo_budget_diario`, `justificacion`, `consejo`, `alerta`, `confianza`. --- ### `optimizer.py` Aplica la decisión del agente sobre Google Ads. Si `DRY_RUN=True`, solo imprime lo que haría. | Acción | Efecto | |--------|--------| | `PAUSAR` | Pausa la campaña vía API | | `AUMENTAR_PRESUPUESTO` / `REDUCIR_PRESUPUESTO` | Ajusta el presupuesto diario | | `MANTENER` | No hace nada | --- ### `airtable_client.py` Gestiona toda la comunicación con Airtable. **Tablas usadas:** | Tabla | Uso | |-------|-----| | `Google Ads Campaigns` | Catálogo de campañas (nombre, CampaignID, PPL, Status) | | `GACampaignMes` | Estado mensual por campaña (coste, conversiones, leads, consejo, criticidad, log) | | `Leads Lake` | Leads recibidos (filtrado por campaña y mes) | | `Cursos` | Relación CursoID ↔ CentroCurso | | `CentroCurso` | PPL y % invalidación por centro | | `CursoMes` | Capping mensual por curso | **Métodos principales:** - `build_campaign_lookups()` — calcula PPL ponderado y capping mensual para todos los cursos - `sync_campaigns_from_google_ads()` — crea/actualiza el catálogo de campañas - `sync_gacampaignmes()` — crea/actualiza registros mensuales de campañas con actividad - `get_active_gacampaignmes()` — devuelve las campañas del mes en curso - `get_leads_this_month_gads()` — cuenta leads Airtable para una campaña Google Ads - `batch_update_gacampaignmes_advice()` — guarda Consejo, Criticidad y Log - `batch_update_gacampaignmes_final_leads()` — guarda ConvLeadsLakeMesFinal y ConvLeadsLakeMesGrupo --- ### `google_ads_client.py` Wrapper sobre la API de Google Ads. - `get_all_campaigns()` — lista todas las campañas no eliminadas - `get_monthly_metrics_all()` — conversiones y coste del mes para todas las campañas (1 query) - `get_campaign_metrics()` — métricas detalladas de una campaña concreta - `set_campaign_budget()` — cambia el presupuesto diario - `pause_campaign()` — pausa una campaña --- ## Criticidad Combina urgencia + acción para priorizar el output y el campo `Criticidad` en Airtable: | Criticidad | Cuándo | |------------|--------| | **Crítico** | Urgencia es `PAUSAR` o `SPRINT` | | **Peligro** | Acción cambia el presupuesto pero urgencia no es crítica | | **Mantener** | Acción es `MANTENER` | --- ## Casos especiales de atribución ### PMX con companion Search (`courses_with_both`) Cuando un curso tiene activos simultáneamente `fco_search_NNN_*` y `fco_pmx_NNN_*`, Google puede reatribuir conversiones del Search al PMX. - La campaña **PMX** usa como `leads_grupo` las **conversiones de Google Ads** (no los leads de Airtable). - Se añade una nota `⚠️ PMX ATTRIBUTION` en el campo `Log` de la campaña Search. ### Campaña Leadform (`_leadform`) Las campañas con sufijo `_leadform` capturan leads directamente dentro de Google (sin que el usuario visite la web). Esos leads **nunca llegan a Airtable**. - Se añade `ℹ️ LEADFORM` en el `Log` de la propia campaña. - Las campañas hermanas del mismo curso reciben `⚠️ LEADFORM COMPANION` en su `Log`. --- ## Configuración (`.env`) ```env AIRTABLE_TOKEN=... AIRTABLE_BASE_ID=... GOOGLE_ADS_DEVELOPER_TOKEN=... GOOGLE_ADS_CLIENT_ID=... GOOGLE_ADS_CLIENT_SECRET=... GOOGLE_ADS_REFRESH_TOKEN=... GOOGLE_ADS_LOGIN_CUSTOMER_ID=... ANTHROPIC_API_KEY=... ``` `DRY_RUN = True` en `config.py` — cambiar a `False` para aplicar cambios reales en Google Ads. --- ## Logs Cada ejecución genera un archivo en `logs/YYYYMMDD_HHMMSS.log` con la salida completa. --- ## Ejecutar ```bash python run.py ```