Resumo
Na VoiceBooker, vemos todos os dias que modelos de linguagem grandes não se comportam como software clássico determinístico. As respostas deles se baseiam em probabilidades, o que significa que, mesmo com a mesma entrada, o comportamento pode variar. Isso aparece muito rapidamente no function calling. Os modelos modernos são muito mais confiáveis do que as gerações anteriores, mas prompts precisos, debugging cuidadoso e a compreensão das interações de contexto continuam essenciais para resultados previsíveis. Para manter aplicações de IA estáveis e confiáveis, os End-to-End-Tests automatizados ajudam a verificar o comportamento do bot de forma sistemática e a detectar regressões cedo.
Introdução
Quem vem do desenvolvimento de software clássico geralmente passa por um pequeno choque cultural ao trabalhar com large language models (LLMs). Durante décadas, nos acostumamos a construir sistemas determinísticos: se um programa produz um erro para uma determinada entrada, corrigimos o código - e, uma vez que a correção funciona, podemos assumir que ela funcionará novamente para a mesma entrada no futuro. Mesma entrada, mesma saída. Esse é o mundo em que engenheiros de software vivem há décadas. Com LLMs, a realidade é diferente.
De programas determinísticos a máquinas de probabilidade
Um large language model não funciona como um programa clássico com árvores de decisão fixas. Em vez disso, ele é guiado por probabilidades. Cada resposta, cada decisão e cada function call é o resultado de uma avaliação estatística dos próximos passos possíveis. Isso leva a um fenômeno que frustra muitos desenvolvedores no início: Um fluxo de conversa idêntico pode produzir resultados diferentes em duas execuções. Você pode pensar nisso como uma roleta: o cenário é o mesmo, mas o resultado ainda pode variar. Desenvolvedores que vêm do mundo de APIs, bancos de dados e lógica de negócio costumam achar essa falta de determinismo incomum ou até problemática no começo.
Nossas primeiras experiências com function calling
Quando tentamos, em 2023, lidar com agendamento de consultas por meio de um LLM pela primeira vez, em alguns momentos quase enlouquecemos. O fluxo da conversa era idêntico:
- o usuário informa o horário desejado
- o bot coleta as informações necessárias
- a função de agendamento deve ser chamada
Mesmo assim, acontecia o seguinte: No primeiro teste, a função era chamada corretamente. No segundo teste, com um fluxo praticamente idêntico, ela de repente não era executada. Para quem desenvolve software clássico, isso inicialmente parece um bug. Na realidade, muitas vezes não é um erro do sistema, mas a consequência natural de um modelo probabilístico que interpreta contexto e toma decisões com base em probabilidades.
A boa notícia: os modelos estão ficando mais confiáveis
Desde os tempos iniciais do GPT-3.5, muita coisa mudou. Os modelos modernos ficaram muito melhores em:
- seguir instruções
- usar ferramentas
- function calling
- entender contexto
- obedecer prompts complexos
A previsibilidade melhorou muito por causa disso. Ainda assim, continua existindo um equilíbrio importante entre confiabilidade e latência.
Por que mais determinismo geralmente significa mais latência
Muitos dos modelos mais fortes de hoje trabalham de forma mais estruturada e confiável do que os anteriores. O preço disso costuma ser um esforço extra de reasoning. Em termos simples: O modelo pensa por mais tempo, analisa mais contexto e, com isso, toma decisões mais robustas. Isso aumenta a probabilidade de que uma função desejada seja chamada corretamente. Ao mesmo tempo, o tempo de resposta aumenta.
No contexto de voz, isso pode ser um problema. Um assistente telefônico que precisa de vários segundos para raciocinar sobre cada function call rapidamente soa artificial para o usuário. Por isso muitos sistemas em produção usam deliberadamente modelos menores e mais rápidos. Esses modelos entregam excelente latência e ainda assim executam funções de forma surpreendentemente confiável - mas somente quando os prompts deixam o mínimo de espaço possível para interpretação. Mesmo pequenas mudanças de contexto podem fazer com que um fluxo que funcionava perfeitamente seja interpretado de outra forma.
Quando as function calls param de acontecer de repente
Um padrão típico na prática: Um bot funciona de forma confiável por semanas. Então é feita uma alteração aparentemente inofensiva no prompt. De repente, uma determinada função passa a ser chamada apenas ocasionalmente - ou deixa de ser chamada. Nessas situações, vale a pena não culpar o modelo de imediato. Muitas vezes a causa está em uma interação inesperada entre várias instruções. Uma nova regra pode competir sem querer com uma instrução já existente. O modelo então fica diante de objetivos conflitantes e toma a decisão totalmente compreensível de não chamar a função.
Prompt debugging em vez de code debugging
Enquanto engenheiros de software clássicos debuggam código, desenvolvedores de LLMs cada vez mais debuggam prompts. Uma abordagem que funcionou bem para nós: baixamos o transcript completo em JSON - incluindo system prompts, definições de ferramentas e entradas do usuário - e depois deixamos outro LLM analisar por que determinado comportamento ocorreu. Os resultados costumam ser surpreendentes. Com frequência o modelo identifica contradições ou ambiguidades que nós mesmos deixamos passar ao escrever o prompt. Não raramente acabamos concordando com o modelo: Depois de olhar com mais atenção, a instrução realmente não era precisa o suficiente ou entrava em conflito com outra instrução. Interessantemente, as pessoas também costumam reagir de forma parecida nessas situações. Se duas instruções são formuladas de maneira contraditória, a margem de interpretação é inevitável.
De unit tests a bot tests
Outra lição importante da prática: Testes manuais não escalam. Quem verifica cada mudança no bot com chats manuais ou chamadas de teste rapidamente perde muito tempo. Foi por isso que introduzimos End-to-End-Tests na VoiceBooker. Um bot pode chamar outro bot ou conversar com ele. Por exemplo, um bot de teste pode receber a tarefa:
Reserve uma mesa para quatro pessoas na terça-feira às 18h.
Como o bot de teste é programável, parâmetros como data, hora ou número de pessoas podem ser variado automaticamente. Na prática, isso cria unit tests para conversational AI. As vantagens são óbvias:
- cenários de teste repetíveis
- regression tests automatizados
- validação rápida de mudanças no prompt
- detecção precoce de mudanças inesperadas de comportamento
Especialmente no modo chat, centenas de casos de teste podem ser executados em pouco tempo, porque a vazão é limitada principalmente pela latência dos modelos usados.
Conclusão
A maior mudança mental ao trabalhar com LLMs provavelmente é abandonar a ideia de determinismo absoluto. LLMs não são programas clássicos. Eles são máquinas de probabilidade. Isso não significa que sistemas confiáveis sejam impossíveis. Com prompts precisos, definições de ferramentas bem feitas, debugging sistemático de prompts e End-to-End-Tests automatizados, hoje já é possível construir aplicações de IA surpreendentemente robustas e previsíveis. A diferença principal em relação ao desenvolvimento de software clássico é que, em vez de otimizar apenas código, passamos cada vez mais a moldar o comportamento de um sistema inteligente. E é justamente aí que estão tanto o maior desafio quanto o maior fascínio do desenvolvimento moderno de IA.

