𝗧𝗵𝗲 𝗡𝘂𝗹𝗹 𝗜𝗻𝗽𝘂𝘁 𝗧𝗵𝗮𝘁 𝗕𝗿𝗼𝗸𝗲 𝗠𝘆 𝗣𝗿𝗼𝗱𝘂𝗰𝘁𝗶𝗼𝗻 𝗔𝗴𝗲𝗻𝘁

The demo ran perfectly for three weeks. Every test input worked. Every output went to the right place. I thought the system was reliable.

Then a supplier sent an email with an empty subject line.

The agent expected a string to extract an order reference. Instead, it received a null value. It did not crash. That would have been better. It generated a fake order reference that looked real. The downstream system processed it. Nobody noticed for four hours.

Demos use inputs you expect. Production uses inputs you do not expect.

I run the agent operation at aienterprise.dk. I saw the full trace. The prompt told the agent to extract the order reference from the subject line. This works if the subject line exists.

If the subject line is missing, a large language model fills the gap. It invents something that looks correct. This is not random noise. It is structured noise. It is dangerous because it looks right. You can catch a failure. You cannot easily catch a confident, wrong answer.

I did not retrain the model. I did not change the prompt. I added a guard before the model call.

Now, a simple check runs first. It asks: is the subject field present and non-empty? If the answer is no, the message goes to a hold queue for a human. The agent never sees the bad input.

This guard is twelve lines of code. It is the most important thing I built this year.

The pattern is simple. If an agent assumes structure, production will eventually send unstructured data. The fix is not a smarter model. The fix is a boundary. You need a check that routes bad input to a human instead of letting the model guess.

Reliability is the only feature. A demo shows an agent can do a task. Production shows an agent does the task at 3 AM on bad input. Only the second part matters to your customers.

My agent now processes 200 operations per day without issues. The hold queue triggers twice a week. A human reviews the odd data. I learn what production looks like.

If you build agents for high-risk categories under the EU AI Act, the deadline is December 2, 2027. This includes employment, biometrics, and education. A system that guesses on bad input will fail an audit. This guard is a compliance minimum.

Reliability is not a feature you add later.

O input nulo que quebrou meu agente de produção e o que o corrigiu

Tenho construído agentes de LLM há algum tempo agora, e a maioria deles funcionava perfeitamente em meus testes locais. No entanto, como qualquer desenvolvedor sabe, o ambiente de produção é um animal completamente diferente.

Recentemente, enfrentei um erro que derrubou meu agente de produção e me fez perder várias horas de sono tentando entender o porquê. O culpado? Um simples valor None.

O Incidente

Tudo parecia estar indo bem. Meu agente estava processando consultas de usuários, chamando ferramentas e gerando respostas de forma fluida. De repente, começamos a receber alertas de erro no nosso sistema de monitoramento.

A mensagem de erro era repetitiva e, à primeira vista, parecia um erro de biblioteca interna:

TypeError: expected string or bytes-like object

O que era estranho, pois eu não estava manipulando bytes diretamente; eu estava apenas lidando com strings de texto para o prompt do LLM.

A Investigação

Comecei a vasculhar os logs. O erro ocorria durante a fase de construção do prompt, especificamente quando o agente tentava integrar o resultado de uma ferramenta (tool) ao contexto da conversa.

Analisei o fluxo:

  1. O usuário faz uma pergunta.
  2. O agente decide usar uma ferramenta (ex: get_weather).
  3. A ferramenta é executada.
  4. O resultado da ferramenta é inserido no prompt.
  5. O LLM gera a resposta.

Ao olhar para os logs de uma execução específica que falhou, percebi algo que passou despercebido: a ferramenta get_weather havia retornado None em vez de uma string.

A Causa Raiz

O problema era o seguinte: em uma condição de erro específica (uma falha de rede na API de clima), minha função get_weather não estava tratando a exceção de forma a retornar uma mensagem de erro amigável. Em vez disso, ela simplesmente retornava o valor padrão do Python para uma função que não encontrou um return explícito: None.

Quando o loop principal do agente tentava fazer isso:

prompt = f"O resultado da ferramenta é: {tool_output}"

O Python tentava converter o None em string para a interpolação, mas em algum ponto da minha lógica de processamento de mensagens (onde eu usava métodos de string como .strip() ou .lower() no resultado da ferramenta antes de inseri-lo), o código quebrava porque NoneType não possui esses métodos.

A Correção

A solução não foi apenas "consertar a função de clima", mas sim tornar o agente resiliente a retornos inesperados de qualquer ferramenta. Implementei duas mudanças principais:

1. Validação de Saída de Ferramentas

Adicionei uma camada de validação que intercepta o retorno de qualquer ferramenta antes que ele chegue ao motor de construção de prompts.

def validate_tool_output(output):
    if output is None:
        return "Erro: A ferramenta não retornou nenhum dado válido."
    if not isinstance(output, str):
        return str(output)
    return output

2. Tipagem Estrita e Valores Padrão

Reforcei o uso de tipos e garanti que todas as funções de ferramenta tivessem um retorno consistente, mesmo em caso de falha.

def get_weather(location: str) -> str:
    try:
        # Lógica de chamada de API
        result = call_weather_api(location)
        return result if result else "Clima não encontrado."
    except Exception as e:
        return f"Erro ao buscar clima: {str(e)}"

Lições Aprendidas

Este incidente me ensinou lições valiosas sobre o desenvolvimento de sistemas baseados em agentes:

  • Nunca confie no retorno de uma ferramenta: Trate cada saída de ferramenta como potencialmente malformada ou nula.
  • Defina esquemas de saída claros: Se uma ferramenta deve retornar uma string, garanta que ela sempre retorne uma string, mesmo que seja uma mensagem de erro.
  • Monitore tipos, não apenas erros: Às vezes, o sistema não "quebra" com uma exceção, mas o comportamento se torna errático porque um tipo de dado inesperado (como None ou um int onde se esperava uma str) infiltrou o fluxo.

Construir agentes de IA é lidar com a incerteza. A robustez não vem apenas do modelo de linguagem, mas da infraestrutura de código que envolve esse modelo.