Add sync detail and per-campaign advice

- Sync shows campaign ID, name and status for new campaigns,
  and field-level changes (before/after) for updated ones
- Agent now returns a 'consejo' field with a specific actionable
  recommendation per campaign
- Summary table includes leads, margin and consejo per campaign

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jose Manuel 2026-04-17 16:16:43 +02:00
parent 2f88eb119d
commit e4362c6ad9
3 changed files with 30 additions and 12 deletions

View File

@ -29,12 +29,14 @@ Devuelve ÚNICAMENTE un JSON válido con esta estructura exacta, sin texto adici
"accion": "PAUSAR | REDUCIR_PRESUPUESTO | AUMENTAR_PRESUPUESTO | MANTENER",
"parametro": 1.0,
"nuevo_budget_diario": 0.0,
"justificacion": "explicación breve en español",
"justificacion": "explicación breve del porqué de la decisión",
"consejo": "acción concreta y específica que debería tomar el gestor (keywords, pujas, anuncios, configuración, etc.)",
"alerta": "texto si hay algo crítico, null si no hay",
"confianza": 0.0
}
El campo nuevo_budget_diario = budget_diario_actual × parametro (calcula el valor final).
El campo consejo debe ser accionable y específico: qué revisar, qué cambiar, qué hacer a continuación.
"""

View File

@ -43,10 +43,10 @@ class AirtableClient:
if r["fields"].get("CampaignID")
}
created = []
updated = []
created = [] # lista de dicts {name, id, status}
updated = [] # lista de dicts {name, id, changes}
to_create = []
to_update = [] # lista de (record_id, changes, name)
to_update = [] # lista de (record_id, changes, name, gid, at_status)
for gc in google_campaigns:
gid = gc["id"]
@ -55,16 +55,25 @@ class AirtableClient:
if at_record is None:
to_create.append({"Campaign Name": gc["name"], "CampaignID": gid, "Status": at_status})
created.append(gc["name"])
created.append({"name": gc["name"], "id": gid, "status": at_status})
else:
changes = {}
change_detail = {}
if at_record["fields"].get("Campaign Name") != gc["name"]:
changes["Campaign Name"] = gc["name"]
change_detail["nombre"] = {
"antes": at_record["fields"].get("Campaign Name"),
"ahora": gc["name"],
}
if at_record["fields"].get("Status") != at_status:
changes["Status"] = at_status
change_detail["status"] = {
"antes": at_record["fields"].get("Status"),
"ahora": at_status,
}
if changes:
to_update.append((at_record["id"], changes, gc["name"]))
updated.append(gc["name"])
updated.append({"name": gc["name"], "id": gid, "changes": change_detail})
# Crear en lotes de 10
for i in range(0, len(to_create), 10):

19
run.py
View File

@ -56,12 +56,13 @@ def run():
sync_result = at.sync_campaigns_from_google_ads(google_campaigns)
if sync_result["created"]:
print(f" ✅ Campañas nuevas importadas ({len(sync_result['created'])}):")
for name in sync_result["created"]:
print(f" + {name}")
for c in sync_result["created"]:
print(f" + [{c['id']}] {c['name']}{c['status']}")
if sync_result["updated"]:
print(f" 🔄 Campañas con nombre actualizado ({len(sync_result['updated'])}):")
for name in sync_result["updated"]:
print(f" ~ {name}")
print(f" 🔄 Campañas actualizadas ({len(sync_result['updated'])}):")
for c in sync_result["updated"]:
for field, val in c["changes"].items():
print(f" ~ [{c['id']}] {c['name']} | {field}: '{val['antes']}''{val['ahora']}'")
if not sync_result["created"] and not sync_result["updated"]:
print(" ✓ Sin cambios en el catálogo.")
print()
@ -109,6 +110,9 @@ def run():
f"(confianza: {decision['confianza']*100:.0f}%)")
print(f" Justificación: {decision['justificacion']}")
if decision.get("consejo"):
print(f" 💡 Consejo: {decision['consejo']}")
if decision.get("alerta"):
print(f" 🚨 {decision['alerta']}")
@ -122,6 +126,7 @@ def run():
"leads": f"{leads}/{campaign['capping_mensual']}",
"cpa": analysis["cpa_actual"],
"margen": f"{analysis['margen']*100:.0f}%",
"consejo": decision.get("consejo", ""),
})
print()
@ -131,7 +136,9 @@ def run():
print("RESUMEN FINAL")
print(f"{'='*55}")
for r in resumen:
print(f" {r['curso'][:35]:<35} | {r['urgencia']:<12} | {r['accion']}")
print(f" {r['curso'][:35]:<35} | {r['urgencia']:<12} | {r['accion']:<25} | {r['leads']} leads | {r['margen']} margen")
if r["consejo"]:
print(f" {'':35} {'💡':>14} {r['consejo']}")
print()