Pergunte em linguagem natural sobre editais de bolsas e regulamentos. O EditalBot responde só com base nos documentos, cita a página da fonte e diz quando não sabe, em vez de inventar prazos e valores como um LLM sem contexto faz.
Projeto desenvolvido por Rodrigo Gandarela Soares de Farias Duca (Engenharia de Computação — Universidade SENAI CIMATEC), set/2026. Temáticas: LLMs, NLP e Engenharia de Dados. Especificação completa (Spec-Driven Development) em
specs/.📖 Documentação técnica detalhada, passo a passo:
DOCUMENTACAO.md
INDEXAÇÃO CONSULTA
PDF ─► extrair (pypdf) ─► limpar ─► chunks de 800 caracteres pergunta
(cabeçalhos, hifenização) (nunca cruzam páginas) │
│ ┌──────────┴──────────┐
embeddings E5 ("passage:") E5 ("query:") BM25
│ └─────► RRF ◄───────┘
▼ │ top-5 trechos
ChromaDB (persistente) ───────────────►─┤
▼
prompt: só os trechos, cite [doc, p. N], recuse se faltar
│
LLM (Claude Haiku 4.5 | Ollama local)
│
validação das citações contra os trechos ─► resposta + fontes
Conjunto-ouro escrito antes de testar o sistema: data/avaliacao/ouro.json,
com 30 perguntas (20 factuais, 4 de raciocínio, 3 sobre outros documentos e 3 fora do escopo), sobre um
corpus de 4 PDFs públicos (187 trechos).
| Configuração | hit@1 | hit@3 | hit@5 | MRR |
|---|---|---|---|---|
| Semântica (E5), chunk 400 | 0,30 | 0,56 | 0,67 | 0,45 |
| Semântica (E5), chunk 800 | 0,41 | 0,59 | 0,74 | 0,54 |
| Semântica (E5), chunk 1200 | 0,37 | 0,63 | 0,67 | 0,50 |
| Híbrida (E5 + BM25, RRF), chunk 800 | 0,59 | 0,85 | 0,89 | 0,73 |
A busca híbrida supera a meta de hit@5 ≥ 0,85. A busca lexical (BM25) recupera o que os embeddings não pegam bem: números, datas e códigos como "2.600", "BDIA-IT02-2026.09" e "B.5".
Resultados E1 (LLM sem RAG) × E2 (RAG) em reports/avaliacao.md, gerados por
python -m src.avaliar quando há um LLM configurado.
python -m venv .venv && .venv\Scripts\activate # Linux/Mac: source .venv/bin/activate
pip install torch --index-url https://download.pytorch.org/whl/cpu
pip install -r requirements.txt
copy .env.example .env # e escolha um LLM (abaixo)Coloque os PDFs em data/pdfs/ (links oficiais em data/documentos.json), e depois:
python -m src.ingestao # indexa (reexecutar não duplica: deduplicação por SHA-256)
python -m src.avaliar --so-recuperacao # métricas de busca, sem custo
python -m src.avaliar # + geração E1 × E2 (precisa de LLM)
streamlit run app/app.py # chat
pytest # 11 testesEscolha do LLM (.env):
- Claude —
ANTHROPIC_API_KEY=...eLLM_PROVIDER=claude. Modelo padrão:claude-haiku-4-5. A avaliação completa custa centavos de dólar. - Ollama (gratuito, local) — instale o Ollama, rode
ollama pull qwen2.5:3be useLLM_PROVIDER=ollama. - Sem LLM, o app funciona em modo de busca: mostra os trechos mais relevantes com a página.
- Chunks que não cruzam páginas. Cada trecho pertence a uma única página, então a citação
[doc, p. N]é sempre exata. - Prefixos do E5. O
multilingual-e5-smallfoi treinado comquery:/passage:, e omitir esses prefixos degrada a busca. - Guardrail de citações (
src/citacoes.py). Toda citação da resposta é conferida contra os trechos recuperados. Citação inexistente aparece como "⚠ fonte não verificada". - Recusa explícita. O prompt exige uma frase fixa quando a resposta não está nos trechos, e a avaliação mede a taxa de recusa correta nas perguntas fora do escopo.
- Privacidade e observabilidade.
logs/consultas.jsonlregistra latências, tokens, trechos usados e se as citações eram válidas, mas não o texto da pergunta, a menos queLOG_PERGUNTAS=1.
src/ingestao.py PDF → páginas limpas → fragmentos que nunca cruzam páginas
src/recuperacao.py ChromaDB + embeddings E5, busca semântica, BM25 e fusão RRF
src/citacoes.py extração e validação das citações [documento, p. N]
src/llm.py provedores Claude e Ollama com interface comum
src/rag.py prompt, geração, guardrail de citações e log de observabilidade
src/avaliar.py conjunto-ouro: hit@k, MRR, E1 (sem RAG) × E2 (RAG)
app/app.py chat Streamlit com upload de PDF e seletor de k/modo
tests/ 11 testes (fragmentação, limpeza, citações) — sem PDF, sem LLM
specs/ visão, requisitos, dados, design, experimentos, tarefas, entrega
DOCUMENTACAO.md guia técnico completo, passo a passo
Qualidade: 11 testes (pytest) e lint (ruff) rodam a cada push pelo
CI, sem precisar de PDFs nem de chave de API.
PDFs públicos da Universidade SENAI CIMATEC: Edital 032/2026 (Bolsas IT em IA), Edital 002/2026 (PRH 27),
Edital 001/2026 (Manufatura Aditiva) e Regulamento do Programa de Bolsas de Estudo. Os PDFs não são
versionados, para não redistribuir versões desatualizadas. Os links estão em data/documentos.json.
- Não substitui o documento oficial: editais têm retificações, e a resposta vale para a versão indexada.
- Tabelas extraídas do PDF perdem a estrutura (ex.: barema, valores por carga horária), o que dificulta algumas buscas.
- Sem OCR: PDFs escaneados são recusados com aviso.
- Com a API externa, a pergunta e os trechos saem da máquina. Para dados sensíveis, use o Ollama local.
- Um PDF enviado pelo usuário pode conter prompt injection. O prompt trata os trechos como dados, mas isso não é uma garantia completa.
Código sob MIT. Os editais e regulamentos são documentos públicos de terceiros, apenas
referenciados por link (data/documentos.json), nunca redistribuídos aqui.