Beleza, amigos, Leo Grant aqui, de volta com outra exploração profunda do mundo selvagem do desenvolvimento de agentes. Hoje, quero falar sobre algo que me atormenta, algo que vi aparecer em fórum após fórum, e honestamente, algo com que eu mesmo tive dificuldades há apenas alguns meses: A armadilha do “Novo Quadro de Agente Brilhante”. Todos nós já estivemos lá, não é?
É 2026, e a impressão é que um novo SDK ou quadro de agente aparece a cada semana. Cada um deles promete ser mais rápido, mais inteligente, mais escalável ou simplesmente mais fácil para criar agentes autônomos. E como alguém que vive e respira esse campo, minha reação inicial é sempre uma mistura de empolgação e FOMO. “É este? É a ferramenta que finalmente torna meu projeto dos sonhos uma realidade sem semanas de código boilerplate?”
Eu me lembro que em outubro, eu estava trabalhando em um agente de assistente financeiro pessoal. A ideia era simples: um agente capaz de monitorar meus gastos, identificar serviços de assinatura que eu poderia não estar usando, e até negociar melhores tarifas em meu nome. Eu tinha um script Python básico rodando, usando um bus de mensagens personalizado e muitos `if/else`. Era desajeitado, isso é certo, mas funcionava.
Então eu vi o anúncio para “Aether”, um novo quadro de agente baseado em Rust que alardeava uma concorrência incrível e uma linguagem de definição de agente declarativa. Meu cérebro reptiliano imediatamente gritou: “Reescreva! Rust é o futuro! Meu código Python é uma vergonha!” Então, passei duas boas semanas migrando todo o meu projeto para o Aether. E adivinha? Embora o Aether seja de fato eficiente e elegante, eu terminei 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 construção, gerenciamento de dependências e gerenciamento de erros específicos do Aether.
Foi uma lição valiosa, e que quero compartilhar com vocês hoje. O maior obstáculo no desenvolvimento de agentes nem sempre é encontrar o “melhor” quadro; é entender quando usar um quadro, e mais importante, quando é melhor se manter com o que você sabe e construir a partir de princípios básicos. Hoje, vamos falar sobre adotar a mentalidade do “construir”, especificamente em relação à comunicação e gerenciamento de estado básico de seus agentes, em vez de se apropriar cegamente do último SDK.
O Problema Principal: Sobreengenharia da Comunicação
Na essência, a maioria dos quadros de agentes tenta resolver dois problemas principais:
- Comunicação Inter-Agente: Como os agentes se comunicam entre si? Filas de mensagens, RPC, estado compartilhado?
- Gerenciamento de Estado do Agente: Como um agente mantém controle do que sabe, do que fez e do que deve fazer em seguida?
E esses são problemas críticos, sem dúvida. Mas muitas vezes, os quadros oferecem uma solução tão completa e opinativa que se torna exagerada para projetos mais simples ou apresenta uma complexidade desnecessária. Meu agente financeiro, por exemplo, precisava apenas se comunicar com um outro “agente” (uma API bancária fictícia) e seu próprio estado interno. Um bus de mensagens completo com um roteamento complexo seria como usar um lançador de foguetes para esmagar uma mosca.
Então, e se simplificássemos? E se pensássemos no estritamente necessário que você precisa para fazer os agentes se comunicarem e lembrarem das coisas? Não se trata de renunciar aos quadros para sempre, mas de construir uma base sólida primeiro, entender os mecanismos subjacentes e então decidir se um quadro realmente agrega valor em vez de simplesmente introduzir encargos adicionais.
Messaging Simples: O Baseline HTTP/JSON
Sendo brutalmente honestos: para um grande número 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 principalmente enviam requisições e recebem respostas.
Consideremos um cenário onde você tem um “Agente de Scraping” que coleta dados de um site e um “Agente Processador” que os limpa e analisa. 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() # Provoca 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" # Substituir 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 um processamento real 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)
É básico, eu sei. Mas também é incrivelmente poderoso. Você tem dois agentes, funcionando como serviços separados, que se comunicam entre si. Sem SDK especial, sem formato de mensagem personalizado. Apenas práticas web padrão. A beleza aqui é que você pode escalá-los independentemente, implantá-los em qualquer lugar e depurar com ferramentas HTTP padrão. Essa abordagem abrange uma quantidade surpreendente das necessidades de comunicação entre agentes sem nenhuma dependência de um quadro.
Gerenciamento de Estado: Abraçando a Persistência
O segundo grande ponto é o estado. Um agente não é realmente um agente se esquecer de tudo entre as execuções. Muitos quadros oferecem um estado em memória ou máquinas de estados complexas. Mas muitas vezes, o que você realmente precisa é de uma persistência simples e confiável.
Para meu agente financeiro, eu precisava armazenar itens como:
- Minhas assinaturas atuais (nome, custo, data de renovação)
- Minhas categorias de despesas
- As tentativas de negociação históricas
No começo, tentei usar alguns mecanismos de estado em memória oferecidos por um quadro, mas assim que o agente reiniciou (o que acontece durante o desenvolvimento, acredite em mim), todos esses dados preciosos desapareceram. Frustrante!
A solução? Um banco de dados simples. Para muitos projetos pessoais ou até mesmo sistemas de produção menores, SQLite é uma excelente escolha. É baseado em arquivo, não requer um servidor separado e o Python tem um excelente 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'})
# Recuperar uma assinatura
netflix_sub = state.get('subscription:netflix')
print(f"Assinatura 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 Netflix atualizada: {state.get('subscription:netflix')}")
# Armazenar uma lista de categorias de despesas
state.set('spending_categories', ['compras', 'entretenimento', 'serviços públicos'])
print(f"Categorias de despesas: {state.get('spending_categories')}")
state.close()
Essa classe `AgentState` é bem básica, mas fornece uma loja de pares chave-valor que persiste entre as reinicializações do agente. Você pode armazenar dicionários, listas, strings – tudo que pode ser serializado em JSON. Para relações mais complexas, você definiria mais tabelas, mas para muitos agentes, uma simples loja de chave-valor é tudo que você precisa para sua “memória”.
Amarrando Tudo: O Agente “Básico”
Então, se combinarmos essas ideias, como é um agente “básico”? É um processo que:
- Pode receber comandos (por exemplo, através de um ponto de extremidade HTTP ou uma simples fila de mensagens).
- Pode realizar ações (por exemplo, fazer requisições HTTP, executar scripts locais).
- Pode se lembrar de coisas (por exemplo, usando um armazenamento de estado persistente como SQLite).
- Tem um loop principal ou um agendador para decidir o que fazer a seguir.
Vamos imaginar nosso Agente Raspador de antes, mas agora com um pouco de memória. Ele deve se lembrar das URLs que já raspou e quando, para evitar fazer trabalho redundante ou tentar novamente tentativas que falharam.
# smart_scraper_agent.py
import requests
import json
import time
from datetime import datetime, timedelta
from agent_state import AgentState # Supondo que agent_state.py está no mesmo diretório
# Vamos usar o FastAPI para receber comandos para iniciar a raspagem
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 raspagem e o envio."""
last_scraped_info = agent_state.get(f'last_scraped:{url}')
if last_scraped_info:
last_scrape_time = datetime.fromisoformat(last_scraped_info['timestamp'])
# Raspagem só se mais de uma hora tiver se passado (exemplo)
if datetime.now() - last_scrape_time < timedelta(hours=1):
print(f"Pulei {url}, raspado recentemente em {last_scrape_time}")
return
print(f"Raspando {url}...")
try:
response = requests.get(url, timeout=10)
response.raise_for_status()
raw_content = response.text
# Envio 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 ao processador: {processor_response.json()}")
# Atualizar estado com uma raspagem 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 ao raspar ou enviar para {url}: {e}")
# Atualizar estado com uma 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 está sendo gerida globalmente
@app.post("/scrape")
async def start_scrape(request: ScrapeRequest, background_tasks: BackgroundTasks):
"""Ponto de extremidade para iniciar uma ação de raspagem."""
background_tasks.add_task(_perform_scrape_and_send, request.url, request.target_processor_url)
return {"message": f"Raspagem de {request.url} iniciada."}
if __name__ == "__main__":
# Você também pode ter uma tarefa agendada aqui que pergunta ao estado por URLs a serem raspadas
# 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)
Esse `smart_scraper_agent.py` combina nossa comunicação HTTP básica com um estado persistente. Ele evita a raspagem redundante em uma janela de tempo definida e armazena o resultado de cada raspagem. É ainda simples, mas começa a mostrar um comportamento "agente" – se lembrar, decidir e agir com base em seu estado interno e em estímulos externos.
Quando considerar um framework (e por quê)
Agora, não estou dizendo que frameworks são ruins. Longe disso. Eles têm absolutamente seu lugar. Você deve começar a considerá-los quando:
- Coordenação complexa: Você tem dezenas ou centenas de agentes que precisam coordenar tarefas complexas, formar equipes ou se descobrir dinamicamente. Aqui, um sólido barramento de mensagens, descoberta de serviços, e potencialmente uma camada de orquestração de agentes se tornam inestimáveis.
- Comportamentos padronizados: Seus agentes precisam implementar comportamentos comuns como agendamento, definição de metas ou compreensão avançada da linguagem natural. Frameworks frequentemente fornecem abstrações ou integrações para isso.
- Necessidades de escalabilidade: Você está lidando com um volume muito alto de mensagens ou um grande número de agentes concorrentes, e precisa de protocolos de comunicação altamente otimizados ou de um gerenciamento de estado distribuído pronto para uso.
- Comunidade e ecossistema: Você deseja usar uma ampla comunidade, plugins existentes e padrões comprovados para arquiteturas de agentes específicas (por exemplo, agentes em conformidade com o padrão FIPA).
Mesmo assim, os princípios de uma comunicação clara e de um gerenciamento de estado sólido dos quais falamos hoje continuam a ser fundamentais. Um bom framework se baseia nesses princípios; ele não substitui a necessidade de compreendê-los.
Pontos a serem lembrados
Meu desejo para você hoje é que você saia com um novo sentimento de empoderamento e uma visão crítica sobre o próximo anúncio de SDK de agente "revolucionário". Aqui está o que eu quero que você retenha:
- Comece simples: Antes de se aprofundar em um framework complexo, defina as necessidades de comunicação e estado absolutas para seu agente. Você pode resolver 80% disso com HTTP/JSON e um banco de dados simples como SQLite? Provavelmente.
- Compreenda as primitivas: Mesmo que você acabe usando um framework, passe algum tempo entendendo como o passamento 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 de seu agente. Faça funcionar. Se você encontrar uma verdadeira barreira de escalabilidade ou complexidade que um framework resolva de forma evidente, então considere adotá-lo. Evite o impulso de "reescrever tudo".
- Concentre-se na lógica do agente: O verdadeiro valor do seu agente reside em sua tomada de decisão, raciocínio e nas tarefas únicas que ele realiza. Não deixe que as preocupações de infraestrutura ofusquem o desenvolvimento dessa lógica básica.
- Escolha as ferramentas com cuidado: Não é porque um framework existe que é a melhor ferramenta para sua tarefa específica. Avalie seus custos em relação aos seus benefícios.
Da próxima vez que você começar um projeto de agente, tente desenvolver você mesmo a comunicação básica e o gerenciamento de estado, mesmo que seja apenas para um proof of concept. Você aprenderá muito, construirá um agente mais resiliente e provavelmente evitará muitas dores de cabeça depois. Continue construindo, continue experimentando e não tenha medo de sujar as mãos com os fundamentos. Leo out.
🕒 Published: