Handoffs Multiagente
Orquestrando enxames autônomos, delegação ponto a ponto, ferramentas sintéticas de transferência e prevenção de ciclos no chatflow-agent.
A coordenação multiagente no chatflow-agent é baseada no modelo de enxames autônomos ponto a ponto (peer-to-peer swarms). Em vez de depender de um coordenador centralizado ou de grafos estáticos engessados, os agentes delegam tarefas autonomamente para parceiros especializados através da invocação de ferramentas sintéticas de transferência (handoff).
Quando um agente aciona um handoff, o contexto conversacional é preservado de forma transparente, o ponteiro do agente ativo é atualizado e o novo agente assume imediatamente o turno dentro do mesmo ciclo de execução.
O Modelo Mental de Enxame
Em uma arquitetura de enxame, cada agente se especializa em um domínio distinto (ex.: triagem, faturamento, suporte técnico, reembolsos). A coordenação ocorre naturalmente através do diálogo:
- Delegação Descentralizada: Os agentes conhecem os parceiros para os quais podem transferir o controle através de sua lista
handoffs. - Tomada de Decisão Dinâmica: O LLM decide em tempo de execução se responde diretamente, se aciona uma ferramenta de domínio ou se transfere o controle para um parceiro.
- Execução Transparente: O usuário interage com um ponto de entrada unificado (
Runner), sem perceber que múltiplos modelos ou agentes especializados colaboram nos bastidores.
Declarando Handoffs entre Agentes
As relações de transferência são declaradas durante a instanciação ou de forma imperativa:
Método 1: Declaração no Construtor
Passe uma lista de instâncias de Agent de destino no parâmetro handoffs:
# [min-version: 0.3.0]
# [executable]
from chatflow_agent import Agent
# Definir primeiro os agentes especialistas de destino
billing_agent = Agent(
name="BillingSpecialist",
instructions="Handle payments, invoice queries, and subscription upgrades.",
model="gemini-2.5-flash",
)
technical_agent = Agent(
name="TechnicalSupport",
instructions="Diagnose software bugs, API errors, and configuration issues.",
model="gemini-2.5-flash",
)
# O agente de triagem roteia para os especialistas
triage_agent = Agent(
name="TriageAgent",
instructions=(
"Welcome the user and determine their needs. "
"Transfer billing questions to BillingSpecialist and technical questions to TechnicalSupport."
),
handoffs=[billing_agent, technical_agent],
model="gemini-2.5-flash",
)Método 2: Handoffs Bidirecionais e Cíclicos
Especialistas frequentemente precisam transferir usuários de volta para a triagem ou entre si. Use agent.add_handoff() para estabelecer vínculos bidirecionais:
# [min-version: 0.3.0]
# [executable]
from chatflow_agent import Agent
support = Agent(name="Support", instructions="General support", model="gpt-4o")
billing = Agent(name="Billing", instructions="Billing support", model="gpt-4o")
# Estabelecer transferência bidirecional
support.add_handoff(billing)
billing.add_handoff(support)
assert any(h.name == "Billing" for h in support.handoffs)
assert any(h.name == "Support" for h in billing.handoffs)Proibição de Auto-Handoff
Um agente não pode registrar uma transferência para si mesmo. Chamar agent.add_handoff(agent) levanta imediatamente um ValueError("Agent 'X' cannot register a handoff to itself.").
Ferramentas Sintéticas de Roteamento nos Bastidores
Quando um agente define parceiros em handoffs, o chatflow-agent sintetiza automaticamente ferramentas de roteamento dedicadas, expostas através de agent.get_all_tools().
Para cada agente parceiro:
- Nome da Ferramenta: Gerado como
transfer_to_<sanitized_name>(ex.:transfer_to_billingspecialist). - Descrição da Ferramenta: Combina o nome do agente alvo com um trecho de suas instruções:
"Hand off the dialogue to 'BillingSpecialist'. Target agent specialty: Handle payments, invoice queries..." - Assinatura: Aceita um parâmetro opcional
reason: str | None = None. - Valor de Retorno: Retorna um payload tipado
Handoff(target_agent_name="BillingSpecialist", reason=...).
Como as ferramentas de handoff se parecem e se comportam exatamente como chamadas de função normais, o modelo aprende naturalmente quando transferir o controle com base na intenção do usuário e nas descrições das ferramentas.
Ciclo de Execução de Handoff no Runner
Quando runner.run_async() processa um turno:
[Mensagem do Usuário]
│
▼
[Agente Ativo] ──(Gera Chamada de Ferramenta)──► transfer_to_billingspecialist(reason="...")
│
▼
[Executa o Handoff]
│
▼
[Alterna Agente Ativo]
(session.set_active_agent)
│
▼
[Próxima Geração do Modelo]
(BillingSpecialist responde)
│
▼
[Resposta Final]- Invocação da Ferramenta: O LLM emite uma chamada de ferramenta para
transfer_to_billingspecialist. - Verificação no Registro: O
Runnerverifica seBillingSpecialistexiste no seu registro interno de agentes. Caso o agente não seja encontrado, levanta umAgentHandoffError. - Alternância do Agente Ativo: O
Runneratualizasession.set_active_agent(target_agent). - Injeção do Resultado do Handoff: Um
ToolResultcontendostatus: "HANDOFF_SUCCESS"e o motivo da transferência é anexado ao histórico da sessão. - Continuação Imediata do Turno: Dentro da mesma chamada
runner.run(), o novo agente ativo é acionado com o histórico atualizado para formular sua resposta.
Prevenção de Ciclos e Limites de Turno
Transferências descontroladas entre agentes poderiam gerar loops infinitos caso os prompts sejam ambíguos. O chatflow-agent protege sua execução com salvaguardas nativas:
max_handoffs_per_turn(Padrão: 5): O número máximo de transferências de agentes permitidas dentro de um único turno de mensagem do usuário. Se esse limite for atingido, o runner interrompe novas transferências e injeta um resultado de erro:"Maximum agent transfers exceeded for a single turn."max_tool_iterations(Padrão: 10): O limite geral de execuções de ferramentas por turno, garantindo encerramento determinístico mesmo se ferramentas encadearem chamadas consecutivas.
from chatflow_agent import Agent, Runner
triage = Agent(name="Triage", instructions="...", model="gpt-4o")
# Configurar limites mais estritos para endpoints de alto volume ou baixo custo
runner = Runner(
starting_agent=triage,
max_handoffs_per_turn=3,
max_tool_iterations=6,
)Diferenças de Versão e Recursos de Pré-lançamento
A geração e sanitização de handoffs receberam melhorias significativas de confiabilidade em versões mais recentes:
Versão Estável (v0.3.4)
- Stable (0.3.4) Sanitização básica via expressão regular:
clean = re.sub(r"[^a-zA-Z0-9_]", "_", name.strip().lower()). - Histórico de sessão compartilhado mantém contexto conversacional completo entre transferências.
Melhorias de Pré-lançamento (v0.4.0rc1)
- Prerelease (0.4.0rc1) Transliteração Unicode NFKD: Normaliza automaticamente caracteres com acentuação para ASCII simples (ex.:
AtenciónAlClientetorna-setransfer_to_atencionalcliente). - Prerelease (0.4.0rc1) Fallbacks Determinísticos com Hash: Nomes de agentes em alfabetos não latinos (ex.: japonês, árabe, cirílico) ou emojis utilizam um prefixo determinístico de hash SHA-256 (
transfer_to_u<hash>) para garantir identificadores válidos. - Prerelease (0.4.0rc1) Desambiguação em Colisões: Se dois agentes gerarem nomes sanitizados idênticos, sufixos numéricos (
_2,_3) são adicionados automaticamente para evitar sobrescritas. - Prerelease (0.4.0rc1) Limite de Caracteres: Identificadores de ferramentas de handoff são limitados a 60 caracteres para conformidade com provedores.
- Prerelease (0.4.0rc1) Encaminhamento de Atualizações de Contexto: O método
session.set_active_agent(agent, context_updates)permite atualizar metadados junto com o evento de transferência.
Exemplo Completo: Enxame de Suporte em Múltiplos Níveis
from chatflow_agent import Agent, Runner, tool
from chatflow_agent.core.memory import SessionStore
# 1. Ferramentas especializadas
@tool
def issue_refund(transaction_id: str, amount: float) -> dict:
"""Process a refund back to customer original payment method."""
return {"status": "REFUNDED", "transaction_id": transaction_id, "amount": amount}
@tool
def lookup_kb_article(topic: str) -> str:
"""Search knowledge base for troubleshooting steps."""
return f"Knowledge base instructions for {topic}: Clear cache and restart application."
# 2. Agentes de destino especializados
billing_agent = Agent(
name="BillingAgent",
instructions="Assist with refunds and payment disputes. Use issue_refund when appropriate.",
tools=[issue_refund],
model="gemini-2.5-flash",
)
tech_agent = Agent(
name="TechSupportAgent",
instructions="Assist with software errors. Use lookup_kb_article for troubleshooting.",
tools=[lookup_kb_article],
model="gemini-2.5-flash",
)
# 3. Agente de triagem
triage_agent = Agent(
name="TriageAgent",
instructions=(
"Welcome the user. Ask what they need help with. "
"Transfer billing/refund issues to BillingAgent and technical bugs to TechSupportAgent."
),
handoffs=[billing_agent, tech_agent],
model="gemini-2.5-flash",
)
# 4. Permitir que os especialistas transfiram de volta para a triagem se necessário
billing_agent.add_handoff(triage_agent)
tech_agent.add_handoff(triage_agent)
# 5. Inicializar o runner
store = SessionStore()
runner = Runner(starting_agent=triage_agent, session_store=store)
# 6. Executar o diálogo
response = runner.run(
session_id="customer-session-101",
user_message="I have an unexpected charge on my invoice and need a refund.",
)
print("Active Agent:", response.active_agent_name)
print("Response:", response.content)
print("Handoff:", response.handoff)Armadilhas Comuns e Boas Práticas
1. Agentes Inalcançáveis no Registro
O Runner indexa automaticamente todos os agentes alcançáveis a partir da árvore de handoffs do starting_agent. Caso um agente seja introduzido de forma dinâmica fora do grafo inicial, registre-o explicitamente:
# Se um agente for introduzido dinamicamente:
runner.register_agent(specialist_agent)2. Instruções de Handoff Excessivamente Genéricas
Certifique-se de que as instruções do agente de destino declarem claramente quais problemas ele resolve. Se dois agentes possuírem descrições sobrepostas, os modelos podem hesitar ou tomar decisões de roteamento imprevisíveis.
3. Preservando a Identidade da Sessão
Nunca crie um novo session_id esperando que uma transferência continue. O estado do handoff e a transição de agente são registrados dentro da sessão ativa.
Ferramentas e Chamada de Funções
Guia arquitetural completo sobre chamada de funções, esquemas de tipos de parâmetros, despacho síncrono/assíncrono e isolamento de erros no chatflow-agent.
Runner e Ciclo de Execução
Guia detalhado sobre o loop conversacional de múltiplos turnos, resolução de motores, fluxo de execução de ferramentas, limites de tempo e reversão transacional no chatflow-agent.