Skip to content

Arquitetura Multi-Agent: Supervisor + Subagentes Especializados

Proposta de migração da arquitetura atual (single agent + multiple tools)
para um modelo de Orchestrator/Supervisor com Subagentes Especializados.


1. Diagnóstico da Arquitetura Atual

Estrutura atual

root_agent (LlmAgent)
├── AgentTool(search_agent)        ← único subagente real
├── AgentTool(faq_agent)           ← único subagente real
├── busca_produtos                 ┐
├── tem_variacao                   │
├── listar_variacoes               │
├── listar_guias_experience        │ 24 tools diretas
├── detalhes_experience            │ no mesmo agente
├── detalhes_guia                  │
├── disponibilidade_calendario     │
├── disponibilidade_horarios       │
├── calcular_preco                 │
├── cotacao_moeda                  │
├── gerar_csv                      │
├── enviar_email_sendgrid          │
├── buscar_usuario                 │
├── buscar_agencia                 │
├── criar_conta_agencia            │
├── criar_conta_viajante           │
├── validar_dados_reserva          │
├── emitir_reserva_guia            │
├── emitir_reserva_experience      │
├── obter_reserva                  │
├── verificar_parcelas             │
├── gerar_token_cartao             │
└── processar_pagamento            ┘

O system_prompt_v2.py tem 329 linhas descrevendo uma state machine completa (RESEARCH → DISCOVERY → SELECTION → QUOTE → IDENTIFICATION → BOOKING → PAYMENT → DONE) que o único agente precisa gerenciar integralmente a cada chamada ao LLM.


2. Prós e Contras

Arquitetura Atual (Single Agent + Multiple Tools)

# Prós Contras
1 Simples de depurar: um único agente, um único prompt Prompt de 329 linhas → alto consumo de tokens a cada turno
2 Context sharing trivial: todas as tools veem o mesmo tool_context e session state LLM precisa "saber tudo" simultaneamente: turismo, reserva, pagamento, FAQ
3 Sem overhead de delegação entre agentes 24 tools no mesmo agente aumentam chance de hallucination de nome
4 Fácil de testar em isolamento com adk run State machine complexa dificulta adição de novos domínios sem regredir outros
5 Menos latência: uma única chamada LLM por turno (sem delegação) Erro em qualquer tool derruba o fluxo inteiro sem isolamento
6 Impossível usar modelos diferentes por domínio (ex: modelo mais barato para FAQ)
7 Sistema de memória e callbacks acoplado a um único ponto

Arquitetura Proposta (Orchestrator + Subagentes)

# Prós Contras
1 Cada subagente tem prompt enxuto e focado no seu domínio Maior complexidade: mais arquivos, mais agents para manter
2 Orquestrador delega → menos tokens por chamada no subagente Delegação via AgentTool adiciona 1 chamada LLM extra por delegação
3 Possível usar modelos diferentes por subagente (ex: Flash para busca, Pro para pagamento) State sharing entre agentes requer atenção: cada AgentTool cria sub-session
4 Isolamento de falhas: erro no payment_agent não afeta catalog_agent Debugging mais complexo: logs espalhados entre múltiplos agentes
5 Fácil adicionar novos domínios sem tocar nos existentes Latência potencialmente maior em fluxos que exigem múltiplas delegações
6 Cada subagente pode ter seus próprios callbacks e configurações Passagem de contexto (product_id, booking_id) precisa ser feita via session state ou retorno explícito
7 Alinha com o state machine já definido: 1 estado = 1 subagente
8 Permite escalar e versionar subagentes independentemente

3. Arquitetura Proposta

Visão Geral

root_agent (Orchestrator)
├── AgentTool(research_agent)       ← STATE: RESEARCH
├── AgentTool(catalog_agent)        ← STATE: DISCOVERY + SELECTION
├── AgentTool(quote_agent)          ← STATE: QUOTE
├── AgentTool(user_agent)           ← STATE: IDENTIFICATION
├── AgentTool(booking_agent)        ← STATE: BOOKING
├── AgentTool(payment_agent)        ← STATE: PAYMENT
├── AgentTool(utility_agent)        ← transversal (CSV, email)
└── AgentTool(faq_agent)            ← já existe

O orquestrador não tem tools diretas — apenas delega para subagentes.
Seu único prompt descreve quando delegar para quem, não como executar.


4. Mapeamento de Tools por Subagente

research_agent

Questões sobre turismo, clima, dicas e busca na internet.

Tool atual Fonte
google_search já existe em search_agent — renomear/reaproveitamento

Prompt foco: consultor de viagens + busca web. Nunca reservas ou preços.


catalog_agent

Descoberta e detalhamento de produtos (experiences e guias).

Tool atual Arquivo
busca_produtos tools/busca_produtos_tool.py
detalhes_experience tools/detalhes_experience_tool.py
detalhes_guia tools/detalhes_guia_tool.py
listar_guias_experience tools/listar_guias_experience_tool.py

Prompt foco: buscar, filtrar e apresentar produtos. Retorna product_id e detalhes para o orquestrador.


availability_agent (pode fundir com catalog_agent se quiser simplicidade)

Disponibilidade de datas e horários.

Tool atual Arquivo
disponibilidade_calendario tools/disponibilidade_calendario_tool.py
disponibilidade_horarios tools/disponibilidade_horarios_tool.py

Prompt foco: dado product_id + mês/data, retorna datas e horários disponíveis.


quote_agent

Cálculo de preço, variações e conversão de moeda.

Tool atual Arquivo
tem_variacao tools/tem_variacao_tool.py
listar_variacoes tools/listar_variacoes_tool.py
calcular_preco tools/calcular_preco_tool.py
cotacao_moeda tools/cotacao_moeda_tool.py

Prompt foco: dado product_id + data + pax, retorna preço final em BRL com breakdown.


user_agent

Identificação e criação de usuários/agências.

Tool atual Arquivo
buscar_usuario tools/booking/buscar_usuario_tool.py
buscar_agencia tools/booking/buscar_agencia_tool.py
criar_conta_agencia tools/booking/criar_conta_agencia_tool.py
criar_conta_viajante tools/booking/criar_conta_viajante_tool.py

Prompt foco: dado email, retorna customer_id ou cria conta. Output limpo para o booking_agent.


booking_agent

Validação e emissão de reservas. (Agente já existe em agents/booking_agent.py — refatorar)

Tool atual Arquivo
validar_dados_reserva tools/booking/validar_dados_reserva_tool.py
emitir_reserva_guia tools/booking/emitir_reserva_guia_tool.py
emitir_reserva_experience tools/booking/emitir_reserva_experience_tool.py
obter_reserva tools/booking/obter_reserva_tool.py

Prompt foco: dado dados completos de reserva, valida e emite. Retorna booking_id + allowCheckout.


payment_agent

Parcelamento, tokenização e processamento de pagamento. (Agente já existe em agents/payment_agent.py — refatorar)

Tool atual Arquivo
verificar_parcelas tools/payment/verificar_parcelas_tool.py
gerar_token_cartao tools/payment/gerar_token_cartao_tool.py
processar_pagamento tools/payment/processar_pagamento_tool.py

Prompt foco: dado booking_id + dados do cartão, processa pagamento. Nunca armazena dados de cartão.


utility_agent

Geração de CSV e envio de email.

Tool atual Arquivo
gerar_csv tools/gerar_csv_tool.py
enviar_email_sendgrid tools/enviar_email_sendgrid_tool.py

Prompt foco: dado dados estruturados, gera arquivo ou envia email. Transversal a qualquer estado.


5. Estrutura de Arquivos Proposta

ifriend_agent/
├── agent.py                          ← root_agent (orchestrator) — prompt enxuto
│
├── prompts/
│   ├── orchestrator_prompt.py        ← instrução do orquestrador (~60 linhas)
│   ├── catalog_prompt.py             ← focado em busca e detalhes
│   ├── availability_prompt.py        ← focado em datas/horários
│   ├── quote_prompt.py               ← focado em preços e variações
│   ├── user_prompt.py                ← focado em identificação
│   ├── booking_prompt.py             ← focado em emissão de reserva
│   ├── payment_prompt.py             ← focado em pagamento
│   └── utility_prompt.py             ← focado em CSV/email
│
├── agents/
│   ├── research_agent.py             ← renomear/refatorar search_agent existente
│   ├── catalog_agent.py              ← NOVO (busca + detalhes + guias)
│   ├── availability_agent.py         ← NOVO (datas + horários)
│   ├── quote_agent.py                ← NOVO (preços + variações + moeda)
│   ├── user_agent.py                 ← NOVO (identificação + conta)
│   ├── booking_agent.py              ← REFATORAR existente
│   ├── payment_agent.py              ← REFATORAR existente
│   ├── utility_agent.py              ← NOVO (CSV + email)
│   └── faq_agent.py                  ← manter como está
│
└── tools/                            ← permanecem inalteradas
    ├── busca_produtos_tool.py
    ├── ...
    ├── booking/
    └── payment/

6. Prompt do Orquestrador (esboço)

O orquestrador substitui o system_prompt_v2.py de 329 linhas por ~60:

Você é o iFriend Orchestrator. Sua única função é entender a intenção do usuário
e delegar para o subagente correto.

ESTADO ATUAL: {state} (gerencie via session state)

DELEGAÇÃO:
- Dúvidas sobre destinos, clima, história → research_agent
- Buscar produtos, ver detalhes, disponibilidade → catalog_agent
- Calcular preço, ver variações → quote_agent
- Identificar usuário/agência → user_agent
- Emitir reserva → booking_agent
- Processar pagamento → payment_agent
- Gerar CSV ou enviar email → utility_agent
- Perguntas frequentes → faq_agent

REGRAS:
- Nunca execute tools diretamente
- Repasse o contexto necessário (product_id, booking_id, email) ao delegar
- Apresente o retorno do subagente ao usuário sem reprocessar
- Máximo UMA pergunta por mensagem

7. Ponto Crítico: Passagem de Contexto entre Agentes

No ADK, quando o orquestrador chama um AgentTool, o subagente não compartilha automaticamente o session state do pai. Há três formas de passar contexto:

Opção A — Via argumento explícito na chamada (recomendado para IDs)

O orquestrador inclui product_id, customer_id, booking_id nos argumentos passados ao AgentTool. Cada subagente declara esses parâmetros em seu input schema.

Opção B — Via session.state compartilhado

Usar output_key no subagente para escrever resultado no state do pai:

catalog_agent = LlmAgent(
    ...
    output_key="catalog_result"  # escreve em session.state['catalog_result']
)
O orquestrador lê state['catalog_result'] e passa para o próximo subagente.

Opção C — Híbrido (mais robusto)

  • IDs críticos → argumento explícito
  • Resultados intermediários → output_key no state
  • Dados sensíveis (cartão) → nunca no state, sempre argumento direto ao payment_agent

8. Plano de Migração Sugerido (incremental)

A migração pode ser feita sem big bang, estado por estado:

Fase O que fazer Risco
0 Manter arquitetura atual. Separar tools em grupos nos imports do agent.py Nenhum
1 Criar catalog_agent com busca_produtos + detalhes_* → substituir no root via AgentTool Baixo
2 Criar quote_agent com calcular_preco + tem_variacao + listar_variacoes + cotacao_moeda Baixo
3 Refatorar booking_agent existente (já existe, só ajustar tools e prompt) Médio
4 Refatorar payment_agent existente (já existe, só ajustar tools e prompt) Médio
5 Criar user_agent e utility_agent Baixo
6 Substituir system_prompt_v2.py pelo prompt enxuto do orquestrador Alto — testar bem
7 Remover tools diretas do root_agent Alto — só após fase 6 validada

9. Decisão Recomendada

Para o estágio atual do projeto, considera-se:

  • Fluxo transacional de reserva e pagamento → subagentes isolados agora
    (booking_agent e payment_agent já existem, refatoração de baixo risco)

  • Catalog + Quote → subagentes na fase seguinte
    (maior impacto no fluxo principal, testar em staging)

  • Orquestrador puro (sem tools diretas) → fase final
    (só após todos os subagentes estarem validados individualmente)

O principal ganho a curto prazo não é a separação em si, mas reduzir o prompt do agente principal — quanto menos o LLM precisa saber simultaneamente, menor a chance de hallucination de nome de tool e menor consumo de tokens.