Estratégias específicas para código e HTML
Aplique uma divisão especializada ao código Python usando separadores de funções baseados em AST, ao HTML usando analisadores conscientes de tags e ao Markdown usando a hierarquia de cabeçalhos.
Estratégias específicas para código e HTML é uma aula grátis de AI Engineering Academy no CoddyKit. Esta é a aula 4 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.
Por que a Divisão Genérica Falha em Documentos Especializados
A divisão baseada em texto foi projetada para prosa, mas os dados do mundo real incluem código-fonte, páginas HTML e documentação Markdown. Dividir o código em um limite fixo de caracteres pode cortar uma função no meio do seu corpo, tornando a parte inútil para a recuperação. Documentos especializados precisam de divisores que compreendam sua estrutura interna, não apenas seu tamanho.
Divisão de Código Python Baseada em AST
A árvore sintática abstrata (AST) de um arquivo Python captura cada função, classe e módulo como um nó estruturado. Percorrendo a AST, o senhor pode extrair cada função ou método como sua própria parte, mantendo juntos a assinatura, a docstring e o corpo. O PythonCodeTextSplitter do LangChain utiliza essa abordagem internamente.
import ast
import textwrap
def extract_functions(source_code: str) -> list[dict]:
tree = ast.parse(source_code)
chunks = []
for node in ast.walk(tree):
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
start = node.lineno - 1
end = node.end_lineno
lines = source_code.splitlines()[start:end]
chunks.append({
'name': node.name,
'code': '\n'.join(lines),
'start_line': node.lineno,
})
return chunksDivisão pelos Limites das Classes
Para bases de código orientadas a objetos, dividir no nível da classe costuma ser melhor do que dividir no nível da função. Uma parte de classe mantém a relação entre os métodos e o estado compartilhado sobre o qual eles operam. O senhor pode incluir a docstring da classe e todos os corpos dos métodos em uma única parte e, depois, criar partes separadas e mais detalhadas apenas para métodos extensos.
from langchain_text_splitters import Language, RecursiveCharacterTextSplitter
python_splitter = RecursiveCharacterTextSplitter.from_language(
language=Language.PYTHON,
chunk_size=1000,
chunk_overlap=100,
)
with open('my_module.py', 'r') as f:
source = f.read()
chunks = python_splitter.create_documents([source])
print(f'Created {len(chunks)} code chunks')Adicionando Metadados de Código às Partes
As partes de código brutas são tão úteis quanto seus metadados. Ao armazenar partes de código em um banco de dados vetorial, inclua o caminho do arquivo, o nome da função, a linguagem de programação e o intervalo de linhas. Esses metadados permitem que o recuperador filtre por linguagem ou arquivo e que o LLM cite a localização exata da fonte em sua resposta.
from langchain_core.documents import Document
def chunk_python_file(filepath: str) -> list[Document]:
with open(filepath) as f:
source = f.read()
functions = extract_functions(source) # from previous example
docs = []
for fn in functions:
docs.append(Document(
page_content=fn['code'],
metadata={
'source': filepath,
'function': fn['name'],
'language': 'python',
'start_line': fn['start_line'],
}
))
return docsHTML: Estrutura Acima dos Caracteres
Os documentos HTML têm uma estrutura hierárquica composta por títulos, seções, parágrafos e listas. Dividir HTML por quantidade de caracteres frequentemente corta as tags, produzindo fragmentos malformados. A abordagem correta é analisar o HTML com um analisador apropriado, como BeautifulSoup, e extrair elementos semanticamente significativos, como as tags <article>, <section> e <p>.
from bs4 import BeautifulSoup
def chunk_html_by_section(html: str) -> list[dict]:
soup = BeautifulSoup(html, 'html.parser')
chunks = []
for tag in soup.find_all(['h1', 'h2', 'h3', 'p', 'li']):
text = tag.get_text(separator=' ', strip=True)
if len(text) > 40: # skip trivial fragments
chunks.append({
'tag': tag.name,
'text': text,
})
return chunksDivisão Hierárquica de HTML por Títulos
Uma estratégia de HTML mais sofisticada agrupa o conteúdo sob o título mais próximo. Cada parágrafo e lista que aparece depois de um título <h2> pertence àquela seção. Ao agrupar o texto com seu título pai, o senhor preserva o contexto do tópico que um parágrafo isolado perderia. O HTMLHeaderTextSplitter do LangChain implementa isso automaticamente.
from langchain_text_splitters import HTMLHeaderTextSplitter
headers_to_split_on = [
('h1', 'Header 1'),
('h2', 'Header 2'),
('h3', 'Header 3'),
]
splitter = HTMLHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
with open('page.html') as f:
html = f.read()
sections = splitter.split_text(html)
for sec in sections[:3]:
print(sec.metadata)
print(sec.page_content[:200])
print('---')Markdown: Respeitando a Hierarquia dos Títulos
A documentação Markdown é organizada com títulos #, ## e ###. O MarkdownHeaderTextSplitter divide o conteúdo nos limites dos títulos e armazena a hierarquia dos títulos nos metadados. Isso significa que cada parte conhece seu caminho completo de títulos, o que melhora muito a relevância do contexto recuperado quando os usuários perguntam sobre seções específicas da documentação.
from langchain_text_splitters import MarkdownHeaderTextSplitter
headers_to_split_on = [
('#', 'H1'),
('##', 'H2'),
('###', 'H3'),
]
md_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
with open('README.md') as f:
markdown = f.read()
docs = md_splitter.split_text(markdown)
for doc in docs[:2]:
print('Metadata:', doc.metadata)
print('Content:', doc.page_content[:300])
print()Divisão Secundária Após a Divisão por Títulos
Depois da divisão por título, algumas seções ainda podem ser longas demais para o limite de tokens do seu modelo de incorporação. O padrão recomendado é uma divisão em duas etapas: primeiro, divida pela hierarquia de títulos para preservar o contexto semântico; em seguida, aplique um divisor baseado em caracteres a qualquer seção que exceda o limite de tamanho da parte. Isso garante que nenhuma parte seja grande demais, preservando os metadados dos títulos.
from langchain_text_splitters import MarkdownHeaderTextSplitter, RecursiveCharacterTextSplitter
header_splitter = MarkdownHeaderTextSplitter(
headers_to_split_on=[('#', 'H1'), ('##', 'H2')]
)
char_splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
)
with open('docs.md') as f:
md = f.read()
header_chunks = header_splitter.split_text(md)
final_chunks = char_splitter.split_documents(header_chunks)
print(f'{len(final_chunks)} final chunks produced')Divisão de PDFs com Reconhecimento de Tabelas
PDFs extraídos com ferramentas como PyMuPDF ou pdfplumber frequentemente perdem a estrutura das tabelas, produzindo linhas de texto desordenadas. Para lidar com isso, use analisadores de PDF que reconheçam o layout, detectem os limites das tabelas e as convertam para o formato Markdown ou CSV antes da divisão. Trate cada tabela como uma única parte, com metadados estruturados que a identifiquem como tabela, e não como prosa.
import pdfplumber
def extract_pdf_chunks(pdf_path: str) -> list[dict]:
chunks = []
with pdfplumber.open(pdf_path) as pdf:
for page_num, page in enumerate(pdf.pages):
# Extract tables separately
for table in page.extract_tables():
rows = ['|'.join(str(c) for c in row) for row in table]
chunks.append({
'type': 'table',
'content': '\n'.join(rows),
'page': page_num + 1,
})
# Extract prose text
text = page.extract_text()
if text:
chunks.append({'type': 'text', 'content': text, 'page': page_num + 1})
return chunksDetecção de Linguagem para Corpora Mistos
As bases de conhecimento empresariais frequentemente combinam diferentes tipos de arquivo: scripts Python, documentação de API em HTML, notas de arquitetura em Markdown e exportações de dados em CSV. Um pipeline robusto de divisão deve detectar o tipo de arquivo pela extensão ou pelo tipo MIME e encaminhar cada documento ao divisor especializado apropriado. Isso evita aplicar a lógica de divisão de código à prosa ou vice-versa.
from pathlib import Path
def route_document(filepath: str) -> list[dict]:
ext = Path(filepath).suffix.lower()
if ext == '.py':
return chunk_python_file(filepath)
elif ext in ('.html', '.htm'):
with open(filepath) as f:
return chunk_html_by_section(f.read())
elif ext == '.md':
# use MarkdownHeaderTextSplitter
return chunk_markdown(filepath)
elif ext == '.pdf':
return extract_pdf_chunks(filepath)
else:
# fallback: plain text recursive splitter
return chunk_plain_text(filepath)Preservando o Contexto com Linhas Circundantes
Ao dividir código por função, costuma ser útil incluir algumas linhas de contexto circundante, como instruções de importação no início do arquivo ou a definição da classe que contém um método. Esse contexto ajuda o LLM a entender quais bibliotecas estão disponíveis e qual é o papel da função dentro da classe mais ampla, melhorando a qualidade das respostas geradas.
def chunk_with_imports(source_code: str, fn_node, lines: list[str]) -> str:
# Gather top-of-file imports (first block before first non-import)
import_lines = []
for line in lines:
stripped = line.strip()
if stripped.startswith('import ') or stripped.startswith('from '):
import_lines.append(line)
elif stripped and not stripped.startswith('#'):
break
fn_body = '\n'.join(lines[fn_node.lineno - 1:fn_node.end_lineno])
return '\n'.join(import_lines) + '\n\n' + fn_bodyVerificação Rápida
Teste sua compreensão sobre as estratégias de divisão específicas para documentos apresentadas nesta lição.
Recapitulação da Lição
Nesta lição, o senhor aprendeu que: a divisão baseada em AST preserva os limites de funções e classes Python; HTMLHeaderTextSplitter e MarkdownHeaderTextSplitter respeitam a hierarquia dos títulos para manter o contexto junto à sua seção; e uma abordagem em duas etapas (divisão por títulos seguida de divisão por caracteres) lida com seções grandes demais sem perder os metadados estruturais. Em seguida, exploraremos a busca híbrida, combinando recuperação densa e esparsa.
Perguntas Frequentes
A aula “Estratégias específicas para código e HTML” é grátis?
Sim — o texto completo de “Estratégias específicas para código e HTML” é 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 “Estratégias específicas para código e HTML”?
Aplique uma divisão especializada ao código Python usando separadores de funções baseados em AST, ao HTML usando analisadores conscientes de tags e ao Markdown usando a hierarquia de cabeçalhos. 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 4 de 4.
Quanto tempo leva a aula “Estratégias específicas para código e HTML”?
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
- Por que a divisão ingênua prejudica a recuperação
- Divisão semântica com similaridade de embeddings
- Recuperação pai-filho e do pequeno para o grande
- Estratégias específicas para código e HTML