Módulo 1

Você não está olhando para “um TCC com app”. Você está olhando para uma arquitetura que transforma relato bruto em triagem jurídica rastreável.

O repo mba-tcc junta três coisas: corpus da Corte IDH, pipeline factual em Python e uma interface web para intake + análise. A ideia central é simples: não confiar na memória do modelo, e sim em navegação orientada por documentos e citações.

📚

Corpus jurídico

Os casos brasileiros da Corte IDH viram base navegável, não só contexto solto jogado no prompt.

⚙️

Pipeline factual

O backend transforma a narrativa em cenário, chama tools e grava artefatos de execução.

🖥️

Interface de triagem

O frontend guia o usuário, mostra o progresso e exibe o raciocínio em formato legível.

💡
Ideia-chave

Esse projeto troca o padrão “pergunte ao modelo e reze” por “busque, recupere, compare e só depois sintetize”. Em software, isso é sair de palpite para procedimento.

👤
Interface web
API FastAPI
📚
Pipeline + corpus
Clique em Next Step para seguir o relato.
CÓDIGO REAL

@app.post("/api/analyze")
async def analyze(payload: TriageRequest) -> dict[str, Any]:
    item = _build_item(payload)
    config = _build_config(payload)
    result = await asyncio.to_thread(run_factual_triage, item, config=config)
    return {"ok": True, "result": result}
        
EM PORTUGUÊS CLARO

Esta rota existe para receber um pedido completo de análise.

O payload vindo da interface é transformado num item interno que o pipeline entende.

As configurações de modelo e limites viram um pacote separado, para a execução ficar controlada.

O trabalho pesado roda fora da thread principal, para a API continuar responsiva.

Quando termina, a API devolve um resultado estruturado em vez de texto solto.

Se você quisesse copiar a ideia central desse projeto para outro domínio, qual parte é realmente indispensável?

Módulo 2

Os personagens principais: quem faz o quê sem pisar no pé dos outros

Pensa nesse repo como uma equipe de investigação. Cada parte tem um papel específico: uma recebe o relato, outra coordena o processo, outra consulta os documentos e outra empacota a resposta final.

apps/ As superfícies executáveis mais recentes
api/ FastAPI que expõe /api e /ws/triage
web/ Next.js que coleta o relato e mostra eventos/resultados
scripts/eval/ Onde mora o pipeline factual real
prototype/factual_triage/ Cenários de teste e evolução metodológica
agentic-search-cadh/ Trilho anterior/adjacente de busca document-first
outputs/triage/ Rastro observável de cada execução
🖥️
Next.js

Cuida da experiência do usuário e do estado visual da sessão.

FastAPI

É o porteiro: recebe, valida, roteia e transmite eventos.

🧠
Python factual

É o investigador: consulta ferramentas, sintetiza e persiste artefatos.

CÓDIGO REAL

function submitAnalysis(e?: React.FormEvent) {
  if (e) e.preventDefault();
  if (!form.narrative.trim() || form.narrative.trim().length < 20) {
    setApiError("A narrativa precisa ter pelo menos 20 caracteres.");
    return;
  }
  const socket = new WebSocket(wsUrl);
}
        
EM PORTUGUÊS CLARO

Quando a pessoa envia o formulário, o frontend intercepta o evento em vez de deixar o navegador recarregar a página.

Antes de qualquer coisa, ele verifica se existe narrativa suficiente para a análise fazer sentido.

Se o texto for curto demais, o erro aparece cedo e barato.

Só depois disso a interface abre o canal em tempo real com o backend.

🧩
Separação de responsabilidades

Quando cada camada tem um papel nítido, você consegue trocar UI, modelo ou mecanismo de busca sem reescrever tudo. Esse é um dos superpoderes de arquiteturas boas.

Se você quiser mudar como o progresso da análise aparece na tela, onde deveria mexer primeiro?

Módulo 3

A jornada do pedido: do texto da vítima ao resultado no navegador

A melhor metáfora aqui é a de uma central de despacho. A interface recolhe o chamado, a API registra a ocorrência e o runner factual distribui o trabalho entre ferramentas especializadas.

1
Intake factual

A narrativa entra com contexto, atores, período e objetivo do usuário.

2
Validação e montagem

A API transforma os campos em um item interno consistente.

3
Execução orientada por tools

O pipeline faz buscas, abre trechos, consulta violações e reparações.

4
Streaming de eventos

Enquanto trabalha, o backend vai narrando o progresso via WebSocket.

5
Resultado persistido

Requests, predictions e traces ficam salvos para auditoria posterior.

CÓDIGO REAL

socket.onopen = () => {
  setStatus("Triagem em andamento.");
  socket.send(
    JSON.stringify({
      title: form.title,
      narrative: form.narrative,
      local: form.local,
      period: form.period,
      actors: form.actors,
      user_goal: form.user_goal,
      model: form.model,
      dry_run: form.dry_run,
    }),
  );
};
        
EM PORTUGUÊS CLARO

Quando o canal abre, a interface avisa visualmente que a triagem começou.

Em seguida ela envia um pacote JSON com tudo o que o backend precisa para iniciar.

Esse pacote mistura conteúdo do caso, metadados e preferências de execução.

Repara no detalhe importante: o frontend não tenta “pensar juridicamente”; ele só entrega o pedido bem formatado.

CÓDIGO REAL

@app.websocket("/ws/triage")
async def triage_socket(websocket: WebSocket) -> None:
    await websocket.accept()
    try:
        raw = await websocket.receive_json()
        payload = TriageRequest.model_validate(raw)
    except Exception as exc:
        await websocket.send_json({"type": "error", "error": f"payload inválido: {exc}"})
        
EM PORTUGUÊS CLARO

A API abre oficialmente a sessão em tempo real.

Depois ela espera um JSON chegar pelo socket, em vez de usar uma requisição simples sem streaming.

Esse JSON passa por validação de schema antes de qualquer execução mais cara.

Se vier quebrado, o usuário recebe erro legível imediatamente.

🚦
Por que WebSocket importa aqui?

Porque a análise não é instantânea. Em tarefas longas, mostrar eventos intermediários reduz ansiedade do usuário e facilita debug quando algo emperra.

Se amanhã esse app precisar mostrar 15 eventos de progresso durante uma análise de 40 segundos, qual escolha arquitetural do repo já ajuda nisso?

Módulo 4

A máquina de evidências: por que esse app deixa migalhas auditáveis em todo lugar

Aqui a metáfora não é tribunal, e sim caixa-preta de voo. Quando algo dá certo ou errado, você quer reconstruir o percurso. É isso que `run_factual_triage` faz ao salvar requests, predictions, traces e retrieval hits.

requests.jsonlO que foi pedido ao sistema naquela execução
predictions.jsonlA resposta final estruturada do modelo/pipeline
tool_traces.jsonlO histórico das ferramentas chamadas durante a análise
retrieval_hits.jsonlOs trechos e recuperações que sustentaram a síntese
errors.jsonlFalhas persistidas para pós-morte e repetição do experimento
CÓDIGO REAL

run_dir = create_ui_run_dir(out_dir)
run_dir.mkdir(parents=True, exist_ok=True)
_write_run_config(run_dir, item, cfg)
requests_path = run_dir / "requests.jsonl"
predictions_path = run_dir / "predictions.jsonl"
errors_path = run_dir / "errors.jsonl"
traces_path = run_dir / "tool_traces.jsonl"
retrieval_path = run_dir / "retrieval_hits.jsonl"
        
EM PORTUGUÊS CLARO

Antes de analisar qualquer caso, o sistema cria uma pasta exclusiva para aquela execução.

Nessa pasta ele grava uma espécie de identidade da corrida: configuração, pedidos, resposta e rastros.

Isso transforma uma sessão efêmera em material observável e repetível.

Em projetos com IA, esse tipo de trilha vale ouro para auditoria e iteração.

🔍

Recuperação visível

Você consegue ver não só o resultado, mas quais buscas e trechos ajudaram a chegar nele.

🧾

Persistência por execução

Cada rodada ganha seu próprio diretório, facilitando comparação entre versões do sistema.

🛡️

Menos magia

Quando o output parece estranho, existe caminho para investigar em vez de só culpar “a IA”.

⚠️
Lição universal

Se um sistema com IA não deixa rastro, você quase sempre vai discutir opinião em vez de evidência quando ele falhar.

Uma análise trouxe um caso análogo claramente ruim. Qual artefato você checaria primeiro para entender por quê?

Módulo 5

Onde mexer quando algo quebra: um mapa mental para dirigir IA e depurar rápido

Agora que você já viu os atores e o fluxo, dá para usar esse repo como um manual de intervenção. A pergunta certa quase nunca é “qual arquivo é o culpado?”, mas “em qual camada nasce esse sintoma?”.

Cenário

O usuário clica em analisar e nada aparece na tela. Não sabe se o problema é validação, socket, pipeline ou renderização.

1

Sem resposta visual? Comece em apps/web.

2

Payload inválido? Confira schema em apps/api/main.py.

3

Rodou mas errou? Siga para scripts/eval e os artefatos em outputs/triage.

CÓDIGO REAL

if (parsed.type === "event" && parsed.event) {
  setEvents((prev) => [...prev, parsed.event]);
} else if (parsed.type === "result" && parsed.result) {
  setResult(parsed.result);
  setStatus("Sessão concluída.");
  setLoading(false);
  socket.close();
}
        
EM PORTUGUÊS CLARO

A interface distingue dois tipos nobres de mensagem: evento de progresso e resultado final.

Eventos entram numa lista viva; resultado fecha a sessão e atualiza o estado principal.

Se o usuário diz “travou”, esse trecho já sugere uma boa pergunta: o socket recebeu eventos? recebeu resultado? recebeu erro?

Esse é o tipo de vocabulário que ajuda muito a conversar melhor com um agente de código.

🪟
Sintoma visual

Campos, loading, status, renderização de resultado: olhe primeiro o frontend.

📨
Sintoma de contrato

Erro de payload, CORS, rota ou WebSocket: a API costuma ser o gargalo.

🧪
Sintoma substantivo

Busca ruim, analogia ruim, schema ruim: mergulhe no pipeline factual e nos artefatos persistidos.

🛠️
O ganho prático para vibe coding

Depois de entender esse mapa, você consegue pedir coisas muito melhores para uma IA: “corrige o tratamento do evento no frontend” é infinitamente melhor do que “o app tá meio bugado”.

Você recebeu o seguinte relato: “o app rodou inteiro, mostrou progresso, mas citou precedentes ruins”. Onde a investigação deve começar?