Skip to content

Repository files navigation

Agente de WhatsApp em Go

Maria atende o público como integrante do backoffice comercial da ATC China Brasil e mantém um fluxo administrativo separado no Agents. O sistema é construído com Eino, OpenAI, PocketBase e whatsmeow.

O que já funciona

  • Login do WhatsApp por QR code no painel ou terminal e reconexão persistente.
  • Recepção de mensagens de texto em conversas individuais.
  • Histórico por conversa, execução do agente e resposta pelo WhatsApp.
  • OpenAI Responses API como provider padrão, encapsulada pelo Eino.
  • Inbox/outbox persistentes, deduplicação por ID do WhatsApp, retry com backoff e recuperação após reinício.
  • PocketBase embarcado, migrations versionadas e APIs administrativas autenticadas.
  • Registro independente para futuras tools e um runner de evals em JSONL.
  • Autorização administrativa por número/JID do WhatsApp, com suporte ao identificador LID alternativo.
  • Personalização ocasional das respostas com o nome do usuário autorizado, sem repetir o nome em toda mensagem.
  • Solicitações públicas de reunião encaminhadas ao responsável mencionado, desde que ele esteja ativo e aceite esse tipo de contato.
  • Qualificação comercial pública da ATC com leads incrementais, trilha de eventos e encaminhamento durável pela outbox.
  • Confirmação durável com um ou vários responsáveis antes da criação automática da reunião e do link do Google Meet.
  • Tool determinística para consultar o relógio e calcular datas relativas ou de calendário no fuso do administrador.
  • Tools para criar, listar, alterar e cancelar lembretes e reuniões.
  • Tool administrativa para criar eventos no Google Calendar com link do Google Meet e convites opcionais.
  • Scheduler persistente que dispara mensagens pela outbox sem executar a OpenAI no horário agendado.
  • Lembrete automático 30 minutos antes de reuniões e desambiguação quando há mais de uma reunião possível.
  • Logs estruturados e encerramento gracioso.
  • Painel administrativo em SvelteKit com login do PocketBase, sessão cifrada e sidebar responsiva.
  • Pausa persistente do atendimento público pelo dashboard ou pelos comandos administrativos /pause e /retomar.

Arquitetura

WhatsApp
   │ eventos / envio
   ▼
whatsmeow ── SQLite de sessão
   │
   ▼
Processor ── inbox/outbox ── PocketBase
   │
   ▼
Eino Agent ── OpenAI Responses API
   │
   └── Google Calendar ── evento + link Google Meet

Cada mensagem é persistida junto com seu inbox_job em uma única transação antes de entrar na fila. Depois, cada conversa é direcionada sempre à mesma partição de processamento. Conversas diferentes podem executar em paralelo, mas as mensagens de uma mesma conversa mantêm a ordem.

Quando Maria usa uma tool, o agendamento e a mensagem final ficam persistidos no PocketBase. Uma goroutine separada adquire os agendamentos vencidos com lease e cria itens na outbox. Essa etapa não chama o modelo e sobrevive a reinícios.

Detalhes e decisões estão em docs/architecture.md.

Requisitos

  • Go 1.26.5.
  • Node.js 24 e pnpm 11.17 para o painel web.
  • Uma chave de API da OpenAI.
  • Um número de WhatsApp que possa vincular um novo aparelho.

Confira todas as dependências fixadas em docs/versions.md.

Configuração local

No ambiente local, o programa carrega automaticamente o arquivo .env do diretório atual. Variáveis já exportadas pelo sistema, Docker ou CI têm prioridade e não são sobrescritas.

cp .env.example .env
# Preencha OPENAI_API_KEY e AGENT_ADMIN_NUMBERS no .env

go build -o bin/agent ./cmd/agent
./bin/agent serve

Cadastre os responsáveis autorizados com números em formato internacional, separados por vírgula e sem @s.whatsapp.net:

AGENT_ADMIN_NUMBERS=5547999999999,5511999999999
AGENT_TIMEZONE=America/Sao_Paulo

Ao iniciar o servidor, esses números são inseridos de forma idempotente em authorized_users. Depois eles também podem ser gerenciados em Configurações no painel Agents. A autorização usa a identidade do evento do WhatsApp, nunca um número escrito na mensagem.

As instruções comportamentais da Maria ficam em arquivos versionados na pasta prompts:

  • prompts/shared.md: identidade, idioma e estilo comuns;
  • prompts/public.md: atendimento institucional;
  • prompts/public-qualification.md: roteiro comercial e regras de qualificação;
  • prompts/admin.md: comportamento administrativo e uso das tools;
  • prompts/public-fallback.txt: resposta segura para bloquear vazamentos no atendimento público.

As informações de referência ficam separadas em Markdown:

  • knowledge/public: informações institucionais disponíveis para qualquer contato e também para administradores;
  • knowledge/admin: procedimentos internos disponíveis somente no fluxo administrativo.

Os arquivos são carregados quando o processo inicia. Alterações exigem reiniciar o binário ou gerar um novo deploy. Regras críticas de autorização, isolamento dos fluxos e funcionamento das tools permanecem no código e não podem ser substituídas pelos documentos.

Crie o primeiro superusuário do PocketBase:

./bin/agent superuser upsert admin@example.com 'uma-senha-forte'

1. Login do WhatsApp

Com o servidor e o painel web em execução, acesse Integrações → WhatsApp e clique em Gerar QR code. O painel acompanha a renovação do QR e confirma automaticamente quando o whatsmeow terminar a autenticação.

Como alternativa administrativa, o mesmo fluxo continua disponível no terminal:

./bin/agent whatsapp:pair

No celular, abra WhatsApp → Configurações → Aparelhos conectados → Conectar um aparelho e escaneie o QR. O QR expira e é renovado pelo whatsmeow enquanto o pareamento estiver ativo.

Depois de aceitar o QR, o agente aguarda a reconexão autenticada do WhatsApp. Somente o estado conectado confirma o vínculo completo; se essa etapa falhar ou expirar, a sessão parcial é removida automaticamente.

Após o pareamento, o whatsmeow salva a identidade e as chaves do aparelho em WHATSAPP_STORE_PATH. Nas próximas execuções, WHATSAPP_AUTO_CONNECT=true reconecta a sessão sem outro QR.

  • Respostas conversacionais exibem “digitando…” e aguardam um intervalo proporcional ao tamanho do texto. Configure com WHATSAPP_TYPING_ENABLED, WHATSAPP_TYPING_MIN_DELAY, WHATSAPP_TYPING_MAX_DELAY e WHATSAPP_TYPING_CHARS_PER_SECOND.
  • Mensagens consecutivas são agrupadas em um lote durável antes da geração da resposta. O atraso cresce com o volume lido, respeita um teto de 30 segundos e substitui jobs antigos da mesma conversa. Configure com WHATSAPP_DEBOUNCE_ENABLED, WHATSAPP_DEBOUNCE_MIN_DELAY, WHATSAPP_DEBOUNCE_MAX_DELAY, WHATSAPP_DEBOUNCE_HARD_LIMIT e WHATSAPP_READING_CHARS_PER_SECOND.
  • Respostas longas podem ser divididas em até três mensagens coerentes. A outbox persiste a ordem e nunca libera uma parte antes da anterior.
  • Textos comerciais oficialmente aprovados ficam em responses/public/. Uma pergunta que corresponda exatamente a um alias usa o texto aprovado sem reescrita do modelo.
  • Documentos recebidos em atendimentos públicos não são analisados pelo modelo. O arquivo original é armazenado de forma protegida e encaminhado pela fila durável aos responsáveis habilitados para solicitações públicas. O limite padrão é 20 MiB e pode ser reduzido com WHATSAPP_MEDIA_MAX_BYTES.
  • Lembretes e notificações agendadas ignoram esse atraso e continuam sendo enviados imediatamente.
  • Respostas e contatos são resumidos por uma fila durável separada. A Maria pode lembrar nomes, preferências explicitamente informadas e a continuidade da conversa sem atrasar o envio da resposta.
  • Em Configurações → Memória de contatos, o administrador pode corrigir ou apagar os fatos e resumos aprendidos. A ação de esquecer preserva o histórico bruto para auditoria.
  • disconnect encerra somente a conexão atual e preserva o vínculo.
  • logout remove o vínculo no WhatsApp; será necessário parear novamente.
  • ./bin/agent whatsapp:unpair --yes força a remoção da sessão local quebrada para permitir um novo QR.
  • Não copie nem publique o banco whatsmeow.db: ele contém material de autenticação sensível.

2. Servidor

./bin/agent serve

Por padrão, o PocketBase e a API ficam em http://127.0.0.1:8090.

curl http://127.0.0.1:8090/api/agent/health

Rotas administrativas exigem autenticação como superusuário do PocketBase:

  • GET /api/whatsapp/status
  • POST /api/whatsapp/pair/start
  • GET /api/whatsapp/pair/status
  • POST /api/whatsapp/pair/cancel
  • POST /api/whatsapp/connect
  • POST /api/whatsapp/disconnect
  • POST /api/whatsapp/logout
  • GET /api/integrations/openai/status
  • GET /api/integrations/google/connect
  • GET /api/integrations/google/status
  • POST /api/integrations/google/disconnect
  • GET /api/admin/conversations
  • GET /api/admin/conversations/{id}/messages
  • POST /api/admin/conversations/{id}/read
  • GET /api/admin/agent-control
  • POST /api/admin/agent-control/pause
  • POST /api/admin/agent-control/resume
  • GET /api/admin/scheduling/recipients
  • GET|POST /api/admin/authorized-users
  • PATCH|DELETE /api/admin/authorized-users/{id}
  • GET|POST /api/admin/reminders
  • PATCH /api/admin/reminders/{id}
  • POST /api/admin/reminders/{id}/cancel
  • GET|POST /api/admin/meetings
  • PATCH /api/admin/meetings/{id}
  • POST /api/admin/meetings/{id}/cancel

O painel administrativo do PocketBase fica em /_/.

Coleções operacionais adicionais:

  • authorized_users: responsáveis que podem usar as tools.
  • coordination_goals, goal_participants e goal_events: solicitações públicas de reunião, confirmações individuais e trilha do fluxo.
  • commercial_leads e commercial_lead_events: estado estruturado da qualificação comercial e trilha de atualizações e encaminhamentos.
  • reminders e meetings: dados de negócio.
  • scheduled_jobs: fila temporal persistente.
  • tool_executions: auditoria de cada chamada.
  • pending_actions: contexto estruturado temporário para desambiguação.
  • google_connections: token OAuth cifrado e status da conta conectada.
  • google_oauth_states: estados OAuth de uso único e PKCE.
  • google_calendar_jobs: fila durável de criação, alteração e cancelamento externo.
  • message_dispatches: envio delegado para um ou mais usuários autorizados.
  • admin_audit_events: auditoria das mutações feitas pelo painel operacional.
  • agent_runtime_settings: estado persistente dos controles operacionais da Maria.

O card Atendimento público no dashboard permite pausar somente as respostas para contatos não autorizados. Durante a pausa, as mensagens recebidas continuam registradas, mas não passam pela OpenAI e não são respondidas nem reproduzidas quando o atendimento for retomado. Administradores continuam conversando com a Maria e podem executar /pause ou /retomar diretamente no WhatsApp. Lembretes, reuniões e entregas já agendadas continuam ativos porque o WhatsApp permanece conectado.

As tools disponíveis para admins incluem resolve_datetime, resolve_authorized_recipients, send_message_to_authorized_users, create_reminder, list_reminders, update_reminder, cancel_reminder, list_meetings, update_meeting e cancel_meeting. Sem Google, create_meeting registra reuniões internas. Quando a integração Google está habilitada, ela é substituída por schedule_google_meet, evitando registros duplicados.

No atendimento público, update_commercial_lead registra apenas dados explicitamente fornecidos e handoff_commercial_lead enfileira o resumo para os usuários ativos marcados para receber solicitações públicas. A Maria só confirma o encaminhamento quando essa operação retorna sucesso.

Quando o pedido cita outro administrador, a Maria primeiro resolve somente os nomes mencionados para IDs internos. Nomes inexistentes ou ambíguos bloqueiam a escrita. O solicitante só é incluído quando pedir isso explicitamente. Mensagens, lembretes e avisos de reunião são persistidos com uma entrega independente por destinatário e não executam inteligência no horário do envio.

Contatos públicos também podem pedir uma reunião com um responsável pelo nome. Nesse fluxo, accepts_public_requests é uma permissão independente do acesso administrativo: somente usuários ativos, com essa opção habilitada e uma conversa válida no WhatsApp recebem o pedido. A Maria nunca apresenta a lista interna de responsáveis. Quando mais de uma pessoa é citada, cada uma responde individualmente e, por padrão, a reunião só é criada depois que todas confirmarem. Recusas e propostas de outro horário voltam ao solicitante pelo mesmo objetivo persistente, sem duplicar a solicitação.

resolve_datetime usa o relógio confiável do servidor e o fuso do administrador para resolver expressões como “daqui 10 minutos”, “amanhã às 14h”, “24/07 às 09h” e “próxima segunda”. A Maria usa o resultado para chamar a tool de criação na mesma execução, sem perguntar ao usuário qual é o horário atual.

3. Google Calendar e Google Meet

Para agendar reuniões reais com link do Meet, ative a Google Calendar API, crie um cliente OAuth Web e conecte a conta dona do calendário. Em desenvolvimento:

  • Authorized JavaScript origins: deixe vazio.
  • Authorized redirect URI: http://localhost:8090/api/integrations/google/callback.

O fluxo completo, as variáveis do .env e os comandos de conexão estão em docs/google-calendar.md.

4. Painel web

O painel fica em web/ e usa o mesmo superusuário da coleção _superusers do PocketBase. Não existe cadastro público. O token administrativo é mantido no servidor do SvelteKit e cifrado em um cookie HttpOnly.

Configure uma chave exclusiva para a sessão:

cp web/.env.example web/.env
openssl rand -base64 32
# Cole o valor em WEB_SESSION_SECRET no arquivo web/.env

Com o agente rodando em 127.0.0.1:8090, abra outro terminal:

make web-install
make web-dev

O painel estará em http://localhost:5173. Após o login, /dashboard mostra o estado real do WhatsApp, OpenAI e Google Calendar. Os botões “Gerenciar” abrem as páginas de cada integração. O WhatsApp pode ser pareado, conectado, desconectado e desvinculado pelo painel. O Google Calendar pode ser conectado e desconectado pelo painel; no retorno do OAuth, o agente redireciona para WEB_APP_URL, cujo padrão local é http://localhost:5173.

Na seção Operação, o painel também oferece:

  • /conversas: caixa de entrada e histórico quase em tempo real, com busca e controle de não lidas;
  • /lembretes: criação, reagendamento, cancelamento e histórico;
  • /reunioes: criação, alteração e cancelamento de reuniões, incluindo o estado de sincronização e os links do Google Calendar e Google Meet.
  • /configuracoes: inclusão, edição, ativação, desativação e remoção de usuários autorizados, incluindo a permissão individual para receber solicitações públicas.

Lembretes e reuniões só podem ser criados para administradores autorizados que já tenham uma conversa do WhatsApp vinculada. As mutações usam os mesmos serviços e filas persistentes das tools da Maria.

A remoção de um usuário autorizado é lógica: o número perde o acesso imediatamente e deixa de aparecer no painel, mas seus lembretes, reuniões e registros de auditoria permanecem íntegros. Cadastrar novamente o mesmo número restaura o registro anterior.

Evals

Com a chave da OpenAI configurada:

./bin/agent eval --dataset evals/datasets/smoke.jsonl
./bin/agent eval --dataset evals/datasets/public/official.jsonl
./bin/agent eval --dataset evals/datasets/public/qualification.jsonl
./bin/agent eval --dataset evals/datasets/public/off-topic.jsonl
./bin/agent eval --dataset evals/datasets/admin/behavior.jsonl

O runner produz JSON e retorna código diferente de zero se qualquer caso falhar. Veja docs/evals.md.

Desenvolvimento

make test
make build
make run
make web-check
make web-test
make web-build

Para remover somente artefatos regeneráveis, caches locais, builds e binários:

make clean

Esse comando não remove .env, data, pb_data nem migrations. Para também remover web/node_modules e forçar uma instalação limpa do painel:

make clean-all-dev
make web-install

Por padrão, os comandos Go usam os caches configurados pelo próprio Go fora do repositório. Quando for necessário isolar o cache, use uma única raiz:

GO_CACHE_ROOT=/private/tmp/agents-go-cache make test

Com Docker para desenvolvimento, crie antes a rede externa usada pelo Traefik:

docker network create dokploy-network
docker compose up --build

O compose.yaml é preparado para produção no Dokploy e não publica portas no host. O Traefik encaminha https://agents.lspr.dev ao painel e direciona /api/* e /_/* ao PocketBase. Para acessar os containers diretamente durante o desenvolvimento, use um override local de portas que não seja enviado para produção.

O roteiro de deploy, variáveis, DNS, inicialização e backups está em docs/deployment-dokploy.md.

Limites importantes

O whatsmeow implementa o protocolo de aparelhos conectados e não é a API oficial WhatsApp Business. Não use para spam ou automação que viole os termos do WhatsApp; bloqueios de conta são um risco operacional. Para operação empresarial com garantias e templates oficiais, mantenha uma futura interface de canal e adicione um adapter para a Cloud API.

Nesta versão, somente texto e conversas individuais são processados. Reuniões podem ser internas ou eventos do Google Calendar com Google Meet; ainda não há Microsoft 365, recorrência, mídia, grupos, human handoff nem webhooks externos.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages