Ein Monitoringsystem und Orchenstrator für LLM-as-a-Judge und LLMs-as-a-Jury.
Die Idee dahinter: Statt einen menschlichen Reviewer jeden Output von eigenen oder fremden LLMs prüfen zu lassen, übernimmt ein anderes LLM diese Aufgabe — und dies nach vorher definierten Metriken und Regeln. Das skaliert, ist reproduzierbar und macht Bewertungskriterien explizit statt implizit.
Anstelle für jeden Use-Case wieder ein Script zu schreiben, das das Judgement gegen irgendwelche LLMs abbildet und auswertungen erzeugt, gießt JudgeForge das in einer flexible Webapp.
JudgeForge ist eine selbst gehostete Plattform die alles mitbringt um die Qualität von Botantworten regelmäßig, kontinuiertlich zu testen.
Also
Vier Container, klare Aufgabenteilung:
| Container | Aufgabe |
|---|---|
| postgres | Single Source of Truth. Alle Tabellen, alle Daten. |
| n8n | Judge-Engine. Liest pending-Items, ruft das LLM, schreibt Scores zurück. |
| api | Fastify + Drizzle ORM. Ingest, CRUD, Analytics, Import. |
| web | React + Vite via Nginx. Dashboard, Explorer, Verwaltung. |
Das Datenmodell ist generisch (angelehnt an Langfuse): metrics (typisiert: numeric, boolean, categorical, text) → polymorphe scores je Item und Metrik. Dazu runs, run_items, rubrics, datasets, human_reviews.
n8n läuft als Sub-Workflow-Pattern: ein Main-Workflow (Schedule + Claim) fan-outet in N Sub-Calls — einen pro Judge. Das erlaubt Multi-Judge-Parallelisierung ohne den Orchestrierungs-Workflow anzufassen.
Was soll das überhaupt mit n8n? Warum nicht alles direkt hardcoded in der Software?
→ JudgeForge will die implementierung z.B. der LangChain Funktionalitäten, LLM Credentials, Endpoints usf. nicht übernehmen, die n8n ohnehin ständig updated. Außerdem ermöglicht man es so dort auch übersichtlich die Workflows zu beobachten, zu debuggen und anpassen zu können wenn man das möchte.
Spätere Versionen von JudgeForge werden diese Architekturentscheidung immer wieder auf die Probe stellen und ggf. anpassen.
Drei Ingest-Wege:
POST /api/ingest mit API-Key-Header — maschinell, z.B. direkt aus der Anwendung die bewertet werden sollJedes Item landet als pending in der Datenbank. Der Judge-Scheduler claimt Items im 1-Minuten-Takt (FOR UPDATE SKIP LOCKED, damit mehrere Worker parallel laufen können) und bewertet sie.
Metriken sind der Kern der Plattform — hier legt man fest, wonach bewertet wird. Jede Metrik hat:
numeric (0..1-Skala), boolean (Pass/Fail), categorical, textrequires_reference: Braucht der Judge eine Goldantwort zum Vergleich?requires_context: Braucht er zusätzlichen Kontext?judge_instructions: Bewertungsanleitung direkt im Prompt — der Judge referenziert sie im Reasoningexplanation: Laienverständliche Beschreibung, fließt automatisch ins FAQ der AppNeue Metriken werden sofort vom Judge genutzt — sie hängen automatisch an der Default-Rubrik.
Das interessanteste Feature: Mehrere Judges parallel pro Frage. Jeder Judge ist einzeln konfigurierbar (Name, Modell, Provider). Die Scores werden je Metrik aggregiert (Strategien: mean, majority, min/max — je nach Metrik-Typ sinnvoll).
Warum das nützlich ist: Wenn ein Judge bei einer Metrik dauerhaft ausbricht, hat entweder die Metrik-Definition ein Problem oder der Provider hat Bias. Das Panel macht das sichtbar.
Drei Wege, das Panel für ein Item zu aktivieren:
Multi-Provider im Sub-Workflow: ein Switch-Node routet nach judge_provider auf vier Branches (Azure / Mistral / Anthropic / Gemini), jeder Branch hat seinen eigenen LLM-Knoten. Modell-Hot-Swap über judge_config — kein Workflow-Edit nötig.
Jeder Re-Judge schreibt einen neuen Score-Row, kein Überschreiben. Auf jedem Score-Eintrag stecken:
judge_prompt_hash (djb2) — erkennt wenn sich der System-Prompt geändert hatmetric_schema_version — bumped bei strukturellen Metrik-Editspending_trigger — warum wurde re-bewertet? (gold_edit, context_edit, manual_requeue)Im Explorer gibt es einen History-View pro Item: LineChart über alle Re-Judges, vertikale Marker bei Schema-Bump (gelb) und Prompt-Change (amber). Damit sieht man sofort ob eine Metrik-Anpassung die Scores verschoben hat.
/docsSelf-contained — kein externer DB-Dienst, kein manuelles Seeding.
# Volumes anlegen
docker volume create judgeforge_pgdata
docker volume create judgeforge_n8ndata
# Repo klonen, Secrets setzen
cp .env.example .env # APP_PASSWORD, INGEST_API_KEY, SESSION_SECRET, DATABASE_URL
docker compose up -d --build
Beim ersten Start: Postgres legt die DB an, die API fuehrt Migrationen und Seed aus (idempotent). Danach n8n einmalig manuell einrichten: Owner-Account, Postgres-Credential (intern ueber Service-Name postgres erreichbar), LLM-Credential, Workflow importieren.
Update:
git pull && docker compose up -d --build
Ports (alle per .env konfigurierbar): Web 8080, API 3000, n8n 5678, Postgres 5432.
Fuer private Repos auf Firmen-Servern: SSH Deploy Key mit Port 443 statt 22 — typische Unternehmens-Firewalls sperren Port 22 nach aussen.
Die DATABASE_URL in der .env bestimmt, gegen welche Datenbank die API laeuft. Umschalten = URL aendern, Stack neu starten.
H@ppy H@cking 🤖⚖️