Atendimento humano¶
O atendimento humano começa quando uma sessão entra em HANDOFF_PENDING. O histórico continua sendo recebido pelo WhatsApp e persistido no DynamoDB; a equipe assume, responde e encerra a conversa em /atendimento. A sessão é a fonte de verdade. A atualização comercial no CRM é uma projeção assíncrona e pode continuar pendente após o encerramento no painel.
Componentes e responsabilidades¶
| Componente | Localização | Responsabilidade |
|---|---|---|
| Entrada e comandos | src/webhook_handler/app.py |
Distingue operador de cliente, autentica webhook e persiste mensagens recebidas durante atendimento humano |
| Handoff | src/core/application/process_message_use_case.py |
Define motivo, estado e resumo operacional; solicita notificação |
| Disponibilidade | src/core/application/operator_availability.py |
Calcula operadores autorizados disponíveis e fila global |
| Notificação | src/core/integrations/handoff/whatsapp.py |
Avisa todos os operadores disponíveis na whitelist |
| Serviço de conversa | src/core/application/human_conversation.py |
assume_conversation, send_human_message, finalize_conversation |
| Autenticação | src/core/application/operator_auth.py |
Link de uso único, sessão diária e logout |
| Painel HTTP | src/emulator/app.py e src/emulator/templates/operator*.html |
Login, lista, histórico, ações e pendências do CRM |
Fluxo operacional¶
sequenceDiagram
participant C as Cliente
participant W as Webhook / Orchestrator
participant D as DynamoDB
participant O as Operador
participant P as Painel /atendimento
W->>D: Sessão HANDOFF_PENDING e resumo
W-->>O: WhatsApp para operadores disponíveis
O->>W: entrar
W->>D: Disponibilidade hoje e hash do acesso
W-->>O: Link de uso único
O->>P: Abrir link e confirmar acesso
P->>D: Consumir grant e criar sessão diária
C->>W: Nova mensagem
W->>D: Persistir inbound na sessão humana
O->>P: Assumir / enviar
P->>D: HANDOFF_PENDING para HUMAN; adquirir lock
P-->>C: Resposta manual pelo WhatsApp
P->>D: Registrar outbound e liberar lock
O->>P: Finalizar
P->>D: CLOSED e intenção de sincronização CRM
Estados e mensagens recebidas¶
| Estado | Comportamento |
|---|---|
HANDOFF_PENDING |
Cliente aguarda equipe. Inbound vai ao histórico sem chamar agente. O webhook pode enviar o aviso fixo de espera; seu recibo impede duplicação do histórico e permite repetir um aviso que falhou. |
HUMAN |
Conversa em atendimento. Inbound fica no histórico; o agente não responde. O painel permite colaboração entre operadores autenticados. |
CLOSED |
Atendimento encerrado; deixa a fila. Um inbound posterior segue a abertura de nova conversa do fluxo normal. |
Se a finalização ganhar a corrida contra a gravação de uma mensagem recebida, o webhook reavalia a sessão e trata o inbound como nova conversa. Essa proteção é coberta por test_finalize_winning_inbound_append_race_reroutes_as_new_conversation, em tests/unit/lambdas/test_webhook_handler.py.
Fila, área e colaboração¶
pending_queue() ordena todas as sessões HANDOFF_PENDING por created_at, da mais antiga para a mais nova. A posição é global, não por segmento. Na interface, pendentes aparecem antes das sessões HUMAN; estas são ordenadas pela atividade mais recente.
O segmento aparece como área (AGRO, ANIMAL, HUMANO, PESQUISA ou GERAL). As áreas organizam a visualização, mas não autorizam acesso nem restringem notificações. assigned_to registra quem assumiu primeiro e é informativo: outro operador autenticado pode responder e finalizar. Um lock de envio de 90 segundos impede dois envios simultâneos na mesma conversa.
Divergência documental
Existem helpers e configurações legadas de operadores por segmento. O caminho atual de notificação usa a interseção entre whitelist efetiva e disponibilidade diária, e envia para todos os elegíveis. Não use HANDOFF_OPERATOR_* como controle de acesso ou garantia de distribuição exclusiva.
Ausência de operador e renotificação¶
Sem operador disponível, a sessão permanece na fila. Isso não é uma falha de criação do handoff. O comando entrar informa a quantidade aguardando e dá acesso ao painel; ele não envia uma mensagem adicional por cliente já na fila.
src/handoff_renotify/app.py executa a cada hora no SAM. renotify_stale_handoffs() considera, por padrão, pendências com 24 horas desde updated_at. Primeiro notifica a equipe e persiste sua marca; só envia novo aviso ao cliente se o último inbound ainda estiver dentro da janela. Marcas separadas de equipe e cliente evitam repetir etapas já concluídas. O mesmo job encerra conversas abandonadas em TRIAGE, QUALIFYING, DATA_COLLECT e CONFIRM, com motivo timeout e etapa comercial perdido; ele não encerra automaticamente HUMAN.
Veja acesso dos operadores, contratos do painel, handoff e máquina de estados.