← Voltar a studyAI — Documentação do Projeto

📚 RAG — Design

studyAI — Documentação do Projeto

Apresentação

Design do RAG — studyAI

Especificação do sistema RAG: chunking, indexação, retrieval e uso por agente.


Índice

  1. Visão geral
  2. Chunking
  3. Indexação
  4. Retrieval
  5. Avaliação do RAG (groundedness)
  6. Melhorias avançadas
  7. Uso por agente
  8. Diagrama do pipeline
  9. Decisões e trade-offs

Visão geral

O RAG é o coração do studyAI. Todos os ficheiros carregados e documentos gerados são indexados para:

  • Content Planner: Encontrar conteúdo relevante para planejar a estrutura
  • Content Writer: Fundamentar a escrita em chunks recuperados
  • RAG QA Agent: Responder perguntas com base no material
  • Question Evaluator: (Opcional) Material de referência para comparação
  • Question Generator: (Opcional) Contexto para gerar perguntas

Scope: Um índice por projeto. Cada projeto tem os seus chunks isolados.

Faseamento (MVP): Sprint 2 pode começar com dense-only para validar o pipeline. BM25 + RRF + cross-encoder adicionados assim que a base estiver estável (Sprint 2 fim ou Sprint 3).


Chunking

Estratégia

Tipo de fonteEstratégiaTamanhoOverlap
PDFPor parágrafo, agrupar até max500–1200 chars50–100 chars
MarkdownPor header (##, ###)500–1200 chars50 chars
TXTPor parágrafo500–1200 chars50 chars

Regras

  1. Respeitar limites semânticos: Não cortar a meio de uma frase ou lista.
  2. Headers como separadores: Em MD, cada ## ou ### pode iniciar novo chunk.
  3. Overlap: Últimos N chars do chunk anterior no início do próximo (evitar perda de contexto).
  4. Metadata por chunk: source, section, page (se PDF), char_start, char_end.

Exemplo (Markdown)

Input:
## Introdução
A colonoscopia é um exame...
## Técnicas
A polipectomia consiste em...

Output chunks:
- chunk_1: "## Introdução\nA colonoscopia é um exame..." (metadata: section=Introdução)
- chunk_2: "## Técnicas\nA polipectomia consiste em..." (metadata: section=Técnicas)

Indexação

Pipeline

Ficheiro/Documento
    → Parse (extrair texto)
    → Chunk (estratégia por tipo)
    → Embed (modelo de embeddings)
    → Store: Vector DB (dense) + índice lexical (BM25)

Índices necessários

ÍndiceUsoImplementação
DenseSimilarity searchpgvector, coluna embedding
Lexical (BM25)Busca por palavrasPostgreSQL tsvector + GIN, ou Elasticsearch/Meilisearch

Para PostgreSQL: coluna content_tsv tsvector com GIN(content_tsv). Atualizar com to_tsvector('portuguese', content).

Modelo de embeddings

  • Modelo: text-embedding-3-small (OpenAI) — 1536 dimensões
  • Alternativa: text-embedding-3-large para mais qualidade (3072 dims)

Metadata por chunk

CampoTipoUso
project_idUUIDFiltro obrigatório (isolamento)
source_typestringfile | document
source_idUUIDfile_id ou document_id
sectionstringSecção do documento
subsectionstringSubsecção (opcional)
pageintPágina (PDF)
char_startintPosição inicial no texto
char_endintPosição final
content_tsvtsvectorÍndice lexical (BM25) — to_tsvector('portuguese', content)
created_attimestampOrdem, debugging

Quando indexar

  • Upload de ficheiro: Após parse (job assíncrono)
  • Documento gerado: Após Content Writer terminar
  • Edição de documento: Re-indexar (ou indexar diff)
  • Apagar ficheiro/documento: Remover chunks do índice

Retrieval

Estratégia: pipeline híbrido em três estágios

Com base em benchmarks e práticas de produção (2024–2025), a abordagem escolhida é o pipeline híbrido em três estágios: hybrid search → RRF → reranking com cross-encoder.


Passo 1 — Hybrid Search (gerar candidatos)

Executam-se duas buscas em paralelo com a mesma query:

TipoComo funcionaPontos fortesLimitações
Dense (vetorial)Query → embedding → similarity search nos chunks. Top-100.Paráfrases, sinónimos, linguagem naturalFalha em exact match (IDs, códigos como ORA-00942, nomes técnicos)
BM25 (lexical)TF-IDF: frequência das palavras da query nos docs. Top-100.Exact match, IDs, códigos, negações ("não suportado")Falha em sinónimos ou linguagem diferente do documento

Resultado: Duas listas (ex: 100 dense + 100 BM25). Com fusão, ~120–150 candidatos únicos. Cobre semântico e lexical.

Implementação:

  • Dense: pgvector, cosine similarity ou inner product. Filtro project_id = X.
  • BM25: PostgreSQL full-text (tsvector/tsquery, ts_rank) ou Elasticsearch/Meilisearch para BM25 nativo.

Passo 2 — RRF (Reciprocal Rank Fusion)

As listas têm scores em escalas diferentes. RRF combina pelos ranks, não pelos scores:

  1. Cada documento recebe score = 1/(k + posição) em cada lista. Ex: k=60, posição 1 → 1/61; posição 10 → 1/70.
  2. Os scores das duas listas são somados. Um doc no top-5 de ambas fica com score alto.
  3. Ordenar por score total e deduplicar.

Resultado: Uma única lista ordenada, sem normalização de scores. Simples e robusto.

Parâmetro k: típico k=60. Ajustar se necessário.


Passo 3 — Reranking com cross-encoder

~120 candidatos, mas o LLM só precisa de 5–10. O cross-encoder avalia cada par (query, chunk) com um único forward pass — mais preciso que o bi-encoder da busca inicial:

  1. Input: [query] [SEP] [chunk] concatenados.
  2. Output: score de relevância (0–1).
  3. Ordenar por score e selecionar top-5 ou top-10.

Resultado: Chunks de alta precisão para o LLM. Elimina falsos positivos que passaram por BM25 ou dense.

Modelos sugeridos: BAAI/bge-reranker-v2-m3, cross-encoder/ms-marco-MiniLM-L-6-v2, ou equivalentes em português.


Parâmetros por agente

AgenteCandidatos (RRF)Final (após rerank)Filtros adicionais
Content Planner~120–15015–20
Content Writer~120–1508–10section (se planeado)
RAG QA Agent~120–1508–10
Question Evaluator~120–1505document_id (opcional)
Question Generator~120–15010document_id

Filtros

  • Obrigatório: project_id = X em todas as pesquisas (multi-tenant).
  • Opcionais: source_type, section, document_id.

Avaliação do RAG

Métricas de avaliação

Para cada resposta RAG (chat, geração), avaliar e guardar:

MétricaDescriçãoOnde guardar
groundedResposta fundamentada nos chunks? (sim/não)rag_evaluations
groundedness_score0–1, score contínuorag_evaluations
answer_relevance0–1, relevância da resposta à perguntarag_evaluations
retrieval_recall0–1, chunks recuperados cobrem a pergunta?rag_evaluations
hallucination_risk0–1, probabilidade de alucinaçãorag_evaluations

Implementação

  • LangSmith evals: Dataset de perguntas → run eval → groundedness score
  • LLM-as-judge: Prompt que compara resposta com chunks, devolve score
  • Guardar: Tabela rag_evaluations (ver DATA_MODEL.md)

Integração

  • Amostragem: 10–20% das respostas de chat
  • Ou: todas em staging, amostra em produção
  • Alertas se hallucination_risk > 0.7

Melhorias avançadas

Query rewriting

Antes do retrieval, reescrever a query para melhor recall:

  • "O que é X?" → "definição de X", "o que significa X"
  • LLM ou regras simples
  • Útil para perguntas vagas

Multi-query retrieval

  • Gerar 2–3 variações da query
  • Retrieval para cada uma
  • Unir resultados (deduplicar, RRF)
  • Melhora recall em perguntas ambíguas

Context compression

  • Chunks recuperados podem ser longos
  • Resumir ou extrair apenas frases relevantes antes de passar ao LLM
  • Reduz tokens, mantém relevância

Embedding version

  • Guardar embedding_model_version em metadata dos chunks
  • Permite re-indexar com novo modelo sem conflitos
  • Útil para A/B de modelos

Uso por agente

Content Planner

Query: Tópico pedido (ex: "técnicas de colonoscopia")

Retrieval: Top-15 a 20 chunks relevantes para ter visão ampla

Uso: Analisar cobertura, propor estrutura, identificar lacunas


Content Writer

Query: Título da secção + contexto (ex: "Polipectomia - técnicas de remoção de pólipos")

Retrieval: Top-8 a 10 chunks, preferencialmente da secção planeada

Uso: Escrever Markdown fundamentado nos chunks. Instrução: "Não inventes; cita apenas o que está nos chunks."


RAG QA Agent

Query: Pergunta do utilizador (ex: "O que é uma colonoscopia?")

Retrieval: Top-8 a 10 chunks mais relevantes

Uso: Construir prompt com chunks como contexto. Resposta + citações (source, excerpt).


Question Evaluator

Query: Pergunta + resposta do user (opcional: para retrieval semântico)

Retrieval: Opcional. Se usar, top-5 chunks do documento da pergunta.

Uso: Comparar user_answer com solution e material. LLM faz a avaliação.


Question Generator

Query: Título do documento ou secção

Retrieval: Opcional. Pode usar o documento completo como contexto (já em memória).

Uso: Gerar perguntas variadas (recall, application, comparison).


Diagrama do pipeline

Indexação

A carregar diagrama…

Retrieval (3 estágios)

A carregar diagrama…

Decisões e trade-offs

DecisãoEscolhaTrade-off
RetrievalHybrid (dense + BM25) + RRF + cross-encoderMelhor recall e precisão; mais latência e custo que dense-only.
Chunk size500–1200 charsMenor: mais preciso, mais chunks. Maior: mais contexto, menos granular.
Overlap50–100 charsReduz perda de contexto; aumenta redundância.
Candidatos100 dense + 100 BM25 → ~120 únicosCobre semântico e lexical.
Final top-k5–20 (por agente)Mais chunks: mais contexto, mais tokens.
Cross-encoderBGE-reranker ou similar+50–100ms por query; ganho significativo em precisão.
Embedding modeltext-embedding-3-smallEquilíbrio custo/qualidade.
Índice por projetoSimIsolamento; project_id obrigatório.
Re-indexar ao editarSimConsistência; custo de re-embed e re-index lexical.

Zona de prática

Sem perguntas. Clica em Editar para adicionar.