Voltar ao dashboard

Diagramas

Componentes
Fluxo de Informacao
Maquinas de Estado
Rotinas Ad-Hoc
InternetUtilizadores
TraefikHTTPS + Let's Encrypt
Hub nginxRouter :80
↓ /corpus/        ↓ /api/chat        ↓ /chat
corpus-appFastAPI :8001
Sources, Crawlers, Ingest
rag-apiFastAPI :8002
Query, Chat, WhatsApp
Chat UIHTML/JS
Streaming SSE
↓                     ↓
PostgreSQL 16Sources, Docs, Chunks
Crawl Logs, Tenants
QdrantVector Store :6333
Dense + BM25 Sparse
RedisCache :6379
Rate Limits, Sessions
↑ APIs Externas ↓
Voyage AIEmbeddings
voyage-3-large (1024d)
Claude Haiku 4.5LLM + Prompt Caching
90% desconto tokens
Pipeline de Ingestao
1
Fonte (AT/DRE)
PDF ou HTML descarregado pelo crawler
httpx + BeautifulSoup
2
Parsing
Extraccao de texto limpo + metadados (titulo, data, tipo, referencia legal)
PyMuPDF (PDF) / BS4 (HTML)
3
Chunking estrutural
Split por artigo (codigos) ou seccao (PIVs). Target 300-500 tokens. Nunca corta meio de frase
regex + tiktoken
4
Contextualizacao
Haiku gera 1-2 frases posicionando o chunk no documento. Prompt cacheado
Claude Haiku 4.5 API
5
Embedding
Contexto + conteudo transformados em vector 1024 dims
Voyage AI (voyage-3-large)
6
Armazenamento
Upsert vector dense + sparse (BM25) + payload com metadata no Qdrant. Metadados em PostgreSQL
qdrant-client + SQLAlchemy
Pipeline de Query
1
Pergunta do utilizador
Via chat web (SSE) ou WhatsApp (webhook)
POST /api/chat
2
Rate limiting
Verificacao por IP ou tenant. Default: 50 queries/hora
Redis counter
3
Embedding da query
Mesma API e dimensao do ingest
Voyage AI (voyage-3-large)
4
Hybrid search
Dense cosine (top-20) + BM25 sparse (top-20), fusao RRF para top-10
Qdrant hybrid query
5
Geracao LLM
Prompt system v1.5 cacheado + top-10 chunks como contexto. Streaming token a token
Claude Haiku 4.5 + Prompt Cache
6
Resposta
Base legal, leitura tecnica, classificacao robustez, citacoes, disclaimer legal
SSE stream / WhatsApp reply
Ciclo de Vida do Documento
Descobertocrawler detecta
download
Descarregadoficheiro local
parse
Processadotexto extraido
chunk + embed
Indexadono Qdrant
Errofalha em qualquer passo
retry
Re-tentativaproximo crawl
Sessao de Crawling
Agendadoscheduler 04:00
trigger
A Correra listar fonte
compara
A Processarnovos docs
fim
Concluidolog em crawl_logs
Falhadotimeout / HTTP error
regista
Alertalog + notificacao
Ciclo de Vida da Query
RecebidaPOST /api/chat
rate check
Autorizadadentro do limite
embed + search
Retrievalhybrid Qdrant
LLM
StreamingSSE response
Rejeitada429 rate limit
Sem Fontesfallback oficial
Actualizacao de Legislacao (ex: novo OE, alteracao a codigo)
Automatico

Crawler detecta alteracao

Crawl diario (04:00 UTC) compara hash do PDF/HTML com versao anterior. Se diferente, marca como actualizado.

Automatico

Re-download do documento

Descarrega nova versao. Regista em crawl_logs com status "updated". Mantem versao anterior como backup.

Automatico

Apagar chunks antigos

Remove do Qdrant todos os pontos com document_id do documento antigo. Apaga registos da tabela chunks.

Automatico

Re-ingestao completa

Parse → chunk → contextualizacao (Haiku) → embedding (Voyage) → upsert Qdrant. Pipeline identica a ingestao inicial.

Automatico

Validacao + log

Confirma que novos chunks existem no Qdrant. Regista duracao e contagens em crawl_logs. Corpus imediatamente actualizado.

Novas Informacoes Vinculativas (novos PIV_ na AT)
Automatico

Crawler lista pagina da AT

Percorre paginacao da area (ex: CIVA). Compara PIV_ IDs encontrados com tabela documents. Novos IDs = documentos novos.

Automatico

Download + registo

Descarrega PDF. Cria registo em documents com status "downloaded". Rate limit: 1 req/s para nao sobrecarregar AT.

Automatico

Ingestao incremental

Apenas os documentos novos passam pela pipeline. Chunks adicionados ao Qdrant (sem tocar nos existentes). Custo: ~$0.003/doc.

Adicao Manual de Documento ao Corpus
Manual

Upload via admin API

POST /admin/ingest com ficheiro PDF/HTML. Admin identifica fonte e tipo. Protegido por ADMIN_API_KEY.

Automatico

Pipeline de ingestao

Mesmo fluxo: parse → chunk → contexto → embed → Qdrant. Documento fica disponivel para queries imediatamente.

Manual

Verificacao de qualidade

Testar query relacionada no /api/search para confirmar que chunks sao recuperados. Ajustar metadados se necessario.

Decisoes Tecnologicas

Vector Database

Qdrant

Metadata filtering nativo, hybrid search (dense + sparse), quantizacao binaria, Rust = eficiente em RAM.

Embeddings

Voyage AI (voyage-3-large)

Sem overhead CPU/RAM no VPS. $0.06/1M tokens, 5x mais barato que OpenAI. Bom desempenho em portugues.

LLM

Claude Haiku 4.5

Prompt Caching = 90% desconto em tokens repetidos. Prompt system v1.5 cacheavel. Rapido, bom em PT.

Chunking

Estrutural + Contextual

Metodo Anthropic: -49% a -67% falhas de retrieval. Chunks legais com contexto do documento.

Retrieval

Hibrido: Dense + BM25 + RRF

BM25 critico para termos legais exactos ("artigo 23.o do CIRC"). RRF funde os dois rankings.

Chat UI

Vanilla HTML/JS + SSE

Sem Node.js no VPS, < 50KB total, streaming nativo via EventSource, zero build step.

WhatsApp

Meta Cloud API

API oficial, gratuito ate 1000 conversas/mes, webhook simples.

Crawler

Python + httpx + BeautifulSoup

Sites AT sao server-rendered HTML simples. Nao precisam de JS runtime.

Pipelines

Pipeline de Ingestao

PDF/HTML (AT/DRE)
  ↓ PyMuPDF / BeautifulSoup
Texto limpo + metadados
  ↓ Chunker estrutural
Chunks 300-500 tokens
  ↓ Claude Haiku (contexto)
Chunk + contexto posicional
  ↓ Voyage AI (embedding)
Vector 1024d + BM25 sparse
  ↓ Qdrant upsert
Armazenado com payload

Pipeline de Query

Pergunta do utilizador
  ↓ Rate limit (Redis)
Embedding da query
  ↓ Voyage AI
Hybrid search Qdrant
  ↓ Dense + BM25 + RRF
Top-10 chunks relevantes
  ↓ Claude Haiku 4.5
Prompt v1.5 (cached) + chunks
  ↓ Streaming SSE
Resposta com citacoes

Custos Estimados

~4 EUR
Uso baixo
500 queries/mes
~7 EUR
Uso moderado
1000 queries/mes
~14 EUR
Uso alto
5000 queries/mes

VPS partilhado (custo incremental ~0). Ingestao inicial one-time: ~17 EUR (~5000 docs).

Plano de Implementacao — 10 Fases

0

Fundacao Done

1-2 dias

Docker-compose unificado, Qdrant + Redis, modelos PostgreSQL expandidos, nginx com rotas novas.

Tarefas

  • docker-compose.yml unificado na raiz (migrar hub + corpus separados)
  • Adicionar servicos Qdrant e Redis
  • Expandir modelos PostgreSQL: tabelas chunks, crawl_logs
  • nginx.conf com rotas /api/chat, /api/search, /chat
  • Scaffold rag/ com FastAPI minimo (health endpoint)
  • .env.example + deploy.sh unificado

Verificacao

  • docker compose up sobe 6 servicos
  • Health endpoints de corpus-app e rag-api respondem 200
  • PostgreSQL tem tabelas novas
  • Qdrant e Redis respondem
1

Pipeline de Ingestao In Progress

3-5 dias

Parser PDF/HTML, chunking estrutural por artigo, contextual embeddings (Haiku), vectorizacao Voyage AI, upsert Qdrant.

Tarefas

  • Parser de documentos — PyMuPDF (PDF) + BeautifulSoup (HTML)
  • Chunker estrutural: split por artigo para codigos, por seccao para PIVs
  • Contextual embeddings: Haiku gera 1-2 frases de contexto por chunk
  • Embedding + upsert: Voyage AI (1024d) + Qdrant batch insert
  • Pipeline orquestrada: ingest por documento, por fonte, ou corpus inteiro
  • Endpoint admin protegido por API key

Ficheiros

  • corpus/app/services/parser.py
  • corpus/app/services/chunker.py
  • corpus/app/services/contextual.py
  • corpus/app/services/embeddings.py
  • corpus/app/services/ingest.py
  • corpus/app/routers/admin.py

Verificacao

  • Parser extrai texto limpo de PDFs
  • Chunks de 300-500 tokens respeitam fronteiras de artigo
  • Qdrant tem collection "corpus" com pontos
  • Re-ingest nao duplica chunks
2

RAG Query Engine Todo

3-4 dias

Retrieval hibrido (dense + BM25 + RRF), Claude Haiku com prompt v1.5 cacheado, streaming SSE, rate limiting.

Tarefas

  • Retriever: embedding query + hybrid search Qdrant + RRF fusion
  • LLM service: Claude Haiku 4.5 com prompt caching + streaming
  • Chat endpoint: POST /api/chat com SSE (eventos: chunk, sources, done)
  • Search endpoint: POST /api/search (retrieval sem LLM, para debug)
  • Rate limiting por IP/tenant via Redis

Ficheiros

  • rag/app/services/retriever.py
  • rag/app/services/llm.py
  • rag/app/services/embeddings.py
  • rag/app/routers/chat.py
  • rag/app/routers/search.py

Verificacao

  • Search retorna chunks relevantes
  • Chat responde em streaming com citacoes
  • Classificacao robustez (verde/amarelo/vermelho) presente
  • Rate limiting funciona (429 apos limite)
3

Chat UI Todo

2-3 dias

Interface web de chat com streaming em tempo real, Markdown, citacoes clicaveis, badges de robustez.

Tarefas

  • Pagina HTML/JS em /chat (vanilla, sem framework)
  • SSE client com EventSource para streaming
  • Markdown rendering (marked.js)
  • Citacoes clicaveis que expandem o chunk fonte
  • Badges robustez: verde/amarelo/vermelho
  • Disclaimer legal permanente no footer
  • Historico de sessao em LocalStorage

Ficheiros

  • hub/html/chat/index.html
  • hub/html/chat/chat.js
  • hub/html/chat/style.css

Verificacao

  • Funciona em desktop e mobile
  • Streaming mostra tokens em tempo real
  • Citacoes e disclaimer presentes
4

Crawler Automatizado Todo

4-6 dias

Crawlers por fonte AT/DRE, scheduler diario, deteccao de novos documentos, re-ingestao automatica.

Tarefas

  • Crawler base: rate limiting, User-Agent, robots.txt compliance
  • Crawler Informacoes Vinculativas: 13 areas, paginacao, deteccao PIV_ novos
  • Crawler Codigos Tributarios: 20 codigos, deteccao update por hash
  • Crawler DRE: legislacao consolidada
  • Scheduler diario (04:00 UTC) com logs em crawl_logs
  • Endpoints admin: trigger manual + consulta logs

Ficheiros

  • corpus/app/crawlers/base.py
  • corpus/app/crawlers/at_vinculativas.py
  • corpus/app/crawlers/at_codigos.py
  • corpus/app/crawlers/dre.py
  • corpus/app/services/scheduler.py

Verificacao

  • Crawl detecta documentos novos
  • Pipeline de ingestao triggered automaticamente
  • Scheduler corre diariamente
  • Re-crawl nao duplica documentos
5

WhatsApp Todo

2-3 dias

Canal WhatsApp via Meta Cloud API. Webhook, formatacao adaptada, disclaimer legal.

Tarefas

  • Setup Meta Cloud API + numero de teste (sandbox)
  • Webhook handler: verificacao + receber/enviar mensagens
  • Formatacao WhatsApp: sem Markdown complexo, max 4000 chars
  • Adaptacao do LLM: respostas mais concisas para WhatsApp
  • Rate limiting por numero de telefone

Ficheiros

  • rag/app/routers/whatsapp.py
  • rag/app/services/whatsapp_client.py

Verificacao

  • Webhook verification funciona
  • Mensagem de teste recebe resposta formatada
  • Disclaimer incluido
6

Multi-tenancy Todo

5-7 dias

Multiplos clientes com dados isolados. API keys, planos, upload de dados privados, conversas persistentes.

Tarefas

  • Tenant CRUD: criacao, API keys (bcrypt), planos free/pro
  • Autenticacao por API key (middleware)
  • Isolamento Qdrant: corpus comum (tenant_id=null) + dados privados
  • Upload dados privados: SAF-T, facturas, docs por tenant
  • Conversas persistentes (JSONB, retencao por plano)
  • Admin: listar tenants, rodar API keys, desactivar

Ficheiros

  • rag/app/middleware/auth.py
  • rag/app/routers/tenant.py
  • corpus/app/services/tenants.py
  • corpus/app/routers/tenants.py

Verificacao

  • Tenant A nao ve dados de Tenant B
  • Queries sem API key retornam 401
  • Retrieval inclui corpus comum + dados privados
  • Rotacao de API key invalida key anterior
7

Revisao de Outputs Todo

3-4 dias

Painel para contabilista rever respostas do LLM, classificar qualidade, dar feedback e melhorar o sistema.

Tarefas

  • Tabela reviews: conversation_id, rating (correcto/parcial/incorrecto), notas do revisor, revisor_id, timestamp
  • Gravar todas as conversas com query, chunks usados e resposta completa (tabela conversations expandida)
  • Painel de revisao em /admin/reviews: lista conversas recentes, filtrar por nao-revistas, por classificacao robustez, por fonte
  • Workflow de revisao: contabilista le pergunta + resposta + fontes citadas, classifica (correcto/parcial/incorrecto), adiciona notas
  • Dashboard com metricas: % respostas correctas, temas problematicos, fontes com mais erros
  • Exportacao: relatorio semanal com respostas incorrectas para ajuste de prompt ou corpus

Ficheiros

  • corpus/app/models.py (Review model)
  • rag/app/routers/chat.py (gravar conversa completa)
  • hub/html/admin/reviews.html
  • hub/html/admin/reviews.js
  • corpus/app/routers/reviews.py (API CRUD reviews)

Verificacao

  • Conversas sao gravadas com query + resposta + chunks
  • Contabilista consegue listar e filtrar conversas
  • Classificacao e notas sao gravadas por conversa
  • Dashboard mostra % correctas e tendencias
8

Monitorizacao e Alertas Todo

2-3 dias

Alertas de nova informacao nas fontes, erros de sistema, bot em baixo. Notificacoes por email e Telegram.

Tarefas

  • Alerta de novo conteudo: apos cada crawl com novos docs, enviar notificacao com resumo (X novos em CIVA, Y actualizados em CIRC)
  • Alerta de erros de sistema: se rag-api nao responde, se Qdrant em baixo, se crawl falha. Health check a cada 5 min
  • Alerta de queries sem resposta: se mais de N queries seguidas caem no fallback, notificar (possivel lacuna no corpus)
  • Canais de notificacao: Telegram bot (simples, gratuito) + email SMTP (para relatorios)
  • Digest diario: resumo enviado as 09:00 com stats do dia anterior (queries, erros, novos docs, reviews pendentes)
  • Endpoint /api/health expandido: status de cada servico, ultima query, ultimo crawl, espaco disco
  • Integracao UptimeRobot (ou similar) para alerta externo de downtime

Ficheiros

  • corpus/app/services/notifications.py (Telegram + email)
  • corpus/app/services/scheduler.py (digest diario)
  • rag/app/services/health.py (health check expandido)
  • rag/app/middleware/monitor.py (tracking queries falhadas)

Verificacao

  • Crawl com novos docs envia notificacao Telegram
  • Servico em baixo gera alerta em menos de 10 min
  • Digest diario chega por email/Telegram as 09:00
  • UptimeRobot monitoriza /api/health
9

Gestao de Fontes Todo

3-5 dias

Admin UI para gerir fontes: adicionar novas, remover antigas, upload de documentos soltos, definir novos schemas de crawling.

Tarefas

  • Admin UI de fontes em /admin/sources: lista todas as fontes com estado, docs, ultimo crawl
  • Adicionar fonte estruturada: definir URL base, tipo de crawler (paginacao AT, DRE consolidado, lista PDFs), frequencia, regex de deteccao
  • Adicionar URLs avulsas: colar uma lista de URLs (PDF ou HTML) para download e ingestao imediata, sem crawler recorrente
  • Upload de documentos soltos: arrastar PDFs para a interface, atribuir a uma fonte e tipo, ingestao automatica
  • Editar fonte: alterar URL, frequencia de crawl, activar/desactivar, forcar re-crawl
  • Remover fonte: desactivar crawl + opcao de apagar chunks do Qdrant (soft delete ou hard delete)
  • Painel de estado: por fonte ver total docs, total chunks, ultimo crawl, erros recentes, espaco ocupado
  • Templates de crawler: ao adicionar fonte, escolher template (AT Vinculativas, AT Codigos, DRE, Generico/PDF list) que pre-configura o schema

Ficheiros

  • hub/html/admin/sources.html (UI gestao)
  • hub/html/admin/sources.js
  • corpus/app/routers/sources.py (expandir CRUD)
  • corpus/app/routers/admin.py (upload docs, URLs avulsas)
  • corpus/app/models.py (campos: crawler_config JSON, active, frequency)
  • corpus/app/crawlers/generic.py (crawler generico para URLs avulsas)

Verificacao

  • Criar fonte nova via UI e ver crawl a funcionar
  • Upload PDF solto fica indexado e pesquisavel
  • Colar lista de URLs inicia download + ingestao
  • Desactivar fonte para crawl sem apagar dados
  • Remover fonte apaga chunks do Qdrant

Total estimado: 26-40 dias de trabalho. Cada fase e independentemente deployavel e testavel.