Pitch: O que aprendi construindo um assistente de IA em tempo real para entrevistas
Há algum tempo comecei a trabalhar em um problema que, à primeira vista, parecia relativamente simples:
ouvir uma conversa, entender a pergunta e gerar uma resposta útil em poucos segundos.
Na prática, descobri que existe uma diferença enorme entre construir um chatbot e construir um sistema de IA que precisa acompanhar uma conversa em tempo real.
O projeto acabou virando o PiriPiri AI, um assistente de IA para entrevistas e reuniões. Mas a ideia deste post não é fazer propaganda.
Quero compartilhar algumas das decisões, erros e problemas técnicos que encontrei durante o desenvolvimento, porque acredito que vários deles aparecem em qualquer produto que combine áudio, speech-to-text e LLMs em tempo real.
- O maior problema não é a qualidade do LLM. É a latência.
Quando comecei a pensar no fluxo, imaginei algo parecido com:
Áudio
↓
Speech-to-Text
↓
LLM
↓
Resposta
Parece simples.
Mas imagine uma entrevista.
O entrevistador pergunta:
"Você comentou que migrou vários serviços de REST para gRPC. Por que tomou essa decisão?"
Se o sistema demora:
2 segundos para detectar o fim da fala;
2 segundos para transcrever;
4 segundos para gerar;
1 segundo para atualizar a interface;
já temos quase 9 segundos de atraso.
Mesmo que a resposta seja excelente, ela chegou tarde demais.
Então comecei a perceber que o objetivo não deveria ser simplesmente:
gerar a melhor resposta possível
mas algo mais próximo de:
gerar uma resposta suficientemente boa
dentro de uma janela de tempo muito pequena
Essa mudança de objetivo afeta praticamente toda a arquitetura.
- Streaming muda completamente o problema
Esperar a pessoa terminar a frase para começar todo o processamento custa tempo demais.
Uma abordagem melhor é tratar a entrada como um fluxo:
microfone
↓
chunks de áudio
↓
transcrição parcial
↓
detecção de intenção/pergunta
↓
contexto
↓
LLM
Com isso, parte do processamento pode começar antes mesmo de a pessoa terminar completamente a pergunta.
Mas aí aparece outro problema.
Considere:
"Me conte sobre aquele projeto em que você..."
Nesse ponto, ainda não sabemos qual projeto.
Se enviarmos cedo demais ao modelo, ele pode começar a responder uma pergunta que ainda nem terminou.
Se esperarmos demais, perdemos a vantagem do streaming.
Esse equilíbrio entre latência e certeza acabou sendo uma das partes mais interessantes do projeto.
- Transcrever a conversa é a parte fácil. Descobrir o que importa é mais difícil.
Uma conversa real não é organizada.
Ela pode ter algo assim:
pergunta
pausa
comentário
confirmação
interrupção
follow-up
outra pergunta
Nem todo trecho merece gerar uma resposta.
Por exemplo:
"Certo."
não deveria disparar o pipeline.
Mas:
"Certo. E por que vocês escolheram PostgreSQL em vez de MongoDB?"
deveria.
Então existe uma camada que precisa tomar decisões como:
isso é apenas continuação da conversa?
isso é uma pergunta?
é um follow-up da pergunta anterior?
precisamos recuperar contexto adicional?
devemos gerar uma resposta agora?
Na prática, descobri que essa camada de orquestração pode ser tão importante quanto o próprio modelo responsável pela resposta.
- Contexto vale mais que um prompt gigante
Outro problema era fazer o sistema conhecer a pessoa que está participando da entrevista.
Imagine um CV contendo:
Experiência
- Reduzi abandono no checkout em 18%
- Migrei 40 serviços de REST para gRPC
- Liderei uma equipe de quatro pessoas
Durante a entrevista aparece a pergunta:
"Onde essa migração quase deu errado?"
Um modelo sem contexto só consegue gerar uma resposta genérica.
Mas colocar o CV inteiro, descrição da vaga, histórico da conversa e várias instruções dentro de cada prompt também não parece ideal.
O contexto cresce rápido, aumenta custo e pode introduzir ruído.
Então comecei a tratar o problema mais como recuperação de informação:
Pergunta atual
↓
identificar assunto
↓
recuperar contexto relevante
↓
CV / vaga / conversa
↓
LLM
Para aquela pergunta específica, talvez o modelo não precise saber absolutamente tudo sobre o candidato.
Ele precisa principalmente dos trechos relacionados à migração.
Isso ajuda tanto na qualidade quanto em latência e custo.
- Respostas menores frequentemente são melhores
Outro erro inicial foi acreditar que respostas mais completas seriam necessariamente mais úteis.
Em uma aplicação em tempo real, uma parede de texto é praticamente inútil.
Compare:
Durante a migração enfrentamos diversos desafios relacionados
à compatibilidade entre os serviços antigos e novos...
com:
Ponto principal:
compatibilidade entre serviços antigos e novos.
O que fiz:
migração gradual + observabilidade por serviço.
Resultado:
reduzimos o risco sem interromper o sistema.
A segunda resposta pode ter menos informação, mas é muito mais fácil de consumir durante uma conversa.
Comecei então a tratar densidade de informação como uma métrica importante.
A pergunta deixou de ser:
Quanto o modelo consegue escrever?
e virou:
Quanto valor ele consegue entregar no menor espaço possível?
- O sistema também precisa saber quando NÃO inventar
Quando você fornece um CV para um modelo, existe uma tentação perigosa.
O modelo recebe:
"Aumentou conversão em 18%"
e começa a completar sozinho:
"Utilizei testes A/B durante três meses..."
Mesmo que isso nunca tenha acontecido.
Em um produto desse tipo, uma resposta convincente mas inventada pode ser muito pior do que uma resposta incompleta.
Por isso, uma das regras importantes passou a ser separar:
informação conhecida
inferência
sugestão de estrutura
Quando não existe informação suficiente, prefiro que o sistema ajude o usuário a organizar o raciocínio em vez de fabricar experiência profissional.
- Um LLM maior nem sempre significa uma experiência melhor
Essa talvez tenha sido uma das conclusões mais contraintuitivas.
Em aplicações tradicionais, é fácil pensar:
modelo melhor = produto melhor
Em tempo real, a equação fica mais próxima de:
qualidade
×
latência
×
consistência
×
custo
Um modelo extremamente inteligente que demora muito pode proporcionar uma experiência pior do que um modelo menor utilizado na etapa correta.
Isso abre espaço para pipelines em que modelos diferentes fazem trabalhos diferentes:
modelo rápido
→ classificação / intenção
modelo principal
→ geração
regras locais
→ tarefas determinísticas
Nem tudo precisa virar uma chamada para o modelo mais caro disponível.
- O desafio de produto acabou sendo tão interessante quanto o de IA
Construir uma primeira versão funcional hoje é relativamente fácil.
O difícil começa depois:
quando iniciar a geração?
quanto contexto enviar?
quanto texto mostrar?
como lidar com interrupções?
como identificar follow-ups?
como evitar respostas inventadas?
o que acontece quando a transcrição erra?
como manter tudo rápido?
quanto isso custa por usuário?
quando simplesmente não gerar nada?
Esse tipo de produto me fez perceber que boa parte do trabalho com IA atualmente não está necessariamente em criar um modelo melhor.
Está em construir uma boa camada ao redor do modelo.
Arquitetura conceitual
Simplificando bastante, o fluxo que tenho explorado pode ser representado assim:
┌───────────────┐
│ CV │
└───────┬───────┘
│
┌───────▼───────┐
│ Contexto │
│ do candidato │
└───────┬───────┘
│
│
Áudio ──► STT ──► Pergunta ─┼──► Context Builder
│ │
Descrição da vaga ──────────┘ │
▼
LLM
│
▼
Resposta curta
│
▼
Interface
Naturalmente existem vários detalhes escondidos nesse desenho, principalmente em streaming, estado da conversa e seleção de contexto.
Mas essa separação ajudou bastante a raciocinar sobre o problema.
O projeto
O produto que nasceu desses experimentos se chama PiriPiri AI.
Ele é uma aplicação desktop em que estou explorando justamente esse problema: usar contexto do candidato e da conversa para fornecer assistência com IA durante entrevistas e também em situações de preparação.
Para quem tiver curiosidade sobre o projeto:
Mas o que eu gostaria mesmo de saber da comunidade é:
como vocês desenhariam esse pipeline para reduzir ainda mais a latência?
Principalmente esta parte:
transcrição parcial
→ detectar pergunta completa
→ decidir quando chamar o LLM
Tenho a impressão de que existe bastante espaço para melhorar essa etapa sem simplesmente trocar por um modelo maior.
Fonte: https://piripiriai.com