Uma empresa SaaS B2B de porte médio, com cerca de 50 engenheiros espalhados por aproximadamente 30 contas AWS, aciona alguém de plantão cerca de 40 vezes por semana, e o chamado só recebe uma resposta real depois de 25 a 35 minutos de coleta manual de contexto: qual conta, qual dashboard, qual runbook, e se esse runbook ainda está correto. Isso acontece antes do diagnóstico começar, não no lugar dele. Esta série constrói a solução: uma pequena plataforma de agentes de IA que faz essa primeira etapa automaticamente, lê tudo o que tem permissão para ler, não muda nada sem o aval de um humano, e para de ser útil no momento em que deixa de seguir essa disciplina.

A empresa, e o que ela realmente paga em trabalho manual

Chamando a empresa pelo que ela é: uma loja SaaS B2B de porte médio, cerca de 50 engenheiros, aproximadamente 30 contas AWS distribuídas por um punhado de linhas de produto, cada uma com seu próprio trio de dev, staging e produção. Ninguém planejou chegar a 30 contas. Aconteceu do jeito comum, um time de cada vez pedindo isolamento, até o AWS Organizations refletir mais o organograma do que a arquitetura.

O trabalho manual não é dramático. Ele se acumula, e quatro números tirados de três meses de dados de plantão e uma retrospectiva desconfortável deixam o tamanho do problema claro:

  • Cerca de 40 chamados por semana caem em quem estiver na rotação, distribuídos de forma desigual entre as 30 contas.
  • De 25 a 35 minutos de cada chamado são gastos montando contexto antes mesmo do diagnóstico começar: a conta certa, o dashboard certo, o runbook certo, e se esse runbook ainda corresponde à realidade.
  • A revisão mensal de FinOps roda cerca de três semanas atrás do gasto real, então um autoscaling group mal configurado ou uma instância GPU ociosa queima dinheiro durante quase um mês inteiro antes de aparecer em um relatório que alguém lê.
  • Uma auditoria recente de runbooks encontrou que cerca de um em cada três runbooks apontava para um recurso, um ARN ou um caminho de console que não existia mais.

Nenhum desses quatro pontos é uma emergência isolada. Juntos, formam um imposto constante sobre o time: algo entre 30 e 40 horas de engenharia por semana gastas na parte mecânica da operação, coletando contexto e reverificando instruções desatualizadas, pagas por pessoas que foram contratadas para construir produto, não para serem montadoras humanas de contexto.

Por que agentes, e por que agora

A resposta honesta para "por que agora" é que as peças finalmente existem como serviços gerenciados, em vez de frameworks internos que alguém precisa manter vivos. O Bedrock oferece um serviço gerenciado de orquestração de agentes, o Agents for Amazon Bedrock, desde que atingiu disponibilidade geral em novembro de 2023, com controle de orquestração melhorado e visibilidade do rastro de raciocínio. O que mudou foi a camada acima dele. O Amazon Bedrock AgentCore atingiu disponibilidade geral em 2025-10-13, em nove regiões, empacotando sete serviços componentes (Runtime, Memory, Identity, Gateway, Code Interpreter, Browser, Observability) que antes significavam construir do zero um armazenamento de sessão, um corretor de credenciais e um pipeline de rastreamento. Em paralelo, a AWS abriu o código do Strands Agents SDK em maio de 2025 sob a licença Apache 2.0 e lançou uma versão 1.0 de produção para Python em 2026-05-21, uma semana antes do início da própria linha do tempo deste post. A infraestrutura gerenciada e a camada de orquestração aberta cruzaram a mesma barra de prontidão para produção nos mesmos poucos meses. É nessa janela que esta plataforma é construída.

Construir versus comprar

O time avaliou comprar primeiro, como deveria ser. Um produto pronto de AIOps ou copiloto de incidentes teria saído mais rápido e não precisaria de Terraform nenhum. A opção perdeu por três razões específicas desta empresa, não dos agentes em geral.

  • A fronteira de mutação não é negociável. Quem executa uma correção precisa assumir uma role IAM com escopo estreito na conta afetada, protegida por uma etapa interna de aprovação. Um produto SaaS pede para você conceder acesso amplo entre contas ou encaixar o próprio fluxo de aprovação dele em cima do seu depois do fato. Ser dono da plataforma significa que o portão de aprovação já nasce nativo, em vez de ser integrado depois.
  • Trinta contas de conhecimento tribal vivem em threads do Slack e na cabeça dos engenheiros, não em um formato que o pipeline de ingestão de um fornecedor entenda. Uma Bedrock Knowledge Base sobre o corpus de runbooks existente, com o time controlando diretamente o chunking e a qualidade da recuperação, é mais viável do que exportar esse conhecimento para terceiros e torcer para que a recuperação deles seja boa o suficiente.
  • A empresa já opera AWS nessa escala. O Terraform, o CI, a rotação de plantão, a estrutura de contas, tudo isso já existe hoje. O custo marginal de construir sobre primitivas nativas da AWS é menor do que o custo de uma segunda relação com fornecedor, uma segunda linha de faturamento e uma segunda revisão de segurança.

O contra-argumento é real: comprar é mais rápido, e rapidez tem valor quando o trabalho manual se acumula semana após semana. A resposta do time foi delimitar a construção com rigor (quatro agentes, um padrão de aprovação, inferência sob demanda para começar, veja a matemática de dimensionamento numa parte posterior) em vez de tratar isso como o projeto eterno de um time de plataforma. O código complementar da série inteira vive em github.com/flightlesstux/agents-on-call; este post é onde o docs/architecture.md dele começa.

A decisão de stack: três formas de rodar um agente no Bedrock

Depois que o time decidiu construir, a próxima decisão foi em qual camada construir. Existiam três opções reais, e o trade-off está longe de ser tão simples quanto "usar a coisa mais nova":

DimensãoBedrock Agents (clássico)AgentCore + StrandsConstruído do zero sobre a Converse API
O que você possuiAction groups definidos no console; a orquestração é da AWSRuntime, identity, memory e gateway gerenciados; o loop do agente é o seu PythonTudo: loop, retries, memory, roteamento de tools, tracing
Maturidade nesta dataGA desde novembro de 2023, o histórico mais longoGA desde 13 de outubro de 2025, cerca de sete meses de uso em produçãoTão maduro quanto o seu próprio código, nem mais nem menos
Portabilidade entre modelos e provedoresSomente modelos BedrockBedrock, Anthropic direto, Ollama e provedores via proxy LiteLLMO que você mesmo conectar
Lock-in de frameworkAlto: a lógica de orquestração vive dentro do formato de action-group do BedrockBaixo: Strands é Python sob Apache 2.0, o mesmo SDK que a Amazon usa internamente no Q Developer, no AWS Glue e no VPC Reachability AnalyzerNenhum, e nenhuma ajuda também
Integração de toolsAction groups definidos por schemas OpenAPIO AgentCore Gateway transforma APIs, funções Lambda e serviços existentes em tools compatíveis com MCP; o próprio MCP é um padrão aberto desde novembro de 2024Tool calling feito manualmente contra a Converse API
ObservabilidadeRastro de raciocínio embutido, com controle limitado sobre como ele é exportadoTraces OTEL para o CloudWatch generative AI observability, em dashboards própriosO que você mesmo instrumentar
Cobertura de TerraformRecursos de action-group já consolidadosRecursos nativos aws_bedrockagentcore_* desde 16 de outubro de 2025, com blocos HCL aninhados de verdade em vez de mapas de atributos em formato JSONNenhum recurso específico de serviço necessário, só IAM e compute
Melhor encaixeUm número pequeno de agentes simples, quando se quer o mínimo de orquestração customizadaMúltiplos agentes que precisam de controle sobre o loop, e um futuro multi-provedorOrquestração tão específica que nenhum framework a expressa bem

O time escolheu AgentCore mais Strands. As peças gerenciadas que eles não queriam possuir (isolamento de sessão, corretagem de credenciais, um gateway entre contas) vieram acompanhadas de uma camada de orquestração que dava para ler e depurar, em vez de uma que precisaria ser reconstruída por engenharia reversa a partir de um console. O AgentCore Runtime, na disponibilidade geral, dá a cada sessão de agente isolamento completo e uma janela de execução de oito horas, com suporte ao protocolo Agent2Agent caso os agentes desta plataforma um dia precisem conversar com agentes de fora dela. A CLI @aws/agentcore, lançada em 22 de abril de 2026, adicionou um fluxo de create, dev, deploy, invoke, logs e traces que suporta Strands diretamente (também LangGraph, LangChain, Google ADK, OpenAI Agents, ou traga o seu próprio), o que fez diferença para o onboarding: ninguém precisou aprender uma ferramenta de deploy totalmente nova no meio do projeto.

O Bedrock Agents clássico continuou genuinamente atraente por méritos próprios: dois anos e meio de histórico em produção, menos código customizado para manter, e um modelo mental mais simples quando um action group é tudo que o caso de uso precisa. Ele perdeu aqui porque quatro agentes compartilhando um padrão de supervisor através de uma fronteira IAM rígida entre múltiplas contas precisavam de mais controle sobre o loop do que os action groups expõem. Construir do zero sobre a Converse API crua foi rejeitado pelo motivo oposto: escrever um armazenamento de sessão, um corretor de credenciais e um pipeline de rastreamento do zero, para algo que a AWS já entrega pronto como AgentCore Runtime, Memory, Identity e Observability, teria gasto os primeiros dois meses do projeto reinventando infraestrutura em vez de escrever agentes.

Arquitetura alvo: hub-and-spoke pela organização

A plataforma é multi-conta, hub-and-spoke, e deliberadamente aditiva. Se ela cair, a organização degrada exatamente para o status quo: os humanos são acionados da mesma forma de sempre. Ela nunca fica no caminho crítico de alertas.

graph TD
  MGMT["management account
org root, SCPs, no workloads"] SEC["security account
org CloudTrail, GuardDuty,
Security Hub delegated admin"] OPS["ops-tooling account
the agent platform"] W1["workload account 1"] W2["workload account 2"] WN["workload account N
~30 total"] MGMT -. SCPs .-> SEC MGMT -. SCPs .-> OPS MGMT -. SCPs .-> W1 MGMT -. SCPs .-> W2 MGMT -. SCPs .-> WN W1 -- alarm events --> OPS W2 -- alarm events --> OPS WN -- alarm events --> OPS OPS -- "assume ops-readonly, read-only tools" --> W1 OPS -- "assume ops-readonly, read-only tools" --> W2 OPS -- "assume ops-readonly, read-only tools" --> WN OPS -. "assume ops-mutate, approved executor only" .-> W1

Cada conta de workload expõe exatamente duas roles para o ops-tooling: ops-readonly, assumível por toda Lambda de tool, construída a partir de managed policies de somente leitura da AWS com denies explícitos empilhados por cima; e ops-mutate, assumível por exatamente uma Lambda, aquela que roda depois que um humano aprova uma ação proposta, restrita a uma allowlist de documentos específicos do SSM Automation. Nenhum agente, nenhuma outra Lambda, nenhum caminho de código além desse único executor pós-aprovação pode assumir ops-mutate. Essa separação, e não uma instrução de prompt, é o que torna o somente-leitura o padrão: ela é imposta pelo IAM, não por pedir educadamente a um modelo.

Dentro do ops-tooling

Dando zoom na única conta onde o resto desta série vive:

graph LR
  subgraph entry["Entry"]
    EB["EventBridge bus
cross-account alarms"] SLACK["Slack app
API Gateway + verifier Lambda"] SCHED["EventBridge Scheduler
daily cost sweep"] end subgraph agents["Agent layer"] SUP["supervisor"] TRI["incident-triage"] RUN["runbook"] COST["cost"] MEM["AgentCore Memory"] ID["AgentCore Identity"] end subgraph models["Model layer"] BR["Bedrock, cross-region
inference profiles"] GR["Bedrock Guardrails"] KB["Knowledge Base
runbook corpus"] end subgraph tools["Tool layer"] GW["AgentCore Gateway"] T1["cloudwatch-read"] T2["logs-read"] T3["cost-read"] T4["ssm-execute"] end subgraph gate["Approval gate"] SF["Step Functions
waitForTaskToken"] EXEC["post-approval executor"] end subgraph obs["Observability"] OTEL["OTEL traces to CloudWatch
GenAI observability"] LOG["invocation logs to S3"] BUD["Budgets + Cost
Anomaly Detection"] end EB --> SUP SLACK --> SUP SCHED --> COST SUP --> TRI SUP --> RUN SUP --> COST TRI --> MEM RUN --> MEM COST --> MEM SUP --> ID TRI --> BR RUN --> BR COST --> BR BR --> GR TRI --> KB RUN --> KB SUP --> GW GW --> T1 GW --> T2 GW --> T3 GW --> T4 T4 --> SF SF -- "context + Slack approval" --> SLACK SF -- approved --> EXEC BR --> OTEL GW --> LOG COST --> BUD

Quatro agentes Strands rodam no AgentCore Runtime atrás de um supervisor: triagem de incidentes, execução de runbook e otimização de custo. O AgentCore Memory guarda estado de sessão e de longo prazo para que o supervisor não fique reexplicando contexto a um especialista a cada salto, e o AgentCore Identity faz a corretagem das credenciais de saída para que o código de nenhum agente carregue um segredo de longa duração. Toda chamada de modelo passa por inference profiles entre regiões para confiabilidade e folga em picos, com Bedrock Guardrails anexado a cada invocação e uma Knowledge Base sobre o corpus de runbooks apoiando os agentes de triagem e de runbook. A camada de tools é inteiramente AgentCore Gateway: Lambdas de privilégio mínimo por trás de MCP, três delas somente leitura e assumindo ops-readonly em um spoke, uma delas, a ssm-execute, a única tool que muta algo, e ela não consegue alcançar uma conta spoke diretamente. Ela só pode entregar uma proposta ao Step Functions, que pausa em waitForTaskToken, posta o contexto completo (diagnóstico, raio de impacto, documento SSM exato e parâmetros) no Slack, e só retoma quando um humano aprova. Cada salto do EventBridge até o Gateway carrega tracing OTEL para o CloudWatch generative AI observability, os logs de invocação do Bedrock caem no S3, e o Budgets junto com o Cost Anomaly Detection monitoram o próprio gasto da plataforma com tags de alocação de custo por agente.

Somente leitura é a diretriz primordial

Todo agente nesta plataforma pode olhar para qualquer coisa relevante e não mudar nada, a menos que um humano já tenha dito sim. Essa é a frase única à qual o resto desta série tem que continuar sendo fiel. Ela se manifesta como três decisões de IAM separadas e nada glamourosas, em vez de um único mecanismo engenhoso: as Lambdas de tool assumem uma role de somente leitura construída a partir de managed policies da AWS com denies explícitos empilhados por cima; a única tool que muta algo não consegue assumir nada em uma conta spoke diretamente; e o único caminho de "o agente propôs uma correção" até "a correção rodou" passa por um wait state do Step Functions que só um clique humano no Slack pode retomar.

Somente leitura não é uma instrução de prompt pedindo para um modelo ter cuidado. Modelos que recebem esse pedido educado ainda assim, de vez em quando, não têm cuidado. Aqui, somente leitura é uma fronteira de IAM que um prompt comprometido, uma chamada de tool alucinada ou um bug qualquer não conseguem atravessar, porque as credenciais para atravessá-la não existem em lugar nenhum ao alcance do agente.

O que "pronto" significa aqui

O final desta série volta a estes números. São os mesmos que o inventário de trabalho manual lá em cima preparou, agora tornados mensuráveis:

  • Tempo mediano entre o chamado e um resumo enriquecido no Slack (logs, dashboards, causa provável) abaixo de 5 minutos, descendo dos atuais 25 a 35 minutos de coleta manual.
  • Zero ações de mutação executam sem uma aprovação humana registrada pelo Step Functions, verificada semanalmente contra o CloudTrail.
  • O atraso mensal do FinOps cai de cerca de três semanas para o mesmo dia, através da varredura diária de custos que traz à tona anomalias antes do fechamento do próximo ciclo de faturamento.
  • A defasagem dos runbooks (ARNs quebrados, caminhos de console mortos) cai de cerca de um em três para menos de um em vinte, detectada continuamente em vez de numa auditoria anual.
  • Um game day trimestral desativa o ops-tooling por completo e confirma que o acionamento de plantão e a execução manual de runbooks continuam funcionando exatamente como antes dele existir, sem nenhuma dependência silenciosa criada.
  • O diagnóstico de triagem de incidentes acerta a causa raiz eventual com frequência suficiente, medida contra um limite numérico que o eval harness definirá numa parte posterior, para que o plantão pare de reverificá-lo manualmente antes de agir sobre ele.

Modos de falha para observar desde o primeiro dia

Três coisas para observar antes de qualquer um tocar no Terraform, porque a definição do cenário é o lugar errado para descobri-las em produção. Se uma tool de leitura não consegue alcançar uma conta spoke, seja porque a assunção de role foi negada ou porque um serviço está degradado, o agente precisa dizer isso e devolver a decisão a um humano, não chutar: um diagnóstico errado dito com confiança é pior do que nenhum diagnóstico. Se o próprio portão de aprovação travar, tanto o wait state quanto a state machine ao redor precisam de timeouts explícitos, ou uma proposta presa envelhece silenciosamente em vez de falhar de forma visível. E se o ops-tooling cair por completo, a plataforma precisa degradar exatamente para o processo de hoje, o que só se sustenta se nada fora do ops-tooling começar a depender silenciosamente da existência dele.

Leia isso a seguir

Os dois diagramas acima, e o layout de contas e IAM que eles descrevem, também vivem como Markdown no repositório complementar em github.com/flightlesstux/agents-on-call, ao lado do Terraform e do Python que esta série constrói parte por parte. Para o lado de banco de dados e infraestrutura de rodar esse tipo de plataforma em escala, as notas de campo estão em ercan.cloud, e o hub fica em ercanermis.com.