LangGraph, CrewAI e Agno: primeiros passos com agentes de IA em Python
Índice
Todo mundo fala de agentes de IA. Poucos explicam o que isso significa na prática — com código, sem buzzword.
A proposta aqui é simples: pegar três frameworks populares de agentes em Python — LangGraph, CrewAI e Agno — e resolver o mesmo problema com cada um. Nada de hello world. Cada exemplo usa ferramentas reais que o agente decide quando e como chamar.
💻 Código-fonte completo: todos os exemplos deste artigo estão no repositório posts.codebase — com instruções de setup para rodar localmente.
Se você já sabe chamar uma API de LLM e quer dar o próximo passo, este post é pra você.
O que é um agente de IA (versão sem hype)
Um agente de IA é um programa que usa um modelo de linguagem (LLM) dentro de um loop. Em vez de receber uma pergunta e devolver uma resposta única, ele repete um ciclo até resolver o problema. Na literatura, esse padrão se chama ReAct (Reason + Act):
Pergunta
→ [LLM pensa] → [Chama ferramenta] → [Observa resultado]
→ [LLM pensa] → [Chama ferramenta] → [Observa resultado]
→ ...
→ Resposta final
Em termos concretos:
- Pensa — analisa o que precisa fazer
- Age — chama uma ferramenta (busca, cálculo, API, banco de dados)
- Observa — lê o resultado da ferramenta
- Repete — até decidir que terminou
A diferença para uma chamada simples de API? O agente decide o que fazer. O mecanismo central que permite isso se chama tool calling: o LLM recebe a lista de ferramentas disponíveis e escolhe qual chamar, com quais argumentos. O LLM é o cérebro; as ferramentas são as mãos.
Os frameworks que vamos ver facilitam esse loop. Cada um com uma filosofia diferente.
Quando NÃO usar um agente
Nem todo problema precisa de um agente. É importante saber disso antes de sair construindo.
Se você já sabe exatamente o que precisa fazer — extrair campos de um texto, classificar um e-mail, gerar um resumo — um pipeline simples é mais barato, mais rápido e mais previsível. Uma chamada direta à API resolve.
Agentes fazem sentido quando:
- O caminho até a resposta não é fixo — o modelo precisa decidir os próximos passos
- Há múltiplas ferramentas possíveis — e a escolha depende do contexto
- Você quer delegar decisões ao modelo em vez de codificar cada
if/else
E os custos são reais:
- Latência: cada iteração do loop é uma chamada ao LLM. Três ferramentas = no mínimo três round-trips
- Tokens: o contexto cresce a cada passo. Mais passos, mais custo
- Imprevisibilidade: o agente pode entrar em loop, chamar ferramentas erradas ou interpretar mal um resultado
Se o caminho é fixo, use um pipeline. Se o caminho é dinâmico, aí sim — agente faz sentido.
O problema que vamos resolver
Para comparar os três frameworks de forma justa, vamos resolver o mesmo problema em todos:
“Quero comprar um notebook. Quanto custa em reais com 10% de desconto?”
O agente precisa:
- Buscar o preço do produto (em USD)
- Converter de dólar para real
- Calcular o desconto sobre o valor em reais
São três ferramentas, três passos dependentes. O resultado de cada passo alimenta o próximo. É o tipo de problema onde um agente brilha — porque a sequência de chamadas não é óbvia sem contexto.
As três ferramentas (iguais nos três frameworks):
def buscar_preco(produto: str) -> str:
"""Busca o preço de um produto em USD. Produtos disponíveis: notebook, monitor, teclado."""
catalogo = {"notebook": 1200.00, "monitor": 450.00, "teclado": 85.00}
preco = catalogo.get(produto.lower())
if preco:
return f"{produto}: US$ {preco:.2f}"
return f"Produto '{produto}' não encontrado."
def converter_moeda(valor: float, de: str, para: str) -> str:
"""Converte um valor entre moedas."""
taxas = {"USD_BRL": 5.20, "BRL_USD": 0.19}
chave = f"{de}_{para}".upper()
taxa = taxas.get(chave)
if taxa:
return f"{valor:.2f} {de} = {valor * taxa:.2f} {para}"
return f"Taxa {de} → {para} não disponível."
def calcular_desconto(valor: float, percentual: float) -> str:
"""Aplica desconto percentual sobre um valor."""
final = valor * (1 - percentual / 100)
return f"Original: {valor:.2f} → Com {percentual}% de desconto: {final:.2f}"
Nos exemplos que seguem, a lógica das ferramentas é a mesma. O que muda é como cada framework orquestra o agente.
1) LangGraph
LangGraph é um framework de orquestração do ecossistema LangChain. A ideia central: você modela o fluxo do agente como um grafo — nós que processam, arestas que conectam, estado que persiste entre passos.
É o mais baixo nível dos três. Você monta cada peça do loop manualmente.
Instalação
pip install langgraph langchain-openai langchain
Exemplo
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langchain_core.messages import HumanMessage, SystemMessage, ToolMessage
from langgraph.graph import StateGraph, MessagesState, START, END
@tool
def buscar_preco(produto: str) -> str:
"""Busca o preço de um produto em USD. Produtos disponíveis: notebook, monitor, teclado."""
catalogo = {"notebook": 1200.00, "monitor": 450.00, "teclado": 85.00}
preco = catalogo.get(produto.lower())
if preco:
return f"{produto}: US$ {preco:.2f}"
return f"Produto '{produto}' não encontrado."
@tool
def converter_moeda(valor: float, de: str, para: str) -> str:
"""Converte um valor entre moedas. Taxas disponíveis: USD↔BRL."""
taxas = {"USD_BRL": 5.20, "BRL_USD": 0.19}
chave = f"{de}_{para}".upper()
taxa = taxas.get(chave)
if taxa:
return f"{valor:.2f} {de} = {valor * taxa:.2f} {para}"
return f"Taxa {de} → {para} não disponível."
@tool
def calcular_desconto(valor: float, percentual: float) -> str:
"""Aplica desconto percentual sobre um valor."""
final = valor * (1 - percentual / 100)
return f"Original: {valor:.2f} → Com {percentual}% de desconto: {final:.2f}"
tools = [buscar_preco, converter_moeda, calcular_desconto]
tools_por_nome = {t.name: t for t in tools}
modelo = ChatOpenAI(model="gpt-4o-mini", temperature=0)
modelo_com_tools = modelo.bind_tools(tools)
def chamar_modelo(state: MessagesState):
mensagens = [SystemMessage(content="Você é um assistente de compras. Sempre use as ferramentas disponíveis para buscar preços, converter moedas e calcular descontos.")] + state["messages"]
return {"messages": [modelo_com_tools.invoke(mensagens)]}
def executar_ferramentas(state: MessagesState):
resultados = []
for chamada in state["messages"][-1].tool_calls:
ferramenta = tools_por_nome[chamada["name"]]
resultado = ferramenta.invoke(chamada["args"])
resultados.append(ToolMessage(content=str(resultado), tool_call_id=chamada["id"]))
return {"messages": resultados}
def decidir_proximo_passo(state: MessagesState):
if state["messages"][-1].tool_calls:
return "ferramentas"
return END
grafo = StateGraph(MessagesState)
grafo.add_node("modelo", chamar_modelo)
grafo.add_node("ferramentas", executar_ferramentas)
grafo.add_edge(START, "modelo")
grafo.add_conditional_edges("modelo", decidir_proximo_passo, ["ferramentas", END])
grafo.add_edge("ferramentas", "modelo")
agente = grafo.compile()
resultado = agente.invoke({
"messages": [HumanMessage(content="Quero comprar um notebook. Quanto custa em reais com 10% de desconto?")]
})
print(resultado["messages"][-1].content)
O que o agente faz por dentro
🤔 Pensando: preciso saber o preço do notebook
🔧 Chamando: buscar_preco("notebook")
📎 Resultado: notebook: US$ 1200.00
🤔 Pensando: agora preciso converter para reais
🔧 Chamando: converter_moeda(1200.00, "USD", "BRL")
📎 Resultado: 1200.00 USD = 6240.00 BRL
🤔 Pensando: agora aplico o desconto de 10%
🔧 Chamando: calcular_desconto(6240.00, 10)
📎 Resultado: Original: 6240.00 → Com 10% de desconto: 5616.00
✅ Resposta: O notebook custa R$ 5.616,00 com 10% de desconto.
Cada seta → no grafo é uma chamada ao LLM. Três ferramentas, três iterações do loop. É isso que acontece “por baixo do capô” em qualquer framework de agentes.
Limitações reais
- Verboso: mesmo um agente simples exige montar nós, arestas, funções de roteamento. Comparado aos outros dois, é bastante código
- Curva de aprendizado: pensar em grafos é natural pra quem tem background em engenharia de software, mas pode ser confuso pra iniciantes
- Dependência do ecossistema LangChain: ferramentas usam
@tooldo LangChain, modelos usam wrappers do LangChain. Trocar depois não é trivial
Quando brilha
Controle total. Você decide cada caminho, cada condição, cada estado. Pra workflows complexos com ramificações, human-in-the-loop e persistência de execução, não tem nada mais flexível.
2) CrewAI
CrewAI pensa em agentes como membros de uma equipe. Cada agente tem um papel, um objetivo e uma história de fundo. Você define tarefas, monta uma “crew” e manda executar. O framework cuida da coordenação.
É o mais alto nível dos três. Menos código, mais rápido pra prototipar. E o diferencial aparece de verdade quando há mais de um agente.
Instalação
pip install crewai crewai-tools
Exemplo: dois agentes colaborando
Aqui a força do CrewAI aparece: um pesquisador encontra o preço e converte a moeda, e um analista aplica o desconto e entrega o resumo.
from crewai import Agent, Task, Crew, Process
from crewai.tools import tool
@tool("BuscaPreco")
def buscar_preco(produto: str) -> str:
"""Busca o preço de um produto em USD. Produtos disponíveis: notebook, monitor, teclado."""
catalogo = {"notebook": 1200.00, "monitor": 450.00, "teclado": 85.00}
preco = catalogo.get(produto.lower())
if preco:
return f"{produto}: US$ {preco:.2f}"
return f"Produto '{produto}' não encontrado."
@tool("ConversorMoeda")
def converter_moeda(valor: float, de: str, para: str) -> str:
"""Converte um valor entre moedas. Taxas disponíveis: USD↔BRL. Parâmetros: valor numérico, moeda de origem (ex: USD), moeda de destino (ex: BRL)."""
taxas = {"USD_BRL": 5.20, "BRL_USD": 0.19}
chave = f"{de}_{para}".upper()
taxa = taxas.get(chave)
if taxa:
return f"{valor:.2f} {de} = {valor * taxa:.2f} {para}"
return f"Taxa {de} → {para} não disponível."
@tool("Desconto")
def calcular_desconto(valor: float, percentual: float) -> str:
"""Aplica desconto percentual. Parâmetros: valor numérico, percentual de desconto."""
final = valor * (1 - percentual / 100)
return f"Original: {valor:.2f} → Com {percentual}% de desconto: {final:.2f}"
# Dois agentes com papéis diferentes
pesquisador = Agent(
role="Pesquisador de preços",
goal="Encontrar preços e converter para a moeda solicitada",
backstory="Especialista em pesquisa de mercado internacional.",
tools=[buscar_preco, converter_moeda],
verbose=True,
)
analista = Agent(
role="Analista financeiro",
goal="Calcular valores finais com descontos e apresentar um resumo claro",
backstory="Analista detalhista que sempre mostra os números.",
tools=[calcular_desconto],
verbose=True,
)
# Tarefas encadeadas: a saída da primeira alimenta a segunda
pesquisa = Task(
description="Encontre o preço do notebook em USD e converta para BRL.",
expected_output="O preço do notebook em reais.",
agent=pesquisador,
)
analise = Task(
description="Aplique 10% de desconto no preço em BRL e apresente um resumo com preço original, desconto e valor final.",
expected_output="Resumo com preço original em BRL, valor do desconto e preço final.",
agent=analista,
)
crew = Crew(
agents=[pesquisador, analista],
tasks=[pesquisa, analise],
process=Process.sequential,
verbose=True,
)
resultado = crew.kickoff()
print(resultado)
O que acontece por dentro
👤 Pesquisador entra em cena
🔧 Chamando: BuscaPreco("notebook")
📎 Resultado: notebook: US$ 1200.00
🔧 Chamando: ConversorMoeda(1200.00, "USD", "BRL")
📎 Resultado: 1200.00 USD = 6240.00 BRL
📤 Entrega: "O notebook custa R$ 6.240,00"
👤 Analista recebe o contexto do pesquisador
🔧 Chamando: Desconto(6240.00, 10)
📎 Resultado: Original: 6240.00 → Com 10% de desconto: 5616.00
✅ Entrega: "Notebook: R$ 6.240,00 → com 10% de desconto: R$ 5.616,00"
O ponto-chave: o analista não recebe a pergunta original — ele recebe a saída do pesquisador como contexto. É isso que faz multi-agente funcionar: um gera, o outro consome.
Limitações reais
- Caixa-preta: a coordenação entre agentes é abstraída. Quando algo dá errado, é difícil debugar o que cada agente decidiu e por quê
- Menos controle: você não escolhe a ordem das chamadas de ferramentas nem o fluxo condicional — o framework decide
- Overhead do LLM: cada agente é uma sessão separada. Dois agentes = mais tokens, mais latência, mais custo. Em problemas simples, um agente solo resolve mais rápido
Quando brilha
Equipes de agentes que colaboram. Pesquisador + escritor + revisor. Tarefas paralelas com papéis claros. Protótipos rápidos de workflows multi-agente.
3) Agno
Agno (antigo Phidata) é o mais pragmático dos três. A filosofia: um agente é um modelo + ferramentas + instruções. Sem abstrações desnecessárias. Funções Python viram ferramentas automaticamente — sem decorators especiais.
É o mais direto. Poucas linhas, agente funcional.
Instalação
pip install agno
Exemplo
from agno.agent import Agent
from agno.models.openai import OpenAIChat
def buscar_preco(produto: str) -> str:
"""Busca o preço de um produto em USD. Produtos disponíveis: notebook, monitor, teclado.
Args:
produto: Nome do produto para buscar.
"""
catalogo = {"notebook": 1200.00, "monitor": 450.00, "teclado": 85.00}
preco = catalogo.get(produto.lower())
if preco:
return f"{produto}: US$ {preco:.2f}"
return f"Produto '{produto}' não encontrado."
def converter_moeda(valor: float, de: str, para: str) -> str:
"""Converte um valor entre moedas. Taxas disponíveis: USD↔BRL.
Args:
valor: Valor numérico a converter.
de: Moeda de origem (ex: USD).
para: Moeda de destino (ex: BRL).
"""
taxas = {"USD_BRL": 5.20, "BRL_USD": 0.19}
chave = f"{de}_{para}".upper()
taxa = taxas.get(chave)
if taxa:
return f"{valor:.2f} {de} = {valor * taxa:.2f} {para}"
return f"Taxa {de} → {para} não disponível."
def calcular_desconto(valor: float, percentual: float) -> str:
"""Aplica desconto percentual sobre um valor.
Args:
valor: Valor numérico original.
percentual: Percentual de desconto a aplicar.
"""
final = valor * (1 - percentual / 100)
return f"Original: {valor:.2f} → Com {percentual}% de desconto: {final:.2f}"
agente = Agent(
model=OpenAIChat(id="gpt-4o-mini"),
tools=[buscar_preco, converter_moeda, calcular_desconto],
instructions="Responda em português. Sempre use as ferramentas para buscar preços, converter moedas e calcular descontos.",
markdown=True,
)
agente.print_response(
"Quero comprar um notebook. Quanto custa em reais com 10% de desconto?",
stream=True,
)
O que o agente faz por dentro
O mesmo loop ReAct dos outros dois:
🤔 Pensando: preciso buscar o preço
🔧 Chamando: buscar_preco("notebook")
📎 Resultado: notebook: US$ 1200.00
🤔 Pensando: converter para BRL
🔧 Chamando: converter_moeda(1200.00, "USD", "BRL")
📎 Resultado: 1200.00 USD = 6240.00 BRL
🤔 Pensando: aplicar desconto
🔧 Chamando: calcular_desconto(6240.00, 10)
📎 Resultado: Original: 6240.00 → Com 10% de desconto: 5616.00
✅ Resposta: O notebook custa R$ 5.616,00 com 10% de desconto.
Repare: mesmas ferramentas, mesma lógica, mesmo resultado. A diferença é que levou ~15 linhas pra definir o agente, contra ~30 do LangGraph e ~35 do CrewAI.
Limitações reais
- Workflows complexos: pra fluxos com ramificações condicionais, loops controlados ou human-in-the-loop, o Agno não tem primitivas nativas — você precisaria implementar manualmente
- Menos maduro: ecossistema menor, comunidade menor, menos exemplos em produção comparado ao LangGraph
- Menos visibilidade: o que o agente faz “por dentro” é menos transparente sem configuração extra de debug
Quando brilha
Caminho mais curto do zero ao agente funcional. Qualquer função Python vira ferramenta. Excelente pra protótipos rápidos e pra usar com modelos locais (Ollama, LlamaCpp).
Comparação
| LangGraph | CrewAI | Agno | |
|---|---|---|---|
| Abstração | Baixa (grafo) | Alta (papéis) | Média (pragmático) |
| Curva de aprendizado | Mais íngreme | Suave | Curta |
| Multi-agente | Sim (manual) | Sim (nativo, com handoff) | Sim (nativo) |
| Ferramentas | @tool do LangChain | @tool próprio | Funções Python puras |
| Melhor para | Workflows complexos | Equipes de agentes | Protótipos rápidos |
| Controle fino | Total | Parcial | Parcial |
| Persistência | Built-in | Via config | Via sessions |
| Debug / visibilidade | Bom (LangSmith) | Médio | Básico |
| Risco principal | Complexidade desnecessária | Caixa-preta | Limitação em fluxos complexos |
Qual escolher?
Se você é iniciante e quer entender agentes na prática: Agno. Menos atrito, menos código, feedback imediato.
Se você quer velocidade pra prototipar com múltiplos agentes: CrewAI. Defina papéis e tarefas, o framework cuida do resto.
Se você vai pra produção séria com fluxos complexos: LangGraph. Mais trabalho inicial, mas controle total sobre cada passo.
Os três são ativamente mantidos e bem documentados. O conselho mais honesto: escolha um e construa algo. Troque depois se precisar. A melhor forma de aprender é experimentando.
E na produção?
Se os exemplos deste post são o ponto de partida, produção é outra história. Algumas coisas que importam quando o agente sai do notebook e vai pro mundo real:
- Observabilidade: registre cada chamada de ferramenta, cada decisão do LLM, cada iteração do loop. Sem logs, debugar agentes é adivinhação
- Retries e timeouts: ferramentas falham, APIs caem, modelos demoram. Defina limites. Um agente que entra em loop infinito queima tokens e dinheiro
- Guardrails: restrinja quais ferramentas o agente pode chamar, valide as entradas antes de executar, limite o número máximo de iterações
- Custo: monitore tokens por execução. Três iterações com GPT-4o são mais baratas que dez com o mesmo modelo. O design do agente afeta direto a conta
Nenhum desses frameworks resolve tudo isso automaticamente. Eles dão o esqueleto. O resto é engenharia.
Framework não resolve o problema — só organiza o caos. Um agente ruim em LangGraph continua ruim em CrewAI ou Agno. A diferença está no design, não na ferramenta.
Se quiser ir mais fundo em agentes, recomendo o post sobre o estado da arte em agentes de IA.
Anterior na série
Fackel: um framework autônomo de pentest baseado em agentes ReAct