CustomAdminDeveloperArchitect32 min de leitura

WhatsApp + Agentforce v2 · Handoff completo bot → cola → humano, ownership real y visibilidad de adjuntos en el Case

O Desafio

La V1 ya recibe adjuntos y responde por Agentforce. Falta: escalar a un humano cuando el cliente lo pide, saber quién está atendiendo, cerrar conversaciones inactivas y que los adjuntos aparezcan también en el Case.

Jonathan Gomez·Agentforce Enterprise ArchitectAtualizado: 30 de julho de 2026
HandoffAgentforceWhatsAppQueueBot UserTimeout SchedulerCustom NotificationsPlatform EventsempApiContentDocumentLinkGenAiFunctionCustom PermissionLWC Record PageInvocable ApexPrompt Templates

Em resumo

  • V2 se apoya en toda la infraestructura de la V1 (webhook Meta, objetos custom, pipelines de adjuntos) y agrega el layer que faltaba: gestión de conversaciones cuando el bot NO puede resolver.
  • El cliente puede pedir 'hablar con una persona' y una GenAiFunction (FDE_afEscalateToHuman) reasigna la conversación a una cola configurable, opcionalmente crea Case, dispara Custom Notifications a los miembros y bloquea al bot de responder.
  • El bot ahora se muestra como owner de la conversación (Bot User real, no 'Automated Process'). Al aceptar un asesor, el owner cambia al User. Reports y list views nativos por owner cuentan la historia.
  • Nuevo tab Administración en el LWC (gated por Custom Permission) donde el admin configura por línea: cola destino, crear caso o no, mensajes al cliente y timeouts bot/humano.
  • Timeout Scheduler cierra conversaciones inactivas cada 5 minutos, con umbrales distintos para modo bot y modo humano.
  • 3 acciones invocables nuevas conectan puntos: FDE_afEscalateToHuman (escalar), FDE_LinkContactToConversation (persistir el contact identificado en la conversación) y las dos que resuelven el 'hueco' de los adjuntos históricos que no se vinculaban al Case cuando llegaba después.
  • Toda la fase queda detrás de un feature flag Handoff_Enabled__c por línea. Rollback es un checkbox, sin redeploy.

La V1 dejó al bot conversando con el cliente y procesando adjuntos. La V2 responde a la pregunta que nadie contestaba: y cuando el bot NO puede, ¿qué? Antes, la conversación quedaba huérfana — sin owner claro, sin ruta al asesor humano, sin cierre automático, y con los adjuntos previos invisibles cuando alguien creaba el Case después. La V2 cierra esos cuatro huecos con piezas mínimas y compostables.

  • Handoff a humano — cuando el cliente lo pide o el bot lo decide, una GenAiFunction transfiere ownership de la conversación a una cola, notifica a los miembros y bloquea al bot para que no responda encima del humano.
  • Ownership del bot visible — mientras el bot atiende, el owner es un Bot User real con nombre propio (no 'Automated Process'). Reports, list views y auditoría dicen la verdad.
  • Cierre automático por inactividad — un scheduler cada 5 minutos cierra conversaciones idle. Timeout distinto para modo bot y modo humano.
  • Adjuntos vinculados al Case y a la conversación — dos acciones invocables Apex hacen el linking bidireccional: cuando llega un adjunto nuevo se enlaza forward al Case si ya existe; cuando el Case se crea después, se hace backfill de los adjuntos históricos.
  • Contact auto-linked a la conversación — una acción invocable persiste el ContactId que el agente ya resolvió, sin duplicar lookups.
  • Tab de administración en el LWC — un admin con Custom Permission configura la política de handoff por línea de WhatsApp: cola, checkbox de crear Case, mensajes al cliente, timeouts. Sin código.
  • Feature flag por línea (Handoff_Enabled__c) — todo lo nuevo vive detrás de un checkbox por WhatsApp_Configuration__c. Se activa por línea de forma independiente. Rollback = destildar.
V1 · Bot atiende adjuntos y responde
V2 · V1 + handoff, ownership, timeouts, backfill
  • Rol del bot
    Responde texto, procesa imagen/audio/documento vía prompt templates, y genera Case cuando el planner lo pide.
    Todo lo de V1 + puede invocar FDE_afEscalateToHuman cuando el cliente pide humano o cuando el propio planner detecta que no puede continuar.
  • Owner de la conversación
    Automated Process (User de sistema para contextos async). Impide reports por owner útiles.
    Bot User real durante modo bot; Queue durante handoff pending; User cuando un asesor acepta. Todos los list views y reports nativos por owner cuentan la historia real.
  • Cuando el bot no puede resolver
    El bot responde con su fallback textual o pide reintentar. Cliente queda sin escalamiento real.
    Cliente pide humano → GenAiFunction escalona → cola → notificaciones a asesores → primer asesor que acepta se lleva la conversación. Bot bloqueado de responder mientras humano atiende.
  • Vida de la conversación
    Session_Expiry_Time__c controla reutilización de conversación (para nuevas sesiones Agentforce). Sin cierre automático.
    Session_Expiry_Time__c conservado como está. Se agrega Idle_Expiry_Time__c que un scheduler evalúa cada 5 min con umbrales distintos según owner (Bot_Idle_Timeout_Minutes__c vs Human_Idle_Timeout_Minutes__c).
  • Adjuntos vs Case
    Adjuntos quedan vinculados solo al WhatsApp_Message__c. Si el Case se crea después, esos adjuntos no aparecen en el Case.
    Dos acciones invocables cierran el hueco: forward al Case cuando el adjunto llega y ya existe Case; backfill cuando el Case se crea y hay adjuntos previos.
  • Contact ↔ Conversation
    El agente identifica al Contact vía lookup pero no persiste el vínculo en WhatsApp_Conversation__c.Contact__c.
    Una acción invocable (FDE_LinkContactToConversation) persiste el vínculo idempotentemente después de que el planner resuelve el Contact.
  • Administración
    Configuración vía metadata de WhatsApp_Configuration__c y setup manual. Cambios requieren edit del registro por developer.
    Nuevo tab Administración en el LWC dashboard, gated por Custom Permission WhatsApp_Admin. El admin configura cola, crear-caso, mensajes al cliente y timeouts sin código.
  • Notificación al equipo
    No hay señal al equipo cuando el bot no puede continuar. La conversación queda en la lista general.
    Custom Notifications al bell de cada miembro del queue al escalar. Custom Notifications al owner cuando llega mensaje nuevo en modo humano. LWC actualiza en vivo vía empApi + Platform Event dedicado.
  • Ubicación del chat en la UI
    Un solo LWC dashboard con lista + panel de chat.
    Mismo dashboard + un LWC nuevo standalone (whatsappConversationRecord) para embed en el record page de WhatsApp_Conversation__c. Altura configurable desde App Builder.
  • Feature flag
    Sin flag. Todo prendido siempre.
    Handoff_Enabled__c por WhatsApp_Configuration__c. Deploy no cambia comportamiento de ninguna línea hasta que el admin la enciende. Rollback = destildar.

El handoff no es un flag mágico. Es una secuencia disciplinada: el LLM decide, invoca una acción Apex, la acción cambia ownership de la conversación, dispara notificaciones, publica un Platform Event para el LWC en vivo, y el gate en el InboundEventHandler impide que el bot responda encima del humano cuando llegue el próximo mensaje.

Ciclo bot → cola → humano

  1. 1

    Cliente en WhatsApp

    Manda 'necesito hablar con una persona' o similar

  2. 2

    WhatsAppInboundEventHandler

    Procesa el mensaje como de costumbre y encola a Agentforce Queueable

  3. 3

    Agentforce (planner)

    El topic 'Escalamiento a asesor humano' se activa y decide invocar FDE_afEscalateToHuman

    Requiere que la Context Variable conversationId tenga visibility=external en Studio

  4. 4

    GenAiFunction FDE_afEscalateToHuman

    Bridge al Apex WhatsAppEscalateAction

  5. 5

    WhatsAppEscalateAction (Apex)

    Lee la config, respeta o crea Case, reasigna conv.OwnerId al Queue, nullea Agentforce_Session_Id__c

    Idempotente frente a re-invocaciones

  6. 6

    Messaging.CustomNotification

    Envía notificación de bell a todos los User members del Queue

  7. 7

    WhatsApp_Message_Notification__e

    Platform Event publicado para que el LWC de cada miembro refresque en vivo

  8. 8

    WhatsAppAPIService.sendTextMessage

    Manda al cliente el mensaje configurado en Escalation_Notify_Message__c

  9. 9

    Humano #1 acepta desde el LWC

    acceptConversation reasigna owner al User con optimistic locking

    Si dos humanos aceptan simultáneamente, solo uno gana

  10. 10

    Cliente responde por WhatsApp

    InboundEventHandler evalúa gate: Owner.Type=User real → skipAgentforce=true, no encola bot

    El bot NO responde porque la conv está con humano

  11. 11

    Humano responde desde el LWC

    sendMessage vía WhatsAppSendMessageQueueable dispara la API de Meta

  12. 12

    Humano cierra desde el LWC

    closeConversationById marca Status=Closed, opcionalmente manda mensaje de cierre

Actores y objetos que participan

Cliente WhatsApp
     │
     ▼
Meta Cloud API  ──►  WhatsAppWebhookHandler (Site público)
                            │
                            ▼
                    WhatsApp_Inbound_Event__e  ──►  WhatsAppInboundEventHandler
                                                            │
                                                            │  [gate: Handoff_Enabled__c + Owner.Type]
                                                            │
                                            ┌───────────────┴───────────────┐
                                            │                               │
                                            ▼                               ▼
                                WhatsAppAgentforceQueueable        (SKIP — humano atiende)
                                            │
                                            ▼
                                Agentforce Runtime API
                                            │  (planner decide escalar)
                                            ▼
                                FDE_afEscalateToHuman
                                            │
                                            ▼
                                WhatsAppEscalateAction
                                    ├─► Case (opcional, respeta existente)
                                    ├─► WhatsApp_Conversation__c
                                    │      OwnerId = Queue
                                    │      Agentforce_Session_Id__c = null
                                    │
                                    ├─► Messaging.CustomNotification → miembros del Queue
                                    ├─► Platform Event → LWC (empApi)
                                    └─► WhatsAppAPIService.sendTextMessage → cliente

Legenda

  • gateAntes de encolar Agentforce, el handler consulta OwnerId de la conversación. Si owner es User real (no bot user ni Automated Process), no encola.
  • QueueGrupo Salesforce tipo Queue. Debe soportar WhatsApp_Conversation__c y Case como sobject types.
  • empApiEl LWC del dashboard y del record page se suscriben a WhatsApp_Message_Notification__e para refresh sin polling.