From face2d3d1baa0117d48665e33c90c52553b7750e Mon Sep 17 00:00:00 2001 From: Carlos Narro Date: Tue, 9 Jun 2026 22:16:42 +0200 Subject: [PATCH] =?UTF-8?q?Handoff=20runtime=20del=20bot=20para=20Sim?= =?UTF-8?q?=C3=B3n:=20estado,=202=20issues=20y=20v=C3=ADa=20Evolution=20AP?= =?UTF-8?q?I?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Documenta lo que funciona (flujo conversacional end-to-end, EPs, worker), los cambios de hoy en el bot (@lid, by-phone, markOnline, modelos, apertura, /qr, /debug) y los 2 problemas de runtime que quedan en su lado: Baileys deja de recibir tras reconectar (vía robusta = Evolution API, no Cloud API oficial) y la persistencia parcial del perfil (revisar logs de persistirTurno). Co-Authored-By: Claude Opus 4.8 --- docs/handoff-bot-runtime-simon.md | 99 +++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 docs/handoff-bot-runtime-simon.md diff --git a/docs/handoff-bot-runtime-simon.md b/docs/handoff-bot-runtime-simon.md new file mode 100644 index 0000000..713b15f --- /dev/null +++ b/docs/handoff-bot-runtime-simon.md @@ -0,0 +1,99 @@ +# Handoff runtime del bot WhatsApp (Luisa) — para Simón + +Estado tras la sesión de depuración del 09-jun. El **flujo conversacional funciona end-to-end**; +quedan **dos problemas de runtime del bot** que se diagnostican/cierran desde tu lado (tienes acceso +a los logs en Dokploy). La parte de la app (EPs) está verificada y no es el problema. + +--- + +## 1. Lo que está PROBADO funcionando + +- **EPs de la app** (en `mvp/b2c`): `conversacion`, `perfil`, `calificacion`, `intento`, `ingesta`, + + `GET /api/leads/:id` y `GET /api/leads/:id/conversacion` y `GET /api/leads/by-phone`. Todos OK. + Verificado: `POST /perfil` con el payload exacto del bot (`{espacio,rangoM2,estilo,botStep}`) → + `200 {ok:true, actualizado:[...]}` y el lead se actualiza. **El EP no es el cuello de botella.** +- **Bot:** apertura proactiva, **resolución del `@lid`**, matching del lead por teléfono, y la + **conversación de cualificación de Luisa** (7 turnos reales: cocina → tamaño → estilo → urgencia). +- **Worker de render** (`mvp/image-worker`): genera renders con `google/gemini-2.5-flash-image`. + +## 2. Cambios que hice hoy en el bot (`mvp/Whatsapp-bot`) — para que no te pillen por sorpresa + +- **Apertura proactiva** al recibir `/whatsapp-start`: `WhatsappService` escucha un `startEmitter` y + envía el primer mensaje (antes solo registraba la sesión y esperaba al cliente). Persiste + `estadoWa/botStep` + intento. +- **Resolución `@lid`** ([`resolverTelefono`](../mvp/Whatsapp-bot/src/whatsapp/whatsapp.service.ts)): + WhatsApp entrega los mensajes desde una dirección `@lid` (p.ej. `239225534443615@lid`), no desde el + número. Se resuelve a número vía `msg.key.remoteJidAlt` o el mapa LID→PN de Baileys. **Esto era la + causa de que el bot ignorara los mensajes entrantes.** +- **Recuperación del lead por teléfono:** si la sesión no está en memoria (reinicio), `getOrCreateContext` + busca el lead en la BD vía `GET /api/leads/by-phone` y re-registra la sesión. +- **`markOnlineOnConnect: true`**: con `false`, tras reconectar el dispositivo quedaba "no disponible" + y WhatsApp **no entregaba** los mensajes. Con `true` empezó a recibir (la conversación de 7 turnos + lo demuestra). +- **Modelos de Claude corregidos** en el env: eran `claude-haiku-4-5` (guion) → inválidos en + OpenRouter; ahora `anthropic/claude-haiku-4.5` y `anthropic/claude-sonnet-4.5` (punto). +- **`BAILEYS_AUTH_DIR`** configurable (subcarpeta del volumen) para empezar una **sesión limpia** + sin perder persistencia. Hoy apunta a `/app/auth_info_baileys/v2`. +- **Endpoints de operación** (servidor de webhooks, puerto 3001): + - `GET /qr` — QR de vinculación como **imagen** (HTTP Basic; usuario cualquiera, contraseña = + `QR_TOKEN`, que está en el env del bot en Dokploy). + - `GET /debug` — estado de conexión + anillo de los últimos eventos entrantes (mismo auth). Útil + para ver `remoteJid`/`remoteJidAlt`, si llega algo, y el resultado del matching. +- Subido el límite de body del worker a 30 MB (las fotos en data URI rompían el 100kb por defecto). + +## 3. Problema A — Baileys deja de recibir tras reconectar + +**Síntoma:** justo tras un **escaneo fresco** del QR, el bot recibe mensajes (conversación OK). Pero +tras cualquier **reconexión** (redeploy, o auto-reconexión de Baileys al caerse el socket), reporta +`connection: "open"` pero **no recibe nada** (`/debug` → `inbound: []` aunque el cliente escriba). +`markOnlineOnConnect: true` ayudó al primer connect pero no lo blinda contra reconexiones. + +**Cómo reproducir/diagnosticar:** +- `GET https://reformix-bot.dv3.com.es/debug` (Basic, contraseña `QR_TOKEN`): si `connection:open` + pero `inbound` no crece cuando el cliente escribe → estás en el estado "zombi". +- Para volver a recibir: sesión nueva → sube `BAILEYS_AUTH_DIR` a `/app/auth_info_baileys/v3`, + redeploy, escanea el QR fresco en `/qr`. **Y evita redeployar después** (cada reconexión arriesga + la recepción). + +**Camino robusto (acordado con Carlos): migrar el transporte de WhatsApp del bot a Evolution API** +(ya está como primaria en el stack — NO la WhatsApp Cloud API oficial). Evolution gestiona la +conexión y entrega los mensajes por **webhook**, sin un socket Baileys en-proceso que se vuelva +zombi. El bot pasaría a: (1) recibir mensajes por webhook de Evolution, (2) enviar por su REST. La +lógica de Claude + los EPs de la app se quedan igual; solo cambia la capa de transporte WhatsApp. + +## 4. Problema B — el perfil extraído no se persiste en el lead durante la conversación + +**Síntoma:** la conversación avanza (Luisa pregunta espacio→tamaño→estilo→urgencia) pero el lead +queda con `botStep` desfasado y `espacio/rangoM2/...` vacíos. + +**Qué sé:** el EP `/perfil` **funciona** con el payload del bot (probado). Así que el fallo está en la +llamada del bot. [`LeadsService.persistirTurno`](../mvp/Whatsapp-bot/src/leads/leads.service.ts#L68) +ya loguea: `Lead X persistido via API: {...} → ok/fallo`. + +**Qué mirar (logs del bot en Dokploy) durante una conversación:** +- Si aparece `→ fallo`: mira el payload logueado. Sospechosos: un valor de enum inválido (p.ej. + `urgencia` fuera de `alta|media|baja`, o `calidadGlobal` fuera de `basica|media|premium`) hace que + TODO el `POST /perfil` dé `422` y no persista nada de ese turno. Mapea los valores que extrae Claude + a los enums de la app antes de enviar. +- Si no aparece la línea: `persistirTurno` no se está llamando → revisa que `claudeService.llamarClaude` + devuelva `entidad`/`nuevoEstado`. +- Ojo también a si el bot se **reinició a media conversación** (Problema A): tras reiniciar deja de + recibir y el flujo se corta. + +## 5. Infra (referencia rápida) + +| Servicio | App Dokploy | Dominio | +| --- | --- | --- | +| Bot | `reformix-bot` (`wY4F14fyEslU-4za_JIbi`) | `reformix-bot.dv3.com.es` (puerto 3001) | +| Worker | `reformix-worker` (`sMQd9zwoyV14q1vm8Vs8U`) | `reformix-worker.dv3.com.es` | +| App | `reformix-b2c` (`lzHDAuPuubbJu94OrkNS_`) | `reformix.dv3.com.es` | + +- Build Dockerfile desde Gitea, autodeploy en push a `main`. Volumen del bot: `/app/auth_info_baileys` + (sesión WhatsApp). `OPENROUTER_API_KEY` y `FUNNEL_API_KEY` en el env de cada app. +- Contrato de los EPs y enums: [`mvp/b2c/api-docs/README.md`](../mvp/b2c/api-docs/README.md). + +--- + +**Resumen:** el flujo está demostrado funcionando; los 2 problemas son de la capa de transporte/runtime +del bot. Recomendación: cerrar el Problema B mirando los logs de `persistirTurno`, y para el Problema A +(estabilidad) migrar a **Evolution API**. Mientras tanto, para una demo: escaneo fresco + no redeployar.