Voltar ao blog
17 de maio de 20266 min de leitura

Agent Spread: Distribuindo Agentes IA por Bounded Contexts Automaticamente

goaiarchitecturedddprogramming

Você tem uma aplicação com 15 pastas, 200 arquivos, 3 linguagens diferentes. Quer usar agentes IA para ajudar no desenvolvimento mas não quer configurar manualmente qual agente cuida de qual parte. A ideia do Agent Spread é: o sistema escaneia sua aplicação, detecta os domínios automaticamente pela estrutura de pastas e convenções de nomenclatura, e distribui agentes especializados em cada área. Cada agente conhece profundamente seu domínio (quais arquivos existem, qual tech stack, qual a complexidade) e ignora o resto. O resultado é que quando você pede "refatorar a autenticação", o agente de auth já sabe exatamente quais arquivos mexer sem precisar varrer o projeto inteiro.

Guia de tópicos:

  • O Problema: Agente Genérico em Projeto Grande
  • Análise Automática de Aplicação
  • Detecção de Tech Stack por Padrões de Arquivo
  • Identificação de Domínios por Convenção
  • Cálculo de Complexidade
  • Planejamento de Distribuição
  • Deploy de Agentes: Criando a Estrutura
  • Configuração Central com orchestra.yaml
  • Considerações Finais
  • Links Indicativos

O Problema: Agente Genérico em Projeto Grande

Um agente IA com contexto de 128k tokens parece ter espaço de sobra. Até você perceber que 200 arquivos de código somam facilmente 500k tokens. O agente não consegue carregar o projeto inteiro no contexto. Então ele trabalha com uma visão parcial, e essa visão parcial leva a sugestões que ignoram código existente, duplicam funcionalidades, ou quebram contratos entre módulos.

A alternativa é dar ao agente apenas os arquivos relevantes para a tarefa. Mas quem decide o que é relevante? Se o usuário pede "adicionar validação de email", é relevante o módulo de auth? O módulo de user? O módulo de notification que envia emails? Depende do projeto. E essa decisão não pode depender do usuário saber a estrutura interna de cor.

O Agent Spread resolve isso na raiz: cada domínio tem seu próprio agente que já conhece seus arquivos. Quando o orquestrador roteia a requisição para o agente certo, esse agente já tem o contexto necessário sem precisar carregar nada extra.

Análise Automática de Aplicação

O AppAnalyzer é o componente que escaneia o projeto e produz um mapa completo da estrutura:

type AppStructure struct {
    RootPath   string
    Domains    map[string]*Domain
    TechStack  []string
    Complexity string
    AgentPlan  map[string][]string
}

type Domain struct {
    Name        string
    Path        string
    Files       []string
    SubDomains  map[string]*Domain
    Complexity  int
    AgentNeeded bool
}

O AppStructure é o resultado final da análise. Contém todos os domínios detectados, a tech stack do projeto, a complexidade geral, e o plano de distribuição de agentes. Cada Domain sabe seu path, seus arquivos de código, e se precisa de um agente dedicado.

A análise acontece em quatro etapas sequenciais: detectar tech stack, analisar domínios, calcular complexidade, e planejar distribuição. Cada etapa alimenta a próxima.

Detecção de Tech Stack por Padrões de Arquivo

A detecção de tech stack funciona por presença de arquivos característicos:

func (aa *AppAnalyzer) detectTechStack() []string {
    var stack []string

    patterns := map[string][]string{
        "Python":     {"*.py", "requirements.txt", "setup.py", "pyproject.toml"},
        "Go":         {"*.go", "go.mod", "go.sum"},
        "JavaScript": {"*.js", "package.json", "*.ts", "*.jsx", "*.tsx"},
        "FastAPI":    {"main.py", "app.py", "**/routers/**"},
        "React":      {"src/App.js", "src/App.tsx", "public/index.html"},
        "Docker":     {"Dockerfile", "docker-compose.yml"},
    }

    for tech, filePatterns := range patterns {
        if aa.hasPatterns(filePatterns) {
            stack = append(stack, tech)
        }
    }

    return stack
}

