Legal RAG bei ARLC 2026: Von 0,034 auf 0,791 in 17 Iterationen mit Skalierungsanalyse
Bei der ARLC 2026-Herausforderung im juristischen Bereich startete die RAG-Pipeline mit einer Grundierungsscore von 0,05 und einem Gesamtscore von 0,034. Deterministische Antworten (S_det) lagen bereits bei 0,857, doch der G-Faktor neutralisierte die Ergebnisse. Der Fehler wurde auf das Format des doc_id zurückgeführt: submission.json erwartete den Dateinamen ohne '.pdf', während der Code den vollständigen Namen übermittelte. Die Korrektur einer einzigen Zeile erhöhte den G-Faktor auf 0,55 und den Gesamtscore auf 0,438 – eine Verbesserung um das 13-fache.
Lernpunkt für RAG-Entwickler: Die Validierung des Submission-Formats muss vor der Optimierung der Retrieval-Phase erfolgen. Selbst perfektes Reranking ist nutzlos, wenn keine korrekte Zuordnung zu (doc_id, page_number) stattfindet.
Pipeline-Architektur: Seitenebene Ingestion und hybrider Suchansatz
Die Pipeline indexiert ganze Dokumentenseiten, wodurch Chunking entfällt. Dies vereinfacht die Seitenebene Grundierung, erfordert jedoch eine Kontext-Destillation vor der LLM-Verarbeitung.
PDF-Parser mit OCR-Fallback
def extract_pages(pdf_path, min_chars=50, ocr_dpi=300):
pages = []
doc = pdfplumber.open(pdf_path)
for i, page in enumerate(doc.pages):
text = page.extract_text() or ""
if len(text.strip()) < min_chars:
text = ocr_page(pdf_path, i + 1, dpi=ocr_dpi)
pages.append({
"doc_id": pdf_path.stem, # ohne .pdf!
"page_number": i + 1,
"text": text
})
return pages
Der Code verarbeitet gescannte PDFs mittels OCR, falls der extrahierte Text minimal ist. doc_id verwendet den Stamm ohne Erweiterung; page_number ist 1-basiert.
Hybride Suche mit RRF
Kombination von BM25 für exakte Übereinstimmungen und Embeddings für Semantik mittels Reciprocal Rank Fusion (k=60):
def hybrid_search(query, bm25_index, embedding_index, pages, top_k=20, rrf_k=60):
bm25_scores = bm25_index.get_scores(tokenize(query))
bm25_ranked = sorted(range(len(pages)), key=lambda i: -bm25_scores[i])
q_emb = embed_model.encode([query[:512]], normalize_embeddings=True)
sim_scores = (embedding_index @ q_emb.T).flatten()
emb_ranked = sorted(range(len(pages)), key=lambda i: -sim_scores[i])
combined = {}
for rank, idx in enumerate(bm25_ranked[:top_k * 3]):
combined[idx] = combined.get(idx, 0) + 1.0 / (rrf_k + rank)
for rank, idx in enumerate(emb_ranked[:top_k * 3]):
combined[idx] = combined.get(idx, 0) + 1.0 / (rrf_k + rank)
return sorted(combined, key=lambda i: -combined[i])[:top_k]
Embedding-Modell: all-MiniLM-L6-v2 (22 M Parameter). RRF benötigt keine Skalierungsnormierung – es arbeitet direkt auf Rängen.
Reranking und Prioritätstags
Ein Cross-Encoder (ms-marco-MiniLM-L-6-v2) reranket die Top-30-Seiten. Eine feste Obergrenze von 30 verhindert einen O(N²)-Anstieg der TTFT.
def rerank(pages, query, top_k=5):
HARD_CAP = 30
priority = [p for p in pages if p.get("_priority")]
non_priority = pages[:HARD_CAP - len(priority)]
all_pages = priority + non_priority
pairs = [(query, p["text"][:512]) for p in all_pages]
scores = cross_encoder.predict(pairs)
for i, p in enumerate(all_pages):
if p.get("_priority"):
scores[i] = 1000.0
ranked = sorted(zip(scores, all_pages), key=lambda x: -x[0])
return [p for _, p in ranked[:top_k]]
Prioritätsseiten (Titelseiten, Artikel) erhalten einen Score von 1000 – garantierte Aufnahme im Kontext.
Dokumentenrouting für Vergleichsfragen
Für Fragen wie »Wer wurde früher in CFI 001/2020 gegenüber CFI 002/2021 ernannt?« wird ein Index aus Titelseiten erstellt:
- Muster: CFI 010/2024 → doc_id
- Doppelte Abfrage mit Round-Robin-Interleaving von Kontexten
Deterministische Fast-Paths für typisierte Antworten
Typen: Zahl (±1 %), boolesch, Name (exakt), Datum (YYYY-MM-DD), Namen (Jaccard), freitext (LLM-Judge).
Beispiele für Regex-Extraktion:
- Gesetzesnummer:
re.search(r"(?:DIFC\s+)?Law\s+No\.\s*(\d+)") - Ausgabedatum: erste Seiten parsen mittels
Date of Issue[:\s]+(\w+ \d+,?\s+\d{4}) - Beträge: monetäre Ansprüche über case_ref extrahieren
Fast-Paths sparen 700 ms bei Haiku-Aufrufen und liefern Antwort + Grundierung.
Bewertungsformel und TTFT-Optimierung
Total = (0,7 × S_det + 0,3 × S_asst) × G × T × F
- G (F-beta, β=2,5): Seitenebene Grundierung – der entscheidende Multiplikator
- F: <1000 ms → +5 %, >3000 ms → -15 %
| TTFT (ms) | Multiplikator |
|-----------|------------|
| <1000 | 1,05 |
| <2000 | 1,02 |
| <3000 | 1,00 |
| >3000 | 0,85–0,99 |
Skalierungswand: Warmup vs. Final
Bei 30 Dokumenten (Warmup) lag der Score bei 0,791. Bei 300 (Final) sank er um 42 %. Gründe:
- Größere Korpus verringert die Retrieval-Qualität
- TTFT steigt linear mit der Größe
- Reranking bei 300+ Seiten schadet F
Endgültige Optimierungen: Caching von Embeddings, Batch-Verarbeitung beim Cross-Encoder, Early Stopping in der hybriden Suche.
Das Entscheidende
- Seitenebene Indexierung: vereinfacht die Grundierung, erfordert aber Kontext-Destillation.
- RRF (k=60): optimale Fusion von BM25 + Embeddings ohne Normierung.
- Prioritätstags: sicherstellt, dass kritische Seiten immer im Kontext sind.
- Fast-Paths: Regex für 80 % der typisierten Abfragen reduziert die TTFT signifikant.
- TTFT-first-Ansatz: Geschwindigkeit bringt kostenlos +5 % Bonus bei gleichbleibender Qualität.
— Editorial Team
Noch keine Kommentare.