03Detalhamento · office3d
Transformar rastros do agente num escritório 3D ao vivo
Os mesmos rastros que eu uso pra depurar o sistema também desenham ele: um escritório isométrico onde cada figura é uma conversa acontecendo. Como eu tirei 5.000 draw calls e deixei em 8 com instanced meshes.
O problema
Toda conversa que os Especialistas atendem já fica registrada como trace: cada turno, cada chamada de ferramenta, cada decisão. É de onde eu depuro o sistema. Um dia percebi que dava pra desenhar esse mesmo dado — um escritório isométrico onde cada figura é uma conversa acontecendo, e você vê os Especialistas trabalhando. Serve pra mostrar numa demo e serve pra eu achar bug, com o mesmo código.
As restrições
- 01Verdade, não teatro. Toda figura tem que ser conversa real vinda dos traces; animação roteirizada um CTO percebe na hora.
- 02Aguentar a sala cheia. Quando o escritório enche, desenhar de forma ingênua vira milhares de draw calls e um slideshow.
- 03Público sem vazar. A mesma cena roda no meu portfólio, e ali ela lê de uma fonte filtrada e opt-in — nunca da interna, com nome de cliente e faturamento.
- 04Interativa: orbitar, dar zoom e clicar numa figura pra ver o que aquela conversa fez.
A arquitetura
agent_traces (Postgres)
a fonte da verdade, já escrita pelos agentes em produção
carregador da cena
agrupa os traces por conversa → posto, balão, desfecho
React Three Fiber
dynamic({ ssr: false }) — o bundle de WebGL nunca toca outras páginas
duas fontes, um componente
logada lê /api/vitrine/* · pública lê /api/showcase/* (allowlist + rate limit)
A plateia de clientes já atendidos é a armadilha de render: um dia inteiro dá milhares de figuras, e desenhadas uma a uma são milhares de draw calls. Um InstancedMesh junta tudo num punhado — a multidão saiu de cerca de 5.000 draw calls pra 8, uma por tipo de malha, com cada posição escrita num buffer só.
A parte difícil
As armadilhas do 3D não aparecem em teste unitário. Aparecem no instante em que você orbita a câmera.
As etiquetas <Html> do drei carregam z-index na casa dos milhões e passavam por cima de um modal que devia estar na frente, então tiveram que se esconder quando o modal da transcrição abre. O mesmo <Html> engolia clique destinado à figura atrás dele. As placas de setor, presas na parede do fundo, escorregavam pro lado conforme a câmera girava, porque estavam numa profundidade diferente das mesas que rotulavam — e nenhum offset fixo em x acerta em todos os ângulos; resolvi ancorando cada placa no centro de profundidade do próprio setor. E o chão brigou por z com os tapetes até eu afastar as coordenadas. Nada disso eu teria previsto lendo documentação.
Trade-offs
- WebGL é bundle de verdade. Eu mantenho ele fora de toda página que não desenha a cena, com import dinâmico só de cliente. O custo só é pago quando o escritório aparece.
- Instancing troca flexibilidade por frame. Todo cliente da multidão divide um material só; estilizar figura por figura seria abrir mão do caminho instanciado. Pra multidão, eu fico com os frames.
- A cena lê quase em tempo real por polling, não por websocket. É mais simples, é amigável a cache, e alguns segundos de atraso ninguém enxerga numa vista ambiente.
O que eu faria diferente
Eu separaria melhor o cenário do escritório dos atores. Hoje a geometria da sala e as figuras movidas por trace moram mais perto do que deviam, então mexer no cenário arrisca o movimento. Com um cenário estático e atores por cima dele, eu cresceria o ambiente na direção do escritório caminhável que eu quero, sem ameaçar a órbita, o zoom e a caminhada — que é o que faz a pessoa lembrar dessa tela.