<?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: Herbert Moller</title>
    <description>The latest articles on DEV Community by Herbert Moller (@herbmoller).</description>
    <link>https://dev.to/herbmoller</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3936304%2Fe73e0ce1-2dcc-413d-a07f-b7b49368a842.png</url>
      <title>DEV Community: Herbert Moller</title>
      <link>https://dev.to/herbmoller</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/herbmoller"/>
    <language>en</language>
    <item>
      <title>Como não ter o número do WhatsApp bloqueado usando automação com IA</title>
      <dc:creator>Herbert Moller</dc:creator>
      <pubDate>Mon, 18 May 2026 02:03:34 +0000</pubDate>
      <link>https://dev.to/herbmoller/como-nao-ter-o-numero-do-whatsapp-bloqueado-usando-automacao-com-ia-lb</link>
      <guid>https://dev.to/herbmoller/como-nao-ter-o-numero-do-whatsapp-bloqueado-usando-automacao-com-ia-lb</guid>
      <description>&lt;p&gt;Você subiu um chatbot no WhatsApp Business API. As primeiras semanas foram tranquilas. Aí de repente o número foi banido. Sem aviso. Sem recurso claro. A linha sumiu, o cliente sumiu junto, e você está olhando para um &lt;code&gt;401 Unauthorized&lt;/code&gt; com a sensação de ter perdido um ativo que levou meses para construir.&lt;/p&gt;

&lt;p&gt;Isso acontece mais do que você imagina. E na maioria dos casos, &lt;strong&gt;não é falha do provedor nem azar&lt;/strong&gt; — é uma falha de governança no comportamento do agente.&lt;/p&gt;




&lt;h2&gt;
  
  
  Por que o WhatsApp detecta e bloqueia automações
&lt;/h2&gt;

&lt;p&gt;O WA mantém um &lt;strong&gt;Health Score por número&lt;/strong&gt; que combina sinais comportamentais e sinais de rede. Ele não olha só o conteúdo — analisa o &lt;em&gt;padrão de uso&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Taxa de envio fora do perfil humano
&lt;/h3&gt;

&lt;p&gt;Um humano raramente envia mais de 20 mensagens em 5 minutos para contatos diferentes. Um bot sem throttling pode atingir 200. O WA identifica isso como blast e aplica throttle progressivo — que, sem correção, vira suspensão.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Latência de resposta determinística
&lt;/h3&gt;

&lt;p&gt;Humanos têm variação natural: às vezes respondem em 3 segundos, às vezes em 40. Bots sem randomização respondem em ~1.2s sempre. Esse padrão é detectável por análise estatística simples.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Taxa de rejeição de template acima de 2%
&lt;/h3&gt;

&lt;p&gt;Se os destinatários estão bloqueando ou denunciando seus HSMs com frequência acima de 2%, o número entra em zona de risco. A Meta publica esse threshold na documentação oficial.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Sessões inconsistentes
&lt;/h3&gt;

&lt;p&gt;Para on-premise (Baileys, WWEBJS): trocar de IP em sessão ativa ou abrir sessões paralelas com o mesmo número gera fingerprint inconsistente — o WA trata como comprometimento de conta.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Ausência de opt-out real
&lt;/h3&gt;

&lt;p&gt;Enviar para quem nunca consentiu ou não ter mecanismo de saída gera denúncias. Denúncia é o sinal com maior peso no Health Score.&lt;/p&gt;




&lt;h2&gt;
  
  
  5 práticas concretas para manter a linha saudável
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Jitter obrigatório entre envios
&lt;/h3&gt;

&lt;p&gt;Nunca envie em cadência fixa. Adicione variação aleatória:&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_with_jitter&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;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;base_delay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;jitter_range&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Envia mensagem com delay variável para simular comportamento humano.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;delay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base_delay&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uniform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jitter_range&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para campanhas em volume, respeite também o fuso do destinatário — enviar às 2h da manhã é outro sinal negativo.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Opt-out com efeito imediato na fila
&lt;/h3&gt;

&lt;p&gt;O opt-out não pode ser decorativo. Precisa interromper imediatamente qualquer envio futuro, incluindo mensagens já agendadas:&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;handle_opt_out&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db_conn&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Registra opt-out e cancela filas pendentes.
    Deve ser chamado ANTES de qualquer outra lógica de resposta.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;db_conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UPDATE contacts SET opted_out_at = NOW() WHERE phone = %s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;,),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;db_conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DELETE FROM message_queue WHERE to_phone = %s AND sent_at IS NULL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phone&lt;/span&gt;&lt;span class="p"&gt;,),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;db_conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Palavras como &lt;code&gt;PARAR&lt;/code&gt;, &lt;code&gt;SAIR&lt;/code&gt;, &lt;code&gt;STOP&lt;/code&gt;, &lt;code&gt;CANCELAR&lt;/code&gt; precisam ser interceptadas antes do processamento pelo LLM. Nunca deixe o modelo decidir o que fazer com um opt-out.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Health Score monitoring via API do Manager
&lt;/h3&gt;

&lt;p&gt;A Meta expõe o Health Score via Graph API. Crie um cron simples que alerta quando o score degradar:&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check_health_score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;waba_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://graph.facebook.com/v19.0/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;waba_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&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;fields&lt;/span&gt;&lt;span class="sh"&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;health_status&lt;/span&gt;&lt;span class="sh"&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;access_token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;access_token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;health_status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;

&lt;span class="c1"&gt;# health_status.can_send_message == "AVAILABLE"  → estado seguro
# health_status.can_send_message == "LIMITED"    → pare envios proativos
# health_status.can_send_message == "BLOCKED"    → só suporte Meta resolve
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Sessões isoladas por número
&lt;/h3&gt;

&lt;p&gt;No modelo on-premise, cada número precisa de isolamento completo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/sessions/
  +5511999990001/
    session.json
    auth_info/          ← pasta exclusiva deste número
  +5511999990002/
    session.json
    auth_info/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;IP dedicado por sessão, processo independente. Nunca compartilhe contexto de sessão entre números.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Janela de mensagem vs. template
&lt;/h3&gt;

&lt;p&gt;O WA tem dois contextos: dentro da janela de 24h (mensagem livre) e fora dela (obrigatório HSM aprovado). Bots que tentam mensagem livre fora da janela geram erro &lt;code&gt;131047&lt;/code&gt;. Erro repetido degrada o Health Score.&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timedelta&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_message_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;last_user_message_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Retorna o tipo de mensagem permitida com base na janela de 24h.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;window_open&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;utcnow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;last_user_message_at&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nf"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hours&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session_message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;window_open&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;template&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  A raiz do problema: agente sem halt automático
&lt;/h2&gt;

&lt;p&gt;As cinco práticas acima resolvem sintomas. O problema estrutural é mais profundo: um agente autônomo sem audit trail e sem mecanismo de halt não tem como ser contido quando começa a errar.&lt;/p&gt;

&lt;p&gt;Governança de agente significa que cada decisão do LLM — enviar mensagem, acionar ferramenta, escalar para humano — passa por uma camada que registra, avalia e pode interromper. Isso inclui:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Audit trail imutável:&lt;/strong&gt; cada mensagem tem registro de qual decisão do agente a gerou, com confiança e contexto&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate limiter por agente&lt;/strong&gt;, separado do rate limit do número: o agente pode ser contido sem derrubar a sessão WA&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fallback automático para humano:&lt;/strong&gt; quando o agente detecta incerteza alta, entrega o contexto para um atendente em vez de alucinar&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Halt imediato em rejeição WA:&lt;/strong&gt; qualquer &lt;code&gt;401&lt;/code&gt;/&lt;code&gt;403&lt;/code&gt; da API dispara parada completa do agente — sem retry, sem backoff, sem "só mais uma vez"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esse último ponto é crítico e frequentemente ignorado. Agentes com loop de reconexão agressivo são dos principais vetores de banimento definitivo.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Regra de ouro:&lt;/strong&gt; reputação de número de WhatsApp é ativo não-recuperável. Uma vez queimada via fingerprinting agressivo, o número está perdido. Governança não é opcional em produção.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;A maioria dos banimentos de WhatsApp não é inevitável. Eles resultam de três padrões evitáveis: cadência sem jitter, opt-out decorativo e agentes sem mecanismo de halt.&lt;/p&gt;

&lt;p&gt;Implemente as cinco práticas acima antes de colocar qualquer automação em produção com volume relevante. E, independente da stack que você usar, trate a camada de governança do agente como componente separado da infraestrutura WA — é uma decisão de arquitetura, não de configuração.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Construindo algo parecido? A &lt;a href="https://api.mukta.cc" rel="noopener noreferrer"&gt;Mukta API&lt;/a&gt; oferece orquestração de agentes autônomos com essas garantias embutidas — audit trail, trust-tier, halt automático em rejeição WA — sobre qualquer provider (Meta Cloud, Twilio ou on-premise). Plano self-service a partir de R$100/mês.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>agents</category>
      <category>python</category>
    </item>
  </channel>
</rss>
