Importante

Você está vendo a versão anterior da nova experiência da Alura que estamos preparando para você. Em breve, ela ganha uma identidade visual novinha totalmente pensada em potencializar seus estudos!

1
resposta

[Projeto] Aula de Chat Robustos ajuste atualizados pra funcionar

Para o projeto funcionar, precisei fazer varias modificações
bloco 3 libs

# Importações necessárias
import os
from langchain_classic.chains import ConversationalRetrievalChain
from langchain_classic.memory import ConversationBufferWindowMemory
from langchain_community.vectorstores import Chroma
from langchain_google_genai import GoogleGenerativeAIEmbeddings, ChatGoogleGenerativeAI
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import CharacterTextSplitter
from langchain_core.documents import Document
import google.generativeai as genai
import warnings
warnings.filterwarnings('ignore')

no bloco 4 Secrets

import os
from google.colab import userdata

os.environ["GOOGLE_API_KEY"] = userdata.get('GEMINI_API_KEY_TEMP')
GOOGLE_API_KEY = os.environ["GOOGLE_API_KEY"]

print("✅ Google API Key configurada!")

bloco 6

import time
import random
from langchain_core.embeddings import Embeddings


class ResilientGoogleEmbeddings(Embeddings):
    """Wrapper com retry + cache em memória para o Gemini Embeddings."""

    def __init__(self, base_embeddings, max_attempts=6, initial_delay=2.0):
        self.base_embeddings = base_embeddings
        self.max_attempts = max_attempts
        self.initial_delay = initial_delay
        self._query_cache = {}
        self._documents_cache = {}

    def _run_with_retry(self, operation, description):
        last_error = None

        for attempt in range(1, self.max_attempts + 1):
            try:
                return operation()
            except Exception as e:
                last_error = e

                if attempt == self.max_attempts:
                    break

                # Backoff exponencial + pequeno jitter para evitar novas
                # colisões/requisições em sequência na API do Google.
                delay = self.initial_delay * (2 ** (attempt - 1))
                delay += random.uniform(0, 0.75)

                print(
                    f"⚠️ Gemini Embeddings falhou ({description}) - "
                    f"tentativa {attempt}/{self.max_attempts}. "
                    f"Nova tentativa em {delay:.1f}s..."
                )
                time.sleep(delay)

        raise RuntimeError(
            f"Gemini Embeddings não respondeu após {self.max_attempts} tentativas. "
            f"Erro original: {last_error}"
        ) from last_error

    def embed_query(self, text):
        text = str(text).strip()
        if not text:
            raise ValueError("Não é possível gerar embedding para uma pergunta vazia.")

        if text in self._query_cache:
            return self._query_cache[text]

        result = self._run_with_retry(
            lambda: self.base_embeddings.embed_query(text),
            "embed_query"
        )
        self._query_cache[text] = result
        return result

    def embed_documents(self, texts):
        texts = [str(text).strip() for text in texts]
        if not texts:
            return []

        missing = [text for text in texts if text not in self._documents_cache]

        if missing:
            result = self._run_with_retry(
                lambda: self.base_embeddings.embed_documents(missing),
                f"embed_documents ({len(missing)} textos)"
            )
            for text, vector in zip(missing, result):
                self._documents_cache[text] = vector

        return [self._documents_cache[text] for text in texts]

    def __getattr__(self, name):
        # Mantém acesso às configurações/atributos do embedder original.
        return getattr(self.base_embeddings, name)


# Embedder oficial do Google Gemini.
# max_retries do componente Google trata algumas falhas internamente;
# o wrapper acima acrescenta uma camada adicional para falhas 500 transitórias.
_base_embeddings = GoogleGenerativeAIEmbeddings(
    model="gemini-embedding-001",
    google_api_key=GOOGLE_API_KEY,
    max_retries=3,
)

embeddings = ResilientGoogleEmbeddings(
    _base_embeddings,
    max_attempts=6,
    initial_delay=2.0,
)


# Cria/abre o mesmo banco Chroma sem duplicar os documentos quando
# a célula for executada novamente.
vectorstore = Chroma(
    collection_name="ia_gemini",
    embedding_function=embeddings,
    persist_directory="./chroma_db_gemini",
)


# Só indexa os documentos na primeira execução.
if vectorstore._collection.count() == 0:
    vectorstore.add_documents(docs)
    print(f"✅ {len(docs)} documentos indexados no Chroma.")
else:
    print(
        f"✅ Chroma reutilizado: {vectorstore._collection.count()} "
        "documentos já indexados."
    )

print("✅ Gemini Embeddings configurado com retry + cache.")

bloco9

#omiti demais códigos só o que mudou aqui
        time.sleep(1)

        resultado = qa_chain.invoke({"question": pergunta})
1 resposta

Oi, Marcelo! Tudo bem?

Ótimo ver você compartilhando os ajustes atualizados que deixaram o projeto de chat funcionando na sua máquina. Esse tipo de contribuição ajuda bastante quem vai passar pelo mesmo caminho, porque as bibliotecas do ecossistema LangChain mudam de nome e de módulo com frequência, e as APIs de embeddings do Gemini costumam devolver erros transitórios quando recebem muitas requisições em sequência.

Seu wrapper ResilientGoogleEmbeddings resolve o ponto certo do problema: o backoff exponencial com jitter evita novas colisões de requisição, e o cache em memória impede que o mesmo texto seja reprocessado, o que economiza cota e tempo de execução.

E você, chegou a medir quantas tentativas o retry precisou fazer em média durante a indexação? Seria interessante saber se seis tentativas foram suficientes no seu caso ou se deu para reduzir esse número.

Alura Conte com o apoio da comunidade Alura na sua jornada. Abraços e bons estudos!