D’accordo, amici, Leo Grant qui, di novo com uma exploração aprofundada do mundo selvagem do desenvolvimento de agentes. Hoje quero falar sobre algo que me preocupa, algo que vi surgir em fórum após fórum, e honestamente, algo com o qual tive dificuldades eu mesmo há apenas alguns meses: A armadilha do “Novo Frame de Agente Brilhante”. Todos nós já estivemos lá, certo?
É 2026, e parece que toda semana aparece um novo SDK ou framework para agentes. 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 pessoa que vive e respira este campo, minha reação inicial é sempre uma mistura de empolgação e FOMO. “É isso? É a ferramenta que finalmente torna meu projeto dos sonhos uma realidade sem semanas de código boilerplate?”
Lembro que em outubro estava trabalhando em um agente de assistente financeiro pessoal. A ideia era simples: um agente capaz de monitorar minhas despesas, identificar serviços de assinatura que eu possa não estar utilizando 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 vi o anúncio para “Aether”, um novo framework para agentes baseado em Rust que ostentava uma concorrência incrível e uma linguagem de definição de agentes declarativa. Meu cérebro reptiliano imediatamente gritou: “Reescreva! Rust é o futuro! Minha bagunça Python é uma vergonha!” Assim, passei boas duas semanas migrando todo meu projeto para o Aether. E adivinha? Mesmo que o Aether seja realmente ágil e elegante, terminei com um projeto que era funcionalmente idêntico à minha versão Python, mas agora tinha que aprender um novo ecossistema inteiro de ferramentas de construção, gerenciamento de dependências e gerenciamento de erros específicos do Aether.
Foi uma lição valiosa, e quero compartilhá-la com vocês hoje. O maior obstáculo no desenvolvimento de agentes não é sempre encontrar o framework “melhor”; é entender quando utilizar um framework e, ainda mais importante, quando é melhor se ater ao que se conhece e construir a partir dos princípios básicos. Hoje falaremos sobre adotar a mentalidade do “build”, especificamente em relação à comunicação e ao gerenciamento do estado básico de seus agentes, em vez de apropriar-se cegamente do último SDK.
O Problema Principal: Sobrecarga da Comunicação
A maioria dos frameworks para agentes, no fundo, busca resolver dois problemas principais:
- Comunicação Inter-Agente: Como os agentes se comunicam entre si? Fila de mensagens, RPC, estado compartilhado?
- Gerenciamento do Estado do Agente: Como um agente rastreia o que sabe, o que fez e o que deve fazer a seguir?
E definitivamente esse é um problema crítico. Mas muitas vezes, os frameworks oferecem uma solução tão abrangente e opinativa que se torna exagerada para projetos mais simples ou introduz uma complexidade desnecessária. Meu agente financeiro, por exemplo, só precisava se comunicar com um outro “agente” (uma API bancária fictícia) e seu estado interno. Um bus de mensagens completo com um roteamento complexo era como usar um lança-foguetes para esmagar uma mosca.
Então, e se simplificássemos? E se refletíssemos sobre o mínimo necessário para fazer os agentes se comunicarem e lembrarem das coisas? Não se trata de renunciar para sempre aos frameworks, mas de construir uma base sólida primeiro, entender os mecanismos subjacentes e depois decidir se um framework realmente agrega valor em vez de simplesmente trazer cargas adicionais.
Messaging Simples: A Base HTTP/JSON
Sejamos 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 broker de mensagens complexo se seus agentes estão principalmente enviando solicitações e recebendo respostas.
Consideremos um cenário em que você tem um “Agente Scraper” que recupera dados de um site e um “Agente Processador” que os limpa e analisa. Como 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() # Gera 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 utilizando 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ê realizaria um tratamento real aqui
print(f"Dados brutos recebidos para tratamento: {payload.raw_data[:50]}...")
processed_result = f"Tratado: {payload.raw_data.upper()}" # Exemplo de tratamento
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. Nenhum SDK especial, nenhum formato de mensagem personalizado. Apenas práticas web padrão. A beleza aqui é que você pode escalar independentemente, distribuí-los em qualquer lugar e depurar com ferramentas HTTP padrão. Essa abordagem cobre uma quantidade surpreendente de necessidades de comunicação agente a agente sem depender de um framework.
Gestão do Estado: Abraçando a Persistência
O segundo grande ponto é o estado. Um agente não é realmente um agente se esquecer tudo entre as execuções. Muitos frameworks oferecem estado na 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 o meu agente financeiro, eu precisava armazenar itens como:
- Meus assinaturas atuais (nome, custo, data de renovação)
- Minhas categorias de despesas
- Os históricos das tentativas de negociação
No início, eu tentei usar alguns mecanismos de estado em memória oferecidos por um framework, mas assim que o agente foi reiniciado (o que acontece durante o desenvolvimento, acredite em mim), todos aqueles dados valiosos desapareceram. Frustrante!
A solução? Um banco de dados simples. Para muitos projetos pessoais ou mesmo sistemas de produção menores, SQLite é uma ótima escolha. Baseia-se em um arquivo, não requer um servidor separado e Python tem um excelente suporte integrado.
“`html
# 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 gastos
state.set('spending_categories', ['grocery', 'entertainment', 'utilities'])
print(f"Categorias de gastos: {state.get('spending_categories')}")
state.close()
Essa classe `AgentState` é muito básica, mas fornece um repositório de pares chave-valor que persiste entre as reinicializações do agente. Você pode armazenar dicionários, listas, strings – tudo o que pode ser serializado em JSON. Para relações mais complexas, você deve definir tabelas adicionais, mas para muitos agentes, um simples repositório chave-valor é tudo que você precisa para a “memória” deles.
Colocando tudo junto: O Agente “Básico”
Então, se combinarmos essas ideias, como aparece um agente “básico”? É um processo que :
- Pode receber comandos (por exemplo, através de um endpoint HTTP ou uma simples fila de mensagens).
- Pode executar ações (por exemplo, fazer solicitações HTTP, executar scripts locais).
- Pode lembrar as coisas (por exemplo, utilizando um repositório de estado persistente como SQLite).
- Tem um loop principal ou um agendador para decidir o que fazer a seguir.
Imaginemos nosso Agente Scraper de antes, mas agora com um pouco de memória. Ele deve lembrar os URLs que já raspou e quando, para evitar fazer trabalho redundante ou tentar novamente tentativas falhas.
“`
# smart_scraper_agent.py
import requests
import json
import time
from datetime import datetime, timedelta
from agent_state import AgentState # Supponhamos que agent_state.py esteja no mesmo diretório
# Usaremos FastAPI para receber comandos para iniciar o scraping
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 executar o scraping 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'])
# Raspe apenas se mais de uma hora tiver passado (exemplo)
if datetime.now() - last_scrape_time < timedelta(hours=1):
print(f"Pulo de {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 ao 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 o estado com um scraping bem-sucedido
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 o scraping ou o envio para {url}: {e}")
# Atualizar o 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 for gerenciada globalmente
@app.post("/scrape")
async def start_scrape(request: ScrapeRequest, background_tasks: BackgroundTasks):
"""Endpoint para ativar uma ação de scraping."""
background_tasks.add_task(_perform_scrape_and_send, request.url, request.target_processor_url)
return {"message": f"Scraping de {request.url} iniciado."}
if __name__ == "__main__":
# Você também pode ter uma tarefa agendada aqui para consultar o estado para URLs a serem raspadas
# Por enquanto, vamos simplesmente 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 evita scraping redundante em uma janela de tempo definida e armazena o resultado de cada scraping. É ainda simples, mas começa a mostrar um comportamento "agente" – lembrar, decidir e agir com base em seu estado interno e estímulos externos.
Quando considerar um framework (e por quê)
Agora, não estou dizendo que frameworks são ruins. Muito pelo contrário. 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 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 planejamento, definição de objetivos ou compreensão avançada de linguagem natural. Frameworks frequentemente fornecem abstrações ou integrações para isso.
- Necessidades de escalabilidade: Você lida com um fluxo de mensagens muito alto ou um grande número de agentes concorrentes, e precisa de protocolos de comunicação altamente otimizados ou de uma gestão de estado distribuído pronta para uso.
- Comunidade e ecossistema: Você quer aproveitar uma vasta comunidade, plugins existentes e modelos testados para arquiteturas de agentes específicas (por exemplo, agentes que obedecem ao padrão FIPA).
Mesmo assim, os princípios de uma comunicação clara e de uma gestão de estado sólida que discutimos hoje permanecem fundamentais. Um bom framework se baseia nesses princípios, não substitui a necessidade de compreendê-los.
Pontos a lembrar
Minha esperança para você hoje é que você possa voltar com um novo senso de autonomia e um olhar crítico sobre o próximo anúncio do SDK de agente "revolucionário". Aqui está o que eu quero que você lembre-se:
- Comece simples: Antes de mergulhar em um framework complexo, defina as necessidades absolutas de comunicação e estado para o seu agente. Você pode resolver 80% disso com HTTP/JSON e um banco de dados simples como SQLite? Provavelmente.
- Compreenda as primitivas: Embora você acabe utilizando um framework, reserve um tempo para entender como funciona a passagem de mensagens e a persistência de estado em um nível fundamental. Esse conhecimento te tornará um melhor depurador e arquiteto.
- Itere, não reescreva: Construa primeiro a funcionalidade do seu agente. Faça com que funcione. Se você encontrar uma verdadeira barreira de escalabilidade ou complexidade que um framework resolva de maneira 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 capacidade de tomar decisões, raciocinar e realizar tarefas únicas. Não deixe que preocupações com infraestrutura ofusquem o desenvolvimento dessa lógica básica.
- Escolha as ferramentas com cuidado: Não é porque um framework existe que ele é a ferramenta certa para sua tarefa específica. Avalie seus custos em relação a seus benefícios.
Na próxima vez que você iniciar um projeto de agente, tente desenvolver você mesmo a comunicação básica e o gerenciamento de estado, mesmo que 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: