<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Engenhoso AI</title>
    <description>The latest articles on DEV Community by Engenhoso AI (engenhoso-ai).</description>
    <link>https://dev.to/engenhoso-ai</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F14274%2Ffb073618-d5e1-48ad-91fa-006e8737b9f2.png</url>
      <title>DEV Community: Engenhoso AI</title>
      <link>https://dev.to/engenhoso-ai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/engenhoso-ai"/>
    <language>en</language>
    <item>
      <title>Do “200 OK” à entrega real: o que aprendi investigando mensagens no Engenhoso AI</title>
      <dc:creator>Robson Concesso | Engenhoso</dc:creator>
      <pubDate>Mon, 17 Aug 2026 20:55:56 +0000</pubDate>
      <link>https://dev.to/engenhoso-ai/do-200-ok-a-entrega-real-o-que-aprendi-investigando-mensagens-no-engenhoso-ai-414e</link>
      <guid>https://dev.to/engenhoso-ai/do-200-ok-a-entrega-real-o-que-aprendi-investigando-mensagens-no-engenhoso-ai-414e</guid>
      <description>&lt;p&gt;Durante o desenvolvimento do Engenhoso AI, encontrei um daqueles problemas que parecem simples quando descritos em uma frase:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;uma mensagem era enviada, o sistema aparentemente funcionava, mas o destinatário nem sempre recebia a resposta como esperado.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;À primeira vista, a pergunta parecia óbvia:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;onde estava o erro?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Mas sistemas distribuídos raramente respondem bem a perguntas excessivamente simples.&lt;/p&gt;

&lt;p&gt;Uma mensagem que atravessa WhatsApp, webhook, aplicação, modelo de inteligência artificial e novamente a infraestrutura de mensageria não percorre um único caminho.&lt;/p&gt;

&lt;p&gt;Ela atravessa uma cadeia de acontecimentos.&lt;/p&gt;

&lt;p&gt;E foi justamente essa cadeia que precisei aprender a observar.&lt;/p&gt;




&lt;h2&gt;
  
  
  O primeiro indício: &lt;code&gt;POST /webhook 200 OK&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Nos logs do serviço que hospeda o webhook do Engenhoso AI, as requisições apareciam normalmente:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;POST /webhook HTTP/1.1 200 OK&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Em um primeiro momento, isso parecia significar que tudo estava funcionando.&lt;/p&gt;

&lt;p&gt;Mas havia um problema conceitual nessa interpretação.&lt;/p&gt;

&lt;p&gt;Um &lt;code&gt;200 OK&lt;/code&gt; apenas informava que determinada requisição havia sido recebida e tratada pelo endpoint.&lt;/p&gt;

&lt;p&gt;Ele não respondia às perguntas seguintes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;era uma mensagem enviada pelo usuário?&lt;/li&gt;
&lt;li&gt;era apenas uma atualização de status da Meta?&lt;/li&gt;
&lt;li&gt;a aplicação chamou a OpenAI?&lt;/li&gt;
&lt;li&gt;a resposta foi gerada?&lt;/li&gt;
&lt;li&gt;a Meta aceitou a resposta para envio?&lt;/li&gt;
&lt;li&gt;a mensagem chegou ao telefone do destinatário?&lt;/li&gt;
&lt;li&gt;ela foi lida?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Foi então que uma distinção aparentemente simples se tornou central para o diagnóstico:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Webhook recebeu ≠ IA respondeu ≠ Meta aceitou ≠ mensagem foi entregue ≠ mensagem foi lida&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Em software, duas etapas consecutivas podem parecer uma coisa só quando não temos instrumentos para observá-las.&lt;/p&gt;




&lt;h2&gt;
  
  
  O teste com comandos locais
&lt;/h2&gt;

&lt;p&gt;O Engenhoso AI possui alguns comandos locais que não dependem de geração por inteligência artificial.&lt;/p&gt;

&lt;p&gt;Um deles é &lt;code&gt;AJUDA&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Durante os testes, esse comando funcionou.&lt;/p&gt;

&lt;p&gt;A mensagem chegou ao webhook, foi reconhecida e o menu foi devolvido ao usuário.&lt;/p&gt;

&lt;p&gt;Isso permitiu retirar alguns suspeitos da investigação.&lt;/p&gt;

&lt;p&gt;Se &lt;code&gt;AJUDA&lt;/code&gt; funcionava, então pelo menos parte deste caminho estava operacional:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WhatsApp → Meta → Webhook → Aplicação → Meta → WhatsApp&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Mas uma pergunta aberta dependia de uma etapa adicional:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Aplicação → OpenAI → Aplicação&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Era necessário investigar essa fronteira separadamente.&lt;/p&gt;




&lt;h2&gt;
  
  
  O contador da API também pode enganar
&lt;/h2&gt;

&lt;p&gt;Durante parte da investigação, o painel da API permaneceu mostrando o mesmo número de solicitações.&lt;/p&gt;

&lt;p&gt;Isso parecia indicar que as perguntas não estavam chegando à OpenAI.&lt;/p&gt;

&lt;p&gt;Mais tarde, porém, o contador mudou.&lt;/p&gt;

&lt;p&gt;As solicitações estavam sendo contabilizadas.&lt;/p&gt;

&lt;p&gt;O que havia ocorrido era uma diferença temporal entre o acontecimento dentro da aplicação e a atualização que eu observava no painel.&lt;/p&gt;

&lt;p&gt;Foi um aprendizado importante:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;um painel de uso é uma ferramenta de observação, mas não deve ser confundido automaticamente com um log em tempo real da aplicação.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Quando investigamos um sistema distribuído, precisamos saber exatamente &lt;strong&gt;o que cada instrumento mede&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Descobrindo a chamada real da OpenAI
&lt;/h2&gt;

&lt;p&gt;Em vez de continuar formulando hipóteses a partir dos painéis, fui ao código.&lt;/p&gt;

&lt;p&gt;A aplicação utiliza a Responses API e uma chamada semelhante a:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;responses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;instructions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;...,&lt;/span&gt;
    &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;reasoning&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;effort&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;OPENAI_REASONING_EFFORT&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;max_output_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_max_output_tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Algumas informações começaram então a ficar objetivas.&lt;/p&gt;

&lt;p&gt;O projeto estava configurado para utilizar um modelo definido por variável de ambiente.&lt;/p&gt;

&lt;p&gt;O esforço adicional de raciocínio estava desativado:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;OPENAI_REASONING_EFFORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;none&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Também encontrei:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;max_retries&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ou seja, o cliente não estava realizando sucessivas tentativas automáticas capazes de explicar sozinho uma espera de vários minutos.&lt;/p&gt;

&lt;p&gt;Mais importante ainda: a aplicação possuía limites explícitos de tempo.&lt;/p&gt;




&lt;h2&gt;
  
  
  Timeout não era apenas uma hipótese
&lt;/h2&gt;

&lt;p&gt;O projeto já possuía uma arquitetura de &lt;em&gt;deadlines&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Na configuração de referência apareciam, entre outros, estes valores:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;OPENAI_TIMEOUT_SECONDS=12&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;OPENAI_WEBHOOK_TOTAL_TIMEOUT_SECONDS=15&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;E o código realmente propagava um prazo para a geração.&lt;/p&gt;

&lt;p&gt;De maneira simplificada:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;generation_task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;_run_openai_generation&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;generation_timed_out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;_wait_for_openai_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;generation_task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;deadline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;openai_deadline&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quando o limite era ultrapassado, o Engenhoso AI selecionava uma resposta de &lt;em&gt;fallback&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Isso explicava uma mensagem que apareceu em um dos testes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Desculpe, não consegui gerar uma resposta agora. Tente novamente em alguns instantes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Poucos minutos depois, a mesma pergunta recebeu uma resposta normal.&lt;/p&gt;

&lt;p&gt;O problema era, portanto, &lt;strong&gt;intermitente&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;E já não fazia sentido atribuir automaticamente vários minutos de espera a uma única chamada indefinida para o modelo.&lt;/p&gt;




&lt;h2&gt;
  
  
  O envio à Meta também tinha prazo
&lt;/h2&gt;

&lt;p&gt;Continuei seguindo o código.&lt;/p&gt;

&lt;p&gt;A própria etapa de envio da resposta possuía um &lt;em&gt;deadline&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;remaining_delivery_seconds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;delivery_deadline&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;remaining_delivery_seconds&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E a chamada era executada dentro de uma janela controlada:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timeout_at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;meta_deadline&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;_send_started_reply&lt;/span&gt;&lt;span class="p"&gt;(...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Isso trouxe outra conclusão importante.&lt;/p&gt;

&lt;p&gt;O projeto já havia sido construído para tentar impedir que uma operação permanecesse indefinidamente presa em uma etapa externa.&lt;/p&gt;

&lt;p&gt;Mas ainda faltava uma informação fundamental:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;o que acontecia depois que a Meta aceitava uma mensagem?&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  O ponto cego: &lt;code&gt;statuses&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;A API do WhatsApp envia callbacks relacionados ao estado das mensagens.&lt;/p&gt;

&lt;p&gt;Entre eles podem aparecer estados como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;sent&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;delivered&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;read&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;failed&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O Engenhoso AI já possuía um teste que verificava que uma atualização de status deveria ser ignorada como mensagem de usuário:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_atualizacao_de_status_e_ignorada&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sent&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Isso estava correto.&lt;/p&gt;

&lt;p&gt;Um callback de status não deve provocar uma nova resposta automática.&lt;/p&gt;

&lt;p&gt;O problema era outro:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ele também desaparecia do ponto de vista da observabilidade.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;O código extraía:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_extract_messages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mas um callback poderia conter, por exemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"statuses"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"delivered"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;em vez de uma nova mensagem.&lt;/p&gt;

&lt;p&gt;Nesse caso, o webhook respondia &lt;code&gt;200 OK&lt;/code&gt;, mas eu não conseguia saber pelo log se aquele evento representava &lt;code&gt;sent&lt;/code&gt;, &lt;code&gt;delivered&lt;/code&gt;, &lt;code&gt;read&lt;/code&gt; ou até &lt;code&gt;failed&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Foi aí que percebi que o problema não era apenas corrigir alguma coisa.&lt;/p&gt;

&lt;p&gt;Era &lt;strong&gt;tornar o sistema capaz de explicar o que estava acontecendo&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Criando observabilidade sem registrar a conversa
&lt;/h2&gt;

&lt;p&gt;Foi criada uma função independente para extrair apenas os estados técnicos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_extract_status_updates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Conceitualmente, ela percorre:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;entry → changes → field = messages → value → statuses&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;e identifica estados como &lt;code&gt;sent&lt;/code&gt;, &lt;code&gt;delivered&lt;/code&gt;, &lt;code&gt;read&lt;/code&gt; e &lt;code&gt;failed&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Mas havia outra preocupação.&lt;/p&gt;

&lt;p&gt;Observabilidade não deveria significar registrar indiscriminadamente informações dos usuários.&lt;/p&gt;

&lt;p&gt;Por isso, o &lt;code&gt;message_id&lt;/code&gt; original não precisava aparecer nos logs.&lt;/p&gt;

&lt;p&gt;Foi utilizada uma referência derivada:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;message_ref&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message_id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()[:&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;message_id&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unknown&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Os registros podem então assumir formas semelhantes a:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;whatsapp_status status=sent ref=8a92e740fb21 timestamp=...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;whatsapp_status status=delivered ref=8a92e740fb21 timestamp=...&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;whatsapp_status status=read ref=8a92e740fb21 timestamp=...&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A mesma referência permite correlacionar os eventos sem registrar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;o texto da conversa;&lt;/li&gt;
&lt;li&gt;o telefone;&lt;/li&gt;
&lt;li&gt;o nome do usuário;&lt;/li&gt;
&lt;li&gt;o identificador original da mensagem.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Para mim, esse ponto é particularmente importante.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Observabilidade não precisa significar abandono da privacidade.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Testar antes de integrar
&lt;/h2&gt;

&lt;p&gt;Antes de conectar essa informação ao fluxo real, acrescentei um teste específico para os estados &lt;code&gt;sent&lt;/code&gt;, &lt;code&gt;delivered&lt;/code&gt;, &lt;code&gt;read&lt;/code&gt; e &lt;code&gt;failed&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A sequência de desenvolvimento foi deliberadamente simples:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;criar extrator → criar teste → ligar ao log → Pull Request → Merge → Deploy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A alteração foi desenvolvida em uma branch separada:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;fix/whatsapp-status-observability&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Depois passou por Pull Request antes de chegar à &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Esse processo pode parecer mais trabalhoso do que editar diretamente a produção.&lt;/p&gt;

&lt;p&gt;Mas existe uma diferença entre fazer uma mudança e construir um histórico compreensível de mudanças.&lt;/p&gt;

&lt;p&gt;Quanto mais o projeto cresce, mais essa diferença importa.&lt;/p&gt;




&lt;h2&gt;
  
  
  A infraestrutura também entrou no experimento
&lt;/h2&gt;

&lt;p&gt;Durante a investigação, o webhook ainda estava executando em uma instância gratuita do serviço de hospedagem.&lt;/p&gt;

&lt;p&gt;Decidi migrá-lo para uma instância paga de entrada.&lt;/p&gt;

&lt;p&gt;No momento da mudança, a configuração selecionada foi:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Starter&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0,5 CPU&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;512 MB de RAM&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A decisão não foi tomada porque eu tivesse demonstrado que a hospedagem era a causa de todas as falhas.&lt;/p&gt;

&lt;p&gt;Essa distinção é importante.&lt;/p&gt;

&lt;p&gt;A migração foi uma forma de retirar uma variável da investigação e dar ao webhook uma infraestrutura mais adequada ao seu papel atual.&lt;/p&gt;

&lt;p&gt;Depois do deploy, o serviço voltou ao estado &lt;code&gt;Live&lt;/code&gt;, enquanto o &lt;em&gt;health check&lt;/em&gt; continuou respondendo &lt;code&gt;200 OK&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A partir desse momento, o comportamento posterior poderia ser comparado com os registros anteriores à migração.&lt;/p&gt;

&lt;p&gt;Não é correto afirmar:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“o plano pago resolveu o problema”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;apenas porque uma mensagem posterior funcionou.&lt;/p&gt;

&lt;p&gt;É necessário observar uma série de interações.&lt;/p&gt;

&lt;p&gt;Essa diferença entre &lt;strong&gt;coincidência temporal&lt;/strong&gt; e &lt;strong&gt;evidência causal&lt;/strong&gt; é um aprendizado que considero importante também fora da programação.&lt;/p&gt;




&lt;h2&gt;
  
  
  O que mudou na maneira de diagnosticar
&lt;/h2&gt;

&lt;p&gt;Antes dessa investigação, uma pergunta poderia ser formulada assim:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Por que a pessoa não recebeu a mensagem?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Agora ela pode ser dividida em perguntas melhores:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;O webhook recebeu o evento?&lt;/li&gt;
&lt;li&gt;Era uma mensagem ou um callback de status?&lt;/li&gt;
&lt;li&gt;A aplicação decidiu chamar a IA?&lt;/li&gt;
&lt;li&gt;A geração terminou ou ocorreu timeout?&lt;/li&gt;
&lt;li&gt;Foi utilizado &lt;em&gt;fallback&lt;/em&gt;?&lt;/li&gt;
&lt;li&gt;A resposta foi enviada para a Meta?&lt;/li&gt;
&lt;li&gt;A Meta marcou como &lt;code&gt;sent&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Foi marcada como &lt;code&gt;delivered&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Foi marcada como &lt;code&gt;read&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Houve &lt;code&gt;failed&lt;/code&gt;?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Quanto melhor a pergunta técnica, menor a necessidade de adivinhar.&lt;/p&gt;




&lt;h2&gt;
  
  
  Uma arquitetura que começou a se tornar observável
&lt;/h2&gt;

&lt;p&gt;Hoje consigo representar o fluxo de forma mais clara:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Usuário → WhatsApp → Meta → Webhook Engenhoso AI&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A partir do webhook, a aplicação pode seguir por um comando local ou por geração de IA.&lt;/p&gt;

&lt;p&gt;Quando existe geração:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Webhook → OpenAI → resposta gerada → Meta&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Depois disso, os callbacks ajudam a observar o que ocorreu com a mensagem:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Meta → &lt;code&gt;sent&lt;/code&gt; → &lt;code&gt;delivered&lt;/code&gt; → &lt;code&gt;read&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ou, quando algo não ocorre como esperado:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Meta → &lt;code&gt;failed&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;O sistema não ficou apenas mais fácil de depurar.&lt;/p&gt;

&lt;p&gt;Ele ficou mais capaz de contar a própria história operacional.&lt;/p&gt;




&lt;h2&gt;
  
  
  O aprendizado maior
&lt;/h2&gt;

&lt;p&gt;Quando comecei essa investigação, eu procurava um erro.&lt;/p&gt;

&lt;p&gt;No decorrer do processo, percebi que havia uma questão anterior ao erro:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;eu possuía instrumentos suficientes para saber onde ele estava?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Nem sempre a próxima melhoria de um sistema é uma nova funcionalidade.&lt;/p&gt;

&lt;p&gt;Às vezes, o que falta é capacidade de observação.&lt;/p&gt;

&lt;p&gt;Um &lt;code&gt;200 OK&lt;/code&gt; pode ser verdadeiro e, ao mesmo tempo, insuficiente.&lt;/p&gt;

&lt;p&gt;A chamada da IA pode ter acontecido e o painel ainda não ter atualizado.&lt;/p&gt;

&lt;p&gt;A Meta pode ter aceitado uma mensagem sem que isso signifique que ela já chegou ao destinatário.&lt;/p&gt;

&lt;p&gt;São estados diferentes.&lt;/p&gt;

&lt;p&gt;E aprender a respeitar essas diferenças talvez seja uma das mudanças mais importantes quando um projeto deixa de ser apenas um experimento e começa a se comportar como um sistema real.&lt;/p&gt;

&lt;p&gt;O Engenhoso AI continua sendo, para mim, um ambiente de aprendizagem.&lt;/p&gt;

&lt;p&gt;Cada dificuldade técnica revela não apenas alguma coisa sobre Python, APIs, webhooks ou inteligência artificial.&lt;/p&gt;

&lt;p&gt;Ela revela também uma forma de pensar:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;antes de corrigir aquilo que não compreendemos, precisamos construir meios para enxergá-lo.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Transparência sobre o processo
&lt;/h2&gt;

&lt;p&gt;Este texto nasceu de uma experiência real de desenvolvimento do projeto Engenhoso AI.&lt;/p&gt;

&lt;p&gt;Ferramentas de inteligência artificial foram utilizadas como apoio na investigação técnica, organização das hipóteses, leitura do código e revisão do texto.&lt;/p&gt;

&lt;p&gt;As decisões sobre arquitetura, testes, alterações no projeto, interpretação dos resultados e publicação permanecem sob responsabilidade do autor.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Engenhoso AI&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;Aprender, construir, observar e compreender.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>webdev</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
