Beleza, pessoal, Leo Grant aqui, de volta com outra exploração profunda no mundo selvagem do desenvolvimento de agentes. Hoje, quero falar sobre algo que tem me incomodado, algo que vi aparecendo em fórum após fórum e, honestamente, algo com que eu mesmo lutei há apenas alguns meses: a Armadilha do “Novo Framework Brilhante para Agentes”. Todos nós já passamos por isso, certo?
Estamos em 2026, e parece que um novo SDK ou framework para agentes aparece a cada duas semanas. Cada um promete ser mais rápido, mais inteligente, mais escalável ou simplesmente mais fácil para construir agentes autônomos. E, como alguém que vive e respira isso, minha reação inicial é sempre uma mistura de empolgação e FOMO. “É esse o certo? É essa a ferramenta que finalmente torna meu projeto pessoal uma realidade sem semanas de código repetitivo?”
Eu me lembro de que, em outubro, estava trabalhando em um agente assistente financeiro pessoal. A ideia era simples: um agente que pudesse monitorar meus gastos, identificar serviços de assinatura que eu poderia não estar usando e até negociar melhores taxas em meu nome. Eu tinha um script básico em Python rodando, usando um barramento de mensagens personalizado e muitos `if/else`. Era desajeitado, claro, mas funcionava.
Então vi o anúncio do “Aether,” um novo framework para agentes baseado em Rust que prometia incrível concorrência e uma linguagem declarativa para definição de agentes. Meu cérebro primitivo imediatamente gritou: “Reescreva tudo! Rust é o futuro! Minha bagunça em Python é uma vergonha!” Então, passei duas semanas sólidas portando meu projeto inteiro para o Aether. E adivinha? Embora o Aether fosse realmente eficiente e elegante, acabei com um projeto que era funcionalmente idêntico à minha versão em Python, mas agora eu precisava aprender todo um novo ecossistema de ferramentas de build, gerenciamento de dependências e tratamento de erros específico para o Aether.
Foi uma lição valiosa, e uma que quero compartilhar com vocês hoje. O maior obstáculo no desenvolvimento de agentes não é necessariamente encontrar o “melhor” framework; é entender quando usar um framework e, mais importante, quando apenas se manter com o que você sabe e construir a partir dos princípios básicos. Hoje, vamos falar sobre abraçar a mentalidade de “construir”, especificamente no que diz respeito à comunicação básica e gerenciamento de estado dos seus agentes, em vez de adotar cegamente o SDK mais recente.
O Problema Central: Sobre-engenharia da Comunicação
A maioria dos frameworks para agentes, em sua essência, tenta resolver dois problemas principais:
- Comunicação entre Agentes: Como os agentes se comunicam entre si? Filas de mensagens, RPC, estado compartilhado?
- Gerenciamento de Estado do Agente: Como um agente mantém o controle do que sabe, do que fez e do que precisa fazer a seguir?
Esses são problemas críticos, sem dúvida. Mas muitas vezes, os frameworks oferecem uma solução tão abrangente e opinativa que se torna excessiva para projetos mais simples ou introduz complexidade desnecessária. Meu agente financeiro, por exemplo, só precisava se comunicar com um outro “agente” (uma API de banco simulada) e seu próprio estado interno. Um barramento de mensagens cheio de recursos complexos era como usar um lança-foguetes para espantar uma mosca.
Então, e se retirássemos tudo isso? E se pensássemos sobre o mínimo absoluto que você precisa para fazer os agentes se comunicarem e lembrarem de coisas? Isso não é sobre evitar frameworks para sempre, mas sobre construir uma base sólida primeiro, entendendo a mecânica subjacente, e depois decidindo se um framework realmente agrega valor ao invés de apenas overhead.
Comunicação Simples: A Base HTTP/JSON
Vamos ser brutalmente honestos: para uma vasta quantidade de casos de uso de agentes, especialmente aqueles que interagem com serviços web ou outros sistemas externos, HTTP e JSON são seus melhores amigos. Eles são onipresentes, bem compreendidos e incrivelmente flexíveis. Você não precisa de um protocolo personalizado ou de um corretor de mensagens complexo se seus agentes estão, principalmente, enviando requisições e recebendo respostas.
Considere um cenário onde você tem um “Agente Raspador” que busca dados de um site e um “Agente Processador” que limpa e analisa esses dados. Como eles se comunicam?
# scraper_agent.py (simplificado)
import requests
import json
def scrape_data(url):
response = requests.get(url)
if response.status_code == 200:
return response.text
return None
def send_to_processor(data):
headers = {'Content-Type': 'application/json'}
payload = {'raw_data': data}
try:
response = requests.post('http://localhost:8001/process', headers=headers, data=json.dumps(payload))
response.raise_for_status() # Levanta uma exceção para erros HTTP
print(f"Dados enviados ao processador: {response.json()}")
except requests.exceptions.RequestException as e:
print(f"Erro ao enviar dados ao processador: {e}")
if __name__ == "__main__":
url_to_scrape = "https://example.com/some_data" # Substitua por uma URL real
raw_content = scrape_data(url_to_scrape)
if raw_content:
send_to_processor(raw_content)
# processor_agent.py (simplificado usando FastAPI)
from fastapi import FastAPI, Request
from pydantic import BaseModel
import uvicorn
app = FastAPI()
class DataPayload(BaseModel):
raw_data: str
@app.post("/process")
async def process_data(payload: DataPayload):
# Em um cenário real, você faria o processamento aqui
print(f"Dados brutos recebidos para processamento: {payload.raw_data[:50]}...")
processed_result = f"Processado: {payload.raw_data.upper()}" # Exemplo de processamento
return {"status": "success", "processed_data": processed_result}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8001)
Isso é básico, eu sei. Mas também é incrivelmente poderoso. Você tem dois agentes, rodando como serviços separados, se comunicando entre si. Nenhum SDK especial, nenhum formato de mensagem personalizado. Apenas práticas web padrão. A beleza aqui é que você pode escalar esses serviços de forma independente, implantá-los em qualquer lugar e depurá-los com ferramentas HTTP padrão. Essa abordagem cobre uma quantidade surpreendente de necessidades de comunicação entre agentes sem qualquer dependência de framework.
Gerenciamento de Estado: Abraçando a Persistência
A segunda grande questão é o estado. Um agente não é muito útil se ele esquece tudo entre as execuções. Muitos frameworks oferecem estado em memória ou máquinas de estado complexas. Mas muitas vezes, o que você realmente precisa é de uma persistência simples e confiável.
Para meu agente financeiro, eu precisava armazenar coisas como:
- Minhas assinaturas atuais (nome, custo, data de renovação)
- Minhas categorias de gastos
- Tentativas de negociação históricas
Inicialmente, tentei usar alguns mecanismos de estado em memória fornecidos por um framework, mas assim que o agente reiniciava (o que acontece durante o desenvolvimento, acredite em mim), todos aqueles dados preciosos se perdiam. Frustrante!
A solução? Um banco de dados simples. Para muitos projetos pessoais ou mesmo sistemas de produção menores, o SQLite é uma escolha fantástica. Ele é baseado em arquivo, não requer servidor separado e o Python tem um ótimo suporte integrado.
# agent_state.py
import sqlite3
import json
class AgentState:
def __init__(self, db_path='agent_data.db'):
self.conn = sqlite3.connect(db_path)
self._create_table()
def _create_table(self):
cursor = self.conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS agent_knowledge (
key TEXT PRIMARY KEY,
value TEXT
)
''')
self.conn.commit()
def set(self, key, data):
cursor = self.conn.cursor()
value = json.dumps(data) # Armazenar objetos complexos como strings JSON
cursor.execute('INSERT OR REPLACE INTO agent_knowledge (key, value) VALUES (?, ?)', (key, value))
self.conn.commit()
def get(self, key):
cursor = self.conn.cursor()
cursor.execute('SELECT value FROM agent_knowledge WHERE key = ?', (key,))
row = cursor.fetchone()
if row:
return json.loads(row[0])
return None
def close(self):
self.conn.close()
# Exemplo de uso:
if __name__ == "__main__":
state = AgentState()
# Armazenar uma assinatura
state.set('subscription:netflix', {'name': 'Netflix', 'cost': 15.99, 'renewal': '2026-04-01'})
state.set('subscription:spotify', {'name': 'Spotify', 'cost': 10.99, 'renewal': '2026-03-25'})
# Obter uma assinatura
netflix_sub = state.get('subscription:netflix')
print(f"Assinatura da Netflix: {netflix_sub}")
# Atualizar uma assinatura
if netflix_sub:
netflix_sub['cost'] = 16.99 # Aumento de preço!
state.set('subscription:netflix', netflix_sub)
print(f"Assinatura da Netflix atualizada: {state.get('subscription:netflix')}")
# Armazenar uma lista de categorias de gastos
state.set('spending_categories', ['supermercados', 'entretenimento', 'utilidades'])
print(f"Categorias de gastos: {state.get('spending_categories')}")
state.close()
Essa classe `AgentState` é super básica, mas fornece um armazenamento chave-valor que persiste entre reinicializações do agente. Você pode armazenar dicionários, listas, strings – qualquer coisa que possa ser serializada em JSON. Para relacionamentos mais complexos, você definiria mais tabelas, mas para muitos agentes, um armazenamento chave-valor simples é tudo que você precisa para a “memória” deles.
Juntando Tudo: O Agente “Esqueleto”
Então, se combinarmos essas ideias, como seria um agente “esqueleto”? É um processo que:
- Consegue receber comandos (por exemplo, via ponto de extremidade HTTP ou uma simples fila de mensagens).
- Consegue realizar ações (por exemplo, fazer requisições HTTP, executar scripts locais).
- Consegue lembrar das coisas (por exemplo, usando um armazenamento de estado persistente como SQLite).
- Tem um loop principal ou agendador para decidir o que fazer a seguir.
Vamos imaginar nosso Agente Raspador de antes, mas agora com uma memória. Ele precisa se lembrar de quais URLs já raspou e quando, para evitar trabalho redundante ou retry de tentativas falhadas.
# smart_scraper_agent.py
import requests
import json
import time
from datetime import datetime, timedelta
from agent_state import AgentState # Presumindo que agent_state.py esteja no mesmo diretório
# Usaremos o FastAPI para receber comandos para iniciar a coleta
from fastapi import FastAPI, BackgroundTasks
from pydantic import BaseModel
import uvicorn
app = FastAPI()
agent_state = AgentState(db_path='scraper_agent_data.db')
class ScrapeRequest(BaseModel):
url: str
target_processor_url: str
def _perform_scrape_and_send(url: str, target_processor_url: str):
"""Função interna para realizar a coleta e envio de dados."""
last_scraped_info = agent_state.get(f'last_scraped:{url}')
if last_scraped_info:
last_scrape_time = datetime.fromisoformat(last_scraped_info['timestamp'])
# Coletar apenas se já passou mais de uma hora (exemplo)
if datetime.now() - last_scrape_time < timedelta(hours=1):
print(f"Pulando {url}, coletado recentemente em {last_scrape_time}")
return
print(f"Coletando {url}...")
try:
response = requests.get(url, timeout=10)
response.raise_for_status()
raw_content = response.text
# Enviar para o processador
headers = {'Content-Type': 'application/json'}
payload = {'raw_data': raw_content}
processor_response = requests.post(target_processor_url, headers=headers, data=json.dumps(payload))
processor_response.raise_for_status()
print(f"Dados de {url} enviados para o processador: {processor_response.json()}")
# Atualizar o estado com a coleta bem-sucedida
agent_state.set(f'last_scraped:{url}', {
'timestamp': datetime.now().isoformat(),
'status': 'success',
'processor_response': processor_response.json()
})
except requests.exceptions.RequestException as e:
print(f"Erro durante a coleta ou envio para {url}: {e}")
# Atualizar o estado com falha
agent_state.set(f'last_scraped:{url}', {
'timestamp': datetime.now().isoformat(),
'status': 'failed',
'error': str(e)
})
finally:
agent_state.close() # Importante fechar a conexão se não for gerenciada globalmente
@app.post("/scrape")
async def start_scrape(request: ScrapeRequest, background_tasks: BackgroundTasks):
"""Endpoint para acionar uma ação de coleta."""
background_tasks.add_task(_perform_scrape_and_send, request.url, request.target_processor_url)
return {"message": f"Coleta de {request.url} iniciada."}
if __name__ == "__main__":
# Você também poderia ter uma tarefa agendada aqui que consulta o estado para URLs a serem coletadas
# Por enquanto, vamos apenas executar o servidor FastAPI.
# Para executar: uvicorn smart_scraper_agent:app --reload --port 8000
uvicorn.run(app, host="0.0.0.0", port=8000)
Este `smart_scraper_agent.py` combina nossa comunicação HTTP básica com um estado persistente. Ele impede a coleta redundante dentro de uma janela de tempo definida e armazena o resultado de cada coleta. Ainda é simples, mas já começa a mostrar algum comportamento "agencial" – lembrando, decidindo e agindo com base em seu estado interno e estímulos externos.
Quando Considerar uma Estrutura (e Por Quê)
Agora, não estou dizendo que estruturas são ruins. Longe disso. Elas têm absolutamente seu lugar. Você deve começar a considerá-las quando:
- Coordenação Complexa: Você tem dezenas ou centenas de agentes que precisam coordenar tarefas complexas, formar equipes ou descobrir uns aos outros dinamicamente. Aqui, um sólido sistema de mensagens, descoberta de serviços e, potencialmente, uma camada de orquestração de agentes tornam-se inestimáveis.
- Comportamentos Padronizados: Seus agentes precisam implementar comportamentos comuns como planejamento, definição de metas ou compreensão avançada da linguagem natural. Estruturas frequentemente fornecem abstrações ou integrações para tais comportamentos.
- Necessidades de Escalabilidade: Você está lidando com um número muito alto de mensagens ou um grande número de agentes concorrentes, e precisa de protocolos de comunicação altamente otimizados ou gerenciamento de estado distribuído desde o início.
- Comunidade & Ecossistema: Você deseja usar uma grande comunidade, plugins existentes e padrões testados em batalha para arquiteturas de agentes específicas (por exemplo, agentes compatíveis com FIPA).
Mesmo assim, os princípios de comunicação clara e gerenciamento sólido de estado que discutimos hoje permanecem fundamentais. Uma boa estrutura se baseia nesses princípios, não substitui a necessidade de compreendê-los.
Conclusões Práticas
Minha esperança para você hoje é que você saia com uma nova sensação de capacitação e um olhar crítico para o próximo anúncio "revolucionário" de SDK de agente. Aqui está o que quero que você lembre:
- Comece Simples: Antes de mergulhar em uma estrutura complexa, delineie as necessidades absolutas mínimas de comunicação e estado para seu agente. Você consegue resolver 80% disso com HTTP/JSON e um banco de dados simples como SQLite? Provavelmente.
- Entenda os Primitivos: Mesmo que você eventualmente use uma estrutura, passe algum tempo entendendo como a passagem de mensagens e a persistência de estado funcionam em um nível fundamental. Esse conhecimento fará de você um melhor depurador e arquiteto.
- Itere, Não Reescreva: Construa primeiro a funcionalidade do seu agente. Faça-o funcionar. Se você atingir um verdadeiro muro de escalabilidade ou complexidade que uma estrutura soluciona de forma demonstrável, então considere adotar uma. Evite o impulso de "reescrever tudo".
- Foque na Lógica do Agente: O verdadeiro valor do seu agente está na sua tomada de decisão, seu raciocínio e as tarefas únicas que ele realiza. Não deixe preocupações com a infraestrutura ofuscarem o desenvolvimento dessa lógica central.
- Escolha Ferramentas com Sabedoria: Só porque uma estrutura existe não significa que é a ferramenta certa para o seu trabalho específico. Avalie seu overhead em relação aos benefícios.
Na próxima vez que você iniciar um projeto de agente, tente construir a comunicação central e o gerenciamento de estado você mesmo, mesmo que seja apenas para um prova de conceito. Você aprenderá muito, construirá um agente mais resiliente e provavelmente evitará muitas dores de cabeça no futuro. Continue construindo, continue experimentando e não tenha medo de sujar as mãos com os fundamentos. Leo fora.
🕒 Published: