Solucionado (ver solução)

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!

Solucionado
(ver solução)
1
resposta

Boa noite.
realizei a divisão do texto em chunks conforme o código abaixo.

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=600,      # Tamanho máximo de cada chunk
    chunk_overlap=150   # Sobreposição entre chunks
)

# Divide os documentos em chunks
chunks = text_splitter.split_documents(documentos)

Após comecei a analisar os chunks gerados e encontrei o seguinte chunk, aparentemente sem contexto o qual desejo tratar nesta postagem.

---chunk 5/61:------------------------------
page_content=***'contagem de glóbulos brancos do sangue) com uma destas substâncias;

  • função da medula óssea prejudicada (ex.: após tratamento citostático) ou doenças do sistema
    hematopoiético (responsável pela produção das células sanguíneas);
  • desenvolvido broncoespasmo (contração dos brônquios levando a chiado no peito) ou outras reaçõe s
    anafilactóides, como urticária (erupçã o na pele que causa coceira), rinite (irritação e inflamação da
    mucosa do nariz), angioedema (inchaço em região subcutânea ou em mucosas) depois do uso de' ***

metadata={'producer': 'PDFium', 'creator': 'PDFium', 'creationdate': 'D:20191206103835', 'source': 'dipirona.pdf', 'total_pages': 10, 'page': 2, 'page_label': '3', 'medicamento': 'dipirona'}

Analisando o texto apresentando, parece estar tratando a respeito da "medula óssea" e do "broncoespasmo", mas ao verificar no arquivo dipirona.pdf há a seguinte frase antes:
"A dipirona monoidratada não deve ser utilizada caso você tenha:"

Esta frase permite identificar o contexto das frases do chunk e que elas tratam das condições em que não deve ser utilizado a dipirona monoidratada, sendo:
- quando há função da medula óssea prejudicada
- ou quando foi desenvolvido broncoespasmo

Então o texto do chunk 5, se avaliado sem a frase que indica o contexto tratado, leva a uma interpretação incorreta.

Como deveria ser tratado esta situação? Deveria adicionar um metadado que ajude a contextualizar o chunk? ou outra sugestão?

1 resposta
solução!

Oi, Eduardo! Tudo bem?

Ótima observação, você chegou sozinho num problema clássico de RAG: a perda de contexto na fronteira do chunk. O RecursiveCharacterTextSplitter corta por caracteres, sem saber que aqueles marcadores estavam subordinados à frase "A dipirona monoidratada não deve ser utilizada caso você tenha:".

Sobre a sua pergunta: metadado sozinho não resolve. O Chroma vetoriza só o page_content, os metadados ficam de fora do embedding. E no RetrievalQA com chain_type="stuff", o que vai para o prompt também é só o page_content. Um metadado de contexto seria invisível para a busca e para o modelo. A regra é: se a informação precisa influenciar a busca ou a resposta, ela tem que estar dentro do texto do chunk.

A solução mais direta é quebrar por seção antes de quebrar por tamanho. Bulas de paciente seguem a estrutura padronizada da RDC 47/2009, com aquelas perguntas numeradas, dá para usá-las como âncora, um exemplo que você pode analisar:

import re
from langchain.schema import Document

# Reagrupa as páginas de cada bula em um texto contínuo
textos = {}
for doc in documentos:
    med = doc.metadata["medicamento"]
    textos[med] = textos.get(med, "") + "\n" + doc.page_content

# aqui quebra nos títulos numerados ("3. QUANDO NÃO DEVO USAR...")
PADRAO_SECAO = re.compile(r"\n(?=\d{1,2}\.\s+[A-ZÁÂÃÉÊÍÓÔÕÚÜÇ])")

secoes = []
for med, texto in textos.items():
    for parte in PADRAO_SECAO.split(texto):
        titulo = parte.strip().split("\n")[0].strip()
        if not re.match(r"^\d{1,2}\.", titulo):
            titulo = "Identificação e apresentação"
        secoes.append(Document(page_content=parte,
                               metadata={"medicamento": med, "secao": titulo}))

chunks = text_splitter.split_documents(secoes)

# devolve o contexto para dentro do texto que será vetorizado
for chunk in chunks:
    chunk.page_content = (f"Medicamento: {chunk.metadata['medicamento']}\n"
                          f"Seção da bula: {chunk.metadata['secao']}\n\n"
                          f"{chunk.page_content}")

Seu chunk 5 passaria a começar com Seção da bula: 3. QUANDO NÃO DEVO USAR ESTE MEDICAMENTO?. O sentido volta, tanto para o embedding quanto para o LLM.

Dois detalhes: rode um print das seções antes de seguir, pra conferir se a extração do PDF não bagunçou a numeração; e apague a pasta ./chroma_bulas antes de recriar o vectorstore, senão o Chroma acumula os chunks antigos junto com os novos.

Aumentar o chunk_overlap ajuda pouco, é problema estrutural, não de tamanho. Se quiser ir além, olhe o ParentDocumentRetriever do LangChain: você indexa chunks pequenos para precisão na busca, mas entrega ao modelo a seção inteira.

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