Split a monolithic triage agent into three scoped micro-agents, wire them together with a shared context dict, and learn when the overhead is worth it.
Quando o agente de triagem de suporte dos módulos anteriores chegou ao uso em produção na plataforma de automação de suporte (de-identificada), ele estava segurando muita coisa de uma vez: o prompt de sistema, o ticket recebido, o histórico de conversa, os trechos da base de conhecimento recuperados para contexto e as regras de escalação. Cada requisição forçava o modelo a equilibrar tudo isso antes de poder escrever uma única palavra. A janela de contexto estava pela metade antes da primeira mensagem do usuário chegar.
O agente funcionava. Mas quando a recuperação falhou — um índice de KB desatualizado, um timeout, um artigo malformado — eu não tinha ideia de onde a falha aconteceu. A lógica de classificação, a recuperação e a resposta estavam todas entrelaçadas em uma chamada. Cada falha parecia igual vista de fora: a resposta estava errada. Depurar uma função que fazia tudo era lento e imprevisível.
A solução não foi um novo prompt. Foi dividir o agente em três menores.
Fator 6 — Construa Agentes Pequenos e Focados. Um agente que faz uma coisa pode ser testado em isolamento, substituído sem reconstruir o pipeline inteiro e observado sem vasculhar saídas não relacionadas.
Pense em micro-agentes como microsserviços. Assim como você não colocaria autenticação, cobrança e entrega de email em um único serviço, você não precisa de um agente que classifica tickets, recupera artigos de KB e compõe respostas. A independência é um trade-off — você ganha depurabilidade e substituibilidade, mas paga em overhead de coordenação. A questão não é "devo sempre usar micro-agentes?" É "essa fronteira vale o esforço?"
Ao final deste módulo você será capaz de:
Aqui está o núcleo do agente de triagem monolítico. Observe quanto ele tenta fazer em um prompt:
Isso funciona até que a recuperação quebre. Quando o índice de KB está desatualizado, a lógica de classificação quebra junto. Quando o formato de resposta muda, o prompt de classificação muda também. Tudo está acoplado — uma função fazendo três trabalhos.
Há também um problema mais sutil. O monolito pede ao modelo para fazer três tarefas cognitivas distintas em uma passagem — categorizar, selecionar, compor — dentro de uma única instrução de prompt. Modelos são bons em cada uma dessas tarefas individualmente. Pedir todas as três de uma vez significa que o prompt precisa ser longo o suficiente para cobrir cada tarefa bem, e o formato de saída precisa codificar todos os três resultados simultaneamente.
Divida em três agentes. Primeiro, o classificador:
Uma chamada de modelo, uma saída estruturada. O classificador não tem conhecimento de artigos de KB ou formato de resposta — seu contrato é ticket-entra, categoria-sai.
A seguir, o agente de recuperação, que recebe apenas a categoria:
O agente de recuperação não sabe sobre tickets ou respostas. Ele recebe uma categoria e um índice, e retorna artigos. Se o índice de KB estiver vazio, retorna uma lista vazia — e essa falha é isolada de todo o resto.
Isolamento não é apenas sobre depuração. Também significa que o agente de recuperação pode ser chamado de outros contextos — uma interface de busca, um agente supervisor, um job em lote que pré-busca artigos para categorias comuns. Quando um agente tem um contrato único e bem definido, torna-se reutilizável fora do pipeline para o qual foi construído.
Por fim, o agente de resposta:
O agente de resposta recebe seus inputs explicitamente — ticket, classificação, artigos — em vez de derivá-los ele mesmo. Sua função é composição, não recuperação ou classificação.
O que a divisão nos trouxe: Cada agente pode ser testado com inputs simulados, substituído por um modelo diferente ou reexecutado em caso de falha — sem tocar nos outros. Quando uma falha de recuperação aconteceu em produção, pude re-executar apenas o agente de recuperação com a mesma categoria de ticket e reproduzir o problema em isolamento.
Três agentes que executam independentemente são úteis. Três agentes que compartilham um dict de contexto são um pipeline. O princípio chave é que cada função deve ser sem estado e reexecutável — o estado vive no dict, não dentro da função.
Fator 7 — Design de Agente Sem Estado. Cada micro-agente é uma função pura: dados os mesmos inputs, produz a mesma saída. O dict de contexto é o estado mutável. Você pode reexecutar qualquer etapa alimentando-a com a mesma fatia de contexto.
Aqui está o runner do pipeline:
O dict carrega tudo adiante. Se classificar falhar, recuperar ainda executa contra a categoria de fallback. Se recuperar falhar, responder ainda executa com artigos vazios. A saída de cada etapa é visível no contexto final — você pode inspecionar exatamente o que cada agente viu e retornou.
Observe a escolha de tratamento de erros aqui: os erros são coletados em uma lista e o pipeline continua em vez de parar na primeira falha. Essa é uma compensação deliberada. A alternativa — parar na primeira exceção — significaria que uma falha de classificação impede que qualquer resposta seja gerada. Para um sistema de suporte, uma resposta degradada é melhor do que nenhuma resposta.
Pense no dict de contexto como um prontuário médico que viaja com cada etapa. Sem ele, cada agente começa do zero; com ele, cada agente herda o quadro completo do que veio antes.
Antes de continuar: se classify retornar a categoria errada — digamos "general" para uma
disputa de cobrança — a etapa de recuperação buscará artigos irrelevantes, e a etapa de
resposta escreverá uma resposta fora do assunto. O erro se propaga silenciosamente.
O que você adicionaria para capturar essa cascata cedo? Considere: uma etapa de validação após classify, um campo de confiança na saída de classificação, ou um fallback para um modelo diferente. Não há uma única resposta certa. Pense onde você colocaria a verificação e qual seria o custo de adicioná-la comparado ao custo de deixar uma resposta fora do assunto chegar a um cliente.
Dividir em três agentes ganha depurabilidade — mas apenas se cada agente registrar suas entradas e saídas. O logging estruturado é o que torna um sistema de micro-agentes observável.
Esse padrão início/sucesso/erro — com extras estruturados — facilita rastrear um ticket pelos
três agentes usando correlação de logs. Para encadear um ticket pelos três agentes, passe um
ticket_id em cada dict de extras. Uma única consulta de log nesse ID retorna o rastro completo
classify → retrieve → respond em ordem de timestamp, sem vasculhar saídas intercaladas de outros
tickets concorrentes.
Aplique o mesmo padrão aos agentes de recuperação e resposta. Os extras específicos diferirão — recuperação registra a categoria recebida e o número de artigos retornados; resposta registra o comprimento da resposta e se a KB estava vazia — mas a forma início/sucesso/erro permanece a mesma. Estrutura consistente significa que uma única consulta de log pode mostrar a linha do tempo completa de qualquer ticket, em ordem, sem adivinhar o que cada linha de log significa.
Agora adicione um circuit breaker ao agente de recuperação. Um circuit breaker dispara após falhas repetidas e retorna um padrão seguro em vez de sobrecarregar um serviço downstream quebrado:
Quando o serviço de KB está fora do ar, o circuit breaker abre após três falhas e retorna listas vazias imediatamente. O agente de resposta já lida com artigos vazios — ele diz "Nenhum artigo de KB disponível" e escreve uma resposta geral. O sistema se degrada graciosamente em vez de sobrecarregar um serviço quebrado e aguardar timeouts em cada ticket.
O insight principal de design aqui é que o padrão seguro do circuit breaker — uma lista vazia — é o mesmo valor que o agente de recuperação retornaria para um índice de KB vazio. O agente de resposta já foi escrito para lidar com esse caso. Adicionar o circuit breaker não exigiu mudanças downstream.
Circuit breakers são observáveis, não invisíveis. A chamada logger.warning("circuit_breaker.tripped") não é opcional. Sem ela, o breaker engole falhas silenciosamente e você não notará que o serviço de recuperação está fora do ar até os clientes começarem a reclamar.
Fator 12 — A Vanguarda é Cara. Adicionar arquitetura antes de ter um problema é overhead, não engenharia. Meça antes de dividir.
Quando apliquei o padrão de micro-agente ao harness do SDLC, dividi uma única etapa de "implementar e revisar" em micro-agentes de planejar, implementar e revisar. O ganho em depurabilidade foi real — etapas de revisão com falha eram reproduzíveis em isolamento. Mas o overhead de coordenação também foi real:
Para o pipeline de triagem, o monolito lidava com uma consulta de cobrança simples em uma chamada em cerca de dois segundos. O pipeline de três agentes leva de três a cinco segundos. Isso é aceitável na escala da plataforma de automação de suporte porque o ganho em depurabilidade vale o custo de latência. Para um bot de FAQ simples servindo perguntas de baixo risco, não teria valido a pena.
Prompt reflexivo: Quando um único agente linear superaria três micro-agentes? Qual é o ponto de cruzamento para seu projeto atual? Considere: volume de tickets, taxa de falha, tempo gasto depurando por incidente e o custo de latência adicional. Se você nunca teve que depurar uma falha de múltiplas etapas em produção, comece com o monolito e divida apenas quando tiver um modo de falha que exija isso.
O padrão de micro-agente ganha seu overhead quando:
Não ganha seu overhead quando:
Uma heurística útil: se você teve que depurar uma falha de produção duas vezes e cada vez não conseguiu identificar imediatamente qual etapa falhou, esse é o sinal para dividir. Se você nunca teve uma falha de produção neste pipeline, mantenha o monolito e invista o orçamento de complexidade economizado em outro lugar.
Ao longo de cinco módulos, o sistema de triagem de suporte cresceu de uma chamada de extração JSON bruta para um pipeline de três agentes com estado explícito, logging por agente, um circuit breaker e um portão de escalação humana. Cada camada foi ganha por um modo de falha concreto:
O framework dos 12 fatores não é uma lista de verificação para aplicar de uma vez. É um conjunto de respostas para problemas específicos que aparecem quando os agentes chegam à produção. O objetivo é reconhecer o problema, aplicar o fator certo e medir se a correção valeu a pena. Esse julgamento — quando adicionar complexidade e quando resistir a ela — é o trabalho de engenharia.
Application-level questions on micro-agent architecture and the trade-offs of splitting agents.
1. The classify step returns `{"category": "general", ...}` for a billing dispute. The retrieve step uses that category to look up KB articles. What happens next?
Correct Answer: B
A wrong category is not an exception — it is a logic error. The pipeline continues normally, each step trusting the previous one. Silent propagation is the key risk in pipelines: a wrong intermediate output produces a wrong final output with no error raised. This is why mid-pipeline validation or a confidence field on classification output can be worth adding.
2. Factor 7 says each micro-agent should be stateless between invocations. What does this mean in practice for the classify function?
Correct Answer: B
Stateless means pure: same inputs, same output. The classify function reads only its argument (ticket) and the Anthropic API. It does not read from or write to a mutable cache, session, or global. This makes it replayable — you can re-run it with the same ticket and reproduce the result — which is the foundation of debuggable pipelines.
3. When `retrieval_breaker.is_open()` returns True, what does retrieve_articles return?
Correct Answer: B
The circuit breaker returns [] immediately — the same value that would result from an empty KB index. The respond agent already handles empty articles gracefully. This means the pipeline degrades without crashing: tickets still get replies, just without KB support, until the retrieval service recovers and the breaker resets.
4. What is the main benefit of passing a shared context dict through the pipeline rather than having agents call each other directly?
Correct Answer: B
When agents call each other directly, state is implicit — hidden inside call stacks and return values. When state flows through an explicit dict, you can print it at any point, serialize it to disk, or feed it to a replay runner. This explicitness is what makes micro-agent pipelines debuggable rather than just fast.
5. When would a single monolithic agent be a better choice than three micro-agents?
Correct Answer: B
Micro-agents earn their coordination overhead only when the failure isolation, independent replaceability, or multi-context reuse is worth the latency and maintenance cost. If you have never debugged a multi-step failure in production, the monolith is the right starting point. Split when you have a failure mode that demands it.
6. In the pipeline runner, `ctx['errors']` is a list and each step catches its own exception and continues. What would happen if classify raised instead of appending to errors?
Correct Answer: B
If any step raises and the exception is not caught by the pipeline runner, the caller receives an exception rather than a context dict. The errors-list pattern is a deliberate degradation strategy: the system produces a fallback reply even when individual steps fail. Whether to use this pattern depends on your SLA — sometimes you want a hard fail, not a silent degradation.
7. You want to add an escalation router: a micro-agent that reads the classification output and decides whether to send the ticket to a human or let it proceed to respond. Where does it belong in the pipeline?
Correct Answer: B
The escalation router needs the classification output (category and urgency) to decide whether human review is needed. Running it before respond but after classify — and potentially after retrieve, if the KB articles influence the decision — gives it the right information. Running it in parallel with classify would mean it cannot use the classification result.
8. Why is `logger.warning('circuit_breaker.tripped', ...)` described as non-optional in the retrieve agent?
Correct Answer: B
An open circuit breaker is invisible if it does not emit a log event. The pipeline continues producing replies, but without KB support. Without the warning, you would see subtly wrong replies in customer complaints before you noticed the retrieval service was down. Observable degradation is what separates a well-engineered failure mode from a silent one.
Muito bem. Veja o que vem a seguir.
Voltar para esta trilhaOu agende a chamada de diagnóstico