# Web Search do Bedrock no GovCloud: um ADR sobre grounding regulado

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.

- URL: https://fernando.moretes.com/blog/web-search-do-bedrock-no-govcloud-um-adr-sobre-grounding-regulado-web-search-o

- Markdown: https://fernando.moretes.com/blog/web-search-do-bedrock-no-govcloud-um-adr-sobre-grounding-regulado-web-search-o/article.md?lang=pt

- Published: 2026-09-03T10:16:16.722Z

- Category: IA & Agentes

- Tags: bedrock, govcloud, grounding, iam, finops, adr, compliance, observability

- Reading time: 8 min

- Source: [Web Search on Amazon Bedrock is now available in AWS GovCloud (US-West)](https://aws.amazon.com/about-aws/whats-new/2026/09/amazon-bedrock-web-aws-govcloud/)

---

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`)

**Pros**
- 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_citation` com 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.

**Cons**
- 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`.

**Verdict:** Escolhida.

### B. Web Search nativo com acesso à web externa (`true` + `ExternalWebAccess`)

**Pros**
- Melhor frescor: em cache miss o Fetch busca a página na origem.

**Cons**
- 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 `...ExternalWebSearchReadOnly` e `...FullAccess` têm hoje permissões efetivas idênticas; "ReadOnly" não restringe nada.

**Verdict:** Rejeitada para carga regulada.

### C. Web Search do Bedrock AgentCore via Gateway

**Pros**
- 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.

**Cons**
- 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:*`.

**Verdict:** Mantida como plano B multi-modelo.

### D. Índice/busca própria (crawler ou API de busca de terceiro)

**Pros**
- Controle total de fontes: allowlist de domínios de verdade, que nenhuma das opções nativas entrega.

**Cons**
- 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.

**Verdict:** 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.

### 🟧 AWS GovCloud (us-gov-west-1) — bedrock-mantle

- Responses API tools=[web_search] (compute)
- openai.gpt-5.6-terra decides if it needs current info (ai)

### 🔐 IAM — bedrock-websearch (Resource: *)

- InvokeSearch aws:RequestedRegion = us-gov-west-1 (security)
- InvokeFetch fetchMode = USE_CACHE_ONLY (security)
- ExternalWebAccess DENIED by SCP (security)

### 🔎 Retrieval tier — in-Region, no cross-Region routing

- Bedrock web index + knowledge graph (data)
- Bedrock page cache title + URL + content (storage)

### 🌐 Outside the AWS boundary

- External web origin reached only on cache miss (external)

### 📋 Audit & provenance — my responsibility

- CloudTrail data events fetchedSources, urlCount (messaging)
- DynamoDB citation ledger PK run#<id> / SK cite#<sha256(url)> (data)
- UI footnotes url_citation offsets (frontend)

### Fluxos

- app -> resp: 1. POST /openai/v1/responses
- resp -> model: 2. tool exposto ao modelo
- model -> g1: 3. query (sem saída de rede)
- g1 -> index: 4. ≤5 observações (low)
- index -> model: 5. título + URL + snippet
- model -> g2: 6. Fetch da URL escolhida
- g2 -> cache: 7. lê só o cache
- g2 -> g3: 8. cache miss → precisa da permissão
- g3 -> web: 9. bloqueado (AccessDenied)
- g2 -> ct: 10. CACHE / EXTERNAL_WEB counts
- model -> app: 11. texto + url_citation (HTTP 200 mesmo se 9 falhou)
- app -> ledger: 12. persiste procedência
- ledger -> ui: 13. renderiza a fonte

> **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

- **security**: 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.
- **reliability**: 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é.

> **Nota do curador:** 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.

**Rating:** Adopt — cache-only, with SCP + CloudTrai

## Referências

- [AWS What's New — Web Search on Amazon Bedrock is now available in AWS GovCloud (US-West) (Sep 2, 2026)](https://aws.amazon.com/about-aws/whats-new/2026/09/amazon-bedrock-web-aws-govcloud/)
- [Amazon Bedrock User Guide — Web Search (models, Regions, search_context_size, external_web_access, citations)](https://docs.aws.amazon.com/bedrock/latest/userguide/web-search.html)
- [Amazon Bedrock User Guide — Identity and access management for Web Search (bedrock-websearch actions, managed policies, ](https://docs.aws.amazon.com/bedrock/latest/userguide/security-web-search.html)
- [Amazon Bedrock User Guide — Monitor Web Search (CloudTrail data events, fetchMode, fetchedSources)](https://docs.aws.amazon.com/bedrock/latest/userguide/monitoring-web-search.html)
- [AWS ML Blog — Introducing Web Search on Amazon Bedrock for foundation model grounding](https://aws.amazon.com/blogs/machine-learning/introducing-web-search-on-amazon-bedrock-for-foundation-model-grounding/)
- [Amazon Bedrock AgentCore pricing — Web Search at US$ 7 per 1,000 queries](https://aws.amazon.com/bedrock/agentcore/pricing/)
- [AWS What's New — OpenAI GPT and NVIDIA Nemotron models on Bedrock receive FedRAMP High and DoD IL-4/5 in AWS GovCloud (U](https://aws.amazon.com/about-aws/whats-new/2026/06/addl-bedrock-model-fedramp-il-5-govcloud/)
- [Unite.AI — Amazon Bedrock gets built-in Web Search for grounded model responses (independent analysis, per-query pricing](https://www.unite.ai/amazon-bedrock-gets-built-in-web-search-for-grounded-model-responses/)
