mozak.tech Arquitetura de IA Corporativa 50%

Parte 1 — Harness: Núcleo e Plano de Controle

1.5 — Motor de Resolução Condicional: Injeção Dinâmica de Tools e Skills Conforme Contexto Operacional

Objetivo da Aula

Explicar por que expor todas as ferramentas disponíveis em toda chamada é uma escolha de arquitetura ruim, não um padrão neutro

Projetar um resolvedor condicional que decide, a cada turno, qual subconjunto de tools/skills é relevante

Diferenciar resolução por regra estática (declarativa) de resolução por relevância dinâmica (semântica)

Quantificar o custo de "poluição de ferramentas" na qualidade de decisão do modelo e no orçamento de tokens

Conectar o motor de resolução condicional com a montagem de contexto do capítulo 1.2 e o RBAC do capítulo 1.3

Por que isso importa

É tentador, ao construir um agente, simplesmente registrar todas as ferramentas disponíveis e deixar o modelo "escolher" a certa a cada turno. Funciona bem com cinco ferramentas. Começa a falhar visivelmente com trinta. Com cem ferramentas registradas, mesmo um modelo forte comete mais erros de seleção — chama a ferramenta errada, ignora a certa porque está "enterrada" entre opções irrelevantes, ou simplesmente consome uma fatia enorme da janela de contexto só descrevendo capacidades que nunca serão usadas naquele turno.

Isso não é uma limitação passageira de modelos atuais — é uma consequência estrutural de como a atenção de um LLM funciona sobre um conjunto de opções: mais opções irrelevantes no contexto significa mais ruído competindo com o sinal relevante. Um harness maduro não resolve isso pedindo para o modelo "ignorar o que não for relevante" — resolve não oferecendo, em primeiro lugar, o que não é relevante para aquele turno específico. Essa é a função do motor de resolução condicional: decidir, antes da chamada ao modelo, qual subconjunto de ferramentas e skills faz sentido no contexto operacional atual.

Impacto direto no seu trabalho: à medida que um sistema de IA corporativo cresce — mais integrações, mais skills, mais ferramentas internas — a pergunta de arquiteto muda de "que ferramentas o agente tem" para "como o agente descobre, a cada momento, apenas as ferramentas que importam agora". Sistemas que não resolvem essa pergunta de forma explícita acabam com agentes lentos, caros e com taxa de erro de seleção de ferramenta crescente conforme o catálogo de capacidades aumenta.

Conceitos Fundamentais

O custo real de expor tudo, sempre

Cada ferramenta registrada no contexto de uma chamada tem um custo em três dimensões, não apenas uma:

Custo 1: Tokens
  Cada definição de ferramenta (nome, descrição, schema de
  parâmetros) consome tokens da janela de contexto — mesmo
  que a ferramenta nunca seja chamada naquele turno.
  50 ferramentas × ~150 tokens de definição cada
  = 7.500 tokens só de "catálogo", antes de qualquer
  trabalho real começar.

Custo 2: Qualidade de decisão
  Quanto mais opções semanticamente próximas o modelo
  precisa distinguir, maior a chance de confundir uma com
  outra (ex: "buscar_cliente" vs "buscar_cliente_v2" vs
  "consultar_cadastro" — três ferramentas plausíveis para
  o mesmo pedido, adicionadas em momentos diferentes do
  projeto).

Custo 3: Superfície de risco
  Toda ferramenta exposta é, potencialmente, uma ferramenta
  que pode ser chamada — inclusive por manipulação via
  prompt injection. Expor uma ferramenta de alto raio de
  impacto (capítulo 1.3) em contextos onde ela não é
  necessária aumenta a superfície de ataque sem benefício
  operacional correspondente.

Esses três custos compõem o argumento central deste capítulo: a lista de ferramentas disponíveis não deveria ser estática — deveria ser resolvida dinamicamente, a cada turno, como função do contexto operacional. Essa resolução é o "motor de resolução condicional" citado na ementa.

Fundamento: Superfície de Ataque

Em segurança de sistemas, "superfície de ataque" é o conjunto total de pontos por onde um sistema pode ser comprometido — quanto maior a superfície exposta, mais oportunidades para exploração, mesmo que a maior parte dela nunca seja usada de forma legítima. Aplicado a agentes de IA: cada ferramenta exposta ao modelo, mesmo uma que "nunca deveria ser chamada naquele contexto", é tecnicamente alcançável se o modelo for manipulado (por instrução ambígua, por conteúdo malicioso injetado via ferramenta de leitura) a chamá-la. Reduzir a superfície — expor só o necessário para a tarefa em curso — é uma mitigação de segurança tão real quanto uma mitigação de performance.

Duas estratégias de resolução

Existem, na prática, duas formas complementares de decidir qual subconjunto de ferramentas/skills expor a cada turno:

Estratégia A — Resolução por regra declarativa (estática)
  Baseada em fatos conhecidos ANTES da chamada ao modelo:
  papel do agente, tipo de tarefa, ambiente.

  regras_de_resolucao:
    - se papel == "agente-leitor-codigo":
        ferramentas = [ler_arquivo, buscar_no_repo, listar_dir]
    - se papel == "agente-revisor-pr":
        ferramentas = [ler_arquivo, comentar_pr, rodar_testes]
    - se tarefa.tipo == "migracao_de_dados":
        ferramentas += [ler_schema_banco, gerar_script_migracao]

  → Determinístico, auditável, rápido (não depende de
    nenhuma chamada extra ao modelo para decidir).

Estratégia B — Resolução por relevância dinâmica (semântica)
  Baseada no conteúdo do pedido do turno atual, usando
  busca semântica sobre um catálogo de ferramentas/skills
  (mesma técnica de embeddings da Parte 2 deste livro).

  pedido_do_usuario = "preciso enviar um relatório por email
                        para o time financeiro"
  candidatos = buscar_ferramentas_similares(pedido_do_usuario,
                                             catalogo_completo,
                                             top_k=5)
  → candidatos: [enviar_email, gerar_relatorio_pdf,
                 listar_contatos_financeiro, ...]

  → Flexível, escala para catálogos grandes (centenas de
    skills), mas introduz uma etapa probabilística extra
    (a busca pode errar o top-k) antes mesmo da chamada
    principal ao modelo.

Na prática, sistemas maduros combinam as duas: a Estratégia A (regra declarativa) define o universo permitido pelo papel/RBAC — nunca é ultrapassada, é o teto de segurança. A Estratégia B (relevância dinâmica) opera dentro desse universo já filtrado, escolhendo o subconjunto mais provável de ser útil para o pedido específico. RBAC nunca é decidido por relevância semântica — seria delegar segurança a um componente probabilístico, violando o princípio do capítulo 1.1.

Aprofundamento Técnico

Arquitetura do resolvedor: onde ele se encaixa no fluxo

function turno_do_agente(estado, mensagem_usuario):
    # Passo 1: universo permitido (RBAC — nunca varia por
    # relevância, só por papel/contexto de risco — cap. 1.3)
    universo_permitido = resolver_por_rbac(estado.papel,
                                            estado.contexto_risco)

    # Passo 2: dentro do universo permitido, resolver o
    # subconjunto relevante para ESTE turno (motor de
    # resolução condicional)
    ferramentas_do_turno = resolver_condicional(
        mensagem_usuario,
        estado.tarefa_em_curso,
        universo_permitido
    )

    # Passo 3: montar contexto (capítulo 1.2) já com o
    # subconjunto de ferramentas resolvido, não o catálogo
    # inteiro
    contexto = montar_contexto(estado, mensagem_usuario,
                                ferramentas_do_turno)

    resposta = chamar_llm(contexto, ferramentas_do_turno)
    ...

Note a ordem: RBAC primeiro (teto de segurança, estático), resolução condicional depois (relevância, dentro do teto). Inverter essa ordem — resolver por relevância primeiro e checar RBAC só no momento de executar — ainda funciona para bloquear a execução indevida, mas expõe desnecessariamente, na definição de ferramentas do contexto, capacidades que o agente nunca poderia de fato usar naquele papel. É ineficiente e aumenta superfície de risco sem necessidade.

Skills como unidade de resolução, não só ferramentas individuais

À medida que o catálogo cresce, resolver ferramenta por ferramenta individualmente se torna menos prático que resolver por skill — um agrupamento coerente de ferramentas, contexto e instruções específicas para uma classe de tarefa (a Parte 4 deste livro aprofunda o design de skills modulares; aqui, o que importa é como elas entram na resolução condicional):

Sem agrupamento em skill (resolução granular, mais frágil):
  candidatos = [ler_planilha, validar_formato_csv,
                calcular_media, calcular_desvio_padrao,
                gerar_grafico, exportar_pdf, ...]
  → o modelo precisa entender sozinho que essas 6
    ferramentas soltas, juntas, resolvem "análise de
    dados financeiros"

Com agrupamento em skill (resolução por pacote coerente):
  skill_resolvida = "analise-financeira"
    → traz consigo: as ferramentas relevantes JÁ agrupadas,
      MAIS instruções específicas de como combiná-las,
      MAIS specs de formatação de saída esperada
  → o motor de resolução condicional decide "esta skill se
    aplica" uma vez, em vez de decidir seis vezes se cada
    ferramenta isolada se aplica

Resolver por skill reduz a carga de decisão do modelo (uma decisão de "isso se aplica" em vez de N decisões de ferramenta individual) e mantém coerência: uma skill carrega junto o conhecimento de como combinar suas ferramentas, não apenas quais ferramentas existem.

Trade-off: resolução dinâmica tem custo de latência e pode errar

A Estratégia B (relevância dinâmica) não é gratuita: buscar candidatos por similaridade semântica adiciona uma chamada extra (a um índice vetorial, ou a um modelo de classificação leve) antes da chamada principal ao LLM — latência extra, e mais um componente probabilístico que pode errar o top-k e deixar de fora exatamente a ferramenta certa. Para catálogos pequenos (menos de ~15-20 ferramentas dentro do universo já filtrado por RBAC), resolução puramente estática por regra declarativa costuma ser suficiente e mais previsível — o custo de manter regras explícitas é menor que o custo de operar um sistema de busca semântica adicional. A resolução dinâmica se justifica quando o catálogo, mesmo após o filtro de RBAC, ainda é grande o suficiente para que "poluição de ferramentas" (Custo 2 da seção anterior) se torne o problema dominante. Escolher a estratégia certa é, como em todo o restante deste livro, uma questão de dimensionar a solução ao tamanho real do problema — não aplicar a técnica mais sofisticada disponível por padrão.

Padrões e Armadilhas

Padrões recomendados

Padrão 1: registre qual subconjunto de ferramentas foi resolvido em cada turno. Para depuração e auditoria, é tão importante saber "quais ferramentas o agente tinha disponíveis neste turno" quanto saber "qual ferramenta ele chamou". Sem esse registro, é impossível diferenciar, a posteriori, um erro de seleção (a ferramenta certa estava disponível e o modelo escolheu a errada) de um erro de resolução (a ferramenta certa nunca chegou a ser oferecida).

Padrão 2: trate a lista de ferramentas resolvida como parte do contrato de teste do agente. Assim como se testa se uma API retorna o schema esperado, é possível — e recomendável — escrever testes que verificam se, para um determinado papel e tipo de tarefa, o resolvedor condicional retorna exatamente o conjunto de ferramentas esperado. Isso transforma uma decisão que poderia ser invisível (por que essa ferramenta apareceu, ou não apareceu, neste contexto?) em algo verificável e regressível.

Padrão 3: prefira nomes de ferramentas distintos e específicos a nomes genéricos versionados. O exemplo "buscar_cliente" vs "buscar_cliente_v2" vs "consultar_cadastro" citado na seção anterior é sintoma de um problema de nomeação, não só de resolução. Ferramentas com nomes que descrevem claramente seu escopo específico (por exemplo, "buscar_cliente_por_cpf" vs "buscar_cliente_por_id_interno") reduzem ambiguidade tanto para o resolvedor automático quanto para quem lê o catálogo depois.

Armadilhas comuns

⚠️ Armadilha 1: usar resolução dinâmica (Estratégia B) para decidir o que é permitido. O que acontece: alguém implementa a busca por relevância semântica e, por conveniência de código, deixa que ela também filtre por permissão — misturando "o que é relevante" com "o que é permitido" no mesmo componente probabilístico. Isso viola a separação estabelecida no capítulo 1.1: permissão é uma decisão determinística, relevância pode ser probabilística, mas as duas nunca deveriam ser resolvidas pelo mesmo mecanismo. Versão correta: RBAC sempre roda antes e de forma independente, como filtro estático que define o teto; a resolução dinâmica só opera dentro desse teto.

⚠️ Armadilha 2: catálogo de ferramentas cresce sem que ninguém audite ferramentas obsoletas. O que acontece: ferramentas antigas, substituídas por versões melhores, continuam registradas no catálogo "por segurança", inflando o universo de candidatos tanto para resolução estática quanto dinâmica, sem trazer valor. Versão correta: um catálogo de ferramentas precisa de um processo de depreciação tão real quanto o de qualquer API interna — remover, não apenas parar de recomendar.

⚠️ Armadilha 3: assumir que resolução por skill elimina a necessidade de RBAC granular dentro da skill. O que acontece: uma skill é aprovada como um todo para um papel, mas internamente ela agrupa uma ferramenta de leitura inofensiva com outra de escrita de alto raio de impacto — e a aprovação do pacote acaba concedendo, sem revisão explícita, a permissão para a parte mais arriscada. Versão correta: RBAC continua sendo avaliado por ferramenta individual mesmo quando o agrupamento em skill simplifica a decisão de relevância; o agrupamento não substitui a granularidade da permissão.