A detecção diferencia linguagem de framework. Um projeto pode ser "Python + FastAPI + Docker" ou "Go + React + Kubernetes". Essa informação vai para a configuração do agente, que usa para contextualizar suas respostas. Um agente que sabe que o projeto usa FastAPI vai sugerir routers e Pydantic models em vez de Flask blueprints.

O hasPatterns faz glob matching no filesystem, incluindo busca recursiva para padrões com **. É rápido porque só verifica existência, não lê conteúdo dos arquivos.

Identificação de Domínios por Convenção

A identificação de domínios usa um mapa de padrões comuns em projetos web:

func (aa *AppAnalyzer) analyzeDomains(structure *AppStructure) error {
    domainPatterns := map[string][]string{
        "auth":     {"auth", "authentication", "login", "users", "accounts"},
        "api":      {"api", "routes", "controllers", "handlers", "endpoints"},
        "models":   {"models", "entities", "schemas", "database", "db"},
        "services": {"services", "business", "logic", "core"},
        "products": {"products", "catalog", "items"},
        "orders":   {"orders", "cart", "checkout"},
        "payment":  {"payment", "billing", "transactions"},
    }

    return filepath.Walk(aa.rootPath, func(path string, info os.FileInfo, err error) error {
        if !info.IsDir() {
            return nil
        }

        dirName := strings.ToLower(info.Name())

        for domain, patterns := range domainPatterns {
            for _, pattern := range patterns {
                if strings.Contains(dirName, pattern) || dirName == pattern {
                    // Registra o domínio e conta seus arquivos
                    structure.Domains[domain] = &Domain{
                        Name:  domain,
                        Path:  path,
                        Files: aa.countFiles(path),
                    }
                }
            }
        }

        return nil
    })
}

O walk percorre toda a árvore de diretórios e classifica cada pasta encontrada. Uma pasta chamada "authentication" é classificada como domínio "auth". Uma pasta chamada "cart" é classificada como domínio "orders". Se o projeto tem uma pasta "src/modules/user-management/", ela é classificada como "auth" porque contém "user".

A contagem de arquivos considera apenas arquivos de código (extensões como .py, .go, .js, .ts, .java). Arquivos de configuração, assets e documentação são ignorados porque não representam complexidade de domínio.

Cálculo de Complexidade

A complexidade do projeto determina se vale a pena distribuir agentes ou se um agente único resolve:

func (aa *AppAnalyzer) calculateComplexity(structure *AppStructure) string {
    totalFiles := 0
    totalDomains := len(structure.Domains)

    for _, domain := range structure.Domains {
        totalFiles += len(domain.Files)
    }

    if totalFiles < 10 && totalDomains < 3 {
        return "simple"
    } else if totalFiles < 50 && totalDomains < 8 {
        return "medium"
    } else {
        return "complex"
    }
}

Projetos simples (menos de 10 arquivos, menos de 3 domínios) não precisam de multi-agente. O overhead de coordenação seria maior que o benefício. Projetos medium ganham agentes por domínio mas sem orquestrador central. Projetos complex ganham agentes por domínio mais um orquestrador que coordena interações entre eles.

Os thresholds foram definidos empiricamente. 10 arquivos é mais ou menos o ponto onde um agente único começa a perder contexto relevante. 50 arquivos é onde a coordenação manual entre domínios fica inviável.

Planejamento de Distribuição

Com domínios identificados e complexidade calculada, o plano de distribuição define onde cada agente vai morar:

func (aa *AppAnalyzer) planAgentDistribution(structure *AppStructure) {
    for domainName, domain := range structure.Domains {
        if len(domain.Files) > 0 {
            agentPath := filepath.Join(domain.Path, "agents")
            structure.AgentPlan[domainName] = []string{agentPath}
        }
    }

    // Orquestrador central para projetos complexos
    if structure.Complexity == "complex" || len(structure.Domains) > 2 {
        mainAgentPath := filepath.Join(structure.RootPath, "orchestra_agents")
        structure.AgentPlan["orchestrator"] = []string{mainAgentPath}
    }
}

Cada agente fica dentro do diretório do seu domínio, numa subpasta agents/. Isso mantém a localidade: o agente de auth fica em auth/agents/, o de products fica em products/agents/. Quando o agente precisa de contexto, os arquivos relevantes estão no diretório pai.

O orquestrador central só é criado para projetos complexos. Ele fica na raiz (orchestra_agents/) e sua responsabilidade é coordenar os outros agentes quando uma requisição cruza domínios.

Deploy de Agentes: Criando a Estrutura

O deploy cria os diretórios e gera configurações YAML para cada agente:

func (aa *AppAnalyzer) createAgentStructure(domain, agentPath string, structure *AppStructure) error {
    os.MkdirAll(agentPath, 0755)

    configContent := fmt.Sprintf(`name: %s_agent
domain: %s
complexity: %d
files_count: %d
tech_stack: %v

responsibilities:
  - Análise de código do domínio %s
  - Refatoração e otimização
  - Testes e validação
  - Documentação técnica

commands:
  analyze: "Analisar código do domínio"
  refactor: "Refatorar seguindo melhores práticas"
  test: "Criar/executar testes"
  document: "Gerar documentação técnica"
`, domain, domain, domainInfo.Complexity, len(domainInfo.Files), structure.TechStack, domain)

    return os.WriteFile(filepath.Join(agentPath, "agent.yaml"), []byte(configContent), 0644)
}

O YAML gerado contém tudo que o agente precisa saber sobre si mesmo: qual domínio ele cuida, quantos arquivos tem, qual a tech stack, e quais comandos ele aceita. Quando o orquestrador carrega um agente, ele lê esse YAML e usa as informações para contextualizar os prompts enviados ao LLM.

Os comandos padrão (analyze, refactor, test, document) são genéricos de propósito. Cada agente pode ter comandos adicionais específicos do seu domínio, mas esses quatro cobrem as operações mais comuns em qualquer módulo de código.

Configuração Central com orchestra.yaml

Além dos agentes individuais, o sistema gera um arquivo central que mapeia toda a distribuição:

app_name: meu_projeto
complexity: complex
tech_stack: [Python, FastAPI, Docker]
total_domains: 5

agents:
  auth:
    - /projeto/auth/agents
  products:
    - /projeto/products/agents
  orders:
    - /projeto/orders/agents
  payment:
    - /projeto/payment/agents
  orchestrator:
    - /projeto/orchestra_agents

orchestration:
  analyze_all: "Analisar todos os domínios"
  refactor_all: "Refatorar aplicação completa"
  test_all: "Executar todos os testes"

Esse arquivo serve como ponto de entrada para o orquestrador. Quando o usuário executa orchestra agents, o sistema lê esse YAML e sabe quais agentes existem, onde estão, e quais comandos de orquestração estão disponíveis.

Considerações Finais

O Agent Spread é a ponte entre "ter um agente IA" e "ter um agente IA que realmente conhece seu projeto". A diferença é contexto. Um agente genérico precisa redescobrir a estrutura do projeto a cada interação. Um agente distribuído já sabe onde está, o que cuida, e quais são seus vizinhos.

O que eu quero que você leve deste artigo é que a distribuição de agentes por domínio não é sobre ter mais agentes. É sobre ter agentes com contexto menor e mais relevante. Um agente que conhece 20 arquivos profundamente é mais útil que um agente que conhece 200 arquivos superficialmente. E a detecção automática por convenção de nomenclatura funciona surpreendentemente bem porque a maioria dos projetos segue padrões previsíveis de organização.

A limitação principal é que projetos com estrutura não convencional (monolitos sem separação de pastas, ou projetos com nomenclatura em idiomas que o detector não conhece) não são bem detectados. Para esses casos, a configuração manual do orchestra.yaml é o fallback. Mas na minha experiência, 80% dos projetos que se beneficiariam de multi-agente já seguem alguma convenção de pastas que o detector reconhece.

Links indicativos: