AI Engineering Academy · Aula

Compressão contextual e filtragem por relevância

Aplique compressão contextual para remover frases irrelevantes dos fragmentos recuperados antes de enviá-los ao LLM, reduzindo o ruído e economizando tokens.

Aula 3 de 413 etapas

Compressão contextual e filtragem por relevância é uma aula grátis de AI Engineering Academy no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de AI Engineering Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de AI Engineering Academy inclui 4 aulas no total.

O problema dos fragmentos recuperados com ruído

Os fragmentos recuperados geralmente contêm conteúdo com relevância mista. Um fragmento de 500 tokens sobre indexação de bancos de dados pode responder às duas primeiras frases da consulta, mas conter seis frases de material não relacionado sobre procedimentos de backup. Enviar o fragmento inteiro ao LLM desperdiça tokens, reduz a relação sinal-ruído e pode fazer o modelo gerar uma resposta fundamentada na parte irrelevante, em vez das frases relevantes.

O que é compressão contextual?

Compressão contextual é uma etapa posterior à recuperação que recebe cada fragmento recuperado e extrai apenas as frases relevantes para a consulta antes de enviar o fragmento ao LLM. O fragmento original é reduzido às partes mais relevantes, diminuindo o uso de tokens e melhorando a qualidade da resposta. O ContextualCompressionRetriever do LangChain envolve qualquer recuperador com um componente compressor que realiza essa extração.

from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import LLMChainExtractor
from langchain_openai import ChatOpenAI

# LLMChainExtractor uses an LLM to extract the relevant portion
llm = ChatOpenAI(model='gpt-4o-mini', temperature=0)
compressor = LLMChainExtractor.from_llm(llm)

compression_retriever = ContextualCompressionRetriever(
    base_compressor=compressor,
    base_retriever=base_retriever,
)

results = compression_retriever.invoke('how does HNSW indexing work?')
for doc in results:
    print(len(doc.page_content), 'chars:', doc.page_content[:150])

LLMChainFilter: filtragem por relevância

Em vez de extrair frases dos fragmentos, o LLMChainFilter toma uma decisão binária: este fragmento é relevante para a consulta ou não? Os fragmentos irrelevantes são descartados completamente antes de chegar ao LLM. Isso é mais barato do que a extração (a chamada ao LLM é mais curta) e útil quando os fragmentos são curtos e coerentes o suficiente para que a extração parcial não ajude. Normalmente, de 20% a 40% dos fragmentos recuperados inicialmente são filtrados.

from langchain.retrievers.document_compressors import LLMChainFilter

filter_compressor = LLMChainFilter.from_llm(
    llm=ChatOpenAI(model='gpt-4o-mini', temperature=0)
)

filtering_retriever = ContextualCompressionRetriever(
    base_compressor=filter_compressor,
    base_retriever=base_retriever,
)

# Fetch 10 docs, filter drops irrelevant ones
results = filtering_retriever.invoke('what are the pgvector distance operators?')
print(f'{len(results)} chunks passed the relevance filter')

Filtragem de relevância baseada em embeddings

Usar um LLM para fazer a filtragem acrescenta latência e custo. Uma alternativa mais barata é a filtragem baseada em embeddings, que calcula a similaridade de cosseno entre o embedding da consulta e o embedding de cada fragmento e descarta os fragmentos abaixo de um limiar de similaridade. Esse processo é determinístico, rápido e gratuito — ele reutiliza os embeddings já calculados durante a recuperação, sem uma chamada adicional ao LLM.

from langchain.retrievers.document_compressors import EmbeddingsFilter
from langchain_openai import OpenAIEmbeddings

embeddings = OpenAIEmbeddings()
embeddings_filter = EmbeddingsFilter(
    embeddings=embeddings,
    similarity_threshold=0.76,  # cosine similarity threshold
)

embedding_retriever = ContextualCompressionRetriever(
    base_compressor=embeddings_filter,
    base_retriever=base_retriever,
)

results = embedding_retriever.invoke('BM25 hyperparameter tuning')
print(f'Filtered to {len(results)} relevant chunks')

Encadeamento de vários compressores

Você pode encadear vários compressores em sequência usando DocumentCompressorPipeline. Um padrão comum é aplicar primeiro um filtro rápido baseado em embeddings para remover fragmentos claramente irrelevantes; depois, dividir os fragmentos longos em frases; e, por fim, aplicar a extração com LLM para obter as frases mais relevantes. Essa abordagem em camadas equilibra custo e precisão.

from langchain.retrievers.document_compressors import DocumentCompressorPipeline
from langchain_community.document_transformers import EmbeddingsRedundantFilter
from langchain_text_splitters import CharacterTextSplitter

# Step 1: split chunks into individual sentences
sentence_splitter = CharacterTextSplitter(
    chunk_size=200,
    chunk_overlap=0,
    separator='. ',
)

# Step 2: remove redundant sentences via embeddings
redundant_filter = EmbeddingsRedundantFilter(embeddings=OpenAIEmbeddings())

# Step 3: keep only relevant sentences
relevance_filter = EmbeddingsFilter(embeddings=OpenAIEmbeddings(), similarity_threshold=0.76)

pipeline = DocumentCompressorPipeline(
    transformers=[sentence_splitter, redundant_filter, relevance_filter]
)

piped_retriever = ContextualCompressionRetriever(
    base_compressor=pipeline,
    base_retriever=base_retriever,
)

Remoção de fragmentos redundantes

Quando vários fragmentos recuperados dizem essencialmente a mesma coisa, enviar todos eles ao LLM desperdiça tokens sem acrescentar informação. EmbeddingsRedundantFilter remove fragmentos quase duplicados calculando a similaridade de cosseno entre pares e descartando os fragmentos muito semelhantes aos que já foram selecionados. Isso é especialmente valioso quando o corpus contém muitos fragmentos sobrepostos devido a configurações de alta sobreposição durante a ingestão.

from langchain_community.document_transformers import EmbeddingsRedundantFilter
from langchain_core.documents import Document

redundant_filter = EmbeddingsRedundantFilter(
    embeddings=OpenAIEmbeddings(),
    similarity_threshold=0.95,  # treat docs with >95% cosine sim as duplicates
)

# Simulate near-duplicate documents
docs = [
    Document(page_content='pgvector is a PostgreSQL extension for vector similarity search'),
    Document(page_content='pgvector extends PostgreSQL to support vector similarity search'),  # near duplicate
    Document(page_content='HNSW and IVFFlat are the two index types in pgvector'),
]

filtered = redundant_filter.transform_documents(docs, query='')
print(f'Kept {len(filtered)} of {len(docs)} documents after deduplication')

Extração de relevância no nível da frase

Para documentos longos nos quais o conteúdo relevante está espalhado, a extração no nível da frase com um codificador cruzado oferece a maior precisão. Pontue cada frase do fragmento recuperado em relação à consulta, mantenha apenas as frases acima de um limiar e reconstrua um documento comprimido. Essa abordagem normalmente reduz o tamanho do contexto em 40% a 70%, preservando todas as frases relevantes.

from sentence_transformers import CrossEncoder
import re

sentence_reranker = CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2')

def compress_with_cross_encoder(
    query: str, chunk: str, min_score: float = 0.0
) -> str:
    sentences = re.split(r'(?<=[.!?]) +', chunk)
    if len(sentences) <= 1:
        return chunk

    pairs = [[query, s] for s in sentences]
    scores = sentence_reranker.predict(pairs)

    relevant = [
        sentence
        for sentence, score in zip(sentences, scores)
        if float(score) >= min_score
    ]
    return ' '.join(relevant) if relevant else chunk

Gerenciamento do orçamento de tokens por meio da compressão

A compressão contextual também é uma ferramenta eficaz de gerenciamento do orçamento de tokens. Se quiser enviar 5 documentos a um modelo com uma janela de contexto limitada, comprimir cada um de 500 tokens para 100 tokens relevantes permite incluir 5 vezes mais informações. Isso é especialmente valioso ao trabalhar com modelos menores e mais rápidos, como o GPT-4o-mini, que têm janelas de contexto mais curtas e requisitos de latência mais rigorosos.

def compress_to_budget(
    query: str,
    docs: list[str],
    total_token_budget: int = 2000,
    tokens_per_char: float = 0.25,
) -> list[str]:
    compressed = []
    used_tokens = 0

    for doc in docs:
        compressed_doc = compress_with_cross_encoder(query, doc)
        doc_tokens = int(len(compressed_doc) * tokens_per_char)

        if used_tokens + doc_tokens <= total_token_budget:
            compressed.append(compressed_doc)
            used_tokens += doc_tokens
        else:
            break  # stop when budget is exhausted

    print(f'Using {used_tokens} tokens across {len(compressed)} docs')
    return compressed

Medição da qualidade da compressão

A compressão introduz um risco: você pode remover acidentalmente uma frase essencial para responder à consulta. Meça a qualidade da compressão comparando as pontuações de fidelidade antes e depois da compressão, usando RAGAS ou um avaliador personalizado baseado em LLM que verifique se o contexto comprimido ainda sustenta a resposta correta. Se a fidelidade cair após a compressão, reduza o limiar ou troque a filtragem pela extração.

def measure_compression_faithfulness(query, original_docs, compressed_docs, llm):
    # Use LLM to check if answer from compressed context matches
    # answer from full context
    def generate_answer(docs, q):
        context = '\n'.join(docs)
        resp = llm.invoke(f'Answer from context:\n{context}\nQ: {q}')
        return resp.content

    full_answer = generate_answer([d.page_content for d in original_docs], query)
    comp_answer = generate_answer(compressed_docs, query)

    # Check semantic similarity between answers
    vecs = [embed(full_answer), embed(comp_answer)]
    sim = cosine_similarity(vecs[0], vecs[1])
    print(f'Answer similarity after compression: {sim:.3f}')
    return sim

Quando não usar a compressão

A compressão contextual nem sempre é benéfica. Para fragmentos curtos e coerentes (com menos de 200 tokens), geralmente o fragmento inteiro é relevante, e a compressão apenas acrescenta latência. Em documentações técnicas nas quais a resposta depende de todas as partes de um procedimento (por exemplo, uma sequência numerada de etapas), filtrar frases individuais pode remover etapas essenciais. Aplique a compressão com critério e sempre valide se ela melhora a qualidade da resposta no seu caso de uso específico.

Pipeline de compressão pronta para produção

Uma pipeline de compressão robusta para produção combina filtragem baseada em embeddings (rápida e barata), remoção de redundâncias (para evitar repetir as mesmas informações) e extração opcional de frases com codificador cruzado (precisa, mas mais lenta). Execute primeiro as etapas rápidas e aplique a etapa mais cara, baseada em LLM, apenas a consultas de alto valor ou quando as etapas rápidas deixarem fragmentos demais. Essa abordagem adaptativa otimiza tanto o custo quanto a qualidade.

class AdaptiveCompressor:
    def __init__(self, embeddings, cross_encoder=None, threshold=0.76):
        self.emb_filter = EmbeddingsFilter(embeddings=embeddings,
                                           similarity_threshold=threshold)
        self.dedup = EmbeddingsRedundantFilter(embeddings=embeddings)
        self.cross_encoder = cross_encoder

    def compress(self, query: str, docs, use_cross_encoder: bool = False):
        # Stage 1: embedding filter
        docs = self.emb_filter.compress_documents(docs, query=query)
        # Stage 2: deduplication
        docs = self.dedup.transform_documents(docs, query=query)
        # Stage 3: optional sentence-level extraction
        if use_cross_encoder and self.cross_encoder:
            docs = [
                type(d)(page_content=compress_with_cross_encoder(
                    query, d.page_content
                ), metadata=d.metadata)
                for d in docs
            ]
        return docs

Verificação rápida

Teste sua compreensão sobre compressão contextual, com base nesta lição.

Resumo da lição

Nesta lição, você aprendeu: a compressão contextual reduz o ruído extraindo ou filtrando conteúdo irrelevante dos trechos recuperados antes que eles cheguem ao LLM, DocumentCompressorPipeline encadeia vários compressores (filtro, deduplicação, extração) em sequência, e a filtragem baseada em embeddings oferece uma alternativa rápida e econômica à compressão baseada em LLM para a maioria dos cenários. A compressão ajuda a gerenciar os limites de tokens e melhora a qualidade das respostas. A seguir, mediremos o impacto real da reclassificação na qualidade da recuperação.

Grátis para começar

Aprenda Python com um tutor de IA — grátis

Escreva e execute código real no seu navegador, obtenha ajuda instantânea de um tutor de IA 24/7 e continue de onde parou na web ou no app.

Cursos
30
Aulas
120

Perguntas Frequentes

A aula “Compressão contextual e filtragem por relevância” é grátis?

Sim — o texto completo de “Compressão contextual e filtragem por relevância” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de AI Engineering Academy, atualize para CoddyKit PRO. O curso de AI Engineering Academy inclui 4 aulas no total.

O que vou aprender em “Compressão contextual e filtragem por relevância”?

Aplique compressão contextual para remover frases irrelevantes dos fragmentos recuperados antes de enviá-los ao LLM, reduzindo o ruído e economizando tokens. Você pratica AI Engineering Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar AI Engineering Academy?

Nenhuma experiência prévia é necessária. AI Engineering Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.

Quanto tempo leva a aula “Compressão contextual e filtragem por relevância”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de AI Engineering Academy?

Sim. Cada aula de AI Engineering Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Por que a recuperação em duas etapas funciona
  2. Reclassificação com codificador cruzado usando Cohere e BGE
  3. Compressão contextual e filtragem por relevância
  4. Medindo o impacto da reclassificação
← Voltar para AI Engineering Academy