1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
| title: "RustyClaw: vou reescrever um agente de IA em Rust (porque o meme pediu)"
date: 2026-02-24T18:00:00+01:00
draft: false
slug: "rustyclaw-manifesto-rewrite-it-in-rust"
slug_en: "rustyclaw-manifesto-rewrite-ai-agent-rust"
description: "Vou portar 8.300 linhas de Python para Rust usando LLMs como copiloto. O objetivo real: testar o desenvolvimento adversarial em um port puro e direto. Com dados honestos e um contador de alucinações."
tags: ["rust", "python", "ia", "llm", "riir", "rustyclaw"]
categories: ["rustyclaw"]
series: ["RustyClaw: Rewrite It In Rust"]
translation:
hash: ""
last_translated: ""
notes: |
- "RIIR": acrônimo do meme "Rewrite It In Rust". Não traduzir, é jargão universalmente conhecido na comunidade Rust.
- "Sr. Cangrejo": referência ao Sr. Siriguejo (Mr. Krabs) de Bob Esponja (SpongeBob). Em inglês usar "Mr. Krabs", em português "Sr. Siriguejo", em francês "M. Krabs".
- "chapuza": significa "trabalho mal feito". Não traduzir literalmente.
- "me la pela": expressão vulgar no sentido de "não estou nem aí". Usar equivalente coloquial para passar o mesmo tom no idioma-alvo.
- "ñapa": sinônimo de gambiarrão, solução rápida e improvisada.
- "conejillo de indias": traduzir diretamente como "cobaia".
- "barra del bar": traduzir como balcão (referência a conversas informais).
- "otro gallo cantaría": significado de "seria outra história". Usar equivalente idiomático local.
social:
publish: true
scheduled_date: 2026-02-28
platforms: ["twitter", "linkedin"]
excerpt: "Vou portar um agente de IA de 8.300 linhas de Python para Rust. O objetivo: testar o desenvolvimento adversarial em um port real. Dados honestos de consumo, custo e alucinações. Porque o que é melhor do que uma AGI? Uma AGI reescrita em Rust."
wordpress:
publish: true
categories: [1]
tags: ["rust", "python", "ia", "llm", "riir", "rustyclaw"]
video:
generate: false
style: "educational"
---
> *"Sabe qual é a melhor coisa no Rust? Que ele não deixa você compilar gambiarras. Sabe qual é a pior coisa? Que tudo que você escreve no início é uma gambiarra."*
> — Sr. Siriguejo (provavelmente)
O que é melhor do que um agente de IA? Um agente de IA *reescrito em Rust*.
Se você está na internet há mais de cinco minutos, conhece o meme. Não importa o projeto: um editor de texto, um servidor DNS, uma calculadora de IMC. Alguém sempre aparece nos comentários dizendo "você deveria reescrever isso em Rust". É o *Rewrite It In Rust* — RIIR para os íntimos — e é tão inevitável quanto a gravidade.
Pois bem, eu vou fazer isso de verdade. Vou portar 8.300 linhas de um agente de IA de Python para Rust. Mas não porque o meme pediu (ok, talvez um pouco). Vou fazer porque preciso de uma cobaia.
## A tese
Passei algumas semanas escrevendo sobre [*falhas silenciosas*](/pt/posts/silent-failure-ia-inventa-testes-tudo-bem/), sobre as [cinco defesas contra alucinações](/pt/posts/cinco-defesas-alucinacoes-codigo/), sobre como um LLM pode gerar código que compila, passa os testes e ainda assim está errado. Eu até lhe dei um nome: **desenvolvimento adversarial**. *Nunca confie, sempre verifique.*
Muita teoria. Agora é hora de colocar isso à prova.
Precisava de um projeto com três características: um escopo definido (nada de um aplicativo novo com requisitos em constante mudança), uma fonte confiável de verdade (o código Python que já funciona) e com complexidade suficiente para que as alucinações do LLM tenham espaço para aparecer. Um port puro e direto atende aos três requisitos. O *input* e o *output* esperado já existem. Se a versão em Rust não se comportar exatamente como a versão em Python, há um bug. Simples assim.
E já que vou portar algo, por que não aproveitar para aprender Rust de verdade? O *borrow checker*, *ownership*, *lifetimes*... Passo anos lendo sobre tudo isso sem botar a mão na massa. Se em vez de estudar tutoriais pela milésima vez, eu me jogasse em um projeto real, outra seria a história.
## O paciente
Chama-se [nanobot](https://github.com/HKUDS/nanobot). É um agente de IA pessoal derivado do OpenClaw: uma ferramenta que conecta LLMs (Claude, GPT, DeepSeek, o que vier) a canais de chat — Telegram, Discord, Slack, email — e os equipa com habilidades. Ele pode ler e editar arquivos, executar comandos, fazer buscas na web, programar tarefas com cron e ainda manter uma memória persistente entre as conversas.
E funciona. Tem meses que funciona. Em Python.
Qual é o problema? Ele é *single-threaded*. Processa uma mensagem por vez. Se você enviar três mensagens seguidas, ele as coloca em fila como o caixa de um mercado no sábado ao meio-dia. Usa apenas 50 MB de RAM para, basicamente, rotear JSON entre APIs. E o código tem aquelas técnicas de *error handling* que dão vergonha alheia: `return f"Erro: {str(e)}"` para todo lado.
Traduzindo: funciona, mas é uma gambiarra ambulante. Candidato perfeito.
## Por que Rust (além do meme)
Eu poderia consertar isso no Python. Poderia colocar mais *asyncio*, tipar melhor os erros com exceções personalizadas, otimizar a memória. Seria o mais sensato.
Mas isso não me dá um *test bench* para o desenvolvimento adversarial. Um refactor em Python não tem uma fonte externa de verdade — o "antes" e o "depois" compartilham linguagem, bibliotecas e os mesmos vieses do LLM. Já portar para outra linguagem resolve isso. Se o output do Rust for diferente do output do Python para o mesmo *input*, algum está mentindo. E é exatamente esse o tipo de verificação que eu quero testar.
Além disso, Rust tem propriedades que tornam o experimento mais interessante:
- **O compilador como primeira defesa.** Nulls, *type mismatch*, *data races*: categorias inteiras de bugs que no Python passam despercebidos mas que, no Rust, não passam pela compilação. Quantas alucinações de LLM o compilador barra antes de atingir um teste? Quero medir isso.
- **Concorrência real.** `tokio` permite um *spawn* por conversa. Em Python isso é um calvário. É a melhoria funcional que justifica o port.
- **Binário estático.** Um executável de 10 MB em vez de um `pip install` com 47 dependências. Para distribuição, não tem preço.
- **Porque é legal.** Ok, isso não é um motivo técnico. Mas não me importo.
## A aventura (e o convite)
RustyClaw — assim se chama o port — será um experimento documentado publicamente. Cada módulo portado será um post. Com dados reais: quantos tokens consumi, quanto custou, quantas vezes a IA alucinou, quanto tempo demorei lutando com o *borrow checker*. Sem maquiagem.
Se eu gastar 3 horas em algo que no Python faria em 10 minutos, vou contar. Se o LLM inventar um *crate* que não existe (spoiler: vai acontecer), será documentado. Se, ao final do port, ficar claro que não valeu a pena, vou admitir.
Todo mundo diz "usei IA para escrever código". Ninguém publica quanto custou, quantas vezes a IA mentiu e se o resultado aguentou no ambiente de produção. É exatamente isso que vou fazer.
E quero que você me acompanhe. Porque vai ser uma aventura — com brigas contra o compilador, momentos de "POR QUE não compila se é ÓBVIO?", e pequenas vitórias quando um teste diferencial passar no verde. Vai ser divertido. Ou pelo menos, honesto.
## A stack (o lembrete)
Se você é pythonista, a coluna da esquerda será familiar. Se você é *rustacean*, a da direita. Se não é nenhum dos dois, bem-vindo ao caos.
| Camada | Python (nanobot) | Rust (rustyclaw) |
|--------|------------------|------------------|
| Runtime async | `asyncio` | `tokio` |
| HTTP | `httpx` | `reqwest` |
| Roteamento de LLM | `litellm` | **Não existe** — roteador próprio |
| Telegram | `python-telegram-bot` | `teloxide` |
| Discord | `websockets` (raw) | `tokio-tungstenite` (raw) |
| Config | `pydantic` | `serde` + `figment` |
| CLI | `typer` | `clap` |
| Erros | `str(e)` | `anyhow` + `thiserror` |
| Logging | `loguru` | `tracing` |
| Copiloto IA | — | Claude Code + Codex |
| Task runner | `make` | `just` |
| Issues | — | `linear` CLI |
A linha que mais dói é LiteLLM. Em Python, ele roteia para mais de 100 provedores de LLM com uma única chamada. Em Rust não existe algo parecido. Teremos que construir um roteador próprio. A boa notícia é que 80% dos provedores são compatíveis com a API da OpenAI, então com `async-openai` + configuração de *base_url* custom cobrimos quase tudo. Anthropic exigirá uma implementação específica.
Por volta de ~300 linhas de código Rust. Parece tranquilo. *Parece.*
As três últimas linhas são o *meta-stack*: ferramentas que usarei durante o port. Claude Code para sessões interativas em que preciso "pensar alto" com o LLM. Codex para ports mecânicos, onde o contexto já está definido e prefiro velocidade. `just` em vez de `make`, porque quero testar algo que não depende de tabs literais para funcionar. E o CLI do `linear` para gerenciar o progresso sem sair do terminal.
## A estratégia anti-alucinações (a parte séria)
É aqui que a teoria do desenvolvimento adversarial encontra a prática. Um LLM assistindo nesse tipo de port tem grande potencial de inventar coisas plausíveis.
O risco principal não é que o código falhe ao compilar. Rust não deixa você compilar lixo. O risco é que compile, passe os testes e funcione de maneira errada sem alertar. Exatamente a *falha silenciosa* que documentei há duas semanas.
Cinco camadas de defesa:
**1. O compilador do Rust.** Elimina nulls, *type mismatch*, *data races*. Primeira linha de defesa gratuita. Mas compilar não significa que esteja correto.
**2. Testes diferenciais.** Mesma entrada → nanobot Python → saída. Mesma entrada → rustyclaw Rust → saída. Se não forem idênticos, há um problema. A fonte de verdade é o código Python já funcional. Essa é a camada mais importante do experimento.
**3. Rastreamento de proveniência.** Cada arquivo portado terá um *header* com o arquivo-fonte em Python original, a sessão do LLM que o gerou e os resultados nos testes diferenciais. Rastreabilidade total.
**4. Verificação de crates.** Cada *crate* sugerido pelo LLM → verificação manual em crates.io e docs.rs. Os LLMs inventam crates que não existem e APIs que não funcionam assim. Tudo com a maior segurança do mundo, como quem afirma que Sydney é a capital da Austrália.
**5. Log de incidentes.** Cada alucinação detectada → *issue* com tag `hallucination`. Material para os posts e para não cometer os mesmos erros.
A regra de ouro:
> **O sistema de verificação precisa ser externo ao gerador.**
Se o LLM gera o código, os testes e os *fixtures*, você estará validando ficção usando mais ficção. Os testes diferenciais contra o código Python original são a camada que quebra esse ciclo. E a vantagem de um port é que essa camada existe naturalmente.
## *Vale a pena?*
Vamos à pergunta incômoda. Será que realmente vale a pena portar isso para Rust?
| Métrica | Python | Rust (estimado) | Vale a pena? |
|---------|--------|-----------------|--------------|
| Latência de resposta | ~200ms overhead | ~5ms overhead | Não. O LLM leva entre 2 e 5 segundos de qualquer jeito. |
| RAM | ~50MB | ~5MB | Não. Meu servidor tem 8GB. |
| Concorrência | 1 mensagem por vez | N mensagens simultâneas | **Sim.** |
| Startup | ~2s | ~50ms | Meh. |
| Binário | `pip install` + 47 deps | Um executável | **Sim.** |
| Segurança de tipos | `str(e)` everywhere | `Result<T, E>` | **Sim.** |
| Que é legal | Não | Sim | Subjetivo. |
Três de sete. Sendo generoso, quatro. A melhoria na latência e no consumo de RAM é irrelevante porque o gargalo sempre será a chamada ao LLM. Concorrência, por outro lado, importa muito para múltiplos usuários. O binário estático é uma verdadeira melhoria. Já a segurança de tipos... depois de ver `str(e)` esconder bugs por meses, isso sim faz diferença.
Vale semanas de trabalho? Como port isolado, provavelmente não. Mas como banco de provas para desenvolvimento adversarial com dados reais publicados, acredito que sim. Ao final da série teremos números para decidir, não opiniões.
## Os números, sem filtros
Cada sessão de trabalho será registrada em um CSV no repositório:
```csv
date,llm,model,module,tokens_in,tokens_out,cost_usd,duration_min,loc_python,loc_rust,hallucinations,tests_pass
|