Add technical documentation (docu.md)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
369bca604e
commit
d58448f698
203
docu.md
Normal file
203
docu.md
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
# 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
|
||||||
|
```
|
||||||
Loading…
x
Reference in New Issue
Block a user