13 KiB
13 KiB
NSCT — Architekturdokumentation
1. Überblick
NSCT (Neutral Search Crawler Tool) ist ein modulares, containerisiertes Recherchesystem. Es durchsucht Webquellen, extrahiert Inhalte, analysiert Behauptungen (Claims) und erzeugt einen neutralen, quellengestützten Bericht.
Kernprinzip: Webcontent ist DATA, keine INSTRUCTION. Der LLM verarbeitet nur strukturierte Daten, niemals rohen Webcontent direkt als Prompt.
Stage 0–22 abgeschlossen. NSCT ist production-ready.
2. Architektur-Diagramm
┌─────────────────────────────────────────────────────────────────────┐
│ Control Plane │
│ ┌──────────┐ ┌───────────┐ ┌────────────┐ ┌─────────────────┐ │
│ │ CLI │ │ REST API │ │ WebSocket │ │ Event Bus │ │
│ └────┬─────┘ └─────┬─────┘ └─────┬──────┘ └────────┬────────┘ │
│ │ │ │ │ │
│ └──────────────┴─────────────┴───────────────────┘ │
│ │
│ ┌───────────────────────Evidence Pipeline───────────────────────┐ │
│ │ │ │
│ │ ┌─────────┐ ┌──────────┐ ┌───────────┐ ┌───────────┐ │ │
│ │ │ Search │──▶│ Fetch │──▶│ Extract │──▶│ Classify │ │ │
│ │ │ (DDG/ │ │(HTTP/ │ │(Trafilat │ │(LLM/Rule)│ │ │
│ │ │ Multi) │ │ Playright)│ │ura/BS4) │ │ │ │ │
│ │ └─────────┘ └──────────┘ └───────────┘ └─────┬─────┘ │ │
│ │ │ │ │
│ │ ┌─────────┐ ┌───────────┐ ┌────────────┐ ┌───┴─────┐ │ │
│ │ │ Index │◀──│ Compare │◀──│ Normalise │◀──│ Claim │ │ │
│ │ │ & Store │ │ (LLM) │ │ & Parse │ │ Ext. │ │ │
│ │ └─────────┘ └─────┬─────┘ └────────────┘ └─────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌──────────────┐ │ │
│ │ │Evidence │ │ │
│ │ │Scoring (6D) │ │ │
│ │ └──────┬───────┘ │ │
│ │ ▼ │ │
│ │ ┌──────────────┐ │ │
│ │ │ Report Gen. │ │ │
│ │ │ (LLM) │ │ │
│ │ └──────┬───────┘ │ │
│ │ ▼ │ │
│ │ ┌──────────────┐ │ │
│ │ │Provenance │ │ │
│ │ │+ Hash │ │ │
│ │ └──────────────┘ │ │
│ └──────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
3. Datenfluss
Query ──▶ SearchProvider.search() ──▶ List[SearchResult]
│
│ (for each result)
│
▼
ContentFetcher.fetch(url) ──▶ Source document (parsed)
│
│ (LLM: extract claims from source)
│
▼
Claim[] ──▶ Normalization ──▶ NormalizedClaim[]
│
│ (LLM: compare claims pairwise)
│
▼
EvidenceRelation[] ──▶ Agreement matrix
│
│ (6-dim evidence scoring)
│
▼
EvidencePackage ──▶ Neutral Synthesis
│
│ (LLM: synthesize report)
│
▼
ResearchReport (Summary + Findings + Disagreements + Uncertainties)
│
▼
research_run_hash (deterministic, reproducible)
4. Module
4.1 src/nsct/api/ — REST API
- rest_research.py: Research CRUD — POST/GET/DELETE + status, sources, claims, evidence, report
- health.py:
/health,/ready,/providersendpunkte - search.py: Search API endpoint
- debug.py: Debug-Endpunkte (nur bei
NSCT_DEBUG=true) - Pattern: Each router is a separate file; mounted in
main.py
4.2 src/nsct/models/ — Pydantic v2 Schemas
- schemas.py: Data transfer objects (SearchQuery, Source, Claim, etc.)
- plan.py: ResearchPlan Schema
- claim.py: Claim Schema mit Provenance
- synthesis.py: Synthese Report Schema
- source_independence.py: Independence Graph Schema
- gap_analysis.py: GapAnalysis Schema
- audio.py: Audio Transcription Schema
- vision.py: Vision Analysis Schema
- Pure Pydantic — no database or API coupling
4.3 src/nsct/storage/ — Database Layer
- models.py: SQLAlchemy 2.0 declarative models
- engine.py: Async engine factory with pool management
- Uses
asyncpgfor PostgreSQL
4.4 src/nsct/providers/ — Provider Interfaces
- abstract.py: Abstract base classes (LLMProvider, VisionProvider, SearchProvider, etc.)
- llm.py: OpenAI-kompatibler LLM Provider
- duckduckgo.py: DuckDuckGo Search Provider
- multi.py: MultiProviderSearch für parallele Abfrage
- vision.py: Qwen2.5-VL-3B Vision Provider
- audio.py: Audio/STT Provider
- metrics.py: Provider-Metriken Tracking
- priority_queue.py: Priority-Queue mit HIGH/NORMAL/LOW Prioritäten
- semaphore.py: Concurrency Semaphore (default: 3)
- No concrete implementations in Stage 0 — just protocols
- All providers accept structured data, never raw web content
4.5 src/nsct/security/ — Security
- policy.py: SSRF protection, URL validation, IP blocklisting
- Called before every outbound HTTP request
4.6 src/nsct/config.py — Configuration
- Pydantic BaseSettings — all values from environment
- Zero hard-coded secrets or URLs
AppSettings.from_env()creates the root configuration
4.7 src/nsct/logging_config.py — Structured Logging
- JSON-formatted log output
- Per-request context tracking (
research_id,llm_request_id) - Global context dict merged into every log record
4.8 src/nsct/metrics.py — Prometheus Metrics
- Counters: search queries, sources fetched, claims, contradictions, completed/failed
- Histograms: research duration
- Gauges: active research runs
4.9 src/nsct/agents/ — Research Planning
- planner.py: LLM-basierte Recherchestrategie (Query Expansion, Bias Reduction)
- validator.py: Plausibilitäts-Prüfung von Research-Plänen
4.10 src/nsct/crawler/ — Content Extraction
- fetcher.py: Asynchroner HTTP-Fetcher mit SSRF-Schutz
- extraction.py: Main Content Extraction (trafilatura, BeautifulSoup)
- normalize.py: Dokumenten-Normalisierung
- pdf.py: PDF-Extraktion
- policy.py: SSRF/Download-Policy
- manager.py: Batch-Verwaltung
4.11 src/nsct/orchestration/ — Pipeline Control
- state.py: State Machine mit 12 Zuständen
- budget.py: Hard Budget Limits (7 config options, frozen)
- models.py: ResearchRun Pydantic Model (frozen, immutable)
- orchestrator.py: Vollständige Pipeline-Steuerung mit Fallbacks
- context_budget.py: Pro-Stage Context Token Limits
- gap_analysis.py: Lückenerkennung für iterative Recherche
4.12 src/nsct/stages/ — Pipeline Stages (5-13)
- stage5_extract_claims.py: Claim Extraction
- stage6_source_independence.py: Source Independence Graph
- stage7_clustering.py: Claim Clustering
- stage7_normalize_numerics.py: Numerical Normalization
- stage8_evidence_scoring.py: 6-dimensional Evidence Scoring
- stage9_synthesis.py: Neutral Synthesis
- stage10_vision.py: Vision Integration
- stage11_audio.py: Audio/STT Integration
- stage13_gap_analysis.py: Iterative Gap Analysis
4.13 src/nsct/provenance.py — Provenance & Reproducibility
- Stage 21: Vollständige Provenance aller Pipeline-Schritte
research_run_hash: Deterministischer Hash für Reproduzierbarkeit
4.14 src/nsct/cli.py — Command Line Interface
- Stage 15:
nsct research,nsct status,nsct report, etc.
5. Interfaces / Protocols
# src/nsct/providers/__init__.py
class LLMProvider(ABC):
async def generate(prompt, system_prompt=None, ...) -> LLMResponse: ...
class VisionProvider(ABC):
async def analyze_image(image_bytes, prompt) -> VisionResponse: ...
class AudioProvider(ABC):
async def transcribe(audio_bytes) -> AudioResponse: ...
class SearchProvider(ABC):
async def search(query, language, limit) -> list[SearchResult]: ...
class ContentFetcher(ABC):
async def fetch(url, **kwargs) -> dict: ...
Alle konkreten Implementationen (DuckDuckGo, Qwen, Trafilatura, etc.) müssen diese Interfaces implementieren — das ermöglicht den Wechsel von Providern ohne Codeänderung im Core.
6. Sicherheitsarchitektur
- Control Plane vs. Evidence Plane: Der LLM verarbeitet nur strukturierte Daten (Claims, NormalizedClaims, EvidenceRelations), niemals rohen Webcontent.
- SSRF-Schutz: Jede outbound-URL wird vor dem Request durch
validate_url()geprüft. - Prompt-Isolation: Webcontent wird niemals direkt als Prompt text eingebettet. Stattdessen wird er in strukturierte JSON-Objekte serialisiert.
- Non-Root-Docker: Der Container läuft als nicht-root User
nsct. - Read-Only-Filesystem: Wo möglich (
read_only: true+tmpfs). - Dropped Capabilities:
cap_drop: [ALL]
7. Datenmodell
Core Tables
| Table | Beschreibung |
|---|---|
search_queries |
Research-Aufträge mit Query, Purpose, Language |
sources |
Extrahierte Webquellen mit Metadaten |
claims |
Behauptungen aus Quellen mit Typ, Konfidenz, Evidence |
evidence_relations |
Vergleichsergebnisse zwischen Claims |
citation_edges |
Quelle-zu-Quelle Referenzen |
research_reports |
Aggregierte Forschungsberichte |
Evidence Scoring (Stage 8)
6 dimensionale Scores für jede Evidenz:
| Dimension | Range | Beschreibung |
|---|---|---|
source_independence |
0–1 | Wie unabhängig ist diese Quelle? |
primary_source_proximity |
0–1 | Wie nah ist die Quelle an der Primärquelle? |
cross_source_support |
0–1 | Wie viele unabhängige Quellen bestätigen? |
contradiction_level |
0–1 | Wie hoch ist der Widerspruch? (invertiert) |
evidence_directness |
0–1 | Wie direkt ist die Evidenz? |
date_relevance_score |
0–1 | Wie aktuell ist die Evidenz? |
8. Nicht-funktionale Anforderungen
| Kriterium | Anforderung |
|---|---|
| Concurrency | Async I/O konsequent (async/await everywhere) |
| Database | PostgreSQL 16+, asyncpg, SQLAlchemy 2.0 |
| Logging | Strukturiert (JSON), jede Anfrage tracebar |
| Config | Environment-only, keine Config-Dateien |
| Testing | pytest-asyncio, FastAPI TestClient, E2E-Tests |
| Deployment | Docker Compose, reproduzierbar |
| LLM Concurrency | Konfigurierbar (NSCT_LLM_MAX_CONCURRENCY), default: 3 |
| Context Budgeting | Pro-Stage Limits (Planner→Claim→Contradiction→Synthesis) |
| Reproducibility | research_run_hash + vollständige Provenance |