🤝 O que é um handoff
Um handoff não é apenas "agente A termina e agente B começa". É uma transferência formal de controle e contexto, com verificação de consistência antes da transferência e confirmação de recebimento pelo agente destino. A formalidade é o que garante que nada se perde na transição.
🤝 O protocolo de handoff formal
O handoff packet
task_id: "T3"
from_agent: "executor_A"
to_agent: "reviewer_B"
result: {dados_do_resultado}
context_summary: "resumo do que foi feito"
handoff_reason: "task_complete"
timestamp: 1748345678
Handoff informal (o problema)
Sem protocolo formal, handoffs informais causam:
- ✗Contexto perdido na transição
- ✗Agente destino começa sem confirmação
- ✗Nenhum log de auditoria da transferência
- ✗Sem rollback quando destino falha
🎛️ Critérios de roteamento
O roteamento define qual agente recebe qual tarefa. A decisão errada desperdiça tokens (agente errado para o domínio) ou tempo (agente sobrecarregado). Quatro critérios cobrem a maioria dos casos reais — use o mais simples que resolve o seu problema.
Roteamento por Conteúdo
O tipo da tarefa determina o agente destino. Análise jurídica → agente jurídico. Código → agente de engenharia. Redação → agente de conteúdo.
if "contrato" in task: → legal_agent
elif "def " in task: → code_agent
else: → general_agent
Roteamento por Carga
O agente com menos tarefas em fila recebe a próxima. Load balancing clássico aplicado a agentes. Ideal quando agentes são equivalentes entre si.
agent = min(agents,
key=lambda a: a.queue_size)
Roteamento por Habilidade
O agente com maior success_rate histórico para esse tipo de tarefa é selecionado. Self-learning routing — melhora com o uso.
agent = max(capable_agents,
key=lambda a: a.success_rate(
task_type=task.type))
Roteamento por Custo
Para orçamento restrito, o agente mais barato compatível com os requisitos da tarefa é selecionado. Balanceia custo e qualidade.
agent = min(capable_agents,
key=lambda a: a.cost_per_token)
🧠 Roteamento dinâmico com LLM classifier
Quando regras determinísticas não são suficientes para classificar a tarefa corretamente, um LLM leve atua como classificador: analisa o input e decide qual agente tem a melhor combinação de habilidades para lidar com ele.
🧠 Implementação do classifier
CLASSIFIER_PROMPT
= """Você é um roteador de tarefas. Analise a tarefa e escolha o agente mais adequado.
Agentes disponíveis:
- researcher: pesquisa web, coleta de dados, fact-checking
- analyst: análise quantitativa, modelagem, métricas
- writer: redação, síntese, geração de relatórios
- coder: código Python/JS, scripts, automação
Tarefa: {task_description}
Responda APENAS com o nome do agente (sem explicação):
"""
async def
route_task(task: str) -> str:response = await llm.complete(
CLASSIFIER_PROMPT.format(task_description=task),
model="claude-haiku-3", # barato
max_tokens=10
)
agent_name = response.strip().lower()
if agent_name not in AVAILABLE_AGENTS:
return "general" # fallback seguro
return agent_name
✓ Quando usar LLM classifier
- ✓Tarefas em linguagem natural ambígua
- ✓Múltiplos domínios que se sobrepõem
- ✓Quando regras determinísticas ficam complicadas
✗ Quando usar regras determinísticas
- ✗Quando você quer zero latência de classificação
- ✗Quando o tipo de tarefa é sempre explícito no input
- ✗Quando o custo do classifier não é aceitável
↩️ Rollback de handoff
O que acontece quando o agente destino falha após receber o handoff? Sem rollback definido, a tarefa fica em limbo — nem no agente origem, nem no destino — e o sistema trava silenciosamente. O rollback formal é o que garante que nenhuma tarefa se perde.
↩️ Two-phase commit para handoffs
Fase 1: Prepare
- → Agente A prepara handoff packet e registra no log como "pending"
- → Agente B é notificado e reserva recursos para receber
- → Agente B confirma "ready" ou responde "not ready"
Fase 2: Commit (ou Abort)
- → Se B confirmou: handoff executado, log atualizado para "committed"
- → Se B não confirmou em timeout: handoff abortado, log como "rolled_back"
- → Rollback: tarefa retorna ao agente A (ou vai para agente de fallback)
⚠️ O handoff fantasma
Sem two-phase commit, existe o risco do "handoff fantasma": Agente A entregou (do ponto de vista dele), mas Agente B nunca recebeu (crash antes de confirmar). Nenhum dos dois sabe que a tarefa está perdida. O sistema continua funcionando, mas sem completar aquela tarefa específica — descoberto pelo usuário final horas depois.
📦 Context passing eficiente
Passar contexto demais é o erro mais comum em handoffs. Se o Agente A processou 50 documentos e passa todo o histórico para o Agente B, você paga pelo contexto duas vezes e pode exceder a context window. Contexto mínimo necessário é uma habilidade de engenharia.
📦 Estratégias de context compression
Sumarização semântica
Use um LLM para sumarizar o trabalho do Agente A em 3-5 bullet points antes de passar para o B. De 10.000 tokens para 200 tokens com 90% da informação relevante preservada.
Extração de entidades relevantes
Identifique e passe apenas as entidades que o Agente B vai usar: nomes, números, URLs, decisões. O resto é irrelevante para a próxima tarefa.
Contexto hierárquico
Passe dois níveis: summary (sempre) + full_context (disponível via referência se o agente precisar). O agente começa com o summary e solicita detalhes sob demanda.
🗺️ Os 4 padrões de handoff
Quatro padrões cobrem praticamente todos os casos de handoff em sistemas reais. Escolha sempre o mais simples que resolve o seu caso — simplicidade é confiabilidade.
Sequential (A→B→C)
Cada agente completa e passa para o próximo em sequência linear. O mais simples e previsível.
Conditional (se X→B, se Y→C)
O agente destino é escolhido com base em uma condição avaliada pelo orquestrador ou pelo próprio agente A.
Parallel (A→B e C simultaneamente)
Agente A passa o resultado para múltiplos agentes simultaneamente. Todos processam em paralelo e os resultados são combinados no fan-in.
Dynamic (A decide em runtime)
O Agente A decide em runtime para qual agente fazer handoff, baseado no resultado do seu próprio processamento. Máxima flexibilidade.
💡 Regra de escolha de padrão
Comece sempre com Sequential. Mude para Conditional quando o destino depende de critério claro. Use Parallel quando latência importa mais que custo. Reserve Dynamic apenas para sistemas que genuinamente não podem ser determinísticos — são os mais difíceis de operar em produção.
✅ Resumo do Módulo 4.6
Próximo Módulo:
4.7 — Execução Paralela: fan-out, fan-in, asyncio.gather() e como gerenciar timeout e custo de agentes paralelos.