← Voltar a studyAI — Documentação do Projeto

📊 Modelo de Dados

studyAI — Documentação do Projeto

Apresentação

Modelo de Dados — studyAI

Schema da base de dados PostgreSQL e relações entre entidades.


Índice

  1. Diagrama ER
  2. Entidades
  3. Vector DB (chunks)
  4. Entidades extendidas (Learning Loop, User Intelligence)
  5. Índices e constraints
  6. Migrations

Diagrama ER

A carregar diagrama…

Entidades

users

ColunaTipoConstraints
idUUIDPK, default gen_random_uuid()
emailVARCHAR(255)NOT NULL, UNIQUE
password_hashVARCHAR(255)NOT NULL
nameVARCHAR(255)
created_atTIMESTAMPTZNOT NULL, default now()
updated_atTIMESTAMPTZNOT NULL, default now()

projects

ColunaTipoConstraints
idUUIDPK, default gen_random_uuid()
user_idUUIDFK users(id) ON DELETE CASCADE
titleVARCHAR(500)NOT NULL
created_atTIMESTAMPTZNOT NULL, default now()
updated_atTIMESTAMPTZNOT NULL, default now()

files

ColunaTipoConstraints
idUUIDPK, default gen_random_uuid()
project_idUUIDFK projects(id) ON DELETE CASCADE
filenameVARCHAR(500)NOT NULL
mime_typeVARCHAR(100)NOT NULL
storage_pathVARCHAR(1000)NOT NULL
sizeINTEGERNOT NULL
statusVARCHAR(20)NOT NULL, default 'pending'
retry_countINTEGERdefault 0
last_errorTEXTÚltima mensagem de erro
created_atTIMESTAMPTZNOT NULL, default now()
updated_atTIMESTAMPTZNOT NULL, default now()

status: pending | processing | processed | error

Retry: Se status=error, job de ingestão retry com backoff (1min, 5min, 15min). Max retry_count=5.


documents

ColunaTipoConstraints
idUUIDPK, default gen_random_uuid()
project_idUUIDFK projects(id) ON DELETE CASCADE
titleVARCHAR(500)NOT NULL
contentTEXT
structureJSONB
statusVARCHAR(20)NOT NULL, default 'draft'
created_atTIMESTAMPTZNOT NULL, default now()
updated_atTIMESTAMPTZNOT NULL, default now()

status: draft | generating | ready | error

structure (exemplo):

{
  "sections": [
    { "title": "Introdução", "subsections": [] },
    { "title": "Técnicas", "subsections": ["Polipectomia", "Mucosectomia"] }
  ]
}

questions

ColunaTipoConstraints
idUUIDPK, default gen_random_uuid()
document_idUUIDFK documents(id) ON DELETE CASCADE
questionTEXTNOT NULL
solutionTEXTNOT NULL
spokenTEXT
typeVARCHAR(50)default 'recall'
difficultyVARCHAR(20)default 'medium'
orderINTEGERdefault 0
created_atTIMESTAMPTZNOT NULL, default now()

type: recall | application | comparison | other

difficulty: easy | medium | hard — para adaptação ao user_level


user_progress

ColunaTipoConstraints
idUUIDPK, default gen_random_uuid()
user_idUUIDFK users(id) ON DELETE CASCADE
question_idUUIDFK questions(id) ON DELETE CASCADE
user_answerTEXTNOT NULL
scoreINTEGER
feedbackTEXT
correctionsJSONB
confidence_scoreFLOAT0–1, agregado de scores
last_reviewed_atTIMESTAMPTZÚltima vez que respondeu
next_review_atTIMESTAMPTZQuando rever (spaced repetition)
repetition_countINTEGERdefault 0
ease_factorFLOATSM-2: fator de facilidade
interval_daysINTEGERSM-2: intervalo em dias até próxima revisão
statusVARCHAR(20)default 'new'
created_atTIMESTAMPTZNOT NULL, default now()

UNIQUE(user_id, question_id): Uma entrada por user por pergunta (última submissão).

status (Learning Loop): new | learning | review | mastered


refresh_tokens (auth)

ColunaTipoConstraints
idUUIDPK
user_idUUIDFK users(id) ON DELETE CASCADE
token_hashVARCHAR(255)NOT NULL
expires_atTIMESTAMPTZNOT NULL
created_atTIMESTAMPTZNOT NULL

Vector DB (chunks)

Com pgvector, os chunks podem ficar na mesma base:

chunks

ColunaTipoConstraints
idUUIDPK
project_idUUIDFK projects(id) ON DELETE CASCADE
source_typeVARCHAR(20)NOT NULL
source_idUUIDNOT NULL
contentTEXTNOT NULL
embeddingVECTOR(1536)NOT NULL
content_tsvTSVECTORPara BM25 / full-text search
metadataJSONB
created_atTIMESTAMPTZNOT NULL

source_type: file | document

content_tsv: to_tsvector('portuguese', content) — índice lexical para hybrid search (BM25).

metadata (exemplo):

{
  "section": "Técnicas",
  "subsection": "Polipectomia",
  "page": 3,
  "char_start": 0,
  "char_end": 500
}

metadata: Adicionar embedding_model_version para tracking de versões.

Índices:

  • HNSW ou IVFFlat em embedding (similarity search)
  • GIN em content_tsv (full-text / BM25)

Entidades extendidas

document_versions (versioning)

ColunaTipoConstraints
idUUIDPK
document_idUUIDFK documents(id) ON DELETE CASCADE
contentTEXTNOT NULL
structureJSONB
versionINTEGERNOT NULL
created_atTIMESTAMPTZNOT NULL

Uso: Histórico de alterações, comparação de evolução, rollback.


user_profile (User Intelligence)

ColunaTipoConstraints
idUUIDPK
user_idUUIDFK users(id) ON DELETE CASCADE, UNIQUE
levelVARCHAR(20)default 'beginner'
weak_topicsJSONB["topic1", "topic2"]
recent_errorsJSONB[{question_id, score, topic}]
preferred_styleVARCHAR(20)default 'detailed'
created_atTIMESTAMPTZNOT NULL
updated_atTIMESTAMPTZNOT NULL

level: beginner | intermediate | advanced

preferred_style: concise | detailed | technical

Ver USER_INTELLIGENCE.md.


conversation_memory (memória de sessão)

ColunaTipoConstraints
idUUIDPK
user_idUUIDFK users(id) ON DELETE CASCADE
project_idUUIDFK projects(id) ON DELETE CASCADE
summaryTEXTResumo da conversa/sessão
embeddingVECTOR(1536)Embedding do summary (para retrieval)
message_countINTEGERNº de mensagens na sessão
last_activity_atTIMESTAMPTZNOT NULL
created_atTIMESTAMPTZNOT NULL
updated_atTIMESTAMPTZNOT NULL

Uso: "Continua o que estavas a dizer", personalização, contexto entre sessões.

UNIQUE(user_id, project_id): Uma memória por user por projeto (atualizada a cada N mensagens).

Ver USER_INTELLIGENCE.md.


chat_sessions

ColunaTipoConstraints
idUUIDPK
project_idUUIDFK projects(id) ON DELETE CASCADE
user_idUUIDFK users(id) ON DELETE CASCADE
titleVARCHAR(255)Título (ex: primeiras N chars da 1ª msg)
created_atTIMESTAMPTZNOT NULL
updated_atTIMESTAMPTZNOT NULL

Uso: Persistir conversas para histórico, reabertura, analytics.


chat_messages

ColunaTipoConstraints
idUUIDPK
session_idUUIDFK chat_sessions(id) ON DELETE CASCADE
roleVARCHAR(20)user | assistant
contentTEXTNOT NULL
intentVARCHAR(50)Intent do Router (se assistant)
trace_idVARCHAR(255)LangSmith run ID
created_atTIMESTAMPTZNOT NULL

message_citations

ColunaTipoConstraints
idUUIDPK
message_idUUIDFK chat_messages(id) ON DELETE CASCADE
sourceVARCHAR(500)filename ou doc title
chunk_idUUIDFK chunks (opcional)
excerptTEXTTrecho citado
created_atTIMESTAMPTZNOT NULL

Uso: Citações por resposta, groundedness na UI.


jobs (tracking de jobs assíncronos)

ColunaTipoConstraints
idUUIDPK
project_idUUIDFK projects(id)
typeVARCHAR(50)NOT NULL
statusVARCHAR(20)NOT NULL
progressINTEGER0–100
messageTEXTMensagem humana ("A escrever secção 2...")
payloadJSONBInput (file_id, topic, etc.)
resultJSONBOutput (document_id, etc.)
errorTEXTSe failed
retry_countINTEGERdefault 0
started_atTIMESTAMPTZQuando começou
completed_atTIMESTAMPTZQuando terminou
created_atTIMESTAMPTZNOT NULL
updated_atTIMESTAMPTZNOT NULL

type: ingest_file | reindex_project | generate_content

status: pending | running | completed | failed

Ver JOBS_ASYNC.md.


response_feedback (feedback loop do user)

ColunaTipoConstraints
idUUIDPK
user_idUUIDFK users(id)
source_typeVARCHAR(20)chat
source_idUUIDchat_message_id ou user_progress_id
helpfulBOOLEANthumbs up/down
commentTEXTopcional
created_atTIMESTAMPTZNOT NULL

Uso: Melhorar prompts, detetar respostas problemáticas. source_id referencia chat_messages.id ou user_progress.id.


rag_evaluations (avaliação do RAG)

ColunaTipoConstraints
idUUIDPK
project_idUUIDFK projects(id)
queryTEXTNOT NULL
responseTEXTNOT NULL
groundedBOOLEANResposta fundamentada nos chunks?
groundedness_scoreFLOAT0–1, score contínuo
answer_relevanceFLOAT0–1, relevância da resposta à pergunta
retrieval_recallFLOAT0–1, chunks recuperados cobrem a pergunta?
hallucination_riskFLOAT0–1
sourceVARCHAR(20)chat
created_atTIMESTAMPTZNOT NULL

Uso: Avaliação automática do RAG, integração com LangSmith evals.


Índices e constraints

-- users
CREATE UNIQUE INDEX idx_users_email ON users(email);

-- projects
CREATE INDEX idx_projects_user_id ON projects(user_id);

-- files
CREATE INDEX idx_files_project_id ON files(project_id);
CREATE INDEX idx_files_status ON files(status);

-- documents
CREATE INDEX idx_documents_project_id ON documents(project_id);
CREATE INDEX idx_documents_status ON documents(status);

-- questions
CREATE INDEX idx_questions_document_id ON questions(document_id);

-- user_progress
CREATE INDEX idx_user_progress_user_id ON user_progress(user_id);
CREATE INDEX idx_user_progress_question_id ON user_progress(question_id);
CREATE UNIQUE INDEX idx_user_progress_user_question ON user_progress(user_id, question_id);

-- conversation_memory
CREATE UNIQUE INDEX idx_conversation_memory_user_project ON conversation_memory(user_id, project_id);

-- chat_sessions, chat_messages
CREATE INDEX idx_chat_sessions_project_user ON chat_sessions(project_id, user_id);
CREATE INDEX idx_chat_messages_session ON chat_messages(session_id);

-- jobs
CREATE INDEX idx_jobs_project_id ON jobs(project_id);
CREATE INDEX idx_jobs_status ON jobs(status);

-- chunks (pgvector + full-text)
CREATE INDEX idx_chunks_project_id ON chunks(project_id);
CREATE INDEX idx_chunks_embedding ON chunks USING hnsw (embedding vector_cosine_ops);
CREATE INDEX idx_chunks_content_tsv ON chunks USING gin (content_tsv);

Migrations

Usar Alembic para migrations:

api/
├── alembic/
│   ├── versions/
│   │   ├── 001_initial.py
│   │   ├── 002_add_chunks.py
│   │   └── ...
│   └── env.py
└── alembic.ini

Ordem sugerida:

  1. users, refresh_tokens
  2. projects, files, documents, document_versions, questions
  3. user_progress, user_profile, conversation_memory
  4. chat_sessions, chat_messages, message_citations
  5. jobs, response_feedback, rag_evaluations
  6. chunks (com extensão pgvector)

Zona de prática

Sem perguntas. Clica em Editar para adicionar.