Web Search do Bedrock no GovCloud: um ADR sobre grounding regulado
Ouvir estudo
gerado ao ouvirGerado apenas no primeiro play
Com tecnologia Amazon Polly + OmniVoice
Em 2 de setembro de 2026 a AWS levou o tool server-side Web Search do Amazon Bedrock para o GovCloud (US-West). Escrevi este ADR porque a decisão real não é "usar ou não busca web" — é escolher em que modo de recuperação você opera, e o default do parâmetro `external_web_access` empurra times regulados exatamente para o lado errado, com HTTP 200 e sem erro visível.
A nota da AWS de 2 de setembro de 2026 tem duas linhas de substância: o tool built-in Web Search do Amazon Bedrock passou a existir no GovCloud (US-West), e ele mantém os dados da requisição dentro da fronteira AWS por padrão. Para quem projeta plataformas de IA em ambiente regulado — governo, mas também banco, seguradora, meio de pagamento — isso não é "mais um recurso". É a diferença entre um agente que cita fontes verificáveis e um agente que responde de memória sobre preços, limites de serviço e regulação que mudaram semana passada. Só que a decisão de adoção não é binária, e o caminho de menor resistência é o errado. Este é o registro de decisão que eu escreveria antes de habilitar isso em produção.
Contexto e forças
O problema que me trouxe aqui é antigo: sistemas de decisão assistida por LLM em domínio regulado precisam de procedência, não de fluência. Quando um analista pergunta "qual é o limite atual dessa cota" ou "esse produto ainda está autorizado", uma resposta sem link auditável é passivo, não ativo.
Até agora eu resolvia isso com uma das duas soluções feias. A primeira: um índice próprio — crawler, normalização, embeddings, reindexação — que ninguém quer operar e que envelhece em dias. A segunda: uma API de busca de terceiro chamada de dentro de uma Lambda, o que significa egress para fora da conta, segredo em rotação, allowlist de saída e o loop de tool-call escrito à mão. Eu mantive exatamente esse desenho por meses no meu próprio pipeline de conteúdo, com SigV4 artesanal, e ele quebrava por motivos que não tinham nada a ver com o problema de negócio.
As forças que pesam nesta decisão, em ordem de dureza: (1) o dado da consulta não pode sair da fronteira do provedor sem controle explícito — em GovCloud isso é requisito, não preferência; (2) toda afirmação exibida ao usuário precisa carregar fonte; (3) o custo é por query e não é desprezível; (4) o time não deve operar índice nem crawler; (5) a autorização precisa ser central, aplicável por Organization e por Região, com trilha de auditoria. A chegada do tool no GovCloud endereça (1) e (4), mas (2), (3) e (5) continuam sendo trabalho meu.
O que o tool é de fato — e onde ele vive
Antes de decidir, vale precisar o objeto. Web Search é um tool server-side exposto apenas pela Responses API no endpoint bedrock-mantle — não existe em bedrock-runtime, nem em Converse, nem em InvokeModel. Você adiciona {"type": "web_search"} ao array tools usando a própria biblioteca cliente da OpenAI com uma API key do Bedrock, e o modelo decide se chama. Isso amarra a decisão a uma família de modelos: na documentação, os suportados no GovCloud (US-West) são openai.gpt-5.6-terra, openai.gpt-5.6-luna e openai.gpt-5.4; nas Regiões comerciais entram também gpt-5.6-sol e gpt-5.5. Não há Claude, Nova ou Mistral nesse caminho.
Internamente são duas operações, e essa distinção é o coração do ADR. Search devolve título, URL e snippet a partir do índice web e do knowledge graph mantidos pela Amazon, e não faz chamada de saída. Fetch recupera o conteúdo de uma URL específica: com external_web_access=false ele lê só o cache do Bedrock; com true ele consulta o cache e vai à web externa apenas em cache miss.
A recuperação é estritamente regional — cada Região opera seu próprio tier de search e fetch, e consultas, fetches, índice e resultados não trafegam entre Regiões. us-gov-west-1 é uma dessas Regiões, ao lado de us-east-1, us-east-2 e us-west-2. O volume de contexto por chamada de Search se controla com search_context_size: low até 5 observações, medium até 11 (default), high até 25 — orçamento compartilhado entre múltiplas queries da mesma chamada. Esse parâmetro não limita quantas chamadas de Search o modelo faz por turno.
Opções consideradas
A. Web Search nativo em modo cache-only (`external_web_access: false`)
- Dado da requisição não sai da fronteira AWS; Search vem do índice do Bedrock e Fetch só do cache.
- Zero infraestrutura: sem crawler, sem índice, sem loop de tool-call, sem segredo de terceiro para rotacionar.
- Citações
url_citationcom offsets de caractere saem do próprio modelo, prontas para renderizar como nota de rodapé. - Não requer a permissão
ExternalWebAccess, então convive com um SCP de negação total dela.
- Frescor limitado ao que já está no índice e no cache da Amazon — você não controla a janela de atualização.
- Amarra o grounding aos modelos GPT no endpoint
bedrock-mantle.
Escolhida.
B. Web Search nativo com acesso à web externa (`true` + `ExternalWebAccess`)
- Melhor frescor: em cache miss o Fetch busca a página na origem.
- A própria documentação da AWS registra risco de exfiltração: um agente pode codificar dados da consulta numa URL e mandar buscá-la.
- Dado da requisição pode sair da fronteira AWS — inaceitável no perímetro que motivou o GovCloud.
- As policies gerenciadas
...ExternalWebSearchReadOnlye...FullAccesstêm hoje permissões efetivas idênticas; "ReadOnly" não restringe nada.
Rejeitada para carga regulada.
C. Web Search do Bedrock AgentCore via Gateway
- Agnóstico de modelo: serve agentes em Claude, Nova ou qualquer outro, não só GPT.
- Preço por query menor — a página do AgentCore lista US$ 7 por 1.000 queries.
- Superfície a operar: Gateway, identidade do agente, assinatura da chamada. Eu tinha exatamente isso e aposentei.
- Governança fica em outro plano de controle, não nos três actions
bedrock-websearch:*.
Mantida como plano B multi-modelo.
D. Índice/busca própria (crawler ou API de busca de terceiro)
- Controle total de fontes: allowlist de domínios de verdade, que nenhuma das opções nativas entrega.
- Egress, segredo, retry, backoff, dedupe e o loop de tool-call passam a ser código seu — e é onde meus incidentes moravam.
- Custo operacional recorrente sem ganho de qualidade proporcional.
Rejeitada, exceto se allowlist de fonte for requisito regulatório explícito.
Decisão
Decisão: adotar a opção A — Web Search nativo em modo cache-only — com o parâmetro explícito e a permissão negada por SCP. A configuração é deliberadamente redundante, porque parâmetro e permissão falham de formas diferentes.
No código, todo chamador manda tools=[{"type":"web_search","external_web_access":false,"search_context_size":"low"}]. low é o default do meu wrapper; medium é opt-in por rota de pesquisa; high exige justificativa, porque 25 observações por chamada inflam o input em algo na ordem de 10 mil tokens por turno.
No perímetro, um SCP nega bedrock-websearch:ExternalWebAccess em toda a Organization. Note que a granularidade de IAM aqui é curta: os três actions (InvokeSearch, InvokeFetch, ExternalWebAccess) só suportam Resource: "*", e a única condition key é a global aws:RequestedRegion. Logo, o "restringir por Região" que o anúncio menciona é literalmente um StringEquals em aws:RequestedRegion — e eu o uso para fixar us-gov-west-1 no perímetro regulado, o que também é a única forma de impedir que uma chamada mal roteada seja atendida por outro tier regional.
Nas roles de aplicação eu não uso AmazonBedrockFullAccess. Uso uma policy própria com InvokeSearch e InvokeFetch sob a condition de Região, porque quero que a ausência de ExternalWebAccess seja uma decisão registrada e não um efeito colateral de qual policy gerenciada alguém anexou.
CloudTrail com data events habilitado para bedrock-websearch é parte da decisão, não um follow-up: sem isso, negações não são reportadas em lugar algum.
Um turno com Web Search: dois portões de IAM e um limite de fronteira
O caminho de uma única chamada da Responses API. Search nunca sai da fronteira; Fetch é o único ponto onde a saída é possível, e é onde o SCP corta. Repare que a resposta ao chamador volta 200 mesmo quando o Fetch é negado.
- Responses API · tools=[web_search]
- openai.gpt-5.6-terra · decides if it needs current info
- InvokeSearch · aws:RequestedRegion = us-gov-west-1
- InvokeFetch · fetchMode = USE_CACHE_ONLY
- ExternalWebAccess · DENIED by SCP
- Bedrock web index · + knowledge graph
- Bedrock page cache · title + URL + content
- External web origin · reached only on cache miss
- CloudTrail data events · fetchedSources, urlCount
- DynamoDB citation ledger · PK run#<id> / SK cite#<sha256(url)>
- UI footnotes · url_citation offsets
A consequência que morde: degradação silenciosa com HTTP 200
external_web_access tem default true, para compatibilidade com a API da OpenAI. Só que AmazonBedrockFullAccess concede InvokeSearch e InvokeFetch e não concede ExternalWebAccess. Nessa combinação — a mais comum — cada tentativa de Fetch falha a autorização de backend antes de ler o cache: você perde o conteúdo de página que teria de graça. E a requisição da Responses API ainda retorna HTTP 200, com anotações url_citation derivadas apenas das observações de Search, sem garantia de que o modelo mencione a falha. Ou seja: qualidade de grounding cai, ninguém vê erro, e nenhum alarme dispara. Pior: a presença de citação não prova que a página foi buscada. A única evidência confiável é o campo additionalEventData.fetchedSources no CloudTrail — que só existe se você tiver habilitado data events para bedrock-websearch, que não vêm por padrão e são cobrados à parte.
Consequências: custo, latência e o que o CloudTrail não conta
Custo. A cobrança é por query — uma query é um pedido de busca. A página do AgentCore lista US$ 7 por 1.000 queries para a variante dele; análises independentes colocam o tool built-in perto de US$ 12 por 1.000 (confirme a taxa vigente para sua Região na página de preços do Bedrock antes de modelar). O detalhe que quebra orçamento não é o preço unitário: é que search_context_size limita observações por chamada, mas não limita quantas chamadas de Search o modelo faz no turno. Um turno multi-hop que reformula a query duas vezes custa três queries. A US$ 0,012 por query, 50 mil turnos/mês a 3 queries dão US$ 1.800/mês só de busca, antes da inferência — e antes do input inflado pelas observações. Modele por turno, não por query, e ponha um teto diário de tokens e de queries no seu próprio wrapper, do lado da aplicação, porque o IAM não conta chamadas.
Idempotência. A chamada não é idempotente: um retry re-executa as buscas e cobra de novo. Meu retry é max_attempts=2 com jitter e um cache de resultado com chave sha256(prompt + modelId + toolConfig), TTL de horas. Timeout do lado do chamador precisa acomodar hops seriais dentro de uma única chamada — no meu gerador de artigos eu opero com 300 s de teto.
Auditoria. Por design, o CloudTrail não expõe o texto da query, as URLs nem os resultados brutos: o texto é tratado como prompt de inferência. Você tem quem chamou, quando, de onde, fetchMode, urlCount, failedUrlCount e fetchedSources. Se o seu regulador quer saber o que o agente consultou, essa resposta não está no CloudTrail — está no ledger de citações que você gravar a partir das anotações url_citation.
Consequências por pilar
Segurança
Cache-only mais SCP negando ExternalWebAccess fecha o único caminho de egress e o vetor de exfiltração por URL que a própria AWS documenta. Em troca, aceito que Resource é sempre * e que a única condition key é aws:RequestedRegion — não há allowlist de domínio no IAM.
Confiabilidade
O modo de falha dominante deixa de ser erro e passa a ser silêncio: Fetch negado com HTTP 200. Mitigo com data events do CloudTrail e um alarme na razão failedUrlCount / urlCount, além de contar respostas sem nenhuma anotação url_citation em rotas que deveriam citar.
Modos de falha que eu vigio depois de decidir
A ilusão do ReadOnly. AmazonBedrockExternalWebSearchReadOnly e AmazonBedrockExternalWebSearchFullAccess têm hoje permissões efetivas idênticas — a versão "ReadOnly" não restringe recuperação externa. Se sua revisão de segurança aprova por nome de policy, ela aprovou acesso à web externa achando que aprovou leitura.
A ilusão de restringir fontes por IAM. Negar InvokeSearch e manter InvokeFetch não confina o modelo às suas fontes: Fetch se aplica a qualquer URL que o modelo produza, inclusive uma que veio do prompt do usuário ou da memória paramétrica dele. Controle de fonte, se for requisito, tem de morar na aplicação — validando cada URL citada contra uma allowlist antes de exibir.
Injeção de prompt via conteúdo recuperado. Snippet e página buscada são entrada não confiável entrando no mesmo contexto que a instrução. Em carga regulada eu trato observação de Search como dado, nunca como comando, e mantenho o filtro de injeção antes e depois do turno — a mesma disciplina que aplico em qualquer RAG.
Frescor que engana. Em cache-only o frescor é o do índice e do cache da Amazon, não o da origem. Para preço, cota e prazo regulatório eu não confio no que voltou sem conferir a data na própria resposta — e escrevo em volta do que não puder confirmar.
Acoplamento de modelo. O tool só existe na Responses API sobre bedrock-mantle, em modelos GPT. Se amanhã a decisão de modelo mudar para Claude ou Nova, o grounding muda de caminho junto — por isso mantive a opção C viva no registro, e não como nota de rodapé.
Eu já vivi esse ADR do lado errado. No meu próprio pipeline de conteúdo eu mantinha um Lambda que falava com um AgentCore Gateway de busca via SigV4 artesanal e tinha um bug de dedupe que sempre respondia "é novo": nenhum erro, HTTP 200, e artigos repetidos sobre o mesmo assunto por dias. Substituí aquilo por busca nativa do provedor e o problema deixou de ser meu. É exatamente a mesma classe de falha do Fetch negado com 200 aqui — e a lição que ficou é a que eu aplicaria de novo: o que a resposta HTTP não conta, você precisa medir por fora. Antes de habilitar Web Search em qualquer conta regulada, eu ligo data events do CloudTrail para bedrock-websearch, aponto external_web_access: false no código, nego ExternalWebAccess por SCP, e só depois abro a rota — na ordem inversa, você descobre o problema por reclamação de usuário, não por alarme.
Veredito
Adote — em modo cache-only, e trate a configuração como parte do controle, não como detalhe de chamada. A chegada do Web Search ao GovCloud (US-West) fecha uma lacuna real: dá grounding citável a cargas de compliance sem crawler, sem índice e sem egress, sobre modelos GPT que já tinham FedRAMP High e DoD IL-4/5 no GovCloud desde junho de 2026. O que não se resolve sozinho é a governança: external_web_access nasce true, AmazonBedrockFullAccess não concede a permissão correspondente, e a combinação degrada o grounding retornando HTTP 200. Escreva false explicitamente, negue ExternalWebAccess por SCP, fixe a Região com aws:RequestedRegion, ligue data events do CloudTrail antes do primeiro tráfego, e mantenha seu próprio ledger de citações — o CloudTrail não guarda o que foi consultado. Feito assim, é o melhor custo-benefício de grounding disponível hoje em ambiente regulado. Feito no default, é uma regressão de qualidade que ninguém vai ver.
Referências
Post-mortems, ADRs e deep dives de arquitetura no seu email — do jeito que um arquiteto lê.
Sem spam · cancele quando quiser
Pergunte ao Fernando sobre isto
Receba uma resposta focada sobre este estudo do meu assistente de IA, baseada no meu trabalho.
Participe da conversa
Entre para comentar
Confirme seu e-mail para participar — você também recebe a newsletter. Sem senha.