---
title: "Como alimentar o Amazon Bedrock Knowledge Bases com dados da web em tempo real usando o Bright Data"
slug: amazon-bedrock-knowledge-bases-with-bright-data
date: 2026-10-15T18:58:03+00:00
modified: 2026-10-15T18:58:06+00:00
permalink: https://brightdata.com.br/blog/procedimentos/amazon-bedrock-knowledge-bases-with-bright-data
type: blog
---

[ Blog ](https://brightdata.com.br/blog "Blog") / [procedimentos](https://brightdata.com.br/blog/procedimentos)







 [procedimentos](https://brightdata.com.br/blog/procedimentos)

# Como alimentar o Amazon Bedrock Knowledge Bases com dados da web em tempo real usando o Bright Data

O Bright Data permite que o Amazon Bedrock Knowledge Bases acesse dados da web bloqueados por renderização JavaScript, proteção contra bots ou restrições geográficas.

 54 min de leitura





 [ ![Satyam Tripathi](https://media.brightdata.com.br/2024/09/Satyam-Tripathi-50x50.png) ](https://brightdata.com/blog/authors/satyam-tripathi)

 [Satyam Tripathi

Technical Writer

 ](https://brightdata.com/blog/authors/satyam-tripathi)





 ![Amazon Bedrock Knowledge Bases with Bright Data](https://media.brightdata.com.br/2026/08/Amazon-Bedrock-Knowledge-Bases-with-Bright-Data.png)





Amazon Bedrock Knowledge Bases é um serviço gerenciado para Retrieval Augmented Generation (RAG). Aponte-o para uma fonte de dados, e ele fragmenta, incorpora e indexa seus documentos, depois responde consultas com citações. Seus conectores incluem Amazon S3, SharePoint e um Web Crawler nativo para páginas públicas.

Esse crawler cobre a web cooperativa. Muitos catálogos de produtos, marketplaces e sites de notícias são renderizados com JavaScript, ficam atrás de proteção contra bots ou servem conteúdo diferente por país. O crawler retorna uma estrutura vazia, um bloqueio ou o conteúdo do país errado. Este guia alimenta essas páginas em uma base de conhecimento através do Bright Data e do S3.

## TL;DR

- O Web Unlocker retorna uma página pública como Markdown em uma única solicitação, a menos que a página precise de interação. Grave cada página no S3 como `.md` mais um sidecar `.metadata.json`.
- Faça hash do conteúdo antes de gravar, para que uma atualização reincorpore apenas as páginas que você reescreveu em vez de todo o corpus.
- Meça a recuperação contra um conjunto padrão de referência antes de escalar, porque o fragmentação é definida na fonte de dados e as chaves de metadados não filtráveis são definidas em um índice do S3 Vectors. Você não pode alterar nenhuma delas depois.
- Roteie por consulta. Recorra a uma busca ao vivo quando a pontuação mais alta estiver abaixo de um piso de relevância.

## Por que o Web Crawler nativo deixa uma lacuna

A [fonte de dados Web Crawler](https://docs.aws.amazon.com/bedrock/latest/userguide/webcrawl-data-source-connector.html) é um conector proprietário, e para suas próprias páginas estáticas é uma boa opção. Hoje suas restrições deixam os dados públicos da web em escala para o Bright Data.

- **Ele suporta sites estáticos.** A documentação diz isso diretamente, e o crawler não recupera páginas renderizadas no lado do cliente. Muitos catálogos, painéis e páginas de listagem são renderizados assim, então o crawler vê uma estrutura vazia onde os dados deveriam estar.
- **Foi construído para a web cooperativa.** O crawler respeita o `robots.txt` conforme a [RFC 9309](https://www.rfc-editor.org/rfc/rfc9309.html), e trata o site como não permitido quando não encontra nenhum. Ele se identifica como `bedrockbot`, e para diante da proteção contra bots por design.
- **Limita-se a 25.000 páginas por sincronização, e suporta um único armazenamento vetorial.** Se você exceder esse limite, a sincronização falha e não ingere nada. Esse armazenamento é atualmente o Amazon OpenSearch Serverless, então o S3 Vectors não é uma opção com o crawler.
- **Ele retorna páginas, não registros limpos.** O crawler ingere HTML. Navegação, avisos de cookies e elementos padrão chegam junto com o conteúdo, e não há etapa de extração estruturada. Seus fragmentos carregam o ruído para as incorporações.

O console rotula o Web Crawler como Preview na tela onde você o escolhe:

![Assistente de criação de base de conhecimento do Amazon Bedrock, Escolher tipo de fonte de dados. Amazon S3 está selecionado e seu cartão observa que você pode adicionar até 5 buckets como fontes de dados. Confluence, Salesforce, SharePoint e Web Crawler estão todos rotulados como Preview, e Custom não está. Uma caixa vermelha marca o cartão do Web Crawler.](https://media.brightdata.com/2026/08/Sy9Mv2Zvze.png)Nada disso torna o crawler a escolha errada para as páginas estáticas e cooperativas para as quais foi construído. No relatório [*Data for AI* 2026](/ai/data-for-ai-report) do Bright Data, 90% das organizações de IA disseram que restrições de acesso estão limitando suas iniciativas. [O Cloudflare reclassificou os crawlers de IA](https://techcrunch.com/2026/07/01/cloudflares-new-policy-pushes-ai-companies-to-pay-for-publishers-content/) em julho de 2026. Disse que bloquearia por padrão crawlers de uso misto a partir de 15 de setembro de 2026, em páginas financiadas por anúncios para novos clientes, novos sites e contas gratuitas inalteradas. Manter o acesso funcionando exige esforço contínuo, e o Bright Data faz isso por você.

## A arquitetura

O pipeline transforma uma lista de URLs públicas em uma base de conhecimento pesquisável e citável, e a mantém atualizada. O Bright Data é responsável pelo acesso e pela conversão em Markdown. O S3 é a entrega. O Bedrock é responsável pela indexação e recuperação.

![Diagrama de fluxo. URLs públicas vão para o Bright Data Web Unlocker, que retorna Markdown. Um Loader Lambda grava arquivos .md mais sidecars .metadata.json no Amazon S3. Uma Bedrock Knowledge Base sincroniza esse bucket, incorpora em um armazenamento vetorial e retorna respostas citadas a um Aplicativo.](https://media.brightdata.com/2026/08/SkyEDnZwfg.png)O diagrama mostra o caminho principal:

1. O loader chama o Web Unlocker do Bright Data para proteção contra bots, roteamento geográfico e renderização de JavaScript, e recebe de volta a página como Markdown.
2. O loader então grava cada página no Amazon S3 como um documento `<slug>.md` mais um sidecar `<slug>.md.metadata.json`.
3. A base de conhecimento sincroniza a fonte de dados do S3 com `StartIngestionJob`, e execuções posteriores são incrementais.
4. O Bedrock fragmenta cada documento, incorpora-o com o Titan Text Embeddings V2, e grava os vetores em um armazenamento como o Amazon S3 Vectors ou o OpenSearch Serverless.
5. Um aplicativo chama `Retrieve` para fragmentos brutos ou `RetrieveAndGenerate` para uma resposta fundamentada e citada.

Três partes do wrapper de produção ficam fora desse caminho principal, e o diagrama não as mostra. Uma programação do Amazon EventBridge aciona o loader com a frequência que você escolher, o AWS Secrets Manager armazena o token do Bright Data, e a base de conhecimento assume uma função IAM com privilégio mínimo. O diagrama desenha o loader como uma Lambda, que é onde ele roda uma vez agendado. Abaixo, você executa o loader a partir da sua própria máquina, para que possa observar cada etapa primeiro.

Gravar no S3 é uma escolha em vez de um padrão, porque você também pode enviar documentos diretamente através de uma fonte de dados personalizada e a API `IngestKnowledgeBaseDocuments`. O bucket é a melhor opção para um corpus raspado. Os objetos permanecem reproduzíveis, os sidecars permitem inspecionar o contrato de metadados, e uma execução com falha deixa o corpus anterior intacto. A Crawl API do Bright Data também entrega nativamente no mesmo bucket.

## Pré-requisitos

- Uma conta AWS com acesso ao Amazon Bedrock em uma Região suportada, e o Amazon Titan Text Embeddings V2 (`amazon.titan-embed-text-v2:0`) habilitado lá. Mais um modelo de geração na mesma Região, se você quiser respostas escritas da Etapa 5 em vez de fragmentos brutos.
- Um principal IAM que possa criar buckets S3, bases de conhecimento e fontes de dados do Bedrock, um armazenamento vetorial, e a função de serviço IAM que a base de conhecimento assume. O [repositório complementar](https://github.com/luminati-io/bedrock-web-kb-brightdata) lista as declarações exatas de política.
- Uma conta Bright Data com um token de API e uma zona **Web Unlocker**. Toda solicitação referencia a zona pelo nome. Um nível gratuito cobre os primeiros 5.000 créditos a cada mês, o suficiente para percorrer todas as etapas abaixo. Verifique o limite e a taxa atuais em [Preços do Web Unlocker](/pricing/web-unlocker), depois defina um limite de gastos na zona.
- Python 3.10 ou posterior com `boto3` e `requests`.
- Conhecimento prático de Amazon S3 e IAM.

O modelo de incorporação define sua dimensão vetorial, e o índice do seu armazenamento vetorial precisa corresponder a ela. Se você criar o armazenamento manualmente e os dois discordarem, a ingestão falha no momento da gravação. O Titan Text Embeddings V2 suporta 256, 512 ou 1024 dimensões.

Verifique sua cota de incorporação antes de construir qualquer coisa, porque ela pode ser zero e nada no fluxo de criação avisa você. Uma conta nova pode ter uma cota sob demanda de 0 solicitações por minuto para o modelo de incorporação. Essa cota permite que você crie a base de conhecimento e a fonte de dados normalmente, depois falha em todo trabalho de ingestão. Confirme isso em uma chamada, e solicite um aumento no Service Quotas se o valor for zero:

```none
aws service-quotas list-service-quotas --service-code bedrock --region YOUR_REGION \
  --query "Quotas[?contains(QuotaName,'Titan Text Embeddings V2')].[QuotaName,Value]" --output text
```

Execute contra a Região que você realmente pretende usar, porque a AWS define a cota por Região, e a diferença pode ser tudo ou nada. Em uma conta, no mesmo dia, o Titan Text Embeddings V2 tinha uma cota sob demanda aplicada de 0 solicitações por minuto e 0 tokens por minuto em US East (N. Virginia):

![AWS Service Quotas para Amazon Bedrock em US East (N. Virginia), filtrado para Titan Text Embeddings V2. Solicitações por minuto e tokens por minuto sob demanda aplicados ambos mostram 0, contra padrões de 6.000 e 300.000. Uma caixa vermelha marca essas linhas.](https://media.brightdata.com/2026/08/Sk6Nw2WvGe.png)Em US East (Ohio) o mesmo modelo tinha 60 solicitações por minuto e 300.000 tokens por minuto:

![O filtro idêntico em US East (Ohio) para a mesma conta, onde a cota aplicada mostra 60 solicitações por minuto e 300.000 tokens por minuto. Uma caixa vermelha marca as duas linhas funcionais.](https://media.brightdata.com/2026/08/BkFBv2bDMl.png)Compare o valor aplicado com o padrão da AWS no console do Service Quotas. Uma cota de 60 contra um padrão de 6.000 significa que a Região que funciona ainda está rodando a um centésimo da taxa padrão de solicitações. A cota de tokens ao lado está no padrão total, então solicitações por minuto é o limite que importa. A cerca de 300 tokens por fragmento, 60 solicitações por minuto equivalem a cerca de 18.000 tokens por minuto contra um teto de 300.000. Isso é suficiente para um corpus pequeno. É um gargalo para um grande.

Duas mensagens de erro neste pipeline nomeiam a causa errada.

- **`AccessDeniedException` em `CreateKnowledgeBase`, nomeando seu usuário e a ação.** Isso geralmente significa um `roleArn` malformado em vez de um problema de permissões, então verifique a string do ARN antes de auditar o IAM.
- **`not able to call specified bedrock embedding model` durante a ingestão.** Isso parece um erro de permissões, e uma cota de incorporação zero também o produz. Invoque o modelo de incorporação diretamente como você mesmo, e se isso também for limitado, você tem um problema de cota.

Ambos são problemas de configuração, não problemas de pipeline, e nenhum retorna uma vez que a conta esteja correta.

## Etapa 1 – Faça scraping das páginas com o Bright Data

O [Web Unlocker](/products/web-unlocker) do Bright Data recebe uma única URL e retorna a página. Verificações de bot e roteamento de proxy acontecem do seu lado, e ele renderiza JavaScript quando você habilita essa opção. Defina `data_format` como `markdown`, e em vez de HTML bruto você recebe a página como Markdown, que é a forma que você quer para RAG.

Você cria a zona Web Unlocker uma vez no painel de controle do Bright Data, e toda solicitação a referencia pelo nome. A zona também carrega a configuração de geolocalização. Para um site que serve conteúdo diferente por país, o Web Unlocker busca a partir do país que você configura lá, não de onde quer que o seu loader esteja rodando. O código abaixo precisa de duas coisas da aba Overview da zona: o nome da zona (`web_unlocker` aqui) e o token de API.

![Painel de controle do Bright Data, a aba Overview da zona web_unlocker, marcada como Active. Access details mostra a chave de API, uma lista de permissões de IP, e um exemplo cURL pronto para rodar contra api.brightdata.com/request. Uma nota diz que o limite de taxa foi definido para 1.000 solicitações por minuto até que fundos sejam adicionados. Caixas vermelhas marcam o nome da zona e a taxa pay-as-you-go de $1,50 por 1.000 solicitações, com uma taxa premium de $2,50 listada abaixo.](https://media.brightdata.com/2026/08/SJKUw2Wwfg.png)Em Python essa chamada é um único POST carregando a zona, a URL de destino e o formato de saída que você quer:

```none
import requests

BRIGHTDATA_TOKEN = "..."   # from your Bright Data account
UNLOCKER_ZONE = "web_unlocker"

def scrape_markdown(url: str) -> str:
    """Fetch a public URL as Markdown via Bright Data Web Unlocker."""
    resp = requests.post(
        "https://api.brightdata.com/request",
        headers={"Authorization": f"Bearer {BRIGHTDATA_TOKEN}"},
        json={
            "zone": UNLOCKER_ZONE,
            "url": url,
            "format": "raw",            # return the response body directly
            "data_format": "markdown",  # convert the page to Markdown
            # add "render": "true" when content exists only after client-side JS
        },
        timeout=120,
    )
    resp.raise_for_status()
    return resp.text
```

Não há sessão de navegador para gerenciar, nenhuma etapa de CAPTCHA, e nenhum parsing de HTML.

Uma falha aqui não parece uma falha. Três coisas retornam um 200 vazio em vez de um erro: um nome de zona errado, um token revogado, ou uma lista de permissões de IP que não corresponde mais ao seu endereço atual. Então `raise_for_status` aceita isso, e seu loader armazena a página como um documento em branco. Verifique o tamanho da resposta, não apenas o código de status. Para um pipeline agendado, ignore a lista de permissões e deixe o token ser a credencial.

Um navegador headless também renderizaria JavaScript para você, e essa parte permanece a mesma. O resto não é uma configuração que você define uma vez. É um fluxo em constante mudança: encontrar um IP de saída que funcione para esse alvo, personalizar cabeçalhos, e rotacionar fingerprints quando a detecção muda. O preço coloca esse risco no Bright Data, já que não cobra solicitações com falha.

Uma vez que você tem a página, ela ainda precisa ser estruturada, parseada e limpa, já que HTML é bagunçado. O formato `markdown` move esse trabalho para o lado do Bright Data.

### Ative a renderização apenas quando a página precisar

O Web Unlocker não inicia um navegador em toda solicitação. Ele usa o caminho rápido para uma página que pode buscar sem executar JavaScript. Para conteúdo que existe apenas após renderização no lado do cliente, defina `render` como `"true"`.

A página de prática quotes.toscrape.com/js injeta seu conteúdo com JavaScript. Um GET simples rendeu 96 caracteres de texto indexável e nenhuma das citações, o caminho rápido retornou uma estrutura de 191 caracteres, e apenas a solicitação renderizada retornou todos os 1.583 caracteres. Nessa página a solicitação renderizada terminou em 7,7 segundos, contra 17,1 segundos no caminho rápido. Essa é uma amostra de um em cada caminho, então trate a renderização como um interruptor de correção em vez de uma penalidade de latência, e meça seus próprios alvos.

O `8_render_check.py` do repositório executa essa verificação de três caminhos contra qualquer URL, então é a primeira coisa a tentar quando um scraping parece suspeitosamente fino. Precisa apenas de um token do Bright Data e nada mais. Você pode apontá-lo para suas próprias URLs e ver o que cada caminho retorna antes de criar um único recurso AWS. Se o conteúdo ainda estiver ausente com a renderização ativada, a página provavelmente precisa de interação, o que a Browser API cobre e o Web Unlocker não. O script sai com código diferente de zero nessa página quando você passa `--probe`:

![Saída do terminal de 8_render_check.py contra quotes.toscrape.com/js. Uma caixa vermelha marca três linhas de resultado. GET simples retorna 96 caracteres, o caminho rápido 191, e a solicitação renderizada 1.583 com o probe FOUND.](https://media.brightdata.com/2026/08/Sygg_w2WwMe.png)As páginas raspadas abaixo são renderizadas no servidor, então a execução padrão deixa `render` desativado.

### Faça scraping de uma lista, não de uma página

O Web Unlocker é uma solicitação por URL, então um pequeno pool de threads mantém a taxa de transferência alta sem uma estrutura pesada:

```none
from concurrent.futures import ThreadPoolExecutor

def scrape_many(urls: list[str], workers: int = 8) -> dict[str, str]:
    """Scrape a batch of URLs. Return {url: markdown} for the ones that succeed."""
    out: dict[str, str] = {}
    with ThreadPoolExecutor(max_workers=workers) as pool:
        for url, md in zip(urls, pool.map(_safe_scrape, urls)):
            if md:
                out[url] = md
    return out

def _safe_scrape(url: str) -> str | None:
    try:
        return scrape_markdown(url)
    except requests.RequestException:
        return None   # a page that fails is skipped, not fatal
```

Uma página com falha retorna `None` e sai do lote em vez de parar a execução. O loader no repositório é mais rigoroso do que esse trecho. Ele tenta novamente duas vezes com um recuo de 1 segundo depois 2 segundos. Ele também trata um corpo vazio ou HTML como uma falha, não como conteúdo. Isso significa que uma página que falhou ao buscar mantém sua versão anterior no corpus, e nada em uma resposta posterior a marca como desatualizada. Trate falhas de busca como um sinal sobre o qual você age, em vez de uma linha em um log.

O Web Unlocker buscou 6 páginas de documentação. Cada solicitação levou entre 24,6 e 36,7 segundos, com uma mediana de 30,6 segundos. Toda etapa numerada aqui foi executada ao vivo contra aquele corpus de 6 páginas em um único dia. Essas páginas são cooperativas, então os tempos medem o próprio pipeline em vez das defesas de um alvo. Desbloquear uma página não é uma busca estática, o que explica o tempo, e a concorrência torna isso viável. As mesmas 6 páginas levaram 43,1 segundos de tempo total com 6 workers, contra os cerca de 3 minutos que uma execução serial levaria.

Mantenha o número de workers baixo mesmo assim, já que o pool mira em um único site. O Bright Data limitou a zona acima a 1.000 solicitações por minuto até você adicionar fundos. Com uma mediana de 30 segundos, isso é muito mais margem do que alguns poucos workers usarão. Verifique o teto da sua zona, e confirme a latência nos seus próprios alvos antes de escalar a lista.

### Prefira o gêmeo Markdown próprio do site

Antes de fazer scraping de um site, verifique se ele já publica o que você está prestes a reconstruir. Muitas plataformas de documentação servem um gêmeo Markdown pronto para LLM de cada página, geralmente como `<page-url>.md`, e listam os gêmeos em um índice `/llms.txt`. A própria documentação do Bright Data publica gêmeos e os indexa em [docs.brightdata.com/llms.txt](https://docs.brightdata.com/llms.txt), então todas as 6 páginas acima tinham um gêmeo. Esses gêmeos são gratuitos, e um único GET os retorna em vez de um scraping de 30,6 segundos. Eles não carregam nenhuma da navegação que o scraping captura.

Eles não são prosa simples. O gêmeo abaixo ainda carrega as próprias tags de componentes da plataforma e URLs de imagens completas, então você troca o chrome do site por marcação que a remoção baseada em linhas não captará:

![Navegador anônimo em docs.brightdata.com/scraping-automation/web-unlocker/quickstart.md mostrando Markdown sem estilo sem navegação, logo, ou caixa de busca, mas ainda carregando tags de componentes Callout, Steps, e Frame e URLs de imagens completas mintcdn.com. Caixas vermelhas marcam a URL .md e as linhas iniciais apontando para docs.brightdata.com/llms.txt.](https://media.brightdata.com/2026/08/ryEYPhWwzx.png)Fora das plataformas de documentação para desenvolvedores, um gêmeo Markdown é raro, e o Web Unlocker faz o trabalho lá. Isso torna a busca uma decisão em duas etapas:

```none
def fetch_page(url: str) -> str:
    """Prefer the site's own Markdown twin. Scrape only when there isn't one."""
    return native_markdown(url) or scrape_markdown(url)
```

`native_markdown` é o auxiliar `src/web_kb/clean.py` do repositório, um `GET` de `<page-url>.md` que retorna `None` quando a página não tem gêmeo. Não remove a marcação de componentes, e a remoção de chrome na Etapa 2 também não. Um corpus rico em gêmeos precisa de sua própria passagem ciente de tags.

Quando você realmente precisa fazer scraping, o Web Unlocker retorna a página em vez de decidir quais partes dela são o conteúdo principal. Em 6 páginas de um site de documentação ao vivo, o Markdown ainda carregava chrome do site: a faixa do logo, um link “Pular para o conteúdo principal”, a caixa de busca, a árvore completa de navegação lateral, e um rodapé “Esta página foi útil”. Esse chrome era cerca de 43% dos caracteres. A Etapa 2 cobre a remoção do que se repete.

## Etapa 2 – Grave documentos limpos e metadados no S3

O Bedrock lê todo arquivo suportado sob o prefixo que você fornece. Cada um se torna um documento de até 50 MB, com um sidecar opcional ao lado para metadados. Você grava ambos.

### Remova o chrome primeiro

Um bloco de navegação é quase idêntico em toda página de um site, então um corpus de 50 páginas incorpora esse mesmo bloco 50 vezes. Você paga para incorporá-lo, paga para armazená-lo, e no momento da consulta esses 50 fragmentos quase idênticos competem com o conteúdo que alguém solicitou. A deduplicação mais adiante nesta etapa não consegue capturar isso, porque as páginas não são duplicatas. Apenas o chrome delas se repete.

Você não precisa de regras específicas do site para removê-lo, porque a maior parte do chrome é texto que se repete. Conte quantas vezes cada linha aparece no lote e descarte as que aparecem na maioria das páginas:

```none
from collections import Counter

KEEP = {"", "-" * 3, "`" * 3}          # blanks, bare rules, and bare fences are never chrome

def strip_repeated(pages: dict[str, str], threshold: float = 0.6) -> dict[str, str]:
    """Drop lines that repeat on most pages of the batch. Those are the chrome."""
    counts = Counter()
    for markdown in pages.values():
        for line in {ln.strip() for ln in markdown.splitlines()}:
            if line not in KEEP and len(line) >= 2:   # >= 2 so the ⌘K hint counts
                counts[line] += 1

    cutoff = max(2, int(len(pages) * threshold))
    boilerplate = {line for line, n in counts.items() if n >= cutoff}
    return {url: "\n".join(ln for ln in md.splitlines() if ln.strip() not in boilerplate)
            for url, md in pages.items()}
```

Na amostra de 6 páginas isso levou o lote de 76.722 para 60.846 caracteres, um corte de 21%. Isso é aproximadamente metade do chrome que a Etapa 1 mediu. A regra só dispara em linhas que são idênticas entre páginas, mas alguns sites redigem sua navegação de forma um pouco diferente em cada página. Linhas que se repetiram na maioria do lote foram de 63 para 0. Cada uma dessas linhas poderia ter se tornado um fragmento duplicado. Todas as 6 páginas mostravam a caixa de busca, a dica de atalho `⌘K`, o seletor de idioma, e todo link de navegação lateral.

As economias são desiguais, porque o chrome é um custo fixo por página. Nesta execução, removeu 41% da página mais curta e 12% da mais longa. Um corpus de muitas páginas curtas pagaria mais para incorporar navegação.

A técnica é uma heurística, não um parser, então audite-a antes de confiar nela em um novo site. Em um corpus rico em código, adicione também as cercas marcadas com linguagem ao `KEEP`, já que `KEEP` protege apenas as simples, e descartar uma cerca de abertura compartilhada deixa sua cerca de fechamento órfã.

O `6_inspect_cleaning.py` do repositório lista toda linha que descartaria, e `--verify` pontua o que sobreviveu contra o próprio gêmeo Markdown de uma página. Nesse corpus, a remoção não perdeu conteúdo exclusivo de página nenhum. Por essa comparação, a única linha faltando em cada página era o mesmo texto padrão do llms.txt. Para HTML bruto em vez de Markdown, use um extrator dedicado como [Trafilatura](https://trafilatura.readthedocs.io/).

Todas essas 6 páginas publicam um gêmeo Markdown, então a execução padrão os usa sempre. Os números de scraping e chrome acima vêm de forçar o caminho do Web Unlocker com `--no-native`.

![Saída do terminal de 6_inspect_cleaning.py em 6 páginas de documentação com o caminho de gêmeo nativo desativado. Relata 63 linhas repetidas, depois uma tabela por página. Caixas vermelhas marcam a contagem de linhas e o total do lote, 76.722 para 60.846 caracteres.](https://media.brightdata.com/2026/08/HkB9vnZvMl.png)### Grave o documento e seu sidecar

O Bedrock pareia um documento com seus metadados apenas pelo nome do arquivo. Para um documento `quickstart-3f9a2c1b04.md`, o arquivo de metadados é `quickstart-3f9a2c1b04.md.metadata.json`. Dentro, cada atributo tem um valor tipado e uma flag `includeForEmbedding`. Essa flag controla se o Bedrock adiciona o valor ao texto incorporado. O loader grava o sidecar, depois o documento, marcado com o hash de conteúdo que permite à próxima execução pular ambos:

```none
import boto3, json, hashlib, re
from datetime import datetime, timezone

REGION = "us-east-2"                    # a Region with non-zero embedding quota
s3 = boto3.client("s3", region_name=REGION)
BUCKET = "web-kb-corpus-111122223333"   # your bucket
PREFIX = "docs"

def slugify(url: str) -> str:
    """Stable, safe object key derived from the URL."""
    digest = hashlib.sha1(url.encode()).hexdigest()[:10]
    tail = url.rstrip("/").split("/")[-1][:40] or "page"
    safe = "".join(c if c.isalnum() or c in "-_" else "-" for c in tail)
    return f"{safe}-{digest}"

def content_hash(markdown: str) -> str:
    """Hash the meaningful text. Normalizing whitespace stops different
    line breaks from looking like a content change."""
    return hashlib.sha256(re.sub(r"\s+", " ", markdown).strip().encode()).hexdigest()

def stored_hash(key: str) -> str | None:
    """The hash saved on the object last run, or None if the object is not there.

    Only a missing object counts as absent. Catching every ClientError would report
    AccessDenied or throttling as a new page, rewrite it, and re-embed the corpus."""
    try:
        return s3.head_object(Bucket=BUCKET, Key=key)["Metadata"].get("content-hash")
    except s3.exceptions.ClientError as exc:
        if exc.response["Error"]["Code"] in ("404", "NoSuchKey", "NotFound"):
            return None
        raise

def put_document(url: str, markdown: str, title: str, section: str,
                 raw: str | None = None) -> str:
    key = f"{PREFIX}/{slugify(url)}.md"
    chash = content_hash(raw or markdown)   # hash the RAW scrape, not the stripped body

    if stored_hash(key) == chash:
        return "unchanged"          # leave the object alone, so the next sync skips it

    metadata = {
        "metadataAttributes": {
            "source_url":  {"value": {"type": "STRING", "stringValue": url},
                            "includeForEmbedding": False},
            "title":       {"value": {"type": "STRING", "stringValue": title},
                            "includeForEmbedding": True},
            "section":     {"value": {"type": "STRING", "stringValue": section},
                            "includeForEmbedding": True},
            "scraped_date":{"value": {"type": "NUMBER",
                                      "numberValue": int(datetime.now(timezone.utc).strftime("%Y%m%d"))},
                            "includeForEmbedding": False},
        }
    }
    # Sidecar first. The document carries the hash the next run compares against, so it is
    # the commit point. The reverse order can leave a new document beside a stale sidecar
    # that never self-heals, because the hash already matches.
    s3.put_object(Bucket=BUCKET, Key=f"{key}.metadata.json",
                  Body=json.dumps(metadata).encode("utf-8"),
                  ContentType="application/json")
    s3.put_object(Bucket=BUCKET, Key=key, Body=markdown.encode("utf-8"),
                  ContentType="text/markdown", Metadata={"content-hash": chash})
    return "written"
```

### Grave apenas quando o hash de conteúdo mudar

O controle de hash no topo de `put_document` mantém uma atualização barata. Em uma sincronização incremental, o Bedrock usa o próprio rastreamento de alterações da fonte de dados para decidir o que reincorporar. A documentação da AWS atualmente não declara qual sinal o Bedrock lê para o S3. Um loader que regrava todo objeto em toda execução atualiza esse sinal independentemente de o conteúdo ter mudado. A suposição segura é que uma sincronização destinada a tocar três páginas reincorpora todo o corpus, e você paga por tudo isso. Comparar o hash de conteúdo antes de gravar significa que você deixa uma página inalterada intocada, e isso não custa nada posteriormente.

[Os dados do Cloudflare sugerem](https://www.cloudflare.com/press/press-releases/2026/cloudflare-allows-the-agentic-internet-to-flourish-with-a-simple-philosophy-your-content-your-rules/) que mais da metade do tráfego de crawlers de IA vai para rebuscar páginas que não mudaram. Esse controle não resolve isso, porque você busca a página de qualquer forma, e economiza apenas a incorporação. A programação de re-sincronização na Etapa 4 controla a própria busca.

Faça hash do scraping bruto em vez do corpo com chrome removido. Essa remoção mais cedo nesta etapa é relativa ao lote, então uma página inalterada em um lote diferente teria um hash diferente de outra forma e reincorporaria à toa. O trecho recebe um argumento `raw` separado exatamente por esse motivo, então passe o texto não removido quando você o chamar.

O loader do repositório mantém os hashes que viu em uma execução, então também descarta duplicatas exatas, onde duas URLs retornam texto idêntico byte a byte. Na prática, isso cobre variações `?utm_source=` e `?print=1`. Uma cópia sindicada ou uma variante paginada difere por alguns bytes e escapa dessa verificação.

Capturar essas precisa de detecção de quase-duplicatas, e o repositório oferece isso como MinHash atrás de uma flag opt-in `--near-dup` no loader. O padrão é um limiar de similaridade de 0,85. Nesse corpus, páginas distintas pontuaram no máximo 0,02, e uma página contra seu próprio gêmeo Markdown pontuou 0,75, então o padrão mantém ambos. A implementação em Python puro serve para corpora de até alguns milhares de páginas. Na latência medida anteriormente, isso é mais do que uma única execução de lote geralmente cobrirá.

### Escolha o que os metadados carregam

O sidecar guarda as duas coisas que um corpus raspado perde no caminho para um armazenamento vetorial. `source_url` dá a todo fragmento recuperado uma citação de volta à página de onde veio, então uma resposta pode mostrar sua fonte. `section` e `scraped_date` se tornam chaves de filtro no momento da consulta. Você pode limitar uma busca a uma parte do corpus, ou a conteúdo que mudou após uma data de corte.

O sidecar também carrega o `title` da página. Definir `includeForEmbedding` como `True` em `title` e `section` os adiciona ao texto incorporado, então uma consulta que nomeia um deles tem mais a comparar. Definir como `False` em `source_url` mantém uma URL longa fora dos vetores, onde adicionaria mais ruído do que sinal.

O Bedrock suporta os tipos de metadados `STRING`, `NUMBER`, `BOOLEAN`, e `STRING_LIST`. Armazenar `scraped_date` como um `NUMBER` no formato `YYYYMMDD` permite filtrar com `greaterThan` e `lessThan` depois. Você não regrava uma página inalterada, então ela mantém sua data anterior. Isso torna `scraped_date` um filtro de idade de conteúdo, não um sinal de que uma página ainda atualiza. Esse sinal precisa de um carimbo de última vez visto separado, e o repositório grava um a cada busca bem-sucedida.

Execute o loader na sua lista de URLs, e o bucket então mantém um documento e um sidecar por página. Confirme os pares no console antes de apontar uma base de conhecimento para o prefixo:

![Console do Amazon S3 mostrando o prefixo docs/ listando 12 objetos em 6 pares, cada documento Markdown seguido de seu sidecar .metadata.json de mesmo nome. Documentos variam de 4,0 a 21,6 KB, sidecars de 590 a 619 bytes. Uma caixa vermelha marca o primeiro par.](https://media.brightdata.com/2026/08/rJ_ow2bvGg.png)## Etapa 3 – Crie a base de conhecimento

Você cria a base de conhecimento uma vez. Depois disso, o pipeline grava arquivos e dispara sincronizações. O console é o caminho mais claro para essa configuração, e o [fluxo de criação de base de conhecimento](https://docs.aws.amazon.com/bedrock/latest/userguide/knowledge-base-create.html) cobre cada tela.

### Escolha gerenciada ou gerenciada pelo cliente

A documentação da AWS recomenda a base de conhecimento totalmente gerenciada por padrão. Dependendo da sua Região, o console também a oferece. Bases de conhecimento gerenciadas rodam seu próprio armazenamento de dados, parsing, e reranking. Elas também adicionam recuperação agentiva e incluem conectores nativos como o Google Drive.

Essa construção segue o caminho gerenciado pelo cliente. Escolher seu próprio armazenamento vetorial dá a você os preços do S3 Vectors abaixo, e deixa o índice na sua conta. Uma base de conhecimento gerenciada pode ingerir os documentos que esse pipeline grava no S3, através da mesma fonte de dados S3. A lacuna do crawler permanece a mesma em ambos os caminhos.

### Defina as escolhas que você não pode alterar depois

Quatro escolhas moldam a recuperação. Três das quatro ficam travadas assim que a base de conhecimento e sua fonte de dados existem.

- **Fonte de dados.** Escolha **Amazon S3** e aponte-a para seu bucket e o prefixo `docs/`. O Bedrock então lê os documentos e seus sidecars de lá a cada sincronização. Uma base de conhecimento aceita até 5 buckets como fontes de dados.
- **Modelo de incorporação.** O Amazon Titan Text Embeddings V2 (`amazon.titan-embed-text-v2:0`) é o padrão para texto, e é o modelo por trás de todo número de recuperação abaixo. Com 1024 dimensões você obtém melhor qualidade de recuperação, e com 256 você obtém menor custo de armazenamento. Os [modelos de incorporação suportados](https://docs.aws.amazon.com/bedrock/latest/userguide/knowledge-base-supported.html) incluem o Amazon Nova Multimodal Embeddings, que vale a pena considerar quando um corpus mistura texto com imagens. Escolha uma vez, porque você não pode alterar o modelo de incorporação em uma base de conhecimento existente.
- **Armazenamento vetorial.** O Amazon S3 Vectors atingiu disponibilidade geral em dezembro de 2025. Ele armazena e consulta vetores em uma base de pagamento por uso sem cluster para rodar, e você não pode trocá-lo depois que a base de conhecimento existe. Crie seu bucket e índice antes de abrir o assistente. Depois aponte o assistente para esse armazenamento existente, em vez de deixar a Amazon criar um para você. A próxima seção explica o porquê. A AWS diz que o S3 Vectors reduz o custo de carregar, armazenar e consultar vetores em até 90% em comparação com soluções alternativas não nomeadas. O Amazon OpenSearch Serverless continua sendo a opção quando você precisa de seus recursos de consulta ou maior taxa de transferência, embora essa construção tenha rodado apenas no S3 Vectors. Para um corpus que você atualiza em uma programação em vez de servir com alto volume de consultas, essa troca geralmente vale a pena. O console também oferece o Amazon Aurora PostgreSQL Serverless. Confirme o status e preços atuais contra a [documentação do S3 Vectors](https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-vectors-bedrock-kb.html) ao construir.
- **Fragmentação.** Artigos raspados têm estrutura de frases e parágrafos, então a fragmentação padrão de aproximadamente 300 tokens em limites de frases é um ponto de partida razoável. Se você faz scraping de longas páginas de referência, a fragmentação hierárquica recupera fragmentos filhos precisos e retorna seus pais mais amplos para contexto, embora o Bedrock recomende contra isso em um armazenamento S3 Vectors. O Bedrock também oferece outras estratégias. A divisão de tamanho fixo usa uma contagem de tokens que você define. A fragmentação semântica agrupa por significado em vez de tamanho. Uma opção sem fragmentação trata cada documento como um único fragmento. Você não pode alterar a estratégia de fragmentação depois de criar a fonte de dados.

O assistente avisa sobre isso para o modelo de incorporação e o armazenamento vetorial, na tela onde você os define:

![Assistente do Bedrock, Configure data storage and processing. Uma linha acima dos painéis diz 'The embeddings model and vector store cannot be changed after creation of Knowledge Base.' O menu suspenso de armazenamento vetorial lista OpenSearch Serverless, S3 Vectors rotulado como new, e Aurora PostgreSQL Serverless. Caixas vermelhas marcam S3 Vectors e o aviso de não poder alterar após a criação.](https://media.brightdata.com/2026/08/ryepwhbvGx.png)### Nomeie as chaves de metadados não filtráveis

Se você escolher o S3 Vectors, marque o texto do fragmento e os próprios metadados do Bedrock como não filtráveis ao criar o índice. Caso contrário, o Bedrock falha ao ingerir a maior parte do seu corpus. O S3 Vectors permite no máximo 2048 bytes de metadados *filtráveis* por vetor, e o Bedrock usa parte disso para o texto do fragmento e sua própria contabilidade. Qualquer coisa exceto um fragmento muito curto excede o orçamento. Após esse overhead, a [documentação da base de conhecimento do S3 Vectors](https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-vectors-bedrock-kb.html) coloca a permissão prática para seus atributos em 1 KB e 35 chaves por vetor. Mantenha os sidecars pequenos.

Na primeira execução, 5 de 6 documentos falharam com `Filterable metadata must have at most 2048 bytes`, e o único que sobreviveu tinha um fragmento de 499 bytes. Nomear ambas as chaves próprias do Bedrock ao criar o índice corrige isso, e você não pode alterar essa escolha depois:

```none
aws s3vectors create-vector-bucket --vector-bucket-name YOUR_VECTOR_BUCKET --region YOUR_REGION

aws s3vectors create-index --vector-bucket-name YOUR_VECTOR_BUCKET --region YOUR_REGION \
  --index-name web-kb-index --data-type float32 --dimension 1024 --distance-metric cosine \
  --metadata-configuration '{"nonFilterableMetadataKeys":["AMAZON_BEDROCK_TEXT","AMAZON_BEDROCK_METADATA"]}'
```

O nome que as pessoas erram é `AMAZON_BEDROCK_TEXT`. `AMAZON_BEDROCK_TEXT_CHUNK` é o nome de campo do OpenSearch, e nomear esse em vez do outro deixa a falha exatamente como estava. O `2_create_kb.py` do repositório complementar cria o índice dessa forma para você. Também se recusa a continuar contra um índice existente que não tenha essas chaves. A alternativa é descobrir o problema durante uma sincronização que reporta sucesso.

O assistente repete o aviso para as estratégias de parsing e fragmentação uma tela depois.

![Assistente de criação de base de conhecimento do Bedrock, Configure data source. O parser padrão do Amazon Bedrock está selecionado sob uma nota de que a opção de parsing não pode ser modificada depois que a base de conhecimento é criada. Caixas vermelhas marcam o aviso de que a estratégia de fragmentação não pode ser modificada depois que a fonte de dados é criada e a opção de fragmentação Default de cerca de 300 tokens.](https://media.brightdata.com/2026/08/rJTpPnWDMe.png)Essa mesma tela carrega uma função de transformação opcional. A [recuperação contextual](https://www.anthropic.com/engineering/contextual-retrieval) acrescenta uma nota escrita por modelo sobre o lugar de cada fragmento no documento antes da incorporação. A Anthropic mediu uma queda de 35% nas falhas de recuperação dos top-20. O Bedrock executa essa função na ingestão, ao custo de uma chamada de modelo por fragmento. Essa construção a deixa desativada, então os números de recuperação abaixo vêm apenas da fragmentação padrão.

Se preferir criar a base de conhecimento em código, esse mesmo `2_create_kb.py` chama `create_knowledge_base` e `create_data_source` do cliente `bedrock-agent`. Ele configura a configuração S3 e a função de serviço IAM para você. O repositório também traz Terraform para todo o lado AWS. É o caminho mais curto se você preferir não clicar em nada disso, e ele define as chaves de metadados não filtráveis acima, para que você não as esqueça.

Quando o fluxo de criação termina, a página de detalhes da base de conhecimento mostra o que você construiu:

![Console do Amazon Bedrock, visão geral da base de conhecimento web-kb. O Status é Available e o tipo RAG é Vector store. A seção Data source lista uma fonte S3 chamada web-corpus, também Available. Caixas vermelhas marcam o status e a linha da fonte de dados.](https://media.brightdata.com/2026/08/B1CRvn-DMx.png)## Etapa 4 – Ingira e mantenha os dados atualizados

Criar a base de conhecimento não lê seus dados. A ingestão faz isso. Uma sincronização, chamada de trabalho de ingestão, lê o bucket, fragmenta e incorpora os documentos, e grava os vetores. Você inicia uma após a primeira carga e novamente após toda atualização:

```none
bedrock_agent = boto3.client("bedrock-agent", region_name=REGION)

def sync(knowledge_base_id: str, data_source_id: str) -> str:
    job = bedrock_agent.start_ingestion_job(
        knowledgeBaseId=knowledge_base_id,
        dataSourceId=data_source_id,
    )
    return job["ingestionJob"]["ingestionJobId"]
```

Após a primeira sincronização completa, sincronizações posteriores são incrementais. O Bedrock ingere arquivos novos e modificados e remove vetores de quaisquer que saíram do bucket, então uma atualização reincorpora apenas as páginas que seu loader reescreveu. Esse comportamento torna o controle de hash na Etapa 2 essencial, já que um objeto que você reescreve com bytes idênticos ainda pode contar como modificado. A chave `slugify` é determinística, então um novo scraping da mesma URL sobrescreve o mesmo objeto em vez de criar uma duplicata.

Isso lida com páginas novas e alteradas, mas nada remove as que você aposenta. O loader só grava, então remover uma URL da sua lista não exclui seu objeto. Esse conteúdo desatualizado permanece no corpus, onde pode aparecer em qualquer resposta posterior. Aposentar uma página é uma etapa de reconciliação separada que lista o bucket, encontra objetos cuja URL desapareceu, e os exclui para que a próxima sincronização limpe seus vetores. O repositório complementar traz isso como `7_reconcile.py`, um script dry-run por padrão. Ele compara contra a lista completa de URLs, então nunca confunde uma página que apenas falhou ao raspar com uma que você removeu.

Combine a programação de re-sincronização com o conteúdo de mudança mais rápida no seu corpus. Posts em redes sociais podem ficar desatualizados em horas, notícias em dias, páginas de preços e financeiras em semanas, enquanto documentação de referência pode ficar atual por meses. Esses são pontos de partida, não medições. Uma base de conhecimento de páginas de notícias pode re-sincronizar diariamente, e uma construída a partir de documentação de produtos semanalmente. Uma programação do Amazon EventBridge pode disparar o scraping-e-sincronização no intervalo que você escolher, então a atualização roda sem um humano iniciá-la.

Leia as estatísticas, não o status. Um trabalho de ingestão reporta `COMPLETE` mesmo quando a maioria de seus documentos falhou. Uma execução que indexou 1 página de 6 ainda parece um sucesso apenas pelo campo de status. O `3_sync.py` do repositório imprime essas contagens e sai com código diferente de zero em falhas:

```none
{"status": "COMPLETE",
 "statistics": {"numberOfDocumentsScanned": 6, "numberOfNewDocumentsIndexed": 6,
                "numberOfModifiedDocumentsIndexed": 0, "numberOfDocumentsDeleted": 0,
                "numberOfDocumentsFailed": 0}}
```

Esse é um resultado medido do corpus de 6 páginas, e levou duas tentativas para chegar lá. A primeira execução reportou `COMPLETE` com `numberOfDocumentsFailed: 5`, todos atingindo o limite de metadados do S3 Vectors descrito na Etapa 3. Quando documentos falham, `failureReasons` no trabalho carrega o erro real e é a primeira coisa a ler. Uma cota de incorporação zero aparece aqui também, e parece um problema de permissões. A ingestão de 6 páginas levou cerca de 25 segundos. Tempo e contagens de fragmentos escalam com seu corpus e como você fragmenta, então meça-os nos seus próprios dados.

A página da fonte de dados lista cada trabalho em seu histórico de sincronização com as estatísticas por documento:

![Página da fonte de dados do console do Amazon Bedrock para web-corpus. O histórico de sincronização mostra um trabalho concluído com Scanned 6, Added 6, Failed 0, Modified 0, Deleted 0. A seção Documents relata 6, e toda linha visível mostra INDEXED. Caixas vermelhas marcam as colunas de contagem e a coluna de status.](https://media.brightdata.com/2026/08/H1_bd2bPzl.png)## Etapa 5 – Consulte a base de conhecimento

O Bedrock oferece uma chamada de recuperação e uma chamada de geração. A que você escolhe depende se você quer a evidência bruta ou uma resposta escrita. Ambas estão no cliente `bedrock-agent-runtime`.

`Retrieve` retorna os fragmentos fonte e nada mais. É a escolha certa quando seu próprio modelo ou aplicativo faz a escrita e você só precisa de contexto fundamentado e citações:

```none
runtime = boto3.client("bedrock-agent-runtime", region_name=REGION)

def retrieve(kb_id: str, query: str, section: str | None = None,
             num_results: int = 5) -> list[dict]:
    config = {"vectorSearchConfiguration": {"numberOfResults": num_results}}
    if section:
        config["vectorSearchConfiguration"]["filter"] = {
            "equals": {"key": "section", "value": section}
        }
    resp = runtime.retrieve(
        knowledgeBaseId=kb_id,
        retrievalQuery={"text": query},
        retrievalConfiguration=config,
    )
    return resp["retrievalResults"]
```

Cada resultado carrega o texto do fragmento, uma pontuação de relevância `score`, a `location` do objeto fonte, e os `metadata` que você gravou na Etapa 2. Nesse corpus, um resultado principal correto pontuou 0,67, um número que você usa para definir o piso de relevância depois. Esses metadados fazem o `filter` opcional funcionar, limitando a busca a uma `section` ou a um intervalo de datas:

![Painel de teste do console do Amazon Bedrock para web-kb. A consulta de segmentação por país retorna 5 fragmentos. O Fragmento 1 pontua 0,67 e seus metadados listam o título Web Unlocker API features e uma source_url. Caixas vermelhas marcam a pontuação de relevância e a linha source_url.](https://media.brightdata.com/2026/08/rkDLdh-Dfl.png)O crawler nativo ingere HTML sem uma etapa de sidecar, então esses atributos não têm de onde vir.

`RetrieveAndGenerate` faz o que `Retrieve` faz, depois escreve a resposta. Ele fundamenta essa resposta nos fragmentos recuperados e retorna citações com ela:

```none
def answer(kb_id: str, query: str, model_arn: str) -> dict:
    resp = runtime.retrieve_and_generate(
        input={"text": query},
        retrieveAndGenerateConfiguration={
            "type": "KNOWLEDGE_BASE",
            "knowledgeBaseConfiguration": {
                "knowledgeBaseId": kb_id,
                "modelArn": model_arn,
            },
        },
    )
    return resp   # resp["output"]["text"] plus resp["citations"]
```

A resposta guarda o texto gerado e um array `citations` que mapeia trechos da resposta aos fragmentos de onde vieram. Como você definiu `source_url` no sidecar, cada citação resolve para a página que você raspou. Uma resposta sobre o preço de um produto então cita a página de preços que o Bright Data buscou.

Se você construir uma base de conhecimento totalmente gerenciada, use `managedSearchConfiguration` no lugar de `vectorSearchConfiguration`, já que bases de conhecimento gerenciadas rodam busca híbrida com reranking ativado por padrão. O `retrieve()` do repositório complementar marca onde trocá-lo.

## Etapa 6 – Meça se a recuperação funciona

Nada no pipeline até agora diz se a base de conhecimento retorna a página certa para as perguntas que seus usuários fazem. Corrigir um tamanho de fragmento errado significa recriar a fonte de dados, então descubra isso em 20 páginas em vez de 20.000.

A ferramenta para isso é um conjunto padrão de referência, uma lista de perguntas, cada uma com as páginas que deveriam respondê-la. O conjunto deste pipeline tem 6 perguntas, e está no repositório como `eval/golden.json`, ao lado de `eval/golden-v1-mislabeled.json`, a primeira versão cuja etiqueta errada esta seção explica:

```none
[
  {"question": "How do I get the page back as markdown instead of HTML?",
   "expect": ["https://docs.brightdata.com/scraping-automation/web-unlocker/features"]},
  {"question": "How do I target a specific country for my request?",
   "expect": ["https://docs.brightdata.com/scraping-automation/web-unlocker/features",
              "https://docs.brightdata.com/scraping-automation/web-unlocker/configuration"]}
]
```

Você então executa toda pergunta através de `Retrieve` e registra onde a página esperada aparece nos resultados:

```none
def evaluate(kb_id: str, golden: list[dict], k: int = 5):
    ranks = []
    for item in golden:
        chunks = retrieve(kb_id, item["question"], num_results=k)
        got = [c.get("metadata", {}).get("source_url", "") for c in chunks]
        # 1-based rank of the first expected page, or None if it never appeared
        ranks.append(next((i + 1 for i, u in enumerate(got) if u in item["expect"]), None))

    hit_rate = sum(r is not None for r in ranks) / len(ranks)
    mrr = sum(1 / r if r else 0 for r in ranks) / len(ranks)
    missed = [g["question"] for g, r in zip(golden, ranks) if r is None]
    return hit_rate, mrr, missed
```

A taxa de acerto é a proporção de perguntas onde a página esperada apareceu de alguma forma. Ela diz se o corpus consegue fundamentar uma resposta em primeiro lugar. A classificação recíproca média calcula a média de 1 dividido pela posição dessa página. Ela cai quando a página certa aparece mas fica na posição 5, e uma taxa de acerto sozinha esconde isso.

A lista de falhas é a parte sobre a qual você age. Uma falha geralmente significa uma de quatro coisas. A página nunca chegou ao corpus, então verifique a saída do loader para uma busca com falha. Os fragmentos são grandes demais, então o texto ao redor dilui a passagem relevante. Os fragmentos são pequenos demais, então a passagem perdeu o contexto que a fazia corresponder. Ou seu conjunto padrão de referência está errado.

A primeira execução de avaliação pontuou 83% hit@5, com uma falha em “Como faço para segmentar um país específico para minha solicitação?” O conjunto padrão de referência esperava a página de configuração. Ler o fragmento retornado pela recuperação resolveu a questão. A passagem da página de recursos continha a documentação de segmentação por país na íntegra. A recuperação estava certa e a etiqueta estava errada. Corrigindo isso, obteve 100% hit@5 com uma classificação recíproca média de 0,88, e 83% hit@3 com uma classificação recíproca média de 0,83. Essas são 6 perguntas contra 6 páginas, então leia-as como um único ponto de dado medido em vez de um benchmark.

![Saída do terminal do script de avaliação, executando scripts/5_evaluate.py contra o conjunto padrão de referência com k igual a 5. Relata 6 perguntas, um hit@5 de 100%, e uma classificação recíproca média de 0,88. Uma caixa vermelha marca as duas linhas de resultado.](https://media.brightdata.com/2026/08/S16Gun-wzl.png)Sempre leia o que a recuperação retornou em uma falha antes de alterar qualquer coisa no pipeline. Apenas o texto recuperado diz se a recuperação ou a etiqueta está errada. Um problema de pipeline te leva ao loader, um problema de fragmentação te leva de volta à fonte de dados, e uma pergunta mal etiquetada só precisa de um conjunto padrão de referência melhor.

Execute isso antes de escalar o corpus, e continue executando. Uma vez que você tenha uma taxa de acerto com a qual está satisfeito, trate-a como um portão de liberação. O script de avaliação do repositório complementar aceita uma flag `--min-hit-rate` exatamente para isso. Uma atualização agendada que silenciosamente degrada a recuperação então falha ruidosamente em vez de entregar respostas piores.

O Bedrock também traz avaliação gerenciada que pontua recuperação e geração com um modelo juiz. Use-a para estudar qualidade, e mantenha o conjunto padrão de referência como o portão de liberação. O conjunto é determinístico, custa apenas as chamadas de recuperação, e roda em segundos nesse tamanho.

## Quando ir além da base de conhecimento

Uma base de conhecimento é uma fotografia de páginas que você escolheu. Você as incorpora uma vez e as consulta muitas vezes. Essa fotografia torna cada consulta barata, e também coloca dois tipos de perguntas fora de seu alcance. Perguntas sobre fatos que mudam mais rápido do que sua programação de sincronização recebem respostas desatualizadas. Perguntas sobre páginas que nunca estiveram no corpus não têm nada para comparar.

Roteie por consulta em vez de escolher uma vez entre uma base de conhecimento persistente e recuperação ao vivo. Pergunte à base de conhecimento primeiro, e quando ela não conseguir fundamentar a pergunta, recorra a uma busca ao vivo do Bright Data:

```none
def route_answer(kb_id: str, query: str, min_score: float = 0.4):
    chunks = retrieve(kb_id, query, num_results=5)
    top = max((c.get("score", 0.0) for c in chunks), default=0.0)

    if chunks and top >= min_score:
        return "knowledge_base", chunks

    # nothing in the corpus covers this, so search and fetch it live
    return "live_web", live_context(query)      # Bright Data SERP + Web Unlocker
```

`live_context` é o auxiliar de busca-depois-busca do repositório em `src/web_kb/live_tool.py`, e precisa de uma zona SERP do Bright Data junto com a zona Web Unlocker da Etapa 1. Você cria a zona SERP da mesma forma e a chama através do mesmo endpoint `api.brightdata.com/request`. Ela retorna a página de resultados como JSON, então o fallback pode escolher URLs para buscar.

O piso de relevância é o sinal de roteamento. O Bedrock retorna uma pontuação com cada fragmento, e quando a melhor cai abaixo do piso, o corpus provavelmente não cobre a pergunta. Responder do corpus mesmo assim é o modo de falha a evitar, porque o resultado é uma resposta confiante citando uma página que não a sustenta.

O 0,4 acima é um ponto de partida, não um valor medido. Fica abaixo do 0,67 que um resultado principal correto pontuou na Etapa 5. Ajuste-o contra o conjunto padrão de referência da Etapa 6, junto com algumas perguntas que você sabe que o corpus não cobre. `5_evaluate.py` imprime a faixa que viu, a pontuação mais baixa em um acerto e a mais alta em uma falha. O piso pertence entre eles. Mantenha essas perguntas extras em um arquivo separado, já que uma pergunta sem página esperada conta como falha contra o portão de liberação.

Bases de conhecimento gerenciadas tomam uma versão dessa decisão internamente, chamada de recuperação agentiva. Manter essa decisão no seu próprio código significa que você pode inspecionar o sinal, o piso, e o fallback, e o caminho ao vivo pode alcançar além da AWS. Conecte-o como um grupo de ações de agente do Bedrock, como uma ferramenta em uma estrutura como [Strands Agents](https://strandsagents.com/), ou como o [servidor MCP](/ai/mcp-server) do Bright Data.

Esse servidor dá a qualquer agente compatível com MCP a mesma busca, desbloqueio, e crawling ao vivo. Em um AgentCore Gateway ele fica ao lado de uma ferramenta que chama `Retrieve`. A regra de roteamento não muda, apenas onde o fallback roda.

Uma base de conhecimento é a escolha certa quando você consulta as mesmas páginas repetidamente, quando precisa de citações, filtros, e controle de acesso, e quando o corpus muda mais lentamente do que você o consulta. A recuperação ao vivo vence quando um fato muda mais rápido do que sua sincronização, quando o espaço de perguntas é aberto, ou quando você incorporaria muito mais do que jamais recuperaria. O sinal mais claro de que o equilíbrio está errado é sincronizar tão frequentemente que você reincorpora a maioria das páginas antes que alguém as consulte. Então você está pagando para manter um índice em vez de usá-lo.

O armazenamento em cache de prompts mudou uma parte dessa regra. Com um corpus pequeno e estável o suficiente para caber na janela de contexto de um modelo, você pode pular a recuperação completamente. Esse pipeline vale a pena construir uma vez que o corpus ultrapassa o cache, ou quando as respostas devem citar páginas exatas, filtros devem limitar consultas, ou páginas atualizam em programações diferentes. Sem nenhuma dessas, armazenar em cache todo o corpus é mais simples.

## Cobertura completa de site com a Crawl API e entrega nativa no S3

O pipeline até agora usa uma lista conhecida de URLs. Quando o corpus cresce, você pode não conhecer toda URL com antecedência, e preferiria não executar o loop de busca você mesmo. A [Crawl API](/products/crawl-api) do Bright Data lida com ambos. Ela descobre e rastreia as páginas internas de um site a partir de uma URL semente, retornando cada página como Markdown. Também pode entregar a saída diretamente no seu bucket S3.

Esse caminho foi montado a partir da documentação em vez de executado. A sequência de chamadas e nomes de campos abaixo seguem a referência de API do Bright Data, então verifique-os contra a [documentação da Crawl API](https://docs.brightdata.com/scraping-automation/crawl-api/overview) antes de depender desse caminho.

Este trecho continua a partir das etapas anteriores: reutiliza `BRIGHTDATA_TOKEN` da Etapa 1 e `REGION` da Etapa 2. Trabalhos de crawling são assíncronos, e a entrega é sua própria chamada em vez de uma opção no gatilho. Você dispara um crawling e recebe um ID de snapshot, sonda até que esse snapshot esteja `ready`, depois o entrega ao endpoint de entrega com um alvo `s3`. A chamada de entrega grava a saída no seu bucket sem nenhuma etapa de download no seu código:

```none
import requests, time

BD = "https://api.brightdata.com"
HEADERS = {"Authorization": f"Bearer {BRIGHTDATA_TOKEN}"}
CRAWL_DATASET_ID = "..."   # the Crawl API dataset ID from your Bright Data control panel
AWS_KEY, AWS_SECRET = "...", "..."   # delivery credentials. Prefer an IAM role ARN with an external ID

def _call(method: str, path: str, **kw):
    resp = requests.request(method, f"{BD}{path}", headers=HEADERS, timeout=60, **kw)
    resp.raise_for_status()
    return resp.json()

def crawl_to_s3(seed_url: str, bucket: str, directory: str) -> str:
    # 1. Trigger. The body is a bare array of input objects, not a wrapper object.
    snapshot = _call("POST", "/datasets/v3/trigger",
                     params={"dataset_id": CRAWL_DATASET_ID,
                             "custom_output_fields": "markdown|url|page_title"},
                     json=[{"url": seed_url}])["snapshot_id"]

    # 2. Poll. Delivery only accepts a snapshot already in `ready` status.
    while (state := _call("GET", f"/datasets/v3/progress/{snapshot}")["status"]) != "ready":
        if state == "failed":
            raise RuntimeError(f"crawl {snapshot} failed")
        time.sleep(15)

    # 3. Deliver. Returns a delivery job ID, which you poll at
    #    GET /datasets/v3/delivery/{id} until its status is "done".
    return _call("POST", f"/datasets/v3/deliver/{snapshot}",
                 json={"type": "s3",
                       "bucket": bucket,
                       "directory": directory,
                       "region": REGION,
                       "credentials": {"aws-access-key": AWS_KEY,
                                       "aws-secret-key": AWS_SECRET},
                       "filename": {"template": "crawl", "extension": "json"}})["id"]
```

O Bright Data entrega registros como JSON, NDJSON, ou CSV em vez de um arquivo Markdown por página, então há uma etapa de normalização entre entrega e ingestão. Uma função Lambda acionada pelo S3 lida com isso, disparando quando a entrega grava a saída em um prefixo de aterrissagem. A função divide cada registro em um documento `<slug>.md` e seu sidecar `.metadata.json` sob o prefixo fonte da base de conhecimento. Essa é exatamente a forma que você gravou manualmente na Etapa 2. A fonte de dados S3 do Bedrock então ingere esses na próxima sincronização.

O exemplo acima passa um par de chaves. Prefira um ARN de função IAM com um ID externo, para que nenhuma chave de longa duração fique na solicitação.

A troca entre os dois caminhos é controle contra escala. O Web Unlocker com seu próprio loader dá a você controle exato sobre todo nome de arquivo e campo de metadados. Isso é ideal para uma lista curada. A Crawl API com entrega no S3 e uma Lambda de normalização dá a você cobertura ampla e nenhum loop de busca próprio. Isso é ideal quando o corpus é uma seção inteira de um site que você quer manter sincronizada.

## Considerações de custo

Trate os números abaixo como um modelo para os seus próprios, não como uma cotação, e calcule o preço do seu próprio corpus contra os [preços do Bright Data](/pricing) e os [preços do Amazon S3](https://aws.amazon.com/s3/pricing/) antes de se comprometer.

ComponenteComo é precificadoO que o impulsionaBright Data Web Unlocker$1,50 por 1.000 solicitações bem-sucedidas em pay-as-you-go, com uma taxa premium de $2,50. Falhas não são cobradas1 solicitação por página raspadaBright Data Crawl API$1,50 por 1.000 registros em pay-as-you-goPáginas descobertas e rastreadas por trabalhoIncorporações Bedrock (Titan V2)Por token de entradaTotal de tokens em todos os fragmentos na ingestão, mais cada consultaArmazenamento vetorialS3 Vectors pay-as-you-go, ou OpenSearch Serverless por hora-OCUTamanho do corpus e volume de consultasAmazon S3Armazenamento e solicitações, alguns centavos nessa escala1 `.md` mais 1 sidecar por páginaRetrieveAndGeneratePor token no modelo de geraçãoVolume de respostas e escolha de modeloAqui está o custo de uma atualização do corpus de 6 páginas, no caminho do Web Unlocker na taxa padrão.

- **Scraping.** 6 solicitações do Web Unlocker, $0,009 a $1,50 por 1.000.
- **Incorporação.** 60.846 caracteres limpos, aproximadamente 15.000 tokens a 4 caracteres cada, cerca de três centésimos de centavo a $0,00002 por 1.000.
- **Armazenamento vetorial.** Cerca de 50 fragmentos de tamanho padrão, bem abaixo de um megabyte, pequeno demais para importar a $0,06 por GB-mês.

Uma atualização semanal desse piloto custa cerca de 4 centavos por mês, quase tudo em solicitações do Web Unlocker. O controle de hash mantém a incorporação em zero quando nada mudou. Se você escalar linearmente, 500 páginas atualizadas semanalmente custam cerca de $3,25 por mês em scraping e menos de um centavo em armazenamento.

Tokens de geração por consulta e o armazenamento vetorial que você escolheu são os dois custos que podem crescer além desses números. Uma coleção clássica do Amazon OpenSearch Serverless tem um custo base mesmo ociosa. Isso é cerca de $175 por mês no mínimo reduzido de dev-test, e cerca do dobro no piso padrão de 2-OCU. A AWS também oferece um tipo de coleção que escala para zero, precificado na [página de preços do OpenSearch Service](https://aws.amazon.com/opensearch-service/pricing/). Contra as coleções clássicas, o S3 Vectors muda a aritmética para atualizações agendadas em volume moderado de consultas.

## Limitações e fortalecimento para produção

Este pipeline é uma arquitetura de referência, não um produto pronto para produção.

- **Atualização é uma programação que você possui.** Uma base de conhecimento é uma fotografia da última sincronização, e entre sincronizações ela fica desatualizada na taxa que seus dados fonte decaem. Levante um alerta quando uma sincronização falhar, porque uma execução pulada deixa dados desatualizados no lugar.
- **Um scraping com falha se esconde atrás de bons dados.** Quando você não consegue buscar uma página, sua versão anterior continua servindo como atual. O loader no repositório nomeia URLs com falha e sai com código diferente de zero para que uma execução agendada as reporte. Detectar uma página que silenciosamente parou de atualizar é uma questão separada. `scraped_date` não pode responder isso, já que uma página pode buscar limpa por meses sem mudar. O repositório carimba uma data de última vez vista em cada busca bem-sucedida e reporta as URLs que não vê desde uma data de corte.
- **Você não pode alterar fragmentação ou incorporação depois.** Teste ambos em uma amostra das suas páginas antes da primeira sincronização completa. Corrigir o modelo significa recriar a base de conhecimento, e corrigir a fragmentação significa recriar a fonte de dados.
- **O conteúdo recuperado é tão bom quanto a página.** O Bright Data retorna o que a página serviu e o converte para Markdown. Não verifica se a página está correta. Para qualquer coisa sobre a qual você agirá, mantenha um humano no processo, e use a citação `source_url` para tornar a verificação um clique em vez de um palpite.
- **O token nos trechos é um espaço reservado.** Qualquer coisa agendada deve lê-lo do AWS Secrets Manager. Nunca deve chegar a um repositório.
- **Os números são medidos, mas não do seu corpus.** Reproduza-os nas suas próprias contas e dados antes de confiar em qualquer número específico.

## Considerações finais

O Amazon Bedrock Knowledge Bases lida com fragmentação, incorporação, e recuperação citada, e seus conectores nativos cobrem os dados que você já possui e as páginas estáticas que são suas. A web pública que muda, renderiza no lado do cliente, e se defende fica fora desse alcance, e geralmente guarda os dados que tornam uma base de conhecimento válida de se construir. O Bright Data fecha essa lacuna, e os dois se encontram no S3.

Para estender o pipeline, adicione programação do EventBridge para atualização automática, use a Crawl API do Bright Data para cobertura completa de site, ou habilite o reranking do Bedrock depois de definir a fragmentação.

*O Web Unlocker do Bright Data inclui um nível gratuito para você começar, a Crawl API cobre crawlings de sites inteiros, e você configura o Amazon Bedrock Knowledge Bases a partir do [console do Amazon Bedrock](https://console.aws.amazon.com/bedrock/). O projeto completo executável, com o loader, o script de configuração da base de conhecimento, e as políticas IAM, está no [repositório complementar](https://github.com/luminati-io/bedrock-web-kb-brightdata).*

## Perguntas frequentes

### O Amazon Bedrock Knowledge Bases consegue rastrear a web sozinho?

Ele tem um Web Crawler nativo em preview, uma boa opção para páginas estáticas que você possui. Hoje lida apenas com sites estáticos, não permite um site sem `robots.txt`, limita-se a 25.000 páginas por sincronização, e precisa do OpenSearch Serverless. Para páginas com JavaScript ou protegidas contra bots, use o Bright Data para renderizar e desbloquear o conteúdo, depois ingira do S3.

### Por que armazenar os dados no S3 em vez de recuperar da web no momento da consulta?

Uma base de conhecimento persistente incorpora cada página uma vez e serve muitas consultas a partir de uma busca vetorial de baixa latência, com citações, filtragem de metadados, e controle de acesso. A recuperação ao vivo através do Bright Data se encaixa em buscas de mudança rápida ou únicas. Muitos sistemas de produção usam ambos, e roteiam por consulta pela pontuação.

### Em que formato as páginas raspadas devem estar para o Bedrock?

Markdown. O Web Unlocker do Bright Data retorna a página como Markdown quando você define `data_format` como `markdown`, e o Bedrock a ingere como um tipo de documento suportado. A conversão remove a marcação HTML mas não a navegação do site, então remova o chrome repetido antes de gravar no S3.

### Como funcionam as citações nas respostas?

Você grava um atributo `source_url` no sidecar `.metadata.json` de cada documento. O Bedrock carrega esses metadados na recuperação, então `Retrieve` os retorna em todo fragmento e `RetrieveAndGenerate` inclui um array de citações que resolve cada trecho da resposta de volta à página de onde veio.

### Por que meu trabalho de ingestão diz COMPLETE quando nada foi indexado?

Porque `status` não é um sinal de sucesso. Um trabalho pode reportar `COMPLETE` quando a maioria de seus documentos falhou, e nesse pipeline ele reportou `COMPLETE` com 5 de 6 falhos. Leia `statistics` para as contagens por documento e `failureReasons` para o erro, que aqui foi o limite de metadados filtráveis do S3 Vectors.

### Como mantenho a base de conhecimento atualizada?

Raspe novamente em uma programação, grave apenas os objetos cujo hash de conteúdo mudou, e inicie um trabalho de ingestão. Esse controle de hash impede que o Bedrock reincorpore uma página inalterada. Combine a programação com a velocidade que suas fontes decaem, de diariamente para notícias a semanalmente ou mais lento para documentação.

### Quantas perguntas um conjunto padrão de referência precisa?

Menos do que as pessoas esperam, e as perguntas precisam ser reais. Perguntas que você extrai do que usuários realmente fazem superam as inventadas, porque o objetivo é encontrar o que seu corpus não consegue fundamentar em vez de produzir um número. Adicione uma toda vez que alguém reportar uma resposta ruim, e o conjunto se torna uma suíte de regressão.

### Quanto custa rodar isso?

Os principais custos são as solicitações do Bright Data, que escalam com páginas vezes frequência de atualização, mais o armazenamento vetorial, e incorporação e geração por token. O S3 Vectors é pay-as-you-go e serve bem a uma atualização agendada, enquanto uma coleção clássica do OpenSearch Serverless custa mesmo ociosa. O piloto de 6 páginas aqui custou cerca de 4 centavos por mês para atualizar semanalmente.



Contate-nosTeste grátis![google social icon](/wp-content/themes/brightdata/assets/images/ic_google.svg)









 Índice







Dedicated Scraper APIs &amp; No-Code Scrapers

Over 1000 scrapers for all popular domains. Simplify your web scraping.

[See pricing](/pricing/web-scraper "See pricing")

Just want data? Skip scraping.

Hundreds of ready-to-use datasets from all popular domains.

[See pricing](/pricing/datasets "See pricing")







 [ ](https://news.ycombinator.com/submitlink?t=Como+alimentar+o+Amazon+Bedrock+Knowledge+Bases+com+dados+da+web+em+tempo+real+usando+o+Bright+Data&u=https://brightdata.com.br/blog/procedimentos/amazon-bedrock-knowledge-bases-with-bright-data) [ ](https://www.linkedin.com/shareArticle?mini=true&title=Como+alimentar+o+Amazon+Bedrock+Knowledge+Bases+com+dados+da+web+em+tempo+real+usando+o+Bright+Data&url=https://brightdata.com.br/blog/procedimentos/amazon-bedrock-knowledge-bases-with-bright-data) [ ](http://www.reddit.com/submit?title=Como+alimentar+o+Amazon+Bedrock+Knowledge+Bases+com+dados+da+web+em+tempo+real+usando+o+Bright+Data&url=https://brightdata.com.br/blog/procedimentos/amazon-bedrock-knowledge-bases-with-bright-data)







##  Você também pode estar interessado em

 [ ![MiniMax M3 with Bright Data](https://media.brightdata.com.br/2026/09/MiniMax-M3-with-Bright-Data.png) ](https://brightdata.com.br/blog/ai/minimax-m3-with-bright-data "Dando a agentes MiniMax M3 auto-hospedados acesso à web em tempo real com o Bright Data")

 [AI



 ![Satyam Tripathi](https://media.brightdata.com.br/2024/09/Satyam-Tripathi-50x50.png)

Satyam Tripathi

Technical Writer





### Dando a agentes MiniMax M3 auto-hospedados acesso à web em tempo real com o Bright Data

Agentes MiniMax M3 auto-hospedados obtêm acesso à web em tempo real usando as ferramentas de busca e scraping do Bright Data. Contorne bloqueios e CAPTCHAs.



 15-Oct-2026

 67 min de leitura

 ](https://brightdata.com.br/blog/ai/minimax-m3-with-bright-data)

 [ ![Agentic Registration](https://media.brightdata.com.br/2026/09/Agentic-Registration.png) ](https://brightdata.com.br/blog/ai/agentic-registration "Registro Agêntico: Como um Agente de IA se Cadastra no Bright Data Com auth.md")

 [AI



 ![Daniel Shashko](https://media.brightdata.com.br/2022/04/Daniel-Shashko-2-50x50.png)

Daniel Shashko

Web Data &amp; AI Expert





### Registro Agêntico: Como um Agente de IA se Cadastra no Bright Data Com auth.md

Como um agente de IA se registra no Bright Data via auth.md e obtém um token de API funcional, com um humano apenas confirmando um código de e-mail.



 15-Oct-2026

 17 min de leitura

 ](https://brightdata.com.br/blog/ai/agentic-registration)

 [ ![Snowflake with Bright Data](https://media.brightdata.com.br/2026/10/Bright-Data-__Snowflake_From-Web-Data-to-AI-Ready-Intelligence.svg) ](https://brightdata.com.br/blog/ai/snowflake-with-bright-data "Bright Data + Snowflake: De Dados da Web para Inteligência Pronta para IA")

 [AI



 ![Antonello Zanini](https://media.brightdata.com.br/2022/12/Antonello-Zanini-2-50x50.jpg)

Antonello Zanini

Technical Writer





### Bright Data + Snowflake: De Dados da Web para Inteligência Pronta para IA

Aprenda a enviar conjuntos de dados da web da Bright Data para o Snowflake, ativá-los com o Cortex AI e escolher entre conjuntos de dados armazenados e acesso MCP ao vivo.



 15-Oct-2026

 18 min de leitura

 ](https://brightdata.com.br/blog/ai/snowflake-with-bright-data)
