6.7 KiB
Leads Optimizer — Documentación técnica
Qué hace
Agente automatizado que cada día:
- Sincroniza el catálogo de campañas de Google Ads → Airtable
- Lee las métricas del mes en curso (coste, conversiones, leads)
- Analiza el rendimiento de cada campaña
- Toma una decisión con IA (Claude) sobre qué hacer con el presupuesto
- Aplica la decisión en Google Ads (o la simula en modo DRY_RUN)
- 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 cursossync_campaigns_from_google_ads()— crea/actualiza el catálogo de campañassync_gacampaignmes()— crea/actualiza registros mensuales de campañas con actividadget_active_gacampaignmes()— devuelve las campañas del mes en cursoget_leads_this_month_gads()— cuenta leads Airtable para una campaña Google Adsbatch_update_gacampaignmes_advice()— guarda Consejo, Criticidad y Logbatch_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 eliminadasget_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 concretaset_campaign_budget()— cambia el presupuesto diariopause_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_grupolas conversiones de Google Ads (no los leads de Airtable). - Se añade una nota
⚠️ PMX ATTRIBUTIONen el campoLogde 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
ℹ️ LEADFORMen elLogde la propia campaña. - Las campañas hermanas del mismo curso reciben
⚠️ LEADFORM COMPANIONen suLog.
Configuración (.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
python run.py