# Pastor Online Sistema web RAG para consulta bíblica. Lê PDFs da Bíblia, indexa o conteúdo em um banco vetorial e responde em chat com um agente pastoral **ancorado exclusivamente nos versículos recuperados** — com as referências sempre à vista. Containerizado com Docker Compose: frontend React, backend FastAPI, ChromaDB e (opcionalmente) Ollama local. --- ## Como funciona ``` ./PDF/*.pdf ──▶ PyMuPDF + OCR (Tesseract) ──▶ parser bíblico ──▶ embeddings │ navegador ◀── SSE ── FastAPI ◀── busca + validação │ ChromaDB ``` **Fidelidade ao texto.** A resposta só é gerada quando há evidência recuperada do corpus. As citações são conferidas no servidor contra as fontes consultadas: uma referência que o modelo inventou é removida **antes** de chegar à tela. Sem evidência, o sistema responde com uma mensagem fixa e não chama o LLM. **Extração.** Se a camada textual do PDF é utilizável, ela é usada (rápido). Se o PDF usa fontes sem mapeamento Unicode — caso comum em PDFs de Bíblia digitalizados — o sistema detecta a corrupção e aplica OCR na página. O resultado do OCR é cacheado por página: reindexações não repetem o trabalho. --- ## Requisitos - Docker com Compose v2 (ou Podman com `podman-compose`) - ~2 GB de RAM livres para o backend - ~1,5 GB de disco para o modelo de embeddings e o banco vetorial --- ## Início rápido ### 1. Coloque os PDFs Copie seus PDFs da Bíblia para a pasta **`./PDF`**. O diretório já contém o `biblia.pdf` desta edição da Almeida Revista e Corrigida. > A pasta `./pdfs` (minúscula) também funciona: basta apontar `PDF_DIR` para ela no > `docker-compose.yml`. O Compose monta `./PDF` como somente-leitura em `/data/pdfs`. ### 2. Configure o provedor de LLM ```bash cp .env.example .env ``` Edite o `.env`. Três caminhos possíveis: | Provedor | Configuração | Observação | |---|---|---| | **Ollama** (local) | `LLM_PROVIDER=ollama` | Subir com `--profile local`; o modelo é baixado na primeira execução | | **OpenAI** | `LLM_PROVIDER=openai` + `OPENAI_API_KEY` | | | **Groq** | `LLM_PROVIDER=groq` + `GROQ_API_KEY` | | | **Anthropic** | `LLM_PROVIDER=anthropic` + `ANTHROPIC_API_KEY` | | ### 3. Suba o sistema ```bash # Com Ollama local: docker compose --profile local up --build -d # Sem Ollama (usando OpenAI, Groq ou Anthropic): docker compose up --build -d ``` Acesse **http://localhost:8080**. ### 4. Acompanhe a indexação A primeira indexação faz OCR de todas as páginas do PDF — são ~3.400 páginas, e o tempo típico fica entre 15 e 30 minutos em CPU. O painel lateral mostra o progresso (PDFs lidos e vetores indexados) e o chat já responde durante a indexação. Para acompanhar os logs: ```bash docker compose logs -f backend ``` --- ## Comandos úteis ```bash # Reiniciar só o frontend (após mudanças no React) docker compose up -d --build frontend # Forçar a reindexação dos PDFs curl -X POST http://localhost:8080/api/reindex # Ver o estado da indexação curl http://localhost:8080/api/status # Rodar os testes do backend docker compose exec backend pytest tests/ -v # Rodar os testes do frontend docker compose exec frontend npm test ``` --- ## API | Rota | Método | Descrição | |---|---|---| | `/api/health` | GET | Saúde do serviço | | `/api/status` | GET | PDFs lidos, vetores indexados, erros | | `/api/reindex` | POST | Força a reindexação (202). 409 se já em andamento | | `/api/chat` | POST | Chat em streaming (SSE) | | `/api/sources` | GET | Trechos consultados na última resposta | O chat responde em Server-Sent Events: ``` event: sources data: {"sources":[{"reference":"Gênesis 1:1","text":"...","page":6}]} event: token data: {"text":"No princípio criou Deus os céus e a terra..."} event: done data: {"citations":["Gênesis 1:1"]} ``` O frontend consome o stream com `fetch` + `ReadableStream`, porque `EventSource` não aceita `POST`. --- ## Configuração Todas as variáveis ficam no `.env`. As principais: | Variável | Padrão | Efeito | |---|---|---| | `LLM_PROVIDER` | `ollama` | `ollama`, `openai`, `groq` ou `anthropic` | | `EMBEDDING_MODEL` | `paraphrase-multilingual-MiniLM-L12-v2` | `all-MiniLM-L6-v2` é menor e mais rápido | | `TOP_K` | `6` | Quantos trechos são recuperados por pergunta | | `MIN_SIMILARITY` | `0.35` | Corte de relevância; acima disso, o sistema responde que o texto não aborda o assunto | | `OCR_DPI` | `200` | Resolução do OCR | | `OCR_LANGUAGE` | `por+eng` | Idiomas do Tesseract | | `REINDEX_TOKEN` | vazio | Se preenchido, `/api/reindex` exige o header `X-Reindex-Token` | | `CORS_ORIGINS` | `http://localhost:8080` | Origens permitidas | | `AUTO_INDEX` | `true` | Indexa automaticamente na inicialização | --- ## Solução de problemas **A indexação não termina / fica em "Indexando".** O OCR é o gargalo. Verifique `docker compose logs backend`. Se o OCR estiver lento, aumente `OCR_DPI` não ajuda — reduza para 150 para acelerar, com perda de acurácia. Para retomar após uma interrupção, basta reiniciar o container: o cache de OCR é preservado no volume e só as páginas faltantes são reprocessadas. **O chat responde que o texto "não aborda diretamente o assunto".** O índice pode estar vazio (`/api/status`) ou a pergunta pode estar longe do corpus. Reduza `MIN_SIMILARITY` para 0.25, ou increase `TOP_K` para 10. Verifique também se a pergunta cita uma referência explícita — nesse caso a busca é determinística e não depende do limiar. **"Nenhum versículo pôde ser extraído".** O PDF pode não ser de uma Bíblia com capítulos e versículos numerados, ou o OCR pode ter falhado. Inspecione o cache de OCR em `/data/vector_db/ocr_cache/` para ver o texto que foi extraído de cada página. **Ollama não sobe.** `docker compose --profile local up -d`. O serviço `ollama-pull` baixa o modelo indicado em `OLLAMA_MODEL` na primeira execução. Para outro modelo: `OLLAMA_MODEL=llama3.2:3b docker compose --profile local up -d`. **Porta 8080 já em uso.** `FRONTEND_PORT=8090 docker compose up -d`. Atualize também `CORS_ORIGINS`. --- ## Estrutura do projeto ``` . ├── docker-compose.yml ├── .env.example ├── PDF/ # PDFs da Bíblia (entrada, somente-leitura) ├── backend/ │ ├── Dockerfile │ ├── requirements.txt │ ├── app/ │ │ ├── config.py # settings via pydantic-settings │ │ ├── models.py # schemas da API │ │ ├── extraction.py # PDF → texto (PyMuPDF + OCR + cache) │ │ ├── bible_parser.py # livro / capítulo / versículo │ │ ├── embedding.py # sentence-transformers │ │ ├── vector_store.py # ChromaDB │ │ ├── indexer.py # manifesto incremental │ │ ├── llm.py # adaptadores Ollama/OpenAI/Groq/Anthropic │ │ ├── chat.py # contexto, validação de citações │ │ └── main.py # rotas FastAPI │ └── tests/ ├── frontend/ │ ├── Dockerfile │ ├── nginx.conf │ └── src/ │ ├── App.tsx │ ├── api.ts │ ├── lib/sse.ts │ └── components/ └── docs/superpowers/ # design e plano de implementação ``` --- ## Segurança - As chaves de API vivem **apenas** no backend, via variáveis de ambiente. Nunca chegam ao bundle do navegador. - Os PDFs são montados somente-leitura; a escrita fica restrita ao volume `/data`. - O conteúdo indexado é entregue ao modelo como **dado delimitado**, nunca como instrução — o que neutraliza tentativas de prompt injection via corpus. - Mensagens acima de 4.000 caracteres são recusadas; o histórico é limitado às últimas 10 trocas. - O endpoint de reindexação rejeita chamadas concorrentes (409) e aceita um token opcional (`REINDEX_TOKEN`). A aplicação é local, sem autenticação. Não a exponha à internet sem colocar um proxy com autenticação à frente.