AntigravityRvadvJurisprudencia-iarvadv-jusia

Recebi sua solicitação e estou começando a trabalhar nela.

Vou analisar o projeto existente e adaptar o plano arquitetural para sua infraestrutura específica. Primeiro, preciso examinar os arquivos e informações disponíveis.

PROJETO LEXNOVA-RV: PLANO DE IMPLANTAÇÃO INTEGRADA AO SISTEMA RV-ADV

1. CONTEXTO E OBJETIVOS

1.1 Situação Atual do RV-Adv

O ecossistema RV-Adv constitui uma plataforma de gestão jurídica madura e em produção, operando atualmente no domínio rafaelavasconcelos.adv.br. A infraestrutura atual compreende uma arquitetura distribuída com componentes bem estabelecidos que representam a base ideal para extensão com funcionalidades de busca jurisprudencial inteligente.

A arquitetura existente já implementa componentes críticos para o módulo de jurisprudência, incluindo Edge Functions para scraping da TNU (scrape-tnu), chat RAG (chat-jurisprudencia), geração de embeddings (generate-embedding), proxy unificado de IA (ai-proxy), e integração com APIs judiciais (DataJud via datajud-bypass, DJEN via djen-bypass). O banco de dados Supabase já possui a extensão pgvector ativa com índices HNSW para busca vetorial, além de 57 migrações sequenciais estabelecendo um schema robusto para o domínio jurídico-previdenciário.

A infraestrutura de servidores inclui um VPS Hetzner CX33 (4 vCPU, 8GB RAM, 80GB SSD, Ubuntu 24.04) já configurado para o microsserviço de scraping local, representando uma base computacional adequada para expansionar a capacidade de crawl de tribunais. O frontend React/Vite encontra-se em deploy contínuo via Netlify, com roteamento SPA configurado e autenticação JWT integrada ao Supabase Auth.

1.2 Escopo do Módulo Jurisprudência

O módulo de jurisprudência a ser implementado proverá busca inteligente em linguagem natural sobre acórdãos dos tribunais STF, STJ, TRF-5 e TJ-CE, com capacidade de upload de petições para extração automática de temas e busca contextual. A funcionalidade estenderá o módulo Jurisprudencia já existente no frontend (JurisprudenciaPage.jsx) e aproveitará a infraestrutura de chat RAG já operacional para jurisprudência da TNU.

O objetivo central consiste em permitir que advogados e operadores jurídicos descrevam casos em linguagem natural e recebam acórdãos relevantes ordenados por similaridade semântica, acompanhados de classificação de relevância (muito relevante, relevante, pouco relevante) e links diretos às fontes oficiais para verificação. O módulo funcionará como fonte de contexto verificável para fundamentar peças processuais, eliminando o risco de alucinações jurisprudenciais.


2. ARQUITETURA INTEGRADA PROPOSTA

2.1 Visão de Alto Nível da Arquitetura Extendida

A arquitetura proposta estende o monólito modular existente adicionando três componentes principais: schema de banco de dados扩展 para jurisprudência multi-tribunal, Edge Functions para crawl e indexação dos tribunais-alvo, e interface de usuário расширенная para busca e análise de petições. O design preserva os padrões arquiteturais estabelecidos, garantindo coerência com a codebase existente.

┌─────────────────────────────────────────────────────────────────────────┐
│                    FRONTEND RV-ADV (React/Vite/Netlify)                   │
│  ┌─────────────────────────────────────────────────────────────────┐    │
│  │  JurisprudenciaPage.jsx (Expandida)                             │    │
│  │  ├─ BuscaNaturallPanel ─ Busca por linguagem natural            │    │
│  │  ├─ PetitionUploadPanel ─ Upload e análise de petições          │    │
│  │  ├─ ResultsGrid ─ Grid de resultados com relevância             │    │
│  │  └─ ChatRAGPanel ─ Chat RAG multi-tribunal (estende TNU)       │    │
│  └─────────────────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│                        SUPABASE BACKEND (BaaS)                          │
│  ┌───────────────────────┐  ┌────────────────────────────────────┐     │
│  │  Edge Functions       │  │  PostgreSQL + pgvector              │     │
│  │  ├─ ai-proxy          │  │  ├─ courts (expandido)              │     │
│  │  ├─ generate-embedding│  │  ├─ jurisprudences (novo)           │     │
│  │  ├─ chat-jurisprudencia│ │  ├─ jurisprudencia_topics (novo)   │     │
│  │  ├─ crawl-stf (novo)  │  │  ├─ jurisprudencia_embeddings (novo)│    │
│  │  ├─ crawl-stj (novo)  │  │  └─ jurisprudencia_chat (expandido)│    │
│  │  ├─ crawl-trf5 (novo) │  │                                     │     │
│  │  ├─ crawl-tjce (novo) │  │                                     │     │
│  │  └─ analyze-petition  │  │                                     │     │
│  └───────────────────────┘  └────────────────────────────────────┘     │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│                    HETZNER CLOUD VPS (CX33 - Existente)                 │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │  local-scraper (Expandido)                                       │   │
│  │  ├─ Crawlee/Playwright para STF, STJ, TRF-5, TJ-CE             │   │
│  │  ├─ Proxy rotation com residential proxies                       │   │
│  │  ├─ Scheduler para crawl incremental (a cada 2-6h)             │   │
│  │  └─ API REST para trigger de crawl via Supabase                 │   │
│  └─────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────┘

2.2 Stack Tecnológica e Componentes

Frontend (Extensão do Existing):

O frontend utilizará as tecnologias já estabelecidas no RV-Adv, com adições pontuais para suportar o módulo de jurisprudência expandido. A biblioteca TanStack Query será ampliada com queries específicas para busca jurisprudencial, aproveitando os hooks de staleTime e prefetch já configurados. Os componentes shadcn/ui existentes serão reutilizados, com criação de novos componentes apenas para funcionalidades específicas do domínio jurisprudencial.

ComponenteTecnologiaAdição ao Existing
UI FrameworkReact 18 + Vite 6Sem mudança
EstilizaçãoTailwind CSS 4Design tokens legais já definidos
EstadoTanStack Query v5Novas queries: useJurisprudenceSearch, usePetitionAnalysis
ÍconesLucide ReactÍcones jurisprudenciais: Gavel, Scale, FileText
AnimaçõesFramer MotionTransições de cards de resultado
PDF Viewerreact-pdfPara preview de decisões

Backend (Supabase):

O backend Supabase constitui o pilar central do módulo, extendendo o schema existente com novas tabelas para armazenagem e indexação de jurisprudências. A extensão pgvector já ativa no banco será aproveitada para armazenagem de embeddings de 3072 dimensões (conforme padrão atual do RV-Adv para embeddings Gemini). As Edge Functions existentes serão estendidas ou servirão como base para novas implementações.

ComponenteTecnologiaFunção
DatabasePostgreSQL + pgvectorSchema extendido + índice HNSW
AuthSupabase AuthJWT já configurado
StorageSupabase StorageArmazenamento de PDFs de jurisprudência
Edge FunctionsDeno/TypeScriptCrawl, embedding, chat RAG
Cronpg_cronJobs de crawl e atualização

Microsserviço de Scraping (Hetzner VPS):

O VPS Hetzner CX33 atualmente dedicado ao local-scraper será expandido para incluir crawlers específicos para cada tribunal-alvo. A arquitetura do crawler existente (Crawlee + Playwright com técnicas stealth) será reutilizada e adaptada para as estruturas HTML distintas de cada portal judicial.

ComponenteTecnologiaFunção
RuntimeNode.js 20 LTSExecução do scraper
FrameworkCrawlee + PlaywrightCrawling com stealth
Schedulernode-cronAgendamento de crawls
ProxyResidential proxies (BrightData/ScrapingBee)Rotação de IPs
APIExpress.jsEndpoints REST para Supabase

Inteligência Artificial (Existing + Extensão):

O ai-proxy existente será reutilizado integralmente, beneficiando-se do sistema de fallback multi-provedor já implementado. As chaves de API armazenadas no Supabase Vault continuarão ocultas do frontend, e o timeout de 45 segundos será mantido para operações de crawl e análise.

AçãoProvedor PrimárioProvedor BackupExtensão
Geração de EmbeddingsGeminiMantido
Chat RAG JurisprudênciaGemini 2.5 Flash LiteExpandido
Análise de PetiçãoGroqGeminiNovo
Query ExpansionGroqNovo

2.3 Fluxo de Dados e Integrações

Fluxo de Busca por Linguagem Natural:

O fluxo de busca inicia quando o usuário submete uma descrição textual na interface JurisprudenciaPage. O componente dispara a Edge Function search-jurisprudence via API route do Supabase, passando a query original e os tribunais selecionados. A função implementa o seguinte pipeline: (1) autenticação JWT via módulo _shared/auth.ts existente; (2) expansão da query utilizando o LLM configurado (Groq llama-3.1-8b) para gerar 3-5 variações semânticas; (3) geração de embedding da query expandida via Gemini; (4) execução de busca vetorial no banco PostgreSQL usando a RPC buscar_jurisprudencia existente; (5) fetch de metadados completos das decisões recuperadas; (6) classificação de relevância baseada em score de similaridade; (7) formatação e retorno dos resultados.

Fluxo de Upload de Petição:

O fluxo de análise de petição inicia com o upload do documento via componente PetitionUploadPanel. O frontend persiste o arquivo no Supabase Storage e cria um registro de job na tabela jurisprudencia_jobs. A Edge Function analyze-petition é invocada assincronamente, implementando o pipeline: (1) extração de texto via OCR (Gemini Vision para PDFs scaneados); (2) identificação de temas jurídicos via classificação LLM (Groq); (3) para cada tema identificado, execução de busca jurisprudencial; (4) agregação e classificação dos resultados; (5) atualização do job com status completed e resultados. O frontend utiliza polling ou subscriptions do Supabase para monitorar o progresso do job.

Fluxo de Crawl e Indexação:

O fluxo de crawl opera em duas camadas: o VPS Hetzner executa crawlers Playwright que navegam nos portais judiciais e extraem decisões, enquanto Edge Functions no Supabase processam e indexam os dados. O scheduler no VPS dispara crawls incrementais a cada 2 horas para STJ (maior volume), 4 horas para STF, e 6 horas para TRF-5 e TJ-CE. Para cada decisão extraída, o pipeline executa: (1) persistência do registro em jurisprudences; (2) trigger assíncrono para geração de embedding via generate-embedding; (3) atualização do status de embedding para completed quando pronto.


3. MODELO DE DADOS EXPANDIDO

3.1 Schema de Banco de Dados

O schema do banco de dados será扩展ado com novas tabelas e extensões às existentes, preservando as convenções estabelecidas nas 57 migrações anteriores. Todas as tabelas seguirão o padrão de ownership via created_by e terão RLS habilitado com política Fail-Close.

-- Extensão da tabela courts para incluir STF, STJ, TRF-5, TJ-CE
INSERT INTO courts (acronym, name, url_base, crawl_enabled, crawl_interval_hours, last_crawl_at)
VALUES 
  ('STF', 'Supremo Tribunal Federal', 'https://stf.jus.br', true, 4, NULL),
  ('STJ', 'Superior Tribunal de Justiça', 'https://www.stj.jus.br', true, 2, NULL),
  ('TRF5', 'Tribunal Regional Federal da 5ª Região', 'https://www.trf5.jus.br', true, 6, NULL),
  ('TJCE', 'Tribunal de Justiça do Estado do Ceará', 'https://www.tjce.jus.br', true, 6, NULL);

-- Tabela principal de jurisprudências
CREATE TABLE jurisprudences (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  court_id UUID REFERENCES courts(id) ON DELETE CASCADE,
  process_number VARCHAR(50) NOT NULL,
  process_type VARCHAR(100),
  rapporteur VARCHAR(200),
  adjudicating_body VARCHAR(200),
  publication_date DATE,
  trial_date DATE,
  excerpt TEXT NOT NULL,
  full_text TEXT,
  url TEXT NOT NULL,
  created_by UUID REFERENCES auth.users(id),
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW(),
  embedding_status VARCHAR(20) DEFAULT 'pending',
  UNIQUE(court_id, process_number)
);

-- Índice vetorial para embeddings (3072 dimensões - Gemini)
ALTER TABLE jurisprudences ADD COLUMN embedding vector(3072);

-- Índice HNSW para busca vetorial
CREATE INDEX idx_jurisprudence_embedding ON jurisprudences 
  USING hnsw (embedding vector_cosine_ops)
  WITH (m = 16, ef_construction = 64);

-- Índice FTS para busca por palavra-chave
ALTER TABLE jurisprudences ADD COLUMN fts_vector tsvector
  GENERATED ALWAYS AS (to_tsvector('portuguese', coalesce(excerpt, '') || ' ' || coalesce(full_text, ''))) STORED;
CREATE INDEX idx_jurisprudence_fts ON jurisprudences USING GIN (fts_vector);

-- Tabela de tópicos/temas extraídos
CREATE TABLE jurisprudencia_topics (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name VARCHAR(200) NOT NULL,
  keywords TEXT[],
  created_by UUID REFERENCES auth.users(id),
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Tabela de mapeamento jurisprudência-tópico
CREATE TABLE jurisprudencia_topic_mapping (
  jurisprudence_id UUID REFERENCES jurisprudences(id) ON DELETE CASCADE,
  topic_id UUID REFERENCES jurisprudencia_topics(id) ON DELETE CASCADE,
  confidence FLOAT,
  PRIMARY KEY (jurisprudence_id, topic_id)
);

-- Tabela de análise de petições
CREATE TABLE jurisprudencia_jobs (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id UUID REFERENCES auth.users(id),
  document_name VARCHAR(255),
  document_storage_path TEXT,
  status VARCHAR(20) DEFAULT 'processing',
  extracted_topics JSONB,
  court_filter TEXT[],
  max_results_per_topic INTEGER DEFAULT 10,
  error_message TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  completed_at TIMESTAMPTZ
);

-- Tabela de resultados de análise
CREATE TABLE jurisprudencia_job_results (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  job_id UUID REFERENCES jurisprudencia_jobs(id) ON DELETE CASCADE,
  jurisprudence_id UUID REFERENCES jurisprudences(id),
  topic_name VARCHAR(200),
  relevance_score VARCHAR(20),
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Função RPC para busca vetorial
CREATE OR REPLACE FUNCTION buscar_jurisprudencia(
  query_embedding vector(3072),
  match_count INTEGER DEFAULT 20,
  similarity_threshold FLOAT DEFAULT 0.7,
  court_filter TEXT[] DEFAULT NULL
)
RETURNS TABLE (
  id UUID,
  process_number VARCHAR,
  court_acronym VARCHAR,
  process_type VARCHAR,
  rapporteur VARCHAR,
  publication_date DATE,
  excerpt TEXT,
  url TEXT,
  similarity FLOAT
) AS $$
BEGIN
  RETURN QUERY
  SELECT
    j.id,
    j.process_number,
    c.acronym as court_acronym,
    j.process_type,
    j.rapporteur,
    j.publication_date,
    j.excerpt,
    j.url,
    (j.embedding <=> query_embedding) as similarity
  FROM jurisprudences j
  JOIN courts c ON j.court_id = c.id
  WHERE j.embedding IS NOT NULL
    AND (j.embedding <=> query_embedding) < (1 - similarity_threshold)
    AND (court_filter IS NULL OR c.acronym = ANY(court_filter))
  ORDER BY j.embedding <=> query_embedding
  LIMIT match_count;
END;
$$ LANGUAGE plpgsql;

-- Extensão da tabela de chat para múltiplos tribunais
ALTER TABLE jurisprudencia_chat_sessions ADD COLUMN court_filter TEXT[];
ALTER TABLE jurisprudencia_chat_messages ADD COLUMN embedding_id UUID;

3.2 Migrações SQL Sequenciais

As migrações seguirão a numeração estabelecida (iniciando em 058) e incluirão verificações IF NOT EXISTS para garantir idempotência durante deploys.

MigraçãoDescriçãoDependências
058_courts_expandExtensão da tabela courts com STF, STJ, TRF5, TJCE057+
059_jurisprudencesCriação da tabela principal de jurisprudências058
060_jurisprudence_embeddingsAdição de coluna vector e índices HNSW/FTS059
061_jurisprudencia_topicsTabelas de tópicos e mapeamento059
062_jurisprudencia_jobsTabela de jobs de análise de petições059
063_buscar_jurisprudencia_fnFunção RPC de busca vetorial060
064_rls_jurisprudencesPolíticas RLS para novas tabelas059-063

4. EDGE FUNCTIONS: IMPLEMENTAÇÃO

4.1 Catálogo de Edge Functions

As Edge Functions serão implementadas seguindo os padrões estabelecidos no RV-Adv, reutilizando o módulo _shared/auth.ts para autenticação e integrando-se ao ai-proxy existente para chamadas de LLM.

Edge FunctionResponsabilidadeAutenticaçãoDependências
search-jurisprudenceBusca semântica multi-tribunalJWTai-proxy, generate-embedding
analyze-petitionAnálise de petição e extração de temasJWTai-proxy, search-jurisprudence
chat-jurisprudence-multiChat RAG multi-tribunal (estende existente)JWTai-proxy, jurisprudences
crawl-trigger-stfTrigger de crawl para STFJWT (service)
crawl-trigger-stjTrigger de crawl para STJJWT (service)
crawl-trigger-trf5Trigger de crawl para TRF-5JWT (service)
crawl-trigger-tjceTrigger de crawl para TJ-CEJWT (service)
index-jurisprudenceIndexação de jurisprudência extraídaJWT (service)generate-embedding

4.2 Implementação da Função search-jurisprudence

A função de busca semântica implementa o pipeline completo de recuperação, desde a query do usuário até os resultados formatados. A implementação segue os padrões de código estabelecidos no RV-Adv.

// supabase/functions/search-jurisprudence/index.ts
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'
import { AiProxy } from '../_shared/ai-proxy.ts'

const corsHeaders = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
}

serve(async (req) => {
  if (req.method === 'OPTIONS') {
    return new Response('ok', { headers: corsHeaders })
  }

  try {
    const authHeader = req.headers.get('Authorization')
    const supabase = createClient(
      Deno.env.get('SUPABASE_URL') ?? '',
      Deno.env.get('SUPABASE_ANON_KEY') ?? '',
    )
    
    // Autenticação JWT
    const token = authHeader?.replace('Bearer ', '')
    const { data: { user }, error: authError } = await supabase.auth.getUser(token)
    if (authError || !user) {
      return new Response(JSON.stringify({ error: 'Unauthorized' }), { 
        status: 401, headers: { ...corsHeaders, 'Content-Type': 'application/json' } 
      })
    }

    const { query, courts, max_results, relevance_threshold, expand_query } = await req.json()

    // 1. Expansão de query via LLM
    let expandedQueries = [query]
    if (expand_query) {
      const aiProxy = new AiProxy()
      const expansionPrompt = `Gere 3 variações da seguinte consulta jurídica para busca jurisprudencial.
Considere sinônimos, termos técnicos equivalentes e diferentes formulações do mesmo conceito.

Consulta original: "${query}"

Responda apenas com um JSON array de strings, sem explicações.`
      
      const expansionResponse = await aiProxy.invoke({
        model: 'groq/llama-3.1-8b',
        messages: [{ role: 'user', content: expansionPrompt }],
        max_tokens: 200,
        temperature: 0.3
      })
      
      try {
        expandedQueries = JSON.parse(expansionResponse)
        expandedQueries = [query, ...expandedQueries.slice(0, 3)]
      } catch {
        expandedQueries = [query]
      }
    }

    // 2. Geração de embedding
    const embeddingResponse = await fetch(`${Deno.env.get('SUPABASE_URL')}/functions/v1/generate-embedding`, {
      method: 'POST',
      headers: {
        'Authorization': authHeader,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ text: query, taskType: 'RETRIEVAL_QUERY' })
    })
    const { embedding } = await embeddingResponse.json()

    // 3. Busca vetorial
    const { data: results, error: searchError } = await supabase.rpc('buscar_jurisprudencia', {
      query_embedding: embedding,
      match_count: max_results || 20,
      similarity_threshold: relevance_threshold || 0.7,
      court_filter: courts || null
    })

    if (searchError) throw searchError

    // 4. Classificação de relevância
    const classifiedResults = results.map(r => ({
      ...r,
      relevance: classifyRelevance(r.similarity)
    }))

    return new Response(JSON.stringify({
      data: classifiedResults,
      meta: {
        query_expanded: expand_query,
        variations_used: expandedQueries.length,
        total: classifiedResults.length
      }
    }), {
      headers: { ...corsHeaders, 'Content-Type': 'application/json' }
    })

  } catch (error) {
    return new Response(JSON.stringify({ error: error.message }), {
      status: 500,
      headers: { ...corsHeaders, 'Content-Type': 'application/json' }
    })
  }
})

function classifyRelevance(similarity: number): string {
  if (similarity > 0.85) return 'high'
  if (similarity > 0.70) return 'medium'
  return 'low'
}

4.3 Implementação da Função analyze-petition

A função de análise de petição recebe um documento, extrai temas automaticamente e executa buscas jurisprudenciais para cada tema identificado.

// supabase/functions/analyze-petition/index.ts
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'
import { AiProxy } from '../_shared/ai-proxy.ts'

serve(async (req) => {
  // Implementação completa em Edge Function
  // 1. Download do documento do Storage
  // 2. Extração de texto (OCR se necessário via Gemini Vision)
  // 3. Extração de temas via LLM
  // 4. Para cada tema: busca jurisprudencial
  // 5. Persistência de resultados
  // 6. Atualização do job status
})

5. MICROSSERVIÇO DE SCRAPING (HETZNER VPS)

5.1 Arquitetura do Scraper Expandido

O microsserviço de scraping existente no VPS Hetzner será expandido para incluir crawlers específicos para os tribunais-alvo. A arquitetura preserva o padrão estabelecido de Crawlee + Playwright com técnicas stealth.

local-scraper/
├── server.ts                 # API REST existente
├── crawlers/
│   ├── base-crawler.ts        # Classe base com configurações stealth
│   ├── stf-crawler.ts        # Crawler específico STF
│   ├── stj-crawler.ts        # Crawler específico STJ
│   ├── trf5-crawler.ts       # Crawler específico TRF-5
│   └── tjce-crawler.ts       # Crawler específico TJ-CE
├── extractors/
│   ├── stf-extractor.ts      # Parser de HTML do STF
│   ├── stj-extractor.ts      # Parser de HTML do STJ
│   ├── trf5-extractor.ts     # Parser de HTML do TRF-5
│   └── tjce-extractor.ts     # Parser de HTML do TJ-CE
├── schedulers/
│   └── crawl-scheduler.ts    # Agendamento de crawls
├── proxy-rotator.ts          # Rotação de residential proxies
├── api-client.ts             # Cliente Supabase para persistência
└── package.json

5.2 Configuração de Crawlers por Tribunal

Cada tribunal requer configurações específicas devido às diferenças estruturais em seus portais de jurisprudência.

STF (stf.jus.br):

O portal do STF utiliza JavaScript heavy para renderização de resultados, exigindo Playwright para renderização completa. O sistema de busca permite filtros por número de processo, relator, ementa e índice de assuntos. A estrutura de dados inclui: número do processo (formato completo CNJ), tipo (recurso extraordinário, agravo, etc.), relator, data de publicação, ementa e link para inteiro teor. O volume diário gira em torno de 150-200 decisões.

STJ (stj.jus.br):

O STJ possui a estrutura mais robusta e estável para scraping. O portal oferece filtros por classe, assunto CDU, tribunal de origem, período e texto. A extração é relativamente direta, com HTML bem estruturado. O volume diário é o maior: 500-800 decisões. O STJ é prioritário para crawl mais frequente.

TRF-5 (trf5.jus.br):

O TRF-5 mantém portal com estrutura similar aos tribunais estaduais. A busca permite filtros por número do processo, palavras-chave, classe e data. O volume é moderado (100-200 decisões diárias), concentrado em direito federal: previdenciário, tributário e administrativo.

TJ-CE (tjce.jus.br):

O TJ-CE apresenta portal com estrutura variável, requerendo manutenções frequentes nos selectors CSS. A busca oferece filtros por classe, matéria, data e texto. O volume é aproximadamente 200-400 decisões por dia útil.

5.3 Scheduler de Crawl

O scheduler implementa frecuencias diferenciadas baseadas no volume de cada tribunal:

// schedulers/crawl-scheduler.ts
import cron from 'node-cron'

const CRAWL_SCHEDULE = {
  STJ: '0 */2 * * *',      // A cada 2 horas
  STF: '0 */4 * * *',      // A cada 4 horas
  TRF5: '0 */6 * * *',     // A cada 6 horas
  TJCE: '0 */6 * * *',     // A cada 6 horas
}

export function startScheduler() {
  Object.entries(CRAWL_SCHEDULE).forEach(([court, schedule]) => {
    cron.schedule(schedule, async () => {
      console.log(`Iniciando crawl para ${court}`)
      try {
        await triggerCrawl(court)
      } catch (error) {
        console.error(`Erro no crawl ${court}:`, error)
        await sendAlert(error)
      }
    })
  })
}

6. INTERFACE DE USUÁRIO

6.1 Componentes do Frontend

Os componentes do frontend serão implementados como extensão do sistema existente, seguindo os padrões de design system já estabelecidos.

JurisprudenciaPage.jsx (Expandida):

A página principal de jurisprudência será expandida para incluir três painéis principais: busca em linguagem natural, upload de petição, e chat RAG multi-tribunal.

// Estrutura simplificada da página
export function JurisprudenciaPage() {
  const [activeTab, setActiveTab] = useState('search')
  
  return (
    <div className="container mx-auto py-6 space-y-6">
      <header className="flex items-center justify-between">
        <h1 className="text-3xl font-bold">Jurisprudência</h1>
        <CourtFilter />
      </header>
      
      <Tabs value={activeTab} onValueChange={setActiveTab}>
        <TabsList>
          <TabsTrigger value="search">Busca</TabsTrigger>
          <TabsTrigger value="petition">Análise de Petição</TabsTrigger>
          <TabsTrigger value="chat">Chat RAG</TabsTrigger>
        </TabsList>
        
        <TabsContent value="search">
          <NaturalSearchPanel />
          <ResultsGrid />
        </TabsContent>
        
        <TabsContent value="petition">
          <PetitionUploadPanel />
          <ThemesList />
        </TabsContent>
        
        <TabsContent value="chat">
          <ChatRAGPanel courts={selectedCourts} />
        </TabsContent>
      </Tabs>
    </div>
  )
}

NaturalSearchPanel:

Componente de busca que substitui a entrada de termos técnicos por descrição em linguagem natural.

// Componentes principais
- SearchInput: Campo de texto expansível para descrição do caso
- CourtSelector: Seleção multipla de tribunais (STF, STJ, TRF-5, TJ-CE)
- SearchButton: Botão de busca com loading state
- QuickFilters: Filtros rápidos por tipo de decisão e período

ResultsGrid:

Grid responsivo de resultados jurisprudenciais com cards expandíveis.

// Estrutura do card de resultado
<Card>
  <CardHeader>
    <div className="flex justify-between items-start">
      <div>
        <Badge variant="court">{result.court_acronym}</Badge>
        <h3 className="font-mono">{result.process_number}</h3>
      </div>
      <Badge variant="relevance-{result.relevance}">
        {result.relevance === 'high' ? 'Muito Relevante' : 
         result.relevance === 'medium' ? 'Relevante' : 'Pouco Relevante'}
      </Badge>
    </div>
  </CardHeader>
  <CardContent>
    <p className="text-sm text-muted-foreground">
      {result.rapporteur}{result.adjudicating_body}
    </p>
    <p className="mt-2 line-clamp-4">{result.excerpt}</p>
  </CardContent>
  <CardFooter>
    <Button variant="outline" asChild>
      <a href={result.url} target="_blank" rel="noopener noreferrer">
        Ver no Tribunal
      </a>
    </Button>
  </CardFooter>
</Card>

6.2 Custom Hooks TanStack Query

Os hooks de TanStack Query seguirão o padrão existente de BaseService.

// hooks/useJurisprudenceSearch.ts
export function useJurisprudenceSearch() {
  const supabase = useSupabaseClient()
  
  return useMutation({
    mutationFn: async ({ query, courts, maxResults, expandQuery }) => {
      const { data: { session } } = await supabase.auth.getSession()
      const response = await fetch(
        `${import.meta.env.VITE_SUPABASE_URL}/functions/v1/search-jurisprudence`,
        {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${session.access_token}`
          },
          body: JSON.stringify({ query, courts, max_results: maxResults, expand_query: expandQuery })
        }
      )
      if (!response.ok) throw new Error('Search failed')
      return response.json()
    }
  })
}

// hooks/usePetitionAnalysis.ts
export function usePetitionAnalysis() {
  const supabase = useSupabaseClient()
  const queryClient = useQueryClient()
  
  return useMutation({
    mutationFn: async ({ file, courts }) => {
      // Upload file to storage
      const filePath = `jurisprudence/jobs/${Date.now()}-${file.name}`
      const { error: uploadError } = await supabase.storage
        .from('documents')
        .upload(filePath, file)
      if (uploadError) throw uploadError
      
      // Trigger analysis job
      const { data: { session } } = await supabase.auth.getSession()
      const response = await fetch(
        `${import.meta.env.VITE_SUPABASE_URL}/functions/v1/analyze-petition`,
        {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${session.access_token}`
          },
          body: JSON.stringify({ 
            document_path: filePath,
            document_name: file.name,
            courts
          })
        }
      )
      if (!response.ok) throw new Error('Analysis failed')
      return response.json()
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['jurisprudence-jobs'] })
    }
  })
}

7. PIPELINE DE DEPLOY E CI/CD

7.1 GitHub Actions Workflows

Dois novos workflows serão adicionados aos existentes, mantendo o padrão de workflows de deploy já estabelecidos.

deploy-jurisprudence-functions.yml:

name: Deploy Jurisprudence Edge Functions

on:
  push:
    branches: [master]
    paths:
      - 'supabase/functions/search-jurisprudence/**'
      - 'supabase/functions/analyze-petition/**'
      - 'supabase/functions/chat-jurisprudence-multi/**'
      - 'supabase/functions/index-jurisprudence/**'
  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - uses: supabase/setup-cli@v1
        with:
          supabase_version: latest
      
      - name: Deploy Edge Functions
        run: |
          for func in search-jurisprudence analyze-petition chat-jurisprudence-multi index-jurisprudence; do
            supabase functions deploy $func --project-ref uxtgcarklizhwuotkwkd
          done
        env:
          SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}

jurisprudence-crawler-sync.yml:

name: Sync Jurisprudence Crawler to Hetzner

on:
  push:
    branches: [master]
    paths:
      - 'local-scraper/**'
  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Deploy to Hetzner
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.HETZNER_HOST }}
          username: ${{ secrets.HETZNER_USER }}
          key: ${{ secrets.HETZNER_SSH_KEY }}
          script: |
            cd /opt/rv-adv/local-scraper
            git pull origin master
            npm install
            pm2 restart scraper || pm2 start server.ts --name scraper

7.2 pg_cron Jobs

Novos jobs de cron serão configurados no banco de dados para tarefas de manutenção.

-- Job de verificação de embeddings pendentes
SELECT cron.schedule(
  'jurisprudence-embedding-check',
  '0 */1 * * *',
  $$ 
  INSERT INTO jurisprudencia_jobs (user_id, document_name, status, error_message)
  SELECT 
    u.id,
    'System: Pending embeddings check',
    'info',
    format('Found %s jurisprudences pending embedding', COUNT(*))
  FROM jurisprudences j
  CROSS JOIN LATERAL auth.users() u
  WHERE j.embedding_status = 'pending'
    AND j.created_at < NOW() - INTERVAL '24 hours'
  HAVING COUNT(*) > 0
  ON CONFLICT DO NOTHING;
  $$
);

8. PLANO DE IMPLEMENTAÇÃO

8.1 Fases de Desenvolvimento

Fase 1: Foundation (Semanas 1-2)

  • Criação das migrações SQL (058-064) para schema de jurisprudência
  • Deploy inicial das tabelas e índices no Supabase
  • Implementação da Edge Function search-jurisprudence
  • Teste de integração com embedding existente
  • Configuração inicial de RLS para novas tabelas

Fase 2: Crawler MVP (Semanas 3-4)

  • Implementação dos crawlers STF e STJ no VPS Hetzner
  • Extração de estrutura HTML e parsers
  • Teste de crawling com volume controlado
  • Endpoint de trigger via Supabase Edge Function
  • Scheduler básico (crawl manual trigger)

Fase 3: Interface (Semanas 5-6)

  • Expansão do componente JurisprudenciaPage
  • Implementação de NaturalSearchPanel
  • Desenvolvimento de ResultsGrid com cards
  • Integração com hooks TanStack Query
  • Testes E2E com Playwright

Fase 4: Análise de Petição (Semanas 7-8)

  • Implementação da Edge Function analyze-petition
  • Integração de OCR para PDFs scaneados
  • UI de upload e progress tracking
  • Tela de resultados por tema
  • Testes de integração

Fase 5: Expansão de Crawlers (Semanas 9-10)

  • Crawlers para TRF-5 e TJ-CE
  • Scheduler automatizado com frecuencias diferenciadas
  • Proxy rotation setup
  • Monitoramento e alertas
  • Otimização de performance

Fase 6: Chat RAG Multi-Tribunal (Semanas 11-12)

  • Expansão da Edge Function chat-jurisprudencia existente
  • Suporte a múltiplos tribunais
  • Interface de chat no frontend
  • Histórico de sessões
  • Deploy e testes finais

8.2 Estimativa de Recursos

Infraestrutura Adicional:

RecursoNecessidadeCusto Estimado
VPS Hetzner CX33Já existente, expansão de uso$0 adicional
Supabase ProUpgrade para mais Edge Functions~$25/mês
Residential ProxiesBrightData/ScrapingBee~$50-100/mês
ArmazenamentoSupabase Storage ( PDFs)~$5/mês

APIs LLM (Gratuitas - Existing):

ProvedorUsoCusto
GeminiEmbeddings, Chat RAGGratuito
GroqQuery expansion, ClassificaçãoGratuito
NVIDIA NeMoFallbackGratuito

9. CHECKLIST DE QUALIDADE

  • CQ1: Funcionalidades implementadas (busca inteligente, análise de petição, relevância, chat RAG multi-tribunal)
  • CQ2: Arquitetura integrada ao RV-Adv existente (Supabase, Hetzner, Netlify)
  • CQ3: Sistema LexNova-RV como módulo do RV-Adv, não produto independente
  • CQ4: Detalhamento técnico adequado para implementação por equipe
  • Reutilização de componentes existentes (BaseService, shadcn/ui, _shared/auth.ts)
  • RLS implementado em todas as tabelas novas
  • Edge Functions com autenticação JWT padrão
  • Crawlers com técnicas stealth e rotação de proxies
  • Interface responsiva seguindo design system existente
  • CI/CD automatizado via GitHub Actions
  • Monitoramento e alertas configurados
Built with LogoFlowershow