
Privacy Router mit OpenClaw bauen: Ein Praxis-Guide mit Code
TL;DR: „Ein Privacy Router braucht drei Dinge: eine Policy-Engine, die Sensitivität klassifiziert, einen lokalen Modell-Pfad und einen Cloud-Pfad. Hier ist, wie man das mit OpenClaw baut."
— Till FreitagIn 30 Sekunden
Der Privacy Router ist ein Konzept – NVIDIA hat es auf der GTC vorgestellt, aber die Implementierungsdetails fehlen noch. Dieser Guide zeigt, wie man das Konzept mit OpenClaw selbst umsetzt: eine Policy-Engine, die Anfragen klassifiziert, und ein Router, der sensible Daten lokal hält und alles andere in die Cloud schickt.
Was wir bauen
Ein Privacy Router mit drei Komponenten:
User Query
└── Policy Engine (klassifiziert Sensitivität)
├── SENSITIVE → lokales Modell (Nemotron / Llama / Qwen)
└── NON_SENSITIVE → Cloud-Frontier (Claude / GPT / Gemini)Voraussetzungen:
- OpenClaw installiert und konfiguriert
- Ein lokales Modell (via Ollama, vLLM oder ähnlich)
- Ein Cloud-API-Key (OpenRouter, Anthropic, OpenAI o.ä.)
Schritt 1: Die Policy-Engine
Die Policy-Engine ist das Herzstück. Sie entscheidet, ob eine Anfrage sensible Daten enthält – bevor sie an ein Modell geht. Kritisch: Diese Entscheidung trifft die Policy, nicht der Agent.
Regelbasierter Ansatz
Für den Anfang reicht ein regelbasierter Klassifikator. Kein ML, kein Overhead – nur Pattern Matching:
from dataclasses import dataclass
from enum import Enum
import re
class Sensitivity(Enum):
SENSITIVE = "sensitive"
NON_SENSITIVE = "non_sensitive"
@dataclass
class PolicyRule:
name: str
patterns: list[str]
sensitivity: Sensitivity
class PolicyEngine:
def __init__(self, rules: list[PolicyRule]):
self.rules = rules
self._compiled = [
(rule, [re.compile(p, re.IGNORECASE) for p in rule.patterns])
for rule in rules
]
def classify(self, text: str) -> Sensitivity:
for rule, patterns in self._compiled:
for pattern in patterns:
if pattern.search(text):
return rule.sensitivity
return Sensitivity.NON_SENSITIVE # Default: nicht-sensibelPolicies definieren
Policies sind deklarativ – keine Logik, nur Daten:
GDPR_POLICIES = [
PolicyRule(
name="personal_data",
patterns=[
r"\b[A-Z][a-z]+ [A-Z][a-z]+\b", # Vor- und Nachname
r"\b\d{2}\.\d{2}\.\d{4}\b", # Geburtsdatum (DE Format)
r"\b[A-Z]{2}\d{2}\s?\w{4,}\b", # IBAN-Muster
],
sensitivity=Sensitivity.SENSITIVE,
),
PolicyRule(
name="health_data",
patterns=[
r"\b(diagnose|patient|krankenhaus|medikament)\b",
r"\b(blutdruck|allergie|therapie|befund)\b",
],
sensitivity=Sensitivity.SENSITIVE,
),
PolicyRule(
name="employee_data",
patterns=[
r"\b(gehalt|personalnummer|sozialversicherung)\b",
r"\b(kündig|abmahnung|arbeitszeugnis)\b",
],
sensitivity=Sensitivity.SENSITIVE,
),
]
engine = PolicyEngine(GDPR_POLICIES)Warum regelbasiert und nicht ML?
Ein ML-Klassifikator für Sensitivität klingt eleganter – aber er hat Nachteile:
| Regelbasiert | ML-basiert | |
|---|---|---|
| Erklärbarkeit | Jede Entscheidung nachvollziehbar | Black Box |
| DSGVO-Compliance | Auditierbar | Schwer nachweisbar |
| False Negatives | Bekannt und kontrollierbar | Unvorhersehbar |
| Latenz | ~0ms | ~10–50ms |
| Wartung | Regeln manuell pflegen | Trainingsdaten + Modell pflegen |
Für die meisten Fälle ist regelbasiert der bessere Start. ML kann später als zweite Schicht ergänzt werden.
Schritt 2: Der Router
Der Router nimmt die Entscheidung der Policy-Engine und leitet die Anfrage an das richtige Modell weiter:
from openclaw import Agent, ModelConfig
class PrivacyRouter:
def __init__(
self,
policy_engine: PolicyEngine,
local_model: ModelConfig,
cloud_model: ModelConfig,
):
self.policy = policy_engine
self.local_model = local_model
self.cloud_model = cloud_model
self._log: list[dict] = []
def route(self, query: str) -> ModelConfig:
sensitivity = self.policy.classify(query)
selected = (
self.local_model
if sensitivity == Sensitivity.SENSITIVE
else self.cloud_model
)
self._log.append({
"query_hash": hash(query), # Kein Klartext loggen!
"sensitivity": sensitivity.value,
"model": selected.name,
})
return selected
def get_audit_log(self) -> list[dict]:
return self._log.copy()Modelle konfigurieren
local = ModelConfig(
name="nemotron-nano",
endpoint="http://localhost:11434/v1", # Ollama
model_id="nemotron:3b",
max_tokens=4096,
)
cloud = ModelConfig(
name="claude-sonnet",
endpoint="https://api.anthropic.com/v1",
model_id="claude-sonnet-4-20250514",
api_key="${ANTHROPIC_API_KEY}", # Aus Umgebungsvariable
max_tokens=8192,
)
router = PrivacyRouter(
policy_engine=engine,
local_model=local,
cloud_model=cloud,
)Schritt 3: Integration in den Agent
Der Router wird als Middleware in den OpenClaw-Agent eingebaut. Der Agent merkt nicht, welches Modell er nutzt – das ist Absicht:
from openclaw import Agent, Tool
class PrivacyAwareAgent(Agent):
def __init__(self, router: PrivacyRouter, tools: list[Tool]):
self.router = router
super().__init__(tools=tools)
async def process(self, user_input: str) -> str:
# 1. Policy entscheidet über Routing
model = self.router.route(user_input)
# 2. Agent läuft mit dem gewählten Modell
response = await self.run(
input=user_input,
model=model,
)
return response.content
# Verwendung
agent = PrivacyAwareAgent(
router=router,
tools=[search_tool, calculator_tool, crm_tool],
)
# Sensible Anfrage → lokal
result = await agent.process(
"Was ist das aktuelle Gehalt von Max Mustermann?"
)
# Allgemeine Anfrage → Cloud
result = await agent.process(
"Fasse die Top-3 Trends im Projektmanagement zusammen"
)Schritt 4: Audit-Trail für Compliance
Für DSGVO-Compliance braucht man einen nachweisbaren Audit-Trail. Wann wurde was wohin geroutet – und warum:
import json
from datetime import datetime
class AuditLogger:
def __init__(self, storage_path: str):
self.path = storage_path
def log_routing_decision(
self,
query_hash: str,
sensitivity: str,
model_used: str,
policy_rule_matched: str | None,
timestamp: datetime | None = None,
):
entry = {
"timestamp": (timestamp or datetime.utcnow()).isoformat(),
"query_hash": query_hash, # Nie den Klartext loggen
"sensitivity": sensitivity,
"model": model_used,
"policy_rule": policy_rule_matched,
"routing_decision": (
"local" if sensitivity == "sensitive" else "cloud"
),
}
with open(self.path, "a") as f:
f.write(json.dumps(entry) + "\n")Wichtig: Den Klartext der Anfrage nie loggen – nur einen Hash. Sonst wird der Audit-Trail selbst zum Datenschutzproblem.
Schritt 5: Sandboxing für den lokalen Pfad
Der lokale Pfad verarbeitet sensible Daten – er braucht stärkere Isolation als der Cloud-Pfad:
from openclaw.sandbox import ContainerSandbox
# Tool-Calls im lokalen Pfad laufen in Containern
local_sandbox = ContainerSandbox(
image="openclaw/tool-runner:latest",
memory_limit="512m",
cpu_limit=1.0,
network="none", # Kein Netzwerkzugriff
read_only_fs=True, # Nur-Lese-Dateisystem
timeout_seconds=30,
)
# Cloud-Pfad braucht weniger Isolation
cloud_sandbox = ContainerSandbox(
image="openclaw/tool-runner:latest",
memory_limit="1g",
cpu_limit=2.0,
network="restricted", # Nur ausgehend, nur HTTPS
timeout_seconds=60,
)Vollständiges Setup
Alles zusammen:
from openclaw import Agent, ModelConfig, Tool
from openclaw.sandbox import ContainerSandbox
# 1. Policy Engine
engine = PolicyEngine(GDPR_POLICIES)
# 2. Modelle
local = ModelConfig(name="nemotron-nano", ...)
cloud = ModelConfig(name="claude-sonnet", ...)
# 3. Router
router = PrivacyRouter(engine, local, cloud)
# 4. Sandboxes
local_sandbox = ContainerSandbox(network="none", ...)
cloud_sandbox = ContainerSandbox(network="restricted", ...)
# 5. Agent
agent = PrivacyAwareAgent(
router=router,
tools=[
Tool("crm_lookup", sandbox=local_sandbox),
Tool("web_search", sandbox=cloud_sandbox),
Tool("calculator", sandbox=cloud_sandbox),
],
)
# 6. Audit
audit = AuditLogger("./audit/routing.jsonl")Erweiterte Patterns
Multi-Level-Routing
Nicht nur binär (sensibel/nicht-sensibel), sondern mit Abstufungen:
class Sensitivity(Enum):
CRITICAL = "critical" # Lokal + Container + Kernel
SENSITIVE = "sensitive" # Lokal + Container
INTERNAL = "internal" # Lokal, ohne Extra-Isolation
PUBLIC = "public" # Cloud erlaubt
class MultiLevelRouter(PrivacyRouter):
def __init__(self, policy_engine, models: dict[Sensitivity, ModelConfig]):
self.policy = policy_engine
self.models = models
def route(self, query: str) -> ModelConfig:
level = self.policy.classify(query)
return self.models[level]Fallback-Ketten
Was passiert, wenn das lokale Modell nicht verfügbar ist?
class ResilientRouter(PrivacyRouter):
async def route_with_fallback(self, query: str) -> ModelConfig:
sensitivity = self.policy.classify(query)
if sensitivity == Sensitivity.SENSITIVE:
if await self._is_healthy(self.local_model):
return self.local_model
else:
# NICHT auf Cloud fallen! Lieber fehlschlagen.
raise RuntimeError(
"Local model unavailable – "
"refusing to route sensitive data to cloud"
)
return self.cloud_modelKritisch: Ein Privacy Router darf bei sensiblen Daten nie auf die Cloud zurückfallen. Lieber fehlschlagen als Datenschutz brechen.
Häufige Fehler
| Fehler | Warum problematisch | Lösung |
|---|---|---|
| Agent entscheidet Sensitivität | Agent kann manipuliert werden (Prompt Injection) | Policy entscheidet, nicht der Agent |
| Cloud als Fallback für sensible Daten | Datenschutz-Verletzung | Fail statt Fallback |
| Klartext im Audit-Log | Log wird selbst zum Datenschutzproblem | Nur Hashes loggen |
| Nur Regex-Patterns | Fehlende Abdeckung | Regeln regelmäßig reviewen + ML als 2. Schicht |
| Kein Sandboxing lokal | Sensible Daten ohne Isolation | Container + Kernel-Härtung |
Fazit
Ein Privacy Router ist kein Hexenwerk – die Grundversion ist in 200 Zeilen Code gebaut. Das Schwierige ist nicht die Technik, sondern die Policies: Welche Daten sind sensibel? Wie granular muss die Klassifikation sein? Wie oft müssen Regeln aktualisiert werden?
Drei Takeaways:
- Start regelbasiert – ML-Klassifikation kann später ergänzt werden, aber Regeln sind auditierbar
- Policy first, Code second – Definiere zuerst die Regeln, dann die Implementierung
- Nie auf Cloud fallen bei sensiblen Daten – Fail > Fallback
→ NemoClaw: Privacy Router erklärt → Agent Sandboxing: Container vs. WASM vs. Kernel → Die 5 Bausteine eines KI-Agenten → Kontakt aufnehmen






