Pular para o conteúdo

Como construimos um pipeline multi-agente de tutor IA em EAD

Tutor IA Studeia: pipeline com 6 agentes (orchestrator, retrieval, pedagogical, evaluation, content, supervisor), fallback Claude/GPT/Grok/Gemini, RAG per-tenant e ConceptMastery bayesiano

2026-05-24 15 min
Resposta curta

O tutor IA do Studeia é um pipeline com 6 agentes especializados: Orchestrator coordena, StudentModel carrega contexto bayesiano, RetrievalAgent executa RAG per-tenant, PedagogicalAgent escolhe estratégia, LLM principal responde (fallback automático Claude/GPT/Grok/Gemini), enquanto EvaluationAgent, ContentAgent e SupervisorAgent rodam em background. 3 agentes são TypeScript puro (zero custo LLM), 3 usam Haiku (~$0.003). Custo total por turno: $0.005–$0.05

Por que pipeline multi-agente

Quando comecamos o tutor IA do Studeia, a tentacao foi obvia: chamar Claude API com system prompt longo e mensagens do aluno. Funciona em demos. Falha em producao.

Quatro problemas estruturais:

  1. Sem memoria persistente do aluno — cada turno trata o aluno como zero. Aluno acabou de errar conceito X 3 vezes em quizzes? LLM nao sabe. Tutor repete explicacao basica que ja deu semana passada.

  2. Sem grounding no material do curso — instituicao tem apostilas, slides, transcricoes de aulas-video. LLM puro nao acessa. Inventa fatos. Cita conceitos errados.

  3. Sem moderacao real — system prompt com "seja seguro" e fraco. Aluno em sofrimento mental aparece. Aluno tenta jailbreak. Aluno usa linguagem inadequada. Tutor precisa reagir adequadamente sem desligar funcionalidade pra alunos legitimos.

  4. Sem feedback loop — sistema nao aprende. Mesmos misconceptions se repetem. Professor nao tem visibilidade dos pontos fracos coletivos da turma.

Solucao: separar responsabilidades em agentes especializados.

A arquitetura

Mensagem do aluno
  ↓
PRE-LLM (sincrono, zero custo LLM)
  1. StudentModelService.getSnapshot()
  2. RetrievalAgent.retrieve()
  3. PedagogicalAgent.select()
  4. buildEnrichedPrompt()
  ↓
LLM PRINCIPAL (streaming, SSE para cliente)
  5. router.stream() com fallback automatico
     Claude Sonnet → GPT-4o → Grok-3 → Gemini Pro
  ↓
POST-LLM (background via after(), fire-and-forget)
  6. EvaluationAgent (Haiku)
  7. ContentAgent (Haiku)
  8. SupervisorAgent (Haiku)

Vamos por cada um.

1. StudentModelService — o "perfil cognitivo"

Antes de qualquer LLM call, carregamos o snapshot do aluno:

const snapshot = await StudentModelService.getSnapshot({
  userId,
  courseId,
});

// Retorna:
{
  conceptMastery: Map<conceptId, { probability, confidenceInterval }>,
  misconceptions: Misconception[],  // ativas + resolving
  episodicMemory: Episode[],         // o que funcionou antes
  quizContext: {
    totalAttempts,
    avgScore,
    passRate,
    weakAreas: string[]              // conceitos com mastery < 0.4
  },
  recentHistory: Message[]           // sliding window 10 msgs
}

ConceptMastery usa Beta distribution bayesiana — cada conceito tem alpha (successes) + beta (failures). Probabilidade = alpha / (alpha + beta). Intervalo de confianca via percentis 5% e 95%.

EpisodicMemory grava insights pedagogicos: "analogia da pizza funcionou pra explicar fracoes", "metafora de cano d'agua falhou pra eletricidade". Sistema aprende o que funciona com cada aluno.

Zero custo LLM. Tudo Prisma queries + calculo determinis tico.

2. RetrievalAgent — RAG tenant-scoped

Em vez do LLM tentar lembrar fatos sobre matematica, biologia, historia, deixamos ele citar o material da propria instituicao.

const chunks = await retrieve({
  query: reformulatedQuery,         // 1. reformula query com contexto
  filters: { tenantId, courseId },  // 2. isolamento absoluto
  k: 10,
  tenantOnlyMode: true,             // 3. nunca cita conteudo de outra instituicao
  boostByWeakAreas: snapshot.quizContext.weakAreas,  // 4. prioriza chunks de areas fracas
});

Per-tenant RAG e critico. Cursinho XYZ tem material proprio sobre ENEM. Universidade ABC tem material proprio sobre Calculo. Tutor cita o material CERTO da instituicao, nao um agregado generico.

Cada chunk tem metadata: { source: "course_lesson", courseId, lessonId, lessonTitle, moduleTitle }. Quando tutor responde, cita: "Conforme explicado na aula 'Geometria Analitica' do modulo 3..."

Voyage AI gera embeddings (1024 dimensoes, fallback OpenAI). pgvector armazena. tenantOnlyMode: true garante que WHERE tenantId = X esta sempre na query. Critical rule do projeto: zero leakage cross-tenant.

3. PedagogicalAgent — adaptacao de estrategia

Determinismo puro. Avalia mastery do aluno no dominio especifico e seleciona uma de 5 estrategias:

MasteryEstrategiaComportamento
< 0.3direct_instructionExplicacao clara, exemplos concretos, passo-a-passo
0.3-0.5scaffoldingDicas progressivas, perguntas guiadas simples
0.5-0.7socraticPerguntas que levam a descoberta
0.7-0.9guided_practiceExercicios com feedback, aplicacao pratica
> 0.9challengeProblemas complexos, conexoes entre conceitos

Ajustes adicionais por divergencia quiz vs chat:

  • Mastery alta no chat + quiz baixo → "compreensao superficial" → nudge DOWN
  • Mastery baixa + quiz alto → "aluno quieto" → nudge UP
  • Quiz pass rate < 40% → cap em scaffolding (nao avanca pra socratic ainda)

Tambem ajusta por idade (User.isMinor), learning style, dominio (matematica vs literatura tem perfis diferentes).

Zero custo LLM. Output: estrategia selecionada + instrucoes especificas pra adicionar ao system prompt.

4. Orchestrator — buildEnrichedPrompt

Monta o system prompt enriquecido:

Voce e um tutor IA para o curso "Calculo I" da instituicao "Cursinho XYZ".

DOMINIO DO ALUNO:
- Limites: mastery 0.78 (alto)
- Derivadas: mastery 0.42 (medio)
- Integrais: mastery 0.15 (baixo)

MISCONCEPTIONS ATIVAS:
- "Aluno confunde dominio com imagem em funcoes" (3 ocorrencias, status: resolving)
- "Aluno aplica derivada de soma a produto" (5 ocorrencias, status: active)

QUIZ PERFORMANCE:
- 14 tentativas total, avgScore 67%, passRate 71%
- Areas fracas: integrais (avg 45%), regra da cadeia (avg 52%)

ESTRATEGIA PEDAGOGICA: guided_practice
- Aluno tem dominio medio em derivadas. Apresente exercicios graduais.
- Reforce conexao entre limites e derivadas (ele ja domina limites).
- Aborda misconception sobre derivada de produto proativamente.

CONTEXTO RAG (do material do curso):
[Aula 3.2 "Regra do Produto"] (Modulo: Calculo Diferencial)
"A derivada de f(x).g(x) NAO e f'(x).g'(x). A regra correta e..."

[Aula 3.5 "Exercicios Resolvidos"] (Modulo: Calculo Diferencial)
"Exemplo: derivar (x^2 + 1).(x - 3) usando regra do produto..."

QUIZ RECENTE (proxima conversa):
Aluno acabou de responder quiz inline com 2 questoes, acertou 1.

INSTRUCOES:
- Responda em portugues
- Cite o material do curso quando relevante (use [Aula X.Y])
- Reconheca o que aluno acertou antes de apontar erro
- Para essa idade (User.ageRange = "young_adult"): linguagem casual sem ser informal demais

5. LLM principal — streaming com fallback

const stream = await router.stream({
  taskType: "chat_tutor",
  messages: enrichedMessages,
  options: { tenantId, userId, sessionId }
});

for await (const chunk of stream.textStream) {
  yield chunk;  // SSE para cliente
}

LLM Router faz:

  1. Resolve provider via TenantTaskModelConfig (admin escolheu Claude Sonnet, ou GPT-4o, etc)
  2. Resolve API key via cascade: TenantApiKey → ProviderApiKey global → process.env
  3. Circuit breaker check (Redis state). Se provider em OPEN: pula direto pra fallback
  4. Metering middleware: rate limit + credit check + cost calculator
  5. Stream Vercel AI SDK (suporta tools, multimodal, structured output)
  6. Em erro: fallback automatico para proximo provider na chain

Fallback chain por tier:

Tier Sonnet (medio): Claude Sonnet → GPT-4o → Grok-3-fast → Gemini Pro
Tier Haiku (rapido): Claude Haiku → GPT-4o-mini → Grok-3-mini → Gemini Flash
Tier Opus (complexo): Claude Opus → GPT-4.5 → Grok-3 → Gemini 2.5 Pro

Tenant NUNCA fica sem tutor. Se Anthropic cai → OpenAI assume. Se OpenAI tambem cai → xAI. Etc.

6. EvaluationAgent — feedback loop

Em background apos resposta do tutor:

after(async () => {
  const evaluation = await router.generateDirect({
    taskType: "chat_evaluation",
    messages: [
      { role: "user", content: "Aluno disse: '...'. Tutor respondeu: '...'. Classifique." }
    ]
  });

  // evaluation: {
  //   understanding: "partial",
  //   detectedMisconceptions: [{ description, concepts, severity }],
  //   suggestedNextStep: "..."
  // }

  // Atualiza ConceptMastery via Bayesian update
  await conceptMasteryEngine.updateFromTurn({
    userId, courseId, evaluation
  });

  // Persiste/atualiza misconceptions
  for (const misc of evaluation.detectedMisconceptions) {
    await misconceptionResolutionService.upsert({
      userId, source: "chat", ...misc
    });
  }
});

Custo: ~$0.001 por turno (Haiku). NAO bloqueia request do aluno.

Misconceptions tem lifecycle de 3 estados: active → resolving → resolved. State machine determina transicoes baseado em evidence (mastery update, quiz pass, tutor abordou explicitamente).

7. ContentAgent — pre-geracao proativa

after(async () => {
  // Aluno demonstra dominio fraco em conceito X
  // Pre-gera exercicio follow-up enquanto aluno le resposta atual
  const exercise = await router.generateDirect({
    taskType: "content_generation",
    messages: [...]
  });

  // Cache Redis 30min
  await redis.set(`next-exercise:${userId}:${conceptId}`, exercise, 1800);
});

Quando aluno termina de ler resposta e diz "me da exercicio", Studeia serve INSTANTANEAMENTE do cache. Sem latencia perceptivel.

Custo: ~$0.001 por turno (Haiku).

8. SupervisorAgent — moderacao

Roda em background apos cada turno. Classifica em 5 niveis de severity x 8 categorias.

Categorias: linguagem impropria, violencia, ilegal, sexual, off_topic, harassment, self_harm, jailbreak_attempt.

Severity: low → medium → high → critical → safety.

3 strikes (LOW/MEDIUM em 7 dias) = quarentena 48h. CRITICAL = quarentena 7 dias.

Self-harm (severity=safety) NUNCA pune o aluno. Em vez disso:

  • Tutor interrompido com mensagem de acolhimento
  • Recursos de crise (Brasil: CVV 188, SAMU 192)
  • Cooldown Redis 24h (nao quarentena)
  • Email URGENTE imediato ao admin institucional

Filosofia: self-harm e crise, nao infracao. Detalhes em Safety Supervisor.

Custo: ~$0.001 por turno (Haiku).

Numeros de producao

Apos 6 meses em producao:

  • ~30 ms latencia adicional dos agentes pre-LLM
  • ~$0.005-$0.05 custo medio por turno
  • 91% taxa de retencao de aluno apos 7 dias (vs ~40% benchmark IA tutor sem state)
  • 3.2x detection rate de misconceptions vs single-call baseline
  • 0 incidentes graves de safety (categorias high/critical)

Trade-offs honestos

Coisas que NAO funcionaram:

  • Tentamos um "MasterAgent" coordenador via LLM para escolher proximo agente dinamicamente. Custo dobrou, latencia subiu 800ms, qualidade NAO melhorou. Voltamos pra determinismo no Orchestrator.

  • Tentamos FineTune do Llama em material de cursos. Caro pra cada tenant. RAG funciona melhor pra knowledge dinamico (instituicao atualiza material toda semana — fine-tune ficaria stale).

  • Tentamos "consensus" entre 3 LLMs (Claude + GPT + Gemini) e pegar resposta com maioria. Custo 3x sem ganho de qualidade significativo. Removemos — fallback chain e suficiente.

Codigo aberto?

Estamos avaliando open-source dos componentes deterministicos (StudentModelService, RetrievalAgent, PedagogicalAgent) como pacote npm. Os agentes LLM-driven (Evaluation, Content, Supervisor) tem prompts que sao IP do Studeia, ficam closed.

Se interessar: abre issue em github.com/donattocosta-lang/studeia/issues.

Veja tambem

FAQ

Por que multi-agente e nao single LLM call?

Single LLM call NAO tem memoria persistente do aluno, NAO sabe quais materiais do curso citar (precisa de RAG), NAO modera saidas inadequadas e NAO atualiza modelo bayesiano do dominio do aluno. Multi-agente resolve cada problema com agente especializado: StudentModel mantem estado, RetrievalAgent busca RAG tenant-scoped, PedagogicalAgent escolhe estrategia, EvaluationAgent classifica misconceptions, ContentAgent pre-gera follow-up, SupervisorAgent modera. Cada um pode ser otimizado separadamente.

Quanto custa por turno completo do pipeline?

Tipicamente $0.005-$0.05 por turno (depende do tamanho da resposta). Breakdown: LLM principal Sonnet $0.005-$0.04 + EvaluationAgent Haiku $0.001 + ContentAgent Haiku $0.001 + SupervisorAgent Haiku $0.001. 3 dos 6 agentes sao TypeScript puro (StudentModel + RetrievalAgent + PedagogicalAgent = zero custo LLM).

Como evita custo explodir com 1000+ alunos?

Quatro mecanismos: (1) Background agents via Next.js `after()` — sem bloquear request. (2) Haiku para tarefas background (~30x mais barato que Sonnet). (3) Metering middleware com rate limit por usuario. (4) Tenant pode trazer propria API key (TenantApiKey) — Studeia nao cobra margem de IA, custos vao direto pra conta do tenant na Anthropic/OpenAI.

Veja tambem

Como construimos um pipeline multi-agente de tutor IA em EAD | Studeia Docs