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.
- 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
/pausee/retomar.
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.
- 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.
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 serveCadastre 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_PauloAo 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'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:pairNo 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_DELAYeWHATSAPP_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_LIMITeWHATSAPP_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.
disconnectencerra somente a conexão atual e preserva o vínculo.logoutremove o vínculo no WhatsApp; será necessário parear novamente../bin/agent whatsapp:unpair --yesforç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.
./bin/agent servePor padrão, o PocketBase e a API ficam em http://127.0.0.1:8090.
curl http://127.0.0.1:8090/api/agent/healthRotas administrativas exigem autenticação como superusuário do PocketBase:
GET /api/whatsapp/statusPOST /api/whatsapp/pair/startGET /api/whatsapp/pair/statusPOST /api/whatsapp/pair/cancelPOST /api/whatsapp/connectPOST /api/whatsapp/disconnectPOST /api/whatsapp/logoutGET /api/integrations/openai/statusGET /api/integrations/google/connectGET /api/integrations/google/statusPOST /api/integrations/google/disconnectGET /api/admin/conversationsGET /api/admin/conversations/{id}/messagesPOST /api/admin/conversations/{id}/readGET /api/admin/agent-controlPOST /api/admin/agent-control/pausePOST /api/admin/agent-control/resumeGET /api/admin/scheduling/recipientsGET|POST /api/admin/authorized-usersPATCH|DELETE /api/admin/authorized-users/{id}GET|POST /api/admin/remindersPATCH /api/admin/reminders/{id}POST /api/admin/reminders/{id}/cancelGET|POST /api/admin/meetingsPATCH /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_participantsegoal_events: solicitações públicas de reunião, confirmações individuais e trilha do fluxo.commercial_leadsecommercial_lead_events: estado estruturado da qualificação comercial e trilha de atualizações e encaminhamentos.remindersemeetings: 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.
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.
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/.envCom o agente rodando em 127.0.0.1:8090, abra outro terminal:
make web-install
make web-devO 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.
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.jsonlO runner produz JSON e retorna código diferente de zero se qualquer caso falhar. Veja docs/evals.md.
make test
make build
make run
make web-check
make web-test
make web-buildPara remover somente artefatos regeneráveis, caches locais, builds e binários:
make cleanEsse 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-installPor 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 testCom Docker para desenvolvimento, crie antes a rede externa usada pelo Traefik:
docker network create dokploy-network
docker compose up --buildO 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.
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.