Avançado 12 minJurídico

RAG sobre jurisprudência Légifrance

Resposta direta

Para um RAG sobre jurisprudência, baixe os arquivos XML abertos da DILA (CASS para os acórdãos publicados no Boletim da Corte de Cassação, INCA para os não publicados), divida cada acórdão conforme suas seções, indexe com BGE-M3 no Qdrant e exija a citação do recurso de cassação e do ECLI. Os arquivos do acervo completo ocupam algumas centenas de MB quando compactados, não dezenas de GB.

A jurisprudência francesa é publicada em dados abertos, mas seus conjuntos de dados têm um escopo específico, uma estrutura XML particular e armadilhas que os tutoriais genéricos ignoram. Este guia constrói um motor de busca semântico local com base nessas decisões: download, leitura do XML, divisão por seções, indexação, busca filtrada e limitações a serem apresentadas aos usuários.

Por Mohamed Meguedmi·Atualização 2026-09-30·Testado no Windows, macOS e Linux

#O que é um RAG jurídico e o que se pede a ele

Um RAG jurídico é um mecanismo de busca semântica em decisões judiciais, combinado a um modelo que redige uma resposta com base nos trechos encontrados. A busca transforma a pergunta em um vetor, encontra os trechos mais próximos em uma base de decisões e, em seguida, o modelo os sintetiza citando suas referências. A vantagem em relação a um modelo isolado é decisiva no direito: o modelo não responde de memória, mas com base em textos que você pode reler, com órgão jurisdicional, data, número do recurso de cassação e identificador ECLI.

Este guia constrói esse mecanismo com dados abertos publicados pela Direção da Informação Legal e Administrativa (DILA), em uma máquina local: nenhuma pergunta de um profissional do direito é enviada a um serviço externo. O caso de uso é uma pergunta como “rescisão de um CDD: quais são as decisões recentes da Corte de Cassação?”, com uma lista de trechos acompanhados de suas fontes como resposta. O sistema não emite um parecer: ele localiza e cita, e cabe ao profissional fazer a qualificação jurídica.

i
O que diferencia este guia
A maioria dos tutoriais de RAG parte de documentos que você possui. Aqui, a dificuldade está em outro lugar: compreender realmente o conteúdo dos conjuntos de dados públicos, sua estrutura XML, seu volume, sua frequência de atualização e suas limitações de cobertura. Cada número da seção seguinte foi coletado nas fontes oficiais.

#Conjuntos de dados oficiais: o que eles realmente contêm

O kit RAG Local

Seus documentos, sua IA: um RAG local confiável para seus PDFs, notas e e-mails — sem enviar nada para a nuvem.

  • Espaço online vitalício
  • PDF + arquivos
  • Atualizações vitalícias

A DILA disponibiliza as decisões em bases distintas, cada uma com seu escopo. Um ponto importante, frequentemente mal compreendido: essas bases não contêm todas as decisões proferidas na França. O acervo da Corte de Cassação publicado sob o nome CASS reúne os acórdãos publicados no Boletim, os das câmaras civis desde 1960 e os da câmara criminal desde 1963, com títulos e resumos redigidos pelos magistrados. Os acórdãos inéditos, não publicados no Boletim, estão em uma base separada, INCA, disponibilizada desde 1989.

As bases de jurisprudência da DILA (de acordo com as fichas data.gouv.fr)
BaseConteúdoArquivo completo de dados (compactado)
CASSDecisões da Corte de Cassação publicadas no Boletim (civis desde 1960, criminais desde 1963)cerca de 248 MB
INCADecisões inéditas da Corte de Cassação, não publicadas no Boletim, desde 1989cerca de 655 MB
CAPPSeleção de decisões civis e penais dos tribunais de apelação e dos órgãos jurisdicionais de primeira instânciaaproximadamente 279 MB
JADEConselho de Estado, tribunais administrativos de apelação, Tribunal dos Conflitos (seleção conforme a jurisdição)cerca de 1,2 GB

Os tamanhos acima são os dos arquivos compactados completos listados no servidor da DILA na consulta de 30 de setembro de 2026: algumas centenas de megabytes compactados por base, muito longe das dezenas de gigabytes às vezes citadas. O volume descompactado é maior, pois cada decisão é um pequeno arquivo XML, mas um computador padrão é suficiente. Meça esse volume no seu disco antes de planejar.

Existe uma segunda opção: a API Judilibre, implementada pela Corte de Cassação para disponibilizar gratuitamente ao público uma base aberta alimentada por decisões proferidas publicamente, eventualmente enriquecidas e pseudonimizadas. O acesso é feito por uma interface de programação com autenticação, enquanto os arquivos da DILA são simples arquivos para download. Para um RAG local, os arquivos são o ponto de partida mais simples; Judilibre se torna pertinente quando você quer um acervo mais completo e atualizado.

!
Dados pessoais: a responsabilidade é sua
A DILA esclarece em suas fichas que a disponibilização de conjuntos de dados que possam conter dados pessoais não isenta quem os reutiliza de cumprir a lei francesa Informatique et Libertés. As decisões são pseudonimizadas (trechos como [V] [S] substituem nomes), mas nunca tente reidentificar pessoas e verifique o escopo do seu projeto com seu encarregado de proteção de dados.

#Baixar o acervo: primeiro a base completa, depois as atualizações

Cada base segue o mesmo esquema no servidor da DILA: um arquivo com a base completa, cujo nome começa com Freemium e termina com global, seguido de arquivos incrementais que trazem as novidades. Na data da consulta, o arquivo com a base completa da Corte de Cassação datava de 13 de julho de 2025, e arquivos semanais o complementavam até o fim de setembro de 2026. Portanto, é necessário baixar a base completa e depois aplicar todos os arquivos incrementais posteriores, em ordem.

Baixar a base completa CASS e depois as atualizações
mkdir -p dila/CASS && cd dila/CASS
curl -O https://echanges.dila.gouv.fr/OPENDATA/CASS/Freemium_cass_global_20250713-140000.tar.gz
tar xzf Freemium_cass_global_20250713-140000.tar.gz

# Puis chaque archive incrémentale, dans l'ordre chronologique
curl -s https://echanges.dila.gouv.fr/OPENDATA/CASS/ | grep -o 'CASS_2026[0-9-]*\.tar\.gz' | sort -u > maj.txt
for f in $(cat maj.txt); do curl -sO https://echanges.dila.gouv.fr/OPENDATA/CASS/$f && tar xzf $f; done

O nome do arquivo compactado com a base completa muda quando a DILA o gera novamente: confira a lista da pasta antes de fixar um nome no código. Evite também baixar a pasta repetidamente: um único arquivo compactado com a base completa e as atualizações incrementais são suficientes, e um espelhamento recursivo sobrecarrega desnecessariamente o servidor público.

#Ler o XML DILA sem confundir os campos

O formato é uma família de definições de tipos de documentos comuns a várias bases, publicada pela DILA sob o nome DTD Légifrance. Em um arquivo semanal de setembro de 2026, a estrutura de um acórdão da Corte de Cassação é a seguinte: um bloco de metadados comuns (identificador, natureza), um bloco de metadados jurídicos (título, data da decisão, jurisdição, resultado) e um bloco específico da justiça judicial (número do processo, composição do órgão julgador, ECLI, indicador de publicação no Boletim). O texto integral está no bloco textual, dentro do elemento de conteúdo, com quebras de linha codificadas em tags br.

Duas armadilhas são comuns nos tutoriais. Primeiro, o campo NUMERO do bloco jurídico é um número interno; o número do recurso de cassação, aquele que os juristas citam, está no bloco específico em NUMEROS_AFFAIRES. Segundo, recuperar todo o texto do arquivo com itertext mistura os metadados com o corpo do acórdão e polui os vetores: é necessário selecionar o elemento de conteúdo.

Analisar a estrutura de um acórdão CASS (estrutura verificada em um arquivo de 2026)
from lxml import etree
from pathlib import Path
import re

def parser(chemin):
    racine = etree.parse(str(chemin)).getroot()

    def val(xp):
        e = racine.find(xp)
        return (e.text or '').strip() if e is not None else None

    contenu = racine.find('.//TEXTE/BLOC_TEXTUEL/CONTENU')
    if contenu is None:
        return None
    for br in contenu.iter('br'):
        br.tail = '\n' + (br.tail or '')
    texte = ''.join(contenu.itertext())
    texte = re.sub(r'[ \t]+', ' ', re.sub(r'\n\s*\n+', '\n', texte)).strip()
    return {
        'id': val('.//META_COMMUN/ID'),
        'titre': val('.//META_JURI/TITRE'),
        'date': val('.//META_JURI/DATE_DEC'),
        'juridiction': val('.//META_JURI/JURIDICTION'),
        'solution': val('.//META_JURI/SOLUTION'),
        'pourvoi': val('.//META_JURI_JUDI/NUMEROS_AFFAIRES/NUMERO_AFFAIRE'),
        'formation': val('.//META_JURI_JUDI/FORMATION'),
        'ecli': val('.//META_JURI_JUDI/ECLI'),
        'texte': texte,
    }

def decisions(dossier):
    for chemin in Path(dossier).rglob('*.xml'):
        try:
            d = parser(chemin)
            if d:
                yield d
        except etree.XMLSyntaxError as e:
            print('ignoré', chemin, e)
→
Ajustar para cada base
Esses caminhos foram registrados no CASS. As bases JADE, CAPP e INCA compartilham a DTD Légifrance, mas possuem blocos específicos diferentes: abra dois ou três arquivos de cada base antes de escrever o parser e teste em uma amostra de cem decisões.

#Dividir pela estrutura da decisão, não pelo número de tokens

Uma decisão recente da Corte de Cassação segue uma estrutura reconhecível. Nos acórdãos analisados, encontram-se os títulos “Faits et procédure”, “Examen des moyens”, depois, para cada argumento, “Énoncé du moyen” e “Réponse de la Cour” e, por fim, o dispositivo introduzido por “PAR CES MOTIFS”. Dividir o texto a cada 700 tokens separa a pergunta apresentada à Corte de sua resposta e produz trechos ambíguos. Divida primeiro de acordo com esses títulos e depois subdivida apenas os blocos longos demais.

Segunda melhoria, que custa pouco e rende muito: coloque o cabeçalho (título da decisão e ECLI) no início de cada trecho. Um trecho isolado do tipo "o tribunal de apelação inverteu o ônus da prova" não informa de qual jurisdição nem de que data provém. Com o cabeçalho, tanto o modelo de embedding quanto o gerador dispõem do contexto. Para decisões mais antigas, cuja estrutura difere, recorra à divisão em parágrafos com sobreposição.

Divisão por seções com cabeçalho
import re

COUPURE = re.compile(r"\n(?=(?:Faits et procédure|Examen des moyens|Sur le |Énoncé du moyen|Enoncé du moyen|Réponse de la Cour|PAR CES MOTIFS))")

def extraits(d, max_car=2200):
    en_tete = f"{d['titre']} ({d['ecli']})\n"
    sortie, tampon = [], ''
    for bloc in COUPURE.split(d['texte']):
        for para in bloc.split('\n'):
            if len(tampon) + len(para) > max_car and tampon:
                sortie.append(en_tete + tampon)
                tampon = ''
            tampon += para + '\n'
    if tampon.strip():
        sortie.append(en_tete + tampon)
    return sortie

#Indexar com BGE-M3 e Qdrant

O BGE-M3 é um modelo de embedding multilíngue que gera vetores de 1.024 dimensões e aceita entradas de até 8.192 tokens, de acordo com sua ficha oficial. Ele lida corretamente com o francês jurídico nos usos comuns; ainda assim, avalie seu desempenho com suas perguntas. O Qdrant é adequado para armazenar os vetores com seus metadados. Seu cliente Python oferece um modo local sem servidor, mas a documentação o destina ao desenvolvimento, à prototipagem e aos testes: para várias centenas de milhares de trechos, inicie o servidor (por exemplo, com Docker).

O exemplo comum em tutoriais contém um erro silencioso: usar o índice da decisão como identificador de um ponto sobrescreve todos os trechos de uma mesma decisão, exceto o último. É necessário um identificador único por trecho. Outro detalhe: para filtrar por data, armazene um inteiro no formato AAAAMMDD nos metadados, o que permite filtrar por intervalo.

Indexação por lote
from sentence_transformers import SentenceTransformer
from qdrant_client import QdrantClient, models

emb = SentenceTransformer('BAAI/bge-m3', device='cuda')
emb.max_seq_length = 1024
client = QdrantClient(host='localhost', port=6333)
if not client.collection_exists('cass'):
    client.create_collection('cass', vectors_config=models.VectorParams(
        size=1024, distance=models.Distance.COSINE))

def indexer(dossier, taille_lot=64):
    n, lot = 0, []
    def vider():
        vecs = emb.encode([x[0] for x in lot], batch_size=32, normalize_embeddings=True)
        client.upsert('cass', points=[models.PointStruct(id=x[2], vector=v.tolist(), payload=x[1])
                                       for x, v in zip(lot, vecs)])
        lot.clear()
    for d in decisions(dossier):
        for texte in extraits(d):
            n += 1
            payload = {k: d[k] for k in ('titre', 'ecli', 'pourvoi', 'juridiction', 'formation', 'solution')}
            payload['date_int'] = int(d['date'].replace('-', ''))
            payload['texte'] = texte
            lot.append((texte, payload, n))
            if len(lot) >= taille_lot:
                vider()
    if lot:
        vider()

A busca codifica a pergunta com o mesmo modelo e solicita ao Qdrant os trechos mais próximos, eventualmente restringidos por um filtro. Os filtros por composição do órgão julgador, resultado da decisão ou data são uma verdadeira vantagem em relação a uma busca tradicional de texto completo: “câmara social, desde 2023” se traduz em duas condições sobre os metadados, e não em uma palavra a mais na consulta.

Busca filtrada
def chercher(question, depuis=None, formation=None, k=8):
    conds = []
    if depuis:
        conds.append(models.FieldCondition(key='date_int', range=models.Range(gte=depuis)))
    if formation:
        conds.append(models.FieldCondition(key='formation', match=models.MatchValue(value=formation)))
    vec = emb.encode(question, normalize_embeddings=True).tolist()
    res = client.query_points('cass', query=vec, limit=k,
                              query_filter=models.Filter(must=conds) if conds else None)
    return res.points

for p in chercher('rupture anticipée du CDD par l employeur', depuis=20230101):
    print(p.payload['titre'], p.payload['pourvoi'], round(p.score, 3))

#Por que a pesquisa semântica sozinha não é suficiente no direito

Um jurista costuma procurar elementos exatos: um número de recurso de cassação, um número de artigo, uma expressão consagrada. A similaridade semântica não os encontra bem, pois dois números próximos não têm nenhuma relação de sentido. Os autores do BGE-M3 também recomendam, na ficha do modelo, o seguinte pipeline para o RAG: busca híbrida seguida de reclassificação. Portanto, adicione uma busca lexical do tipo BM25 em paralelo aos vetores, combine as duas listas e depois passe os melhores candidatos por um modelo de reclassificação.

Em um acervo jurídico, esse acréscimo é a melhoria que mais importa depois de uma boa divisão em trechos. Os guias sobre busca híbrida e reranqueamento detalham a implementação; este guia se limita ao que é específico da jurisprudência.

#Geração, atualizações e controle de citações

Para a geração, envie ao modelo os cinco a oito melhores trechos com suas referências e exija que ele responda apenas com base nesses trechos. Um modelo de 9 bilhões de parâmetros, como Qwen 3.5 9B (6,6 GB na biblioteca Ollama), é suficiente para resumir trechos; Mistral Small 24B (14 GB) exige 16 GB de memória de vídeo. O prompt deve exigir que o modelo cite, para cada afirmação, o número do recurso de cassação e o ECLI, e responda “nenhuma decisão encontrada” quando os trechos não responderem à pergunta.

Prompt de sistema de síntese
Tu es un assistant de recherche en jurisprudence. Tu réponds uniquement à partir des
extraits fournis. Pour chaque affirmation, cite entre crochets le numéro de pourvoi
et l'ECLI de la décision. Si les extraits ne permettent pas de répondre, écris :
aucune décision retrouvée. Tu ne donnes pas d'avis juridique.

Quanto às atualizações, a DILA publica arquivos incrementais, aproximadamente uma vez por semana para CASS e INCA, conforme as listas consultadas. Um processo agendado deve baixar os que faltam, analisá-los e adicionar os trechos, com identificadores estáveis para evitar duplicações. Por fim, verifique por meio de um programa se cada referência citada na resposta está presente nos trechos fornecidos: é a mesma lógica de controle do guia sobre a análise de contratos.

#Limites a serem exibidos aos usuários

Cobertura parcial
CASS contém apenas os acórdãos publicados no Boletim, INCA os não publicados, CAPP uma seleção de decisões dos tribunais de apelação: a ausência de uma decisão na base não prova que ela não exista.
Decisões ainda não incorporadas à base ou muito recentes
Uma decisão muito recente pode ainda não estar no arquivo incremental mais recente. Confira também no Légifrance se o caso for sensível.
Evolução do direito
Um acórdão antigo pode ter sido superado por uma mudança de entendimento jurisprudencial ou uma reforma. O sistema recupera texto, mas não avalia a autoridade atual de uma solução jurídica.
Pseudonimização
Os nomes estão ocultos. Nunca busque descobrir a identidade das partes.
Nenhum parecer jurídico
A ferramenta ajuda a encontrar e citar. A qualificação dos fatos, a aplicação ao caso e a orientação jurídica continuam sendo responsabilidade do profissional.
FAQ
O que é um RAG jurídico?+
É um sistema que encontra, em uma base de decisões ou textos, trechos relacionados a uma pergunta e depois faz um modelo redigir uma resposta com base apenas nesses trechos, incluindo suas referências. Seu valor no direito reside na rastreabilidade: cada afirmação aponta para uma decisão que o profissional pode reler.
Onde encontrar a jurisprudência da Corte de Cassação em dados abertos?+
No servidor da DILA, nos arquivos CASS para os acórdãos publicados no Boletim e INCA para os não publicados, ou via a API Judilibre da Corte de Cassação. As fichas estão em data.gouv.fr. Os arquivos são simples arquivos XML, fáceis de processar localmente.
O acervo está completo?+
Não. CASS contém os acórdãos publicados no Boletim, INCA os acórdãos não publicados, CAPP uma seleção de decisões dos tribunais de apelação, JADE uma seleção para a justiça administrativa. Para um acervo mais amplo, a Corte de Cassação oferece Judilibre. Nunca afirme que uma decisão não existe porque ela não está presente em sua base.
Qual modelo de embedding escolher para o francês jurídico?+
BGE-M3 é uma escolha comum: multilíngue, com vetores de 1.024 dimensões e entradas de até 8.192 tokens, segundo sua ficha técnica. Nenhum ranking geral substitui um teste: elabore vinte perguntas de juristas com as decisões esperadas e compare o recall de vários modelos nessa amostra.
É necessária uma GPU para indexar decisões?+
Isso não é obrigatório, mas a codificação de centenas de milhares de trechos é muito mais rápida com uma placa gráfica. Sem GPU, a indexação funciona: programe-a para a noite, em lotes, e execute-a apenas uma vez. Uma vez construída a base, a busca por uma pergunta isolada continua rápida no processador, e as atualizações semanais são leves.
É possível usar essas decisões em um produto comercial?+
As fichas do data.gouv.fr indicam a licença de cada conjunto de dados, geralmente uma licença aberta que permite a reutilização com menção à fonte. A DILA lembra, no entanto, que quem reutiliza os dados continua sujeito à lei francesa Informatique et Libertés. Verifique a licença e peça ao seu encarregado de proteção de dados que valide o projeto.
Este guia ajudou você?

Um comentário, um erro ou uma observação? Avise-nos; isso ajuda a melhorar o guia para todos.