Let Anthropic API decide chunk relevance instead of local prefilter

Updated both structure_batch and reformulate_batch prompts to return null
for chunks without actionable requirements (definitions, TOCs, scope-only).
Explicit instruction to always process annexes/appendices as they often
contain concrete technical requirements.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-17 16:44:01 +01:00
parent a7f7e57dd7
commit 653aad57e3

View File

@@ -1102,12 +1102,15 @@ Gib JSON zurück mit diesen Feldern:
Du DARFST den Originaltext verwenden (Quellen sind jeweils angegeben). Du DARFST den Originaltext verwenden (Quellen sind jeweils angegeben).
{doc_context} {doc_context}
WICHTIG: WICHTIG:
- Erstelle fuer JEDEN Chunk ein separates Control mit verstaendlicher, praxisorientierter Formulierung. - Pruefe JEDEN Chunk: Enthaelt er eine konkrete Pflicht, Anforderung oder Massnahme?
- Wenn JA: Erstelle ein vollstaendiges, eigenstaendiges Control mit praxisorientierter Formulierung.
- Wenn NEIN (reines Inhaltsverzeichnis, Begriffsbestimmung ohne Pflicht, Geltungsbereich ohne Anforderung, reine Verweiskette): Gib null fuer diesen Chunk zurueck.
- BEACHTE: Anhaenge/Annexe enthalten oft KONKRETE technische Anforderungen — diese MUESSEN als Control erfasst werden!
- Jedes Control muss eigenstaendig und vollstaendig sein — nicht auf andere Controls verweisen. - Jedes Control muss eigenstaendig und vollstaendig sein — nicht auf andere Controls verweisen.
- Qualitaet ist wichtiger als Geschwindigkeit. Jedes Control muss die gleiche Qualitaet haben wie ein einzeln erstelltes. - Qualitaet ist wichtiger als Geschwindigkeit.
- Antworte IMMER auf Deutsch. - Antworte IMMER auf Deutsch.
Gib ein JSON-Array zurueck mit GENAU {len(chunks)} Objekten. Jedes Objekt hat diese Felder: Gib ein JSON-Array zurueck mit GENAU {len(chunks)} Elementen. Fuer Chunks ohne Anforderung gib null zurueck. Fuer Chunks mit Anforderung ein Objekt mit diesen Feldern:
- chunk_index: 1-basierter Index des Chunks (1, 2, 3, ...) - chunk_index: 1-basierter Index des Chunks (1, 2, 3, ...)
- title: Kurzer praegnanter Titel auf Deutsch (max 100 Zeichen) - title: Kurzer praegnanter Titel auf Deutsch (max 100 Zeichen)
- objective: Was soll erreicht werden? (1-3 Saetze, Deutsch) - objective: Was soll erreicht werden? (1-3 Saetze, Deutsch)
@@ -1131,7 +1134,12 @@ Gib ein JSON-Array zurueck mit GENAU {len(chunks)} Objekten. Jedes Objekt hat di
# Map results back to chunks by chunk_index (or by position if no index) # Map results back to chunks by chunk_index (or by position if no index)
controls: list[Optional[GeneratedControl]] = [None] * len(chunks) controls: list[Optional[GeneratedControl]] = [None] * len(chunks)
skipped_by_api = 0
for pos, data in enumerate(results): for pos, data in enumerate(results):
# API returns null for chunks without actionable requirements
if data is None:
skipped_by_api += 1
continue
# Try chunk_index first, fall back to position # Try chunk_index first, fall back to position
idx = data.get("chunk_index") idx = data.get("chunk_index")
if idx is not None: if idx is not None:
@@ -1195,15 +1203,19 @@ Gib ein JSON-Array zurueck mit GENAU {len(chunks)} Objekten. Jedes Objekt hat di
f"Text (nur zur Analyse, NICHT kopieren, NICHT referenzieren):\n{chunk.text[:1500]}" f"Text (nur zur Analyse, NICHT kopieren, NICHT referenzieren):\n{chunk.text[:1500]}"
) )
joined = "\n\n".join(chunk_entries) joined = "\n\n".join(chunk_entries)
prompt = f"""Analysiere die folgenden {len(chunks)} Pruefaspekte und formuliere fuer JEDEN ein EIGENSTAENDIGES Security Control. prompt = f"""Analysiere die folgenden {len(chunks)} Pruefaspekte und formuliere fuer JEDEN mit konkreter Anforderung ein EIGENSTAENDIGES Security Control.
KOPIERE KEINE Saetze. Verwende eigene Begriffe und Struktur. KOPIERE KEINE Saetze. Verwende eigene Begriffe und Struktur.
NENNE NICHT die Quellen. Keine proprietaeren Bezeichner (kein O.Auth_*, TR-03161, BSI-TR etc.). NENNE NICHT die Quellen. Keine proprietaeren Bezeichner (kein O.Auth_*, TR-03161, BSI-TR etc.).
WICHTIG: WICHTIG:
- Pruefe JEDEN Aspekt: Enthaelt er eine konkrete Pflicht, Anforderung oder Massnahme?
- Wenn JA: Erstelle ein vollstaendiges, eigenstaendiges Control.
- Wenn NEIN (reines Inhaltsverzeichnis, Begriffsbestimmung ohne Pflicht, Geltungsbereich ohne Anforderung): Gib null fuer diesen Aspekt zurueck.
- BEACHTE: Anhaenge/Annexe enthalten oft KONKRETE technische Anforderungen — diese MUESSEN erfasst werden!
- Jedes Control muss eigenstaendig und vollstaendig sein — nicht auf andere Controls verweisen. - Jedes Control muss eigenstaendig und vollstaendig sein — nicht auf andere Controls verweisen.
- Qualitaet ist wichtiger als Geschwindigkeit. Jedes Control muss die gleiche Qualitaet haben wie ein einzeln erstelltes. - Qualitaet ist wichtiger als Geschwindigkeit.
Gib ein JSON-Array zurueck mit GENAU {len(chunks)} Objekten. Jedes Objekt hat diese Felder: Gib ein JSON-Array zurueck mit GENAU {len(chunks)} Elementen. Fuer Aspekte ohne Anforderung gib null zurueck. Fuer Aspekte mit Anforderung ein Objekt mit diesen Feldern:
- chunk_index: 1-basierter Index des Aspekts (1, 2, 3, ...) - chunk_index: 1-basierter Index des Aspekts (1, 2, 3, ...)
- title: Kurzer eigenstaendiger Titel (max 100 Zeichen) - title: Kurzer eigenstaendiger Titel (max 100 Zeichen)
- objective: Eigenstaendige Formulierung des Ziels (1-3 Saetze) - objective: Eigenstaendige Formulierung des Ziels (1-3 Saetze)
@@ -1225,6 +1237,8 @@ Gib ein JSON-Array zurueck mit GENAU {len(chunks)} Objekten. Jedes Objekt hat di
controls: list[Optional[GeneratedControl]] = [None] * len(chunks) controls: list[Optional[GeneratedControl]] = [None] * len(chunks)
for pos, data in enumerate(results): for pos, data in enumerate(results):
if data is None:
continue
idx = data.get("chunk_index") idx = data.get("chunk_index")
if idx is not None: if idx is not None:
idx = int(idx) - 1 idx = int(idx) - 1