Colaboração entre múltiplos agentes na prática: guia para escolher entre 4 padrões de arquitetura

Dados de uma pesquisa da Anthropic mostram que sistemas multiagente alcançam desempenho 90,2% superior ao de um único Agent. Um Agent isolado tem três problemas críticos: limite de contexto (quando os 200 mil tokens estouram, a saída começa a perder o sentido), capacidades dispersas (habilidades demais o transformam em um “faz-tudo”) e dificuldade de depuração (quando ele falha, é difícil localizar a origem do problema). Um sistema de colaboração entre múltiplos agentes é como a “arquitetura de microsserviços” do mundo da IA: cada Agent faz apenas uma coisa e colabora por meio de mensagens bem definidas.
Os quatro principais padrões de arquitetura atendem a cenários diferentes: Subagents (orquestração central) são adequados ao paralelismo entre domínios independentes; Skills (carregamento sob demanda), a processos sequenciais com várias etapas; Handoffs (orientado a estado), a conversas em várias etapas; e Router (distribuição paralela), a consultas em várias fontes de dados. Este artigo aborda os critérios de escolha, os problemas encontrados em produção e uma implementação em LangGraph para ajudar você a decidir entre Subagents e Skills e a gerenciar o estado entre Agents.
Por que precisamos de sistemas multiagente
Para ser sincero, o maior problema de um único Agent não é “se ele funciona”, mas “por quanto tempo ele continua funcionando bem”.
Você já viu um Agent que estava escrevendo um código muito bom começar, de repente, a gerar coisas sem sentido? Ou fez uma pergunta apenas sobre A, mas ele desviou para B, depois C e acabou trazendo até Z para a conversa? Isso não acontece porque o Agent é “burro”, mas porque sua janela de contexto ficou sobrecarregada.
Um único Agent tem três problemas críticos.
Limite de contexto. Por mais poderoso que seja, um Agent continua limitado a uma janela de 200 mil tokens. Se você pedir que ele faça ao mesmo tempo revisão de código, análise de segurança e otimização de desempenho, já será muito se ele conseguir lembrar metade de tudo. Eu já vi um Agent passar as primeiras 20 interações falando de Python e, na 21ª, começar a produzir JavaScript: ele havia esquecido completamente o que deveria fazer.
Capacidades dispersas. Quanto mais habilidades você coloca em um Agent, mais ele se transforma em um “faz-tudo”: sabe um pouco de tudo, mas não domina nada. Ao pedir uma revisão de código, talvez ele escreva um teste unitário; ao pedir documentação, talvez aproveite para refatorar o código. Senso de direção? Nenhum.
Dificuldade de depuração. Quando um Agent falha, você não sabe em qual parte ocorreu o problema. O prompt ficou longo demais? A chamada de uma ferramenta falhou? Ou o contexto foi contaminado? Investigar isso é como procurar uma agulha no palheiro.
Em termos simples, um sistema multiagente é a “arquitetura de microsserviços” do mundo da IA. Cada Agent faz uma única coisa e a faz bem. Eles colaboram por meio de mensagens claras, em vez de concentrar tudo em um “super-Agent”.
Google, LangChain e Anthropic promovem essa abordagem. Segundo um relatório da O’Reilly, o número de artigos sobre Agents em 2025 passou de 820 no início do ano para mais de 2.500, ou seja, triplicou. Por quê? Porque ficou claro que a era em que um único Agent resolvia tudo chegou ao fim.
Quatro padrões principais de arquitetura
LangChain e Google resumiram alguns dos padrões de arquitetura multiagente mais difundidos. Depois de testar vários projetos, percebi que cada um serve a um cenário específico. Escolher errado é como “usar um canhão para matar um mosquito” ou “tomar sopa com hashis”.
Subagents (subagentes): orquestração central
Este é o padrão mais intuitivo: um “Agent principal” atua como comandante de um grupo de “Agents auxiliares”. Os Subagents são ferramentas do Agent principal, que decide quem chamar e quando.
Solicitação do usuário → Agent principal (coordenador) → distribuição aos Subagents A/B/C → consolidação dos resultados → resposta ao usuário
Quando usar? Quando sua tarefa envolve vários domínios independentes. Em um sistema de atendimento, por exemplo, um Subagent pode consultar pedidos, outro processar reembolsos e outro lidar com reclamações. Cada domínio tem sua própria base de conhecimento e suas próprias ferramentas; o Agent principal cuida apenas do “encaminhamento”.
Exemplo de código (LangGraph):
from langgraph.prebuilt import create_react_agent
# Define os Subagents
order_agent = create_react_agent(
model="claude-3-5-sonnet-20241022",
tools=[query_order, update_order],
prompt="Você é especialista em pedidos e trata apenas questões relacionadas a pedidos."
)
refund_agent = create_react_agent(
model="claude-3-5-sonnet-20241022",
tools=[check_refund_policy, process_refund],
prompt="Você é especialista em reembolsos e trata apenas questões relacionadas a reembolsos."
)
# O Agent principal usa os Subagents como ferramentas
main_agent = create_react_agent(
model="claude-3-5-sonnet-20241022",
tools=[order_agent, refund_agent], # Os Subagents são ferramentas
prompt="Você coordena o atendimento e encaminha cada dúvida ao especialista adequado."
)
Vantagens: o contexto fica bem isolado e cada Subagent vê apenas o necessário. A execução paralela é eficiente.
Desvantagens: cada Subagent representa uma chamada independente ao LLM, o que eleva o consumo de tokens. Compartilhar estado entre Subagents também exige etapas adicionais.
Skills (habilidades): carregamento sob demanda
Um Agent com várias “personalidades”. Skills são, em essência, modelos de prompt carregados dinamicamente. O Agent troca de “papel” de acordo com a tarefa, mas continua sendo o mesmo Agent.
Solicitação do usuário → único Agent → carrega a Skill “revisão de código” → executa → carrega a Skill “geração de documentação” → executa
Quando usar? Quando a tarefa precisa ser processada de forma sequencial, mas cada etapa exige conhecimentos diferentes. Um assistente de programação, por exemplo, pode usar o modo “desenvolvedor” para escrever código e o modo “redator técnico” para produzir documentação.
Exemplo de código:
# Estrutura do diretório de Skills
skills/
├── code_review.md # Prompt de revisão de código
├── doc_writer.md # Prompt de geração de documentação
└── security_audit.md # Prompt de auditoria de segurança
# Carrega uma Skill dinamicamente
def load_skill(skill_name: str) -> str:
with open(f"skills/{skill_name}.md") as f:
return f.read()
# Exemplo de uso
agent = create_react_agent(
model="claude-3-5-sonnet-20241022",
tools=[...],
prompt=load_skill("code_review") # Troca em tempo de execução
)
Vantagens: é leve e dispensa a sobrecarga de coordenação entre Agents adicionais. Consome menos tokens que Subagents.
Desvantagens: o contexto se acumula. Depois de trocar de Skill 10 vezes, o conteúdo das 9 Skills anteriores ainda está no contexto, que fica cada vez mais confuso.
Handoffs (transferências): padrão orientado a estado
Os Agents transferem a tarefa entre si como jogadores passando a bola. O Agent A termina sua parte e “entrega” o estado ao Agent B, que dá continuidade. É como uma corrida de revezamento.
Solicitação do usuário → Agent A (coleta informações) → transfere → Agent B (analisa o problema) → transfere → Agent C (propõe a solução)
Quando usar? Em conversas com várias etapas. Pense em um fluxo de suporte técnico: coletar o problema → diagnosticar → propor uma solução → confirmar a resolução. Cada etapa pode exigir conhecimentos diferentes.
Exemplo de código:
from langchain_core.tools import tool
# Define as ferramentas de transferência
@tool
def handoff_to_diagnosis(issue_summary: str) -> str:
"""Transfere o problema ao especialista em diagnóstico."""
return f"Problema recebido: {issue_summary}. Iniciando o diagnóstico..."
@tool
def handoff_to_solution(diagnosis_result: str) -> str:
"""Transfere o diagnóstico ao especialista em soluções."""
return f"Preparando uma solução com base no diagnóstico: {diagnosis_result}..."
# Cadeia de Agents
triage_agent = create_react_agent(
tools=[handoff_to_diagnosis],
prompt="Você faz a triagem: coleta o problema do usuário e o encaminha ao especialista em diagnóstico."
)
diagnosis_agent = create_react_agent(
tools=[handoff_to_solution],
prompt="Você é especialista em diagnóstico: analisa a causa raiz e encaminha o resultado ao especialista em soluções."
)
Vantagens: o fluxo da conversa é natural e segue a lógica da colaboração humana. Cada Agent se concentra apenas na etapa atual.
Desvantagens: o gerenciamento de estado é complexo. É preciso garantir que o formato dos dados transferidos pelo Agent A ao Agent B esteja correto; caso contrário, a cadeia será interrompida.
Router (roteador): distribuição paralela
Um “Agent roteador” analisa a solicitação, aciona vários Agents especializados em paralelo e, por fim, sintetiza os resultados.
Solicitação do usuário → Router (classificação) → Agents A/B/C em paralelo → síntese dos resultados → resposta ao usuário
Quando usar? Quando uma solicitação exige consultas a várias fontes de dados. Em uma base de conhecimento corporativa, por exemplo, o Router identifica o tipo de pergunta, consulta em paralelo documentos internos, APIs externas e bancos de dados e, por fim, sintetiza a resposta.
Exemplo de código:
from langgraph.graph import StateGraph
# Define os nós de execução paralela
async def query_internal_docs(state):
# Consulta documentos internos
return {"internal_results": [...]}
async def query_external_api(state):
# Consulta a API externa
return {"external_results": [...]}
async def query_database(state):
# Consulta o banco de dados
return {"db_results": [...]}
async def synthesize(state):
# Sintetiza todos os resultados
all_results = state["internal_results"] + state["external_results"] + state["db_results"]
return {"final_answer": summarize(all_results)}
# Monta o grafo paralelo
graph = StateGraph(State)
graph.add_node("internal", query_internal_docs)
graph.add_node("external", query_external_api)
graph.add_node("database", query_database)
graph.add_node("synthesize", synthesize)
# Execução paralela
graph.add_edge("router", ["internal", "external", "database"])
graph.add_edge(["internal", "external", "database"], "synthesize")
Vantagens: execução paralela e maior velocidade. Não há estado; cada consulta é independente.
Desvantagens: não é adequado a conversas com várias interações. Cada solicitação é nova, e o Agent não se lembra do que foi discutido na interação anterior.
Framework de decisão para escolher a arquitetura
Depois de tudo isso, qual deles escolher? Montei um fluxo de decisão simples:
Qual é a sua necessidade?
│
├─→ Vários domínios independentes precisam ser processados em paralelo?
│ │
│ └─→ Subagents (orquestração central)
│
├─→ Um único Agent precisa alternar Skills em várias etapas?
│ │
│ └─→ Skills (carregamento sob demanda)
│
├─→ Um fluxo sequencial, com transferências de etapa em etapa?
│ │
│ └─→ Handoffs (orientado a estado)
│
└─→ É preciso consultar e combinar várias fontes de dados?
│
└─→ Router (distribuição paralela)
Talvez o fluxo, sozinho, ainda não seja intuitivo o bastante. Por isso, organizei uma tabela comparativa:
| Padrão | Desenvolvimento distribuído | Paralelismo | Conversa em várias etapas | Interação direta com o usuário | Consumo de tokens |
|---|---|---|---|---|---|
| Subagents | Alto | Alto | Alto | Baixo | Alto |
| Skills | Alto | Médio | Alto | Alto | Baixo |
| Handoffs | Nenhum | Nenhum | Alto | Alto | Médio |
| Router | Médio | Alto | Nenhum | Médio | Alto |
Como interpretar esta tabela?
- Desenvolvimento distribuído: sua equipe desenvolve módulos diferentes em paralelo? Nesse caso, Subagents e Skills funcionam bem, pois cada integrante pode cuidar de um Subagent ou de uma Skill.
- Paralelismo: velocidade é prioridade? Router e Subagents executam vários Agents em paralelo e oferecem a maior eficiência.
- Conversa em várias etapas: o usuário precisa interagir várias vezes? Handoffs e Skills oferecem suporte natural ao fluxo de conversa.
- Interação direta com o usuário: o usuário conversa diretamente com os Agents auxiliares? Skills e Handoffs permitem isso; Router, não.
- Consumo de tokens: se o custo for importante, Skills gastam menos, enquanto Router e Subagents gastam mais.
Minha experiência é: comece pelo simples. Primeiro, valide um MVP com Skills ou Handoffs; só migre para Subagents ou Router quando encontrar um gargalo. Não comece com uma arquitetura distribuída. Eu conheço bem a dor da engenharia excessiva.
Pontos essenciais para uma implementação em produção
Entre uma demonstração e a produção existe um oceano. Já encontrei todos os problemas a seguir.
Gerenciamento de estado
Quando vários Agents compartilham estado, o problema mais frequente são as “condições de corrida”: dois Agents escrevem ao mesmo tempo na mesma variável. No fim, qual valor prevalece?
A solução do LangGraph é output_key: cada Agent só pode escrever em sua própria chave.
from langgraph.graph import StateGraph, MessagesState
class GraphState(MessagesState):
security_result: str = "" # Exclusiva do Agent de segurança
style_result: str = "" # Exclusiva do Agent de estilo
perf_result: str = "" # Exclusiva do Agent de desempenho
# O Agent de segurança escreve apenas em security_result
async def security_agent(state: GraphState):
result = await analyze_security(state["messages"])
return {"security_result": result} # Escreve apenas nesta chave
# O Agent de estilo escreve apenas em style_result
async def style_agent(state: GraphState):
result = await analyze_style(state["messages"])
return {"style_result": result}
Assim, seja em paralelo ou em sequência, cada Agent atua apenas em sua própria área, sem interferir nos outros.
Outro problema comum é a “contaminação de contexto”. O Agent B lê a saída do Agent A mesmo sem precisar dela. Minha solução é adicionar ao estado um campo relevant_keys, para que cada Agent leia apenas as chaves necessárias.
Otimização de desempenho
O consumo de tokens em um sistema multiagente pode virar um poço sem fundo. Estas são algumas técnicas para economizar:
1. Subagents consomem 67% menos tokens que Skills em cenários com vários domínios
Segundo os testes do LangChain, quando uma tarefa envolve três domínios independentes, Subagents consomem um terço dos tokens usados por Skills. Por quê? Porque Subagents isolam o contexto: cada um vê apenas o conteúdo de sua área. Com Skills, o contexto de todas elas se acumula e cresce continuamente.
2. Padrões com estado economizam de 40% a 50% das chamadas repetidas
Se a tarefa tiver muitas consultas repetidas — a mesma pergunta feita dez vezes, por exemplo — use o padrão Handoffs com estado para que o Agent se lembre das respostas anteriores. Segundo os dados do LangChain, manter estado reduz quase pela metade o número de chamadas ao LLM em comparação com um padrão sem estado.
3. Limite o número de iterações no padrão de reflexão
Muita gente gosta de adicionar “reflexão” ao Agent: ele verifica a própria saída, identifica problemas e gera uma nova resposta. Esse recurso é útil, mas pode entrar em loop infinito. Eu costumo definir max_iterations=2 ou 3 e forçar a saída depois desse limite.
from langgraph.checkpoint.memory import MemorySaver
# Define o limite de iterações
graph = create_react_agent(
model="claude-3-5-sonnet-20241022",
tools=[...],
checkpointer=MemorySaver(),
config={"configurable": {"max_iterations": 3}} # No máximo 3 reflexões
)
Problemas comuns
Loop infinito: o Agent chama a si mesmo, que chama a si mesmo, que chama a si mesmo… e nunca termina. Solução: defina max_iterations e uma condição clara de saída.
def should_continue(state):
if state["iteration_count"] >= 3:
return "end"
if "done" in state["messages"][-1].content:
return "end"
return "continue"
Crescimento do contexto: o Agent parece ficar cada vez mais “burro”, e as respostas ficam mais curtas. Na maioria dos casos, há conteúdo demais no contexto. Solução: use o padrão Blackboard (quadro compartilhado), mantenha apenas o contexto necessário e faça limpezas periódicas.
Sobrecarga de coordenação: à medida que o número de Agents cresce, o custo de comunicação aumenta exponencialmente. Testei um sistema que passou de 3 para 10 Agents, e o tempo de resposta subiu de 2 para 15 segundos. Solução: combine Agents com funções parecidas e mantenha o total abaixo de 5.
Exemplo completo de implementação
Depois de tanta teoria, vamos à prática. Montei um sistema multiagente de revisão de código usando o padrão Router + ParallelAgent.
Arquitetura: o Router identifica a linguagem e o tipo do código → aciona em paralelo três Agents, de auditoria de segurança, verificação de estilo e análise de desempenho → sintetiza os resultados em um relatório.
from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, END
from langchain_anthropic import ChatAnthropic
# Define o estado
class CodeReviewState(TypedDict):
code: str
language: str
security_issues: list
style_issues: list
perf_issues: list
final_report: str
# Inicializa o LLM
llm = ChatAnthropic(model="claude-3-5-sonnet-20241022")
# Router: identifica a linguagem
async def route_code(state: CodeReviewState) -> dict:
code = state["code"]
# Verificação simples; na prática, é possível classificar com um LLM
if "def " in code or "import " in code:
language = "python"
elif "function" in code or "const " in code:
language = "javascript"
else:
language = "unknown"
return {"language": language}
# Agent de auditoria de segurança
async def security_audit(state: CodeReviewState) -> dict:
code = state["code"]
prompt = f"""Você é especialista em auditoria de segurança. Verifique os seguintes problemas no código:
- Risco de injeção de SQL
- Vulnerabilidades XSS
- Exposição de informações sigilosas
- Dependências inseguras
Código:
{code}
Retorne os problemas como uma lista JSON. Cada item deve conter: line (número da linha), severity (gravidade) e description (descrição).
"""
response = await llm.ainvoke(prompt)
# Analisa o resultado...
return {"security_issues": []}
# Agent de verificação de estilo
async def style_check(state: CodeReviewState) -> dict:
code = state["code"]
language = state["language"]
prompt = f"""Você é especialista em estilo de código. Verifique os seguintes aspectos deste código {language}:
- Convenções de nomenclatura
- Formatação do código
- Integridade dos comentários
Código:
{code}
Retorne os problemas como uma lista JSON.
"""
response = await llm.ainvoke(prompt)
return {"style_issues": []}
# Agent de análise de desempenho
async def perf_analysis(state: CodeReviewState) -> dict:
code = state["code"]
prompt = f"""Você é especialista em análise de desempenho. Verifique os seguintes problemas no código:
- Complexidade de tempo elevada
- Loops desnecessários
- Risco de vazamento de memória
Código:
{code}
Retorne os problemas como uma lista JSON.
"""
response = await llm.ainvoke(prompt)
return {"perf_issues": []}
# Relatório consolidado
async def generate_report(state: CodeReviewState) -> dict:
security = state.get("security_issues", [])
style = state.get("style_issues", [])
perf = state.get("perf_issues", [])
total_issues = len(security) + len(style) + len(perf)
report = f"""# Relatório de revisão de código
## Visão geral
- Linguagem: {state['language']}
- Total de problemas: {total_issues}
## Problemas de segurança ({len(security)})
{format_issues(security)}
## Problemas de estilo ({len(style)})
{format_issues(style)}
## Problemas de desempenho ({len(perf)})
{format_issues(perf)}
## Recomendações
Com base na análise acima, priorize a correção dos problemas de segurança...
"""
return {"final_report": report}
# Monta o grafo
graph = StateGraph(CodeReviewState)
graph.add_node("router", route_code)
graph.add_node("security", security_audit)
graph.add_node("style", style_check)
graph.add_node("perf", perf_analysis)
graph.add_node("report", generate_report)
# Fluxo: Router → três verificações em paralelo → geração do relatório
graph.set_entry_point("router")
graph.add_edge("router", "security")
graph.add_edge("router", "style")
graph.add_edge("router", "perf")
graph.add_edge("security", "report")
graph.add_edge("style", "report")
graph.add_edge("perf", "report")
graph.add_edge("report", END)
# Compila
app = graph.compile()
# Uso
async def review_code(code: str):
result = await app.ainvoke({"code": code})
return result["final_report"]
Ao executar este exemplo com um trecho de 100 linhas, os três Agents trabalham em paralelo e entregam o resultado em cerca de 3 a 5 segundos. Em execução sequencial, seriam necessários pelo menos 10 segundos.
Claro que esta é apenas uma versão básica. Em produção, você também precisa adicionar cache (não revisar o mesmo código novamente), revisão incremental (analisar apenas o que mudou) e feedback humano (permitir que o usuário marque falsos positivos). Todas essas extensões, porém, podem ser construídas sobre esta arquitetura.
Conclusão
Depois de tudo isso, a ideia pode ser resumida em três frases:
Lógica fundamental: escolher o padrão é mais importante do que escolher o framework. LangGraph, AutoGen e CrewAI são ótimas ferramentas, mas nem o melhor framework salva o uso de Router para resolver um problema que exige Handoffs.
Estratégia intermediária: comece pelo simples e evolua aos poucos. Primeiro, valide um MVP com Skills ou Handoffs; considere Subagents ou Router somente depois de encontrar um gargalo. Engenharia excessiva é a maior armadilha — eu já caí nela, então evite repetir o erro.
Aplicação em produção: concentre-se em gerenciamento de estado, desempenho e custo. Quando você resolve o consumo de tokens, os loops infinitos e a contaminação de contexto, seu sistema multiagente fica estável em produção.
Próximo passo: abra a documentação do LangGraph, escolha um padrão e implemente o sistema multiagente mais simples possível em 50 linhas de código. Não complique: primeiro, faça funcionar.
Como criar um sistema de colaboração entre múltiplos agentes
Crie do zero um sistema multiagente para revisão de código
- 1
Step 1: Escolha o padrão de arquitetura
Escolha o padrão de arquitetura adequado às características da tarefa - 2
Step 2: Defina a estrutura de estado
Use TypedDict para definir o estado compartilhado entre vários Agents - 3
Step 3: Crie os nós dos Agents
Crie uma função de nó independente para cada Agent - 4
Step 4: Monte o grafo de execução
Use o StateGraph do LangGraph para montar o fluxo de execução - 5
Step 5: Adicione o gerenciamento de estado
Use output_key para evitar condições de corrida
FAQ
Qual é a diferença entre Subagents e Skills?
Quando devo usar o padrão Handoffs?
Como evitar condições de corrida em um sistema multiagente?
Como controlar o consumo de tokens em um sistema multiagente?
Para quais cenários o padrão Router é indicado?
Como impedir que um Agent entre em loop infinito?
16 min de leitura · Publicado em: 25 mar 2026 · Atualizado em: 8 set 2026
Guia de engenharia de AI Agents
Se você chegou pela busca, o caminho mais rápido é ir para o post anterior ou próximo desta série.
Anterior
Computer-Use Agent: deixe a IA operar seu computador
Uma análise completa da tecnologia Claude Computer Use, dos fundamentos à prática. Inclui implantação com Docker, exemplos de código, análise de concorrentes e boas práticas de segurança para você entender a automação de computadores com IA
Parte 5 de 13
Próximo
Gerenciamento de estado no LangGraph na prática: checkpoint, thread state e recuperação de falhas
Guia prático de gerenciamento de estado no LangGraph em 2026: entenda checkpoint, thread state, recuperação de falhas, comparação com AutoGen e monitoramento para projetar uma arquitetura de agentes resiliente e pronta para produção.
Parte 7 de 13



Comentários
Entre com GitHub para comentar