feat(ultraplan-local): v1.6.0 — /ultraresearch-local deep research command
Add /ultraresearch-local for structured research combining local codebase analysis with external knowledge via parallel agent swarms. Produces research briefs with triangulation, confidence ratings, and source quality assessment. New command: /ultraresearch-local with modes --quick, --local, --external, --fg. New agents: research-orchestrator (opus), docs-researcher, community-researcher, security-researcher, contrarian-researcher, gemini-bridge (all sonnet). New template: research-brief-template.md. Integration: --research flag in /ultraplan-local accepts pre-built research briefs (up to 3), enriches the interview and exploration phases. Planning orchestrator cross-references brief findings during synthesis. Design principle: Context Engineering — right information to right agent at right time. Research briefs are structured artifacts in the pipeline: ultraresearch → brief → ultraplan --research → plan → ultraexecute. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
commit
baa2d0220b
488 changed files with 213221 additions and 0 deletions
|
|
@ -0,0 +1,567 @@
|
|||
# Data Anonymization and Privacy Compliance
|
||||
|
||||
**Last updated:** 2026-02
|
||||
**Status:** GA
|
||||
**Category:** Data Engineering for AI
|
||||
|
||||
---
|
||||
|
||||
## Introduksjon
|
||||
|
||||
Personvern og databeskyttelse er fundamentale krav for enhver AI-losning som behandler personopplysninger. GDPR (og den norske Personopplysningsloven) stiller strenge krav til hvordan persondata samles inn, behandles og beskyttes. For AI-systemer er dette spesielt utfordrende: ML-modeller kan utilsiktet memorere persondata fra treningsdatasettet, og RAG-systemer kan eksponere sensitiv informasjon i svar.
|
||||
|
||||
Microsoft tilbyr flere verktoy for personvernbeskyttelse: Azure Language PII-deteksjon for a identifisere og maskere personopplysninger, Microsoft Purview for dataklassifisering og governance, og SmartNoise for differensiell personvern. Disse verktoyene kan integreres i datapipelines i Fabric for a sikre at AI-modeller trenes pa korrekt anonymiserte data.
|
||||
|
||||
For norsk offentlig sektor, som er underlagt bade GDPR, Personopplysningsloven og sektorspesifikke krav (f.eks. Helseregisterloven, Politiregisterloven), er systematisk anonymisering og personvernbeskyttelse ikke bare god praksis -- det er lovpalagt.
|
||||
|
||||
---
|
||||
|
||||
## Differential Privacy Techniques
|
||||
|
||||
### Hva er differensiell personvern?
|
||||
|
||||
Differensiell personvern (DP) garanterer matematisk at ingen enkeltperson kan identifiseres fra resultatet av en dataanalyse. Prinsippet: tilforing av kontrollert stoy gjor det umulig a avgjore om en spesifikk person er i datasettet.
|
||||
|
||||
```
|
||||
Formell definisjon:
|
||||
For alle datasett D1 og D2 som skiller seg med maks 1 rad,
|
||||
og alle mulige resultater S:
|
||||
|
||||
Pr[M(D1) i S] <= e^epsilon * Pr[M(D2) i S]
|
||||
|
||||
epsilon (privacy budget): Lavere = sterkere personvern, mer stoy
|
||||
```
|
||||
|
||||
### Privacy Budget (epsilon)
|
||||
|
||||
| Epsilon | Personvernniva | Bruksomrade |
|
||||
|---------|---------------|-------------|
|
||||
| 0.1 | Svart sterkt | Helsedata, sensitiv forskning |
|
||||
| 1.0 | Sterkt | Generell offentlig statistikk |
|
||||
| 3.0 | Moderat | Intern analyse, dashboards |
|
||||
| 10.0 | Svakt | Testing, lav-risiko data |
|
||||
|
||||
### SmartNoise-implementering
|
||||
|
||||
```python
|
||||
# SmartNoise - Microsoft/OpenDP-prosjektet for differensiell personvern
|
||||
# pip install opendp smartnoise-sql
|
||||
|
||||
from opendp.measurements import make_laplace
|
||||
from opendp.domains import atom_domain
|
||||
from opendp.metrics import absolute_distance
|
||||
|
||||
def dp_count(true_count: int, epsilon: float = 1.0) -> float:
|
||||
"""
|
||||
Legg til Laplace-stoy for differensielt privat telling.
|
||||
"""
|
||||
sensitivity = 1 # En person kan endre tellingen med maks 1
|
||||
scale = sensitivity / epsilon
|
||||
|
||||
import numpy as np
|
||||
noise = np.random.laplace(0, scale)
|
||||
return max(0, true_count + noise) # Aldri negativ telling
|
||||
|
||||
def dp_mean(values, epsilon: float = 1.0, lower_bound: float = 0, upper_bound: float = 100):
|
||||
"""
|
||||
Beregn differensielt privat gjennomsnitt.
|
||||
"""
|
||||
import numpy as np
|
||||
n = len(values)
|
||||
true_mean = np.mean(values)
|
||||
|
||||
sensitivity = (upper_bound - lower_bound) / n
|
||||
scale = sensitivity / epsilon
|
||||
|
||||
noise = np.random.laplace(0, scale)
|
||||
return true_mean + noise
|
||||
|
||||
# Eksempel: Privat gjennomsnitt av inntektsdata
|
||||
incomes = [450000, 520000, 380000, 620000, 490000]
|
||||
private_mean = dp_mean(incomes, epsilon=1.0, lower_bound=0, upper_bound=2000000)
|
||||
print(f"Differensielt privat gjennomsnitt: {private_mean:,.0f} NOK")
|
||||
```
|
||||
|
||||
### Differensiell personvern i ML-trening
|
||||
|
||||
```python
|
||||
# DP-SGD (Differentially Private Stochastic Gradient Descent)
|
||||
# For trening av modeller med personverngarantier
|
||||
|
||||
# Med opacus (PyTorch)
|
||||
# pip install opacus
|
||||
|
||||
"""
|
||||
from opacus import PrivacyEngine
|
||||
|
||||
model = YourModel()
|
||||
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
|
||||
|
||||
privacy_engine = PrivacyEngine()
|
||||
model, optimizer, dataloader = privacy_engine.make_private_with_epsilon(
|
||||
module=model,
|
||||
optimizer=optimizer,
|
||||
data_loader=dataloader,
|
||||
epochs=10,
|
||||
target_epsilon=3.0, # Privacy budget
|
||||
target_delta=1e-5,
|
||||
max_grad_norm=1.0
|
||||
)
|
||||
|
||||
# Tren som vanlig - opacus handterer stoy-tilforing automatisk
|
||||
for epoch in range(10):
|
||||
for batch in dataloader:
|
||||
optimizer.zero_grad()
|
||||
loss = criterion(model(batch), labels)
|
||||
loss.backward()
|
||||
optimizer.step()
|
||||
|
||||
# Sjekk faktisk privacy-forbruk
|
||||
epsilon = privacy_engine.get_epsilon(delta=1e-5)
|
||||
print(f"Faktisk epsilon: {epsilon:.2f}")
|
||||
"""
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## K-Anonymity and L-Diversity
|
||||
|
||||
### K-Anonymitet
|
||||
|
||||
K-anonymitet sikrer at hver kombinasjon av quasi-identifikatorer forekommer i minst k rader:
|
||||
|
||||
```python
|
||||
def check_k_anonymity(df, quasi_identifiers: list, k: int = 5) -> dict:
|
||||
"""
|
||||
Sjekk om et datasett oppfyller k-anonymitet.
|
||||
|
||||
Args:
|
||||
df: DataFrame
|
||||
quasi_identifiers: Kolonner som kan brukes til re-identifisering
|
||||
k: Minimum gruppestorrelse
|
||||
"""
|
||||
# Grupper etter quasi-identifikatorer
|
||||
groups = df.groupBy(quasi_identifiers).count()
|
||||
|
||||
# Finn grupper med faerre enn k elementer
|
||||
violating = groups.filter(F.col("count") < k)
|
||||
total_groups = groups.count()
|
||||
violating_groups = violating.count()
|
||||
|
||||
min_group_size = groups.agg(F.min("count")).collect()[0][0]
|
||||
|
||||
return {
|
||||
"k_anonymous": violating_groups == 0,
|
||||
"k_value": k,
|
||||
"total_groups": total_groups,
|
||||
"violating_groups": violating_groups,
|
||||
"min_group_size": min_group_size,
|
||||
"recommendation": f"Oek generalisering" if min_group_size < k else "OK"
|
||||
}
|
||||
|
||||
# Sjekk k-anonymitet for helsedatasett
|
||||
result = check_k_anonymity(
|
||||
df_health_data,
|
||||
quasi_identifiers=["age_group", "postal_area", "gender"],
|
||||
k=5
|
||||
)
|
||||
```
|
||||
|
||||
### Generaliseringsstrategier for k-anonymitet
|
||||
|
||||
```python
|
||||
def generalize_for_k_anonymity(df, generalizations: dict):
|
||||
"""
|
||||
Generaliser quasi-identifikatorer for a oppna k-anonymitet.
|
||||
|
||||
Args:
|
||||
generalizations: {kolonne: generaliseringsfunksjon}
|
||||
"""
|
||||
result = df
|
||||
for col_name, gen_func in generalizations.items():
|
||||
result = result.withColumn(col_name, gen_func(F.col(col_name)))
|
||||
return result
|
||||
|
||||
# Generaliseringsfunksjoner
|
||||
generalizations = {
|
||||
# Alder -> aldersgruppe (5-aarsintervaller)
|
||||
"age": lambda c: (F.floor(c / 5) * 5).cast("int"),
|
||||
|
||||
# Postnummer -> postomrade (forste 2 siffer)
|
||||
"postal_code": lambda c: F.substring(c, 1, 2),
|
||||
|
||||
# Fodselsdato -> fodeselsar
|
||||
"birth_date": lambda c: F.year(c),
|
||||
|
||||
# Kommune -> fylke
|
||||
"municipality": lambda c: F.substring(c, 1, 2) # Forste 2 siffer = fylke
|
||||
}
|
||||
|
||||
df_generalized = generalize_for_k_anonymity(df_sensitive, generalizations)
|
||||
|
||||
# Verifiser
|
||||
result = check_k_anonymity(df_generalized, ["age", "postal_code", "gender"], k=5)
|
||||
print(f"K-anonym: {result['k_anonymous']}, Min gruppestorrelse: {result['min_group_size']}")
|
||||
```
|
||||
|
||||
### L-Diversitet
|
||||
|
||||
L-diversitet utvider k-anonymitet ved a kreve at sensitive attributter har minst l forskjellige verdier i hver gruppe:
|
||||
|
||||
```python
|
||||
def check_l_diversity(df, quasi_identifiers: list, sensitive_column: str, l: int = 3):
|
||||
"""
|
||||
Sjekk om et datasett oppfyller l-diversitet.
|
||||
"""
|
||||
# Tell unike verdier av sensitiv attributt per gruppe
|
||||
diversity = df.groupBy(quasi_identifiers).agg(
|
||||
F.countDistinct(sensitive_column).alias("distinct_sensitive"),
|
||||
F.count("*").alias("group_size")
|
||||
)
|
||||
|
||||
violating = diversity.filter(F.col("distinct_sensitive") < l)
|
||||
min_diversity = diversity.agg(F.min("distinct_sensitive")).collect()[0][0]
|
||||
|
||||
return {
|
||||
"l_diverse": violating.count() == 0,
|
||||
"l_value": l,
|
||||
"min_diversity": min_diversity,
|
||||
"violating_groups": violating.count()
|
||||
}
|
||||
|
||||
# Sjekk l-diversitet for diagnosekoder
|
||||
result = check_l_diversity(
|
||||
df_health_data,
|
||||
quasi_identifiers=["age_group", "postal_area"],
|
||||
sensitive_column="diagnosis_code",
|
||||
l=3
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## PII Detection and Masking
|
||||
|
||||
### Azure Language PII Detection
|
||||
|
||||
Azure Language tilbyr avansert PII-deteksjon med stotte for 50+ kategorier:
|
||||
|
||||
```python
|
||||
from azure.ai.textanalytics import TextAnalyticsClient
|
||||
from azure.core.credentials import AzureKeyCredential
|
||||
|
||||
def detect_and_redact_pii(text: str, endpoint: str, key: str,
|
||||
categories_to_redact: list = None) -> dict:
|
||||
"""
|
||||
Detekter og masker PII i tekst med Azure Language.
|
||||
|
||||
Args:
|
||||
text: Tekst a analysere
|
||||
categories_to_redact: Spesifikke PII-kategorier a maskere
|
||||
"""
|
||||
client = TextAnalyticsClient(
|
||||
endpoint=endpoint,
|
||||
credential=AzureKeyCredential(key)
|
||||
)
|
||||
|
||||
response = client.recognize_pii_entities(
|
||||
documents=[text],
|
||||
categories_filter=categories_to_redact,
|
||||
language="no" # Norsk
|
||||
)
|
||||
|
||||
result = response[0]
|
||||
|
||||
return {
|
||||
"original_text": text,
|
||||
"redacted_text": result.redacted_text,
|
||||
"entities": [
|
||||
{
|
||||
"text": entity.text,
|
||||
"category": entity.category,
|
||||
"subcategory": entity.subcategory,
|
||||
"confidence": entity.confidence_score,
|
||||
"offset": entity.offset,
|
||||
"length": entity.length
|
||||
}
|
||||
for entity in result.entities
|
||||
]
|
||||
}
|
||||
|
||||
# Eksempel
|
||||
result = detect_and_redact_pii(
|
||||
text="Ola Nordmann bor i Storgata 15, 0184 Oslo. Hans personnummer er 01019012345.",
|
||||
endpoint="https://myservice.cognitiveservices.azure.com/",
|
||||
key="your-api-key",
|
||||
categories_to_redact=["Person", "Address", "NorwayIdentityNumber"]
|
||||
)
|
||||
# Output: "***** bor i *****, ***** Oslo. Hans personnummer er *****."
|
||||
```
|
||||
|
||||
### PII-deteksjon i Fabric-pipelines
|
||||
|
||||
```python
|
||||
# Batch PII-deteksjon i PySpark
|
||||
from pyspark.sql import functions as F
|
||||
from pyspark.sql.types import ArrayType, StructType, StructField, StringType, DoubleType
|
||||
|
||||
def batch_pii_detection(df, text_column: str, endpoint: str, key: str):
|
||||
"""
|
||||
Kjor PII-deteksjon pa en hel DataFrame-kolonne.
|
||||
"""
|
||||
@F.udf(returnType=StringType())
|
||||
def redact_pii_udf(text):
|
||||
if not text:
|
||||
return text
|
||||
|
||||
from azure.ai.textanalytics import TextAnalyticsClient
|
||||
from azure.core.credentials import AzureKeyCredential
|
||||
|
||||
client = TextAnalyticsClient(
|
||||
endpoint=endpoint,
|
||||
credential=AzureKeyCredential(key)
|
||||
)
|
||||
|
||||
try:
|
||||
response = client.recognize_pii_entities(
|
||||
documents=[text], language="no"
|
||||
)
|
||||
return response[0].redacted_text
|
||||
except Exception:
|
||||
return text # Returner original ved feil
|
||||
|
||||
return df.withColumn(f"{text_column}_redacted", redact_pii_udf(F.col(text_column)))
|
||||
|
||||
# Bruk pa treningsdata for RAG
|
||||
df_documents = spark.table("lakehouse.default.raw_documents")
|
||||
df_redacted = batch_pii_detection(
|
||||
df_documents,
|
||||
text_column="content",
|
||||
endpoint=endpoint,
|
||||
key=api_key
|
||||
)
|
||||
|
||||
# Lagre redaktert versjon for AI-trening
|
||||
df_redacted.select("doc_id", "content_redacted", "metadata") \
|
||||
.write.format("delta").mode("overwrite") \
|
||||
.saveAsTable("lakehouse.default.training_documents_anonymized")
|
||||
```
|
||||
|
||||
### PII-kategorier relevant for norsk offentlig sektor
|
||||
|
||||
| Kategori | Azure-kode | Eksempler |
|
||||
|----------|-----------|----------|
|
||||
| Personnummer | NorwayIdentityNumber | 01019012345 |
|
||||
| Personnavn | Person | Ola Nordmann |
|
||||
| Adresse | Address | Storgata 15, 0184 Oslo |
|
||||
| Telefonnummer | PhoneNumber | +47 90000000 |
|
||||
| E-post | Email | ola@firma.no |
|
||||
| Bankkonto | InternationalBankingAccountNumber | NO9386011117947 |
|
||||
| Organisasjonsnummer | Organization | 123 456 789 |
|
||||
| Helseinfo (PHI) | HealthcareEntities | Diagnose, medisin |
|
||||
|
||||
---
|
||||
|
||||
## Right-to-Be-Forgotten Implementation
|
||||
|
||||
### GDPR Artikkel 17: Retten til sletting
|
||||
|
||||
```python
|
||||
from delta.tables import DeltaTable
|
||||
|
||||
class GDPRDeletionService:
|
||||
"""
|
||||
Implementer rett til sletting (GDPR Art. 17) i Delta Lake.
|
||||
"""
|
||||
|
||||
def __init__(self, tables_with_personal_data: list):
|
||||
self.tables = tables_with_personal_data
|
||||
self.deletion_log_table = "lakehouse.default.gdpr_deletion_log"
|
||||
|
||||
def process_deletion_request(self, person_id: str, request_id: str):
|
||||
"""
|
||||
Slett alle personopplysninger for en person pa tvers av tabeller.
|
||||
"""
|
||||
results = {}
|
||||
|
||||
for table_config in self.tables:
|
||||
table_name = table_config["table"]
|
||||
id_column = table_config["id_column"]
|
||||
strategy = table_config.get("strategy", "hard_delete")
|
||||
|
||||
try:
|
||||
delta_table = DeltaTable.forName(spark, table_name)
|
||||
|
||||
if strategy == "hard_delete":
|
||||
# Slett raden helt
|
||||
delta_table.delete(f"{id_column} = '{person_id}'")
|
||||
|
||||
elif strategy == "anonymize":
|
||||
# Anonymiser i stedet for a slette
|
||||
anon_columns = table_config.get("anonymize_columns", [])
|
||||
update_set = {col: F.lit("SLETTET") for col in anon_columns}
|
||||
update_set["is_anonymized"] = F.lit(True)
|
||||
update_set["anonymized_date"] = F.current_timestamp()
|
||||
|
||||
delta_table.update(
|
||||
condition=f"{id_column} = '{person_id}'",
|
||||
set=update_set
|
||||
)
|
||||
|
||||
elif strategy == "pseudonymize":
|
||||
# Erstatt med pseudonym
|
||||
import hashlib
|
||||
pseudonym = hashlib.sha256(
|
||||
f"{person_id}_{request_id}".encode()
|
||||
).hexdigest()[:12]
|
||||
|
||||
delta_table.update(
|
||||
condition=f"{id_column} = '{person_id}'",
|
||||
set={id_column: F.lit(f"PSEUDO_{pseudonym}")}
|
||||
)
|
||||
|
||||
results[table_name] = {"status": "OK", "strategy": strategy}
|
||||
|
||||
except Exception as e:
|
||||
results[table_name] = {"status": "ERROR", "error": str(e)}
|
||||
|
||||
# Logg slettingen
|
||||
self._log_deletion(request_id, person_id, results)
|
||||
|
||||
return results
|
||||
|
||||
def _log_deletion(self, request_id, person_id, results):
|
||||
"""Logg slettingsforesporselen for compliance-formaal."""
|
||||
log_entry = spark.createDataFrame([{
|
||||
"request_id": request_id,
|
||||
"person_id_hash": hashlib.sha256(person_id.encode()).hexdigest(),
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"tables_processed": len(results),
|
||||
"all_successful": all(r["status"] == "OK" for r in results.values()),
|
||||
"details": json.dumps(results)
|
||||
}])
|
||||
log_entry.write.format("delta").mode("append") \
|
||||
.saveAsTable(self.deletion_log_table)
|
||||
|
||||
def vacuum_after_deletion(self, retention_hours: int = 0):
|
||||
"""
|
||||
Kjor VACUUM for a fysisk fjerne slettede data.
|
||||
ADVARSEL: Setter retensjon til 0 timer = ingen tidsreise mulig.
|
||||
"""
|
||||
spark.conf.set("spark.databricks.delta.retentionDurationCheck.enabled", "false")
|
||||
|
||||
for table_config in self.tables:
|
||||
spark.sql(f"VACUUM {table_config['table']} RETAIN {retention_hours} HOURS")
|
||||
|
||||
spark.conf.set("spark.databricks.delta.retentionDurationCheck.enabled", "true")
|
||||
|
||||
# Konfigurasjon
|
||||
tables_config = [
|
||||
{"table": "lakehouse.default.customers", "id_column": "person_id", "strategy": "hard_delete"},
|
||||
{"table": "lakehouse.default.transactions", "id_column": "customer_id", "strategy": "anonymize",
|
||||
"anonymize_columns": ["customer_name", "email", "phone"]},
|
||||
{"table": "lakehouse.default.ml_features", "id_column": "entity_id", "strategy": "pseudonymize"},
|
||||
{"table": "lakehouse.default.embeddings", "id_column": "source_person_id", "strategy": "hard_delete"}
|
||||
]
|
||||
|
||||
gdpr_service = GDPRDeletionService(tables_config)
|
||||
result = gdpr_service.process_deletion_request("12345678901", "REQ-2026-001")
|
||||
```
|
||||
|
||||
### TTL (Time-to-Live) for automatisk sletting
|
||||
|
||||
```python
|
||||
# Implementer TTL for partisjonerte Delta-tabeller
|
||||
def enforce_ttl(table_name: str, partition_column: str, retention_days: int):
|
||||
"""
|
||||
Slett partisjoner eldre enn retention_days.
|
||||
Nyttig for a overholde GDPR-krav om minimering av lagringstid.
|
||||
"""
|
||||
cutoff_date = (datetime.now() - timedelta(days=retention_days)).strftime("%Y-%m-%d")
|
||||
|
||||
delta_table = DeltaTable.forName(spark, table_name)
|
||||
delta_table.delete(f"{partition_column} < '{cutoff_date}'")
|
||||
|
||||
# VACUUM for a fysisk fjerne filene
|
||||
spark.sql(f"VACUUM {table_name}")
|
||||
|
||||
print(f"Slettet data eldre enn {cutoff_date} fra {table_name}")
|
||||
|
||||
# Kjor daglig: Slett persondata eldre enn 13 maaneder
|
||||
enforce_ttl("lakehouse.default.customer_interactions", "interaction_date", retention_days=395)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Privacy Impact Assessments
|
||||
|
||||
### DPIA-rammeverk for AI-systemer
|
||||
|
||||
| Fase | Aktivitet | Verktoy |
|
||||
|------|----------|---------|
|
||||
| **1. Kartlegging** | Identifiser persondata i AI-systemet | Microsoft Purview Data Map |
|
||||
| **2. Vurdering** | Vurder noodvendighet og proporsjonalitet | DPIA-mal fra Datatilsynet |
|
||||
| **3. Risikoanalyse** | Identifiser risiko for de registrerte | Risk Assessment Framework |
|
||||
| **4. Tiltak** | Implementer tekniske og organisatoriske tiltak | Anonymisering, tilgangsstyring |
|
||||
| **5. Dokumentasjon** | Dokumenter vurderingen | Protokoll, behandlingsregister |
|
||||
| **6. Konsultasjon** | Konsulter personvernombud / Datatilsynet | Ved hoy risiko |
|
||||
|
||||
### Automatisert personvern-sjekk i CI/CD
|
||||
|
||||
```python
|
||||
def privacy_check_before_deployment(model_artifacts_path: str) -> dict:
|
||||
"""
|
||||
Automatisert personvernsjekk for ML-modeller.
|
||||
Kjores som del av CI/CD-pipeline.
|
||||
"""
|
||||
checks = {}
|
||||
|
||||
# 1. Sjekk at treningsdata er anonymisert
|
||||
training_data_path = f"{model_artifacts_path}/training_data_manifest.json"
|
||||
manifest = json.load(open(training_data_path))
|
||||
|
||||
checks["anonymized_training_data"] = manifest.get("anonymization_applied", False)
|
||||
|
||||
# 2. Sjekk at modellen ikke memorerer PII
|
||||
# (Sample inference med kjente PII-verdier)
|
||||
checks["pii_leakage_test"] = run_pii_leakage_test(model_artifacts_path)
|
||||
|
||||
# 3. Sjekk at DPIA er utfylt
|
||||
checks["dpia_completed"] = os.path.exists(f"{model_artifacts_path}/dpia_signed.pdf")
|
||||
|
||||
# 4. Sjekk at personvernombud er konsultert
|
||||
checks["dpo_approved"] = manifest.get("dpo_approval_date") is not None
|
||||
|
||||
# 5. Sjekk retensjonspolicy
|
||||
checks["retention_policy_defined"] = manifest.get("data_retention_days") is not None
|
||||
|
||||
all_passed = all(checks.values())
|
||||
|
||||
return {
|
||||
"passed": all_passed,
|
||||
"checks": checks,
|
||||
"recommendation": "DEPLOY" if all_passed else "BLOKKER - Personvernkrav ikke oppfylt"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Referanser
|
||||
|
||||
- [What is Azure Language PII detection?](https://learn.microsoft.com/en-us/azure/ai-services/language-service/personally-identifiable-information/overview) -- PII-deteksjon og maskering
|
||||
- [PII filter in Azure AI Foundry](https://learn.microsoft.com/en-us/azure/ai-foundry/openai/concepts/content-filter-personal-information) -- PII-filtrering for LLM-er
|
||||
- [Responsible AI - Privacy and security](https://learn.microsoft.com/en-us/azure/machine-learning/concept-responsible-ai) -- SmartNoise og Counterfit
|
||||
- [Data privacy for cloud-scale analytics](https://learn.microsoft.com/en-us/azure/cloud-adoption-framework/scenarios/cloud-scale-analytics/secure-data-privacy) -- Dataklassifisering og konfidensialitetsskjema
|
||||
- [PII entity categories](https://learn.microsoft.com/en-us/azure/ai-services/language-service/personally-identifiable-information/concepts/entity-categories) -- Alle stottede PII-kategorier
|
||||
- [Transparency note for PII](https://learn.microsoft.com/en-us/azure/ai-foundry/responsible-ai/language-service/transparency-note-personally-identifiable-information) -- Bruksomrader og begrensninger
|
||||
- [Data governance with Microsoft Purview](https://learn.microsoft.com/en-us/purview/data-governance-master-data-management) -- Purview for dataklassifisering
|
||||
|
||||
---
|
||||
|
||||
## For Cosmo
|
||||
|
||||
- **Bruk denne referansen** naar kunder behandler personopplysninger i AI-systemer og trenger anonymiserings- og personvernstrategier.
|
||||
- **Azure Language PII-deteksjon er forstevalget** for a identifisere og maskere personopplysninger i tekst -- bade for treningsdata og RAG-dokumenter. Stotter norsk sprak.
|
||||
- **GDPR-sletting i Delta Lake krever VACUUM**: Delta Lake sin tidsreise betyr at slettede data forblir tilgjengelige i transaksjonsloggen til VACUUM kjores. Planlegg VACUUM i trad med slettekrav.
|
||||
- **K-anonymitet er minimum for publisering**: For datasett som deles utenfor organisasjonen, krev minimum k=5 anonymitet. For helsedata, bruk l-diversitet i tillegg.
|
||||
- **For norsk offentlig sektor**: Datatilsynets DPIA-mal er obligatorisk for AI-systemer med hoy risiko. Integrer personvernsjekk i CI/CD-pipeline for a sikre at ingen modell deployes uten godkjent DPIA.
|
||||
Loading…
Add table
Add a link
Reference in a new issue