feat(control-pipeline): incremental dedup + ENISA CRA ingestion
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-consent (push) Successful in 43s
CI / test-python-voice (push) Successful in 33s
CI / test-bqas (push) Successful in 37s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-consent (push) Successful in 43s
CI / test-python-voice (push) Successful in 33s
CI / test-bqas (push) Successful in 37s
BatchDedup since-Parameter (services/batch_dedup_runner.py + api): - Neuer 'since: datetime' Param scoped Phase 1 + Phase 2 SQL auf created_at >= since. - Phase 2 checkpoint wird beim scoped Lauf geloescht (verhindert Skip neuer Atomics deren control_id alphabetisch unter dem stale last_id liegt). - 6-13x schneller fuer nachgeschobene Dokumente (19k statt 172k Atomics). - Doku: control-pipeline/docs/incremental-dedup.md. Neue Scripts: - gpre1_object_groups_incremental.py: Append neuer Objects an object_groups via bge-m3 nearest-neighbor (threshold default 0.85, empfehlbar 0.78 fuer breiteres Synonym-Matching). Pure INSERT/UPDATE, kein DELETE. - gpre2_master_controls_incremental.py: Non-destructive Master-Controls-Update. Existing MCs unangetastet (UUIDs + master_control_id bleiben), nur neue Members appended + neue MCs fuer Object-Groups die jetzt min-phases erreichen. - ingest_enisa_cra.py: Ingestion der 8 CRA-relevanten ENISA-Dokumente (Standards Mapping, EUCC-Implementation, NIS2 TIG, SRP FAQ, EUCC Eval Methodology, CVD Policies, Threat Landscape 2025). chunk_strategy=legal, requirement_strength=guidance|consultation_draft|evidentiary. Quelldaten: legal-sources/enisa/enisa_cra_single_reporting_platform_faq.html (PDFs sind .gitignore-gefiltert). Ergebnis dieser Pipeline-Iteration: - 1.296 neue CRA-Controls + 19.652 atomare Children - +362 neue Master-Controls, 10.017 existing erweitert - Total: 13.950 MCs, 620 CRA-MCs (vorher 566), 1.304 CRA-Atomics (vorher 841) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
# Incremental BatchDedup für nachgeschobene Dokumente
|
||||
|
||||
Eingefuehrt am 2026-05-18. Pattern fuer alle zukuenftigen Einzeldokument-Ingestionen.
|
||||
|
||||
## Problem
|
||||
|
||||
Der Default-BatchDedup-Runner lief gegen ALLE `pass0b` Atomics ohne Filter
|
||||
(WHERE decomposition_method = 'pass0b' AND release_state NOT IN ('deprecated','duplicate')).
|
||||
Das sind bei uns ~172k Controls. Pace ~5k/h → 25-40h Laufzeit. Bei jedem
|
||||
hinzugefuegten Dokument der gleiche volle Lauf — auch wenn das neue Dokument
|
||||
nur 1-2k Atomics erzeugt.
|
||||
|
||||
Zusaetzliches Risiko: Phase 1 schreibt master_controls erst am Ende. Ein
|
||||
Container-Crash mitten im Lauf (z.B. via Qdrant-Timeout) verwirft 100%
|
||||
des In-Memory-Fortschritts.
|
||||
|
||||
## Loesung — `since` Parameter
|
||||
|
||||
`POST /v1/canonical/generate/batch-dedup` akzeptiert jetzt:
|
||||
|
||||
```json
|
||||
{
|
||||
"dry_run": false,
|
||||
"since": "2026-05-18T02:53:00+00:00"
|
||||
}
|
||||
```
|
||||
|
||||
Effekt:
|
||||
- Phase 1 (intra-group dedup) laedt nur Controls mit `created_at >= since`
|
||||
- Phase 2 (cross-group dedup) filtert ebenfalls auf `created_at >= since`
|
||||
- Phase 2 Checkpoint wird vor Lauf-Start geloescht (sonst skippt stale
|
||||
`last_control_id` neu erzeugte Atomics deren control_id alphabetisch
|
||||
davor liegt)
|
||||
|
||||
Phase 2 sucht weiter im **vollen** Qdrant-Index `atomic_controls_dedup`,
|
||||
findet also Matches zu alten Master Controls und verlinkt korrekt.
|
||||
|
||||
## Wann verwenden
|
||||
|
||||
| Szenario | Empfehlung |
|
||||
|---|---|
|
||||
| Einzelnes neues Dokument ingestiert + Pass 0a + Pass 0b durchgelaufen | `since` setzen auf Zeitpunkt vor Pass 0b |
|
||||
| Mehrere kleine Updates seit letztem Full-Dedup | `since` setzen auf Zeitpunkt nach letztem Full-Dedup |
|
||||
| Initial-Setup oder Pipeline-Major-Update | KEIN `since` — full run |
|
||||
| Verdacht auf Drift / Quality-Regression | KEIN `since` — full run |
|
||||
|
||||
## Workflow nach Einzeldokument-Ingestion
|
||||
|
||||
```bash
|
||||
# 1. Pass 0a auf neue Controls (Obligations extrahieren)
|
||||
curl -X POST .../v1/canonical/generate/run-pass0a -d '{...}'
|
||||
|
||||
# 2. Pass 0b Decomposition Submit (Atomics erzeugen)
|
||||
curl -X POST .../v1/canonical/generate/submit-pass0b -d '{...}'
|
||||
|
||||
# 3. Wenn Anthropic Batch durch: process-batch
|
||||
curl -X POST .../v1/canonical/generate/process-batch -d '{
|
||||
"batch_id": "msgbatch_...",
|
||||
"pass_type": "0b"
|
||||
}'
|
||||
|
||||
# 4. Inkrementell deduppen (NEU, statt 25h full run)
|
||||
curl -X POST .../v1/canonical/generate/batch-dedup -d '{
|
||||
"dry_run": false,
|
||||
"since": "<ISO-Datetime kurz vor Pass-0b-Start>"
|
||||
}'
|
||||
```
|
||||
|
||||
## Pace-Beobachtung (CRA-Lauf 2026-05-18)
|
||||
|
||||
- Total neue Atomics: 19.423
|
||||
- Phase 1 multi-groups: 568 (Rest 18.101 sind Singletons → direkt Master)
|
||||
- Phase 2 Cross-Group: ~3-4h erwartet
|
||||
- Vergleich: Full-Run waere 25-40h gewesen, scoped 6-13x schneller.
|
||||
|
||||
## Implementation-Details (fuer Wartung)
|
||||
|
||||
Geaenderte Dateien:
|
||||
- `services/batch_dedup_runner.py` — `run()` + `_load_merge_groups()` +
|
||||
`_run_cross_group_pass()` SQL-Queries
|
||||
- `api/control_generator_routes.py` — `BatchDedupRequest.since` Feld +
|
||||
Handler reicht durch
|
||||
|
||||
Backwards-kompatibel: ohne `since` aequivalent zum alten Verhalten.
|
||||
|
||||
## Bekannte Limits
|
||||
|
||||
1. **Phase 2 Checkpoint wird beim scoped Lauf geloescht.** Wenn waehrend
|
||||
eines `since`-Laufs ein voller Run dazwischen geschoben werden soll
|
||||
(sollte nicht passieren), muss neu starten.
|
||||
2. **Phase 1 commit-Granularitaet nicht angefasst.** Bei Crash mitten in
|
||||
Phase 1 ohne `since` bleibt der Verlust gleich. Aber: scoped Phase 1
|
||||
ist so kurz (Minuten), dass das praktisch egal ist.
|
||||
3. **Singleton-Atomics werden direkt Master ohne Cross-Check.** Wenn ein
|
||||
neues Singleton-Atomic semantisch identisch zu einem alten Master
|
||||
ist, faengt das nur Phase 2 (via Qdrant). Funktioniert solange Phase 2
|
||||
nicht uebersprungen wird (dry_run=false ist Pflicht).
|
||||
|
||||
## Memory-Eintrag
|
||||
|
||||
Siehe `~/.claude/projects/-Users-benjaminadmin-Projekte-breakpilot-core/memory/feedback_incremental_dedup.md`
|
||||
Reference in New Issue
Block a user