Como funciona
POST /api/institution/courses/[courseId]/rag-ingest
Body: { "mode": "full" | "incremental" }
↓
1. Lista aulas publicadas do curso
2. Para cada aula, extrai texto por tipo:
- rich_text → strip HTML
- slides → join text elements + speaker notes
- quiz → join question + explanation por questao
- pdf → document-extractor (PyPDF + Adobe extract fallback)
- video → LiveClassTranscription.transcriptionText (se aprovada)
- assignment → instructions
3. Chunking: 800 tokens, 200 overlap, preserva estrutura semantica
4. Embeddings via Voyage AI (1024 dims, fallback OpenAI)
5. Cria ContentBlock + ContentEmbedding com metadata:
{ source: "course_lesson", courseId, lessonId, lessonTitle,
moduleTitle, ingestionId }
6. Status final em CourseRagIngestion
Modes
mode: "full"
Deleta TODOS os ContentBlock + ContentEmbedding do curso e re-ingere tudo.
Quando usar:
- Primeira ingestao do curso
- Apos reorganizacao maior (modulos renomeados, aulas reordenadas)
- Suspeita de embeddings corrompidos
Custo: proporcional ao tamanho total do curso. Tipicamente $0.004 por 30 aulas.
mode: "incremental"
Identifica aulas modificadas apos ultima ingestao (updatedAt > lastIngestionAt), deleta apenas chunks dessas aulas e reingere.
Quando usar:
- Edicoes pontuais
- Auto-sync (recomendado para producao)
- Adicao de novas aulas
Custo: proporcional ao delta. Tipicamente $0.0001 por aula modificada.
Auto-sync
Course.autoSyncRag: Boolean @default(false)
Quando true:
- Toda edicao de aula via API
/api/institution/courses/[id]/modules/[mid]/lessons/[lid]dispara reingestao incremental - Roda via Next.js
after()(nao bloqueia a request do admin) - Falha silenciosa registrada em
CourseRagIngestion.errorsJSON
Recomendado para producao. Mantenha desligado durante setup inicial do curso para evitar embeddings desperdicados.
Status e debug
GET /api/institution/courses/[courseId]/rag-ingest
Response:
{
"ingestionId": "uuid",
"courseId": "uuid",
"mode": "incremental",
"status": "completed", // pending | running | completed | failed
"startedAt": "2026-05-23T10:00:00Z",
"completedAt": "2026-05-23T10:01:34Z",
"stats": {
"lessonsProcessed": 5,
"chunksCreated": 47,
"tokensEmbedded": 12450,
"costUsd": 0.0006
},
"errors": []
}
Embeddings provider
| Provider | Modelo | Dims | Custo/1K tokens |
|---|---|---|---|
| Voyage AI (primary) | voyage-3 | 1024 | $0.00005 |
| OpenAI (fallback) | text-embedding-3-large (com dimensions: 1024) | 1024 | $0.00013 |
Direct fetch (AI SDK v3 nao suporta parametro dimensions):
// Voyage AI
fetch("https://api.voyageai.com/v1/embeddings", {
body: JSON.stringify({
model: "voyage-3",
input: texts,
input_type: "document"
})
})
Retrieval em runtime
Durante o chat tutor, RetrievalAgent executa:
const chunks = await retrieve({
query: reformulatedQuery,
filters: { tenantId, courseId },
k: 10,
tenantOnlyMode: true, // exclui fallback para conteudo global
boostByWeakAreas: studentModel.quizContext.weakAreas
});
tenantOnlyMode: true garante que o tutor NUNCA cita conteudo de outro tenant — mesmo se houver conteudo similar no banco.
Deletar RAG do curso
DELETE /api/institution/courses/[courseId]/rag-ingest
Remove todos ContentBlock + ContentEmbedding + CourseRagIngestion do curso. Util para arquivar cursos sem ocupar pgvector.
Limitacoes
- Imagens: nao sao convertidas em embeddings. Roadmap: descricao automatica via vision LLM antes do embedding.
- Equacoes matematicas: extraidas como LaTeX-like text. RAG funciona mas qualidade depende do markup original.
- Videos sem transcripcao: nao sao ingeridos. Configure auto-transcricao em MediaAsset (Whisper → Google STT) antes.
- Tamanho maximo por curso: pgvector suporta milhoes de vetores mas latencia de retrieval cresce. >10K chunks por curso pode ficar lento — considere quebrar em sub-cursos.