Minne och integritet
Kunna designa minnen som respekterar radering, samtycke och dataminimering.
Förkunskaper
Intuition
Ett agentminne är en personuppgiftssamling som växer av sig själv. Det gör det till en av de känsligaste delarna i ett AI-system.
Fyra frågor som måste besvaras innan minnet byggs:
| Fråga | Varför |
|---|---|
| Vad sparas — och vad sparas inte? | dataminimering |
| Hur länge? | lagringsminimering |
| Kan användaren se och ändra det? | rättelse och öppenhet |
| Vad händer vid radering? | rätten att bli glömd |
Den svåraste är den sista. Att radera en rad i databasen är enkelt. Men minnet kan ha spridit sig: till sammanfattningar, till embeddingar, till loggar, till en modell som finjusterats på det.
Grundprincipen är därför att designa för radering från början — inte att lösa det när begäran kommer.
Formellt
Var en personuppgift kan hamna, och hur svårt den är att få bort:
| Plats | Radering |
|---|---|
| Minnesposten | lätt |
| Sammanfattningar | kräver omgenerering utan den posten |
| Embeddingar i index | lätt om id:t är spårbart, annars svårt |
| Samtalsloggar | lätt om de har gallringstid |
| Audit-loggar | ska inte raderas — men får inte innehålla mer än nödvändigt |
| Backuper | tekniskt svårt; hanteras med gallringsrutin |
| Finjusterade modellvikter | i praktiken omöjligt |
Sista raden är skälet till att man inte ska finjustera på användarnas minnen. Gör man det blir radering ett löfte man inte kan hålla.
Designprinciper som fungerar:
- Spara referenser, inte kopior. Låt sammanfattningar peka på källminnen så att de kan genereras om när något raderas.
- Kategorisera vid skrivning. Varje minne får en typ (preferens, mål, faktum, känsligt) och en gallringstid som följer av typen.
- Filtrera vid skrivning, inte vid läsning. Det som aldrig sparas kan inte läcka.
- Spårbart id genom hela kedjan — från minnespost till embedding till sammanfattning.
- Radering är en operation, inte en manuell process. Den ska gå att köra och verifiera.
Vad som aldrig ska sparas i ett agentminne:
| Kategori | Varför |
|---|---|
| Personnummer, adresser, kontaktuppgifter till tredje part | inte nödvändigt för funktionen |
| Hälsouppgifter | särskild kategori enligt GDPR art. 9 |
| Uppgifter om andra personer | den registrerade är inte användaren |
| Lösenord och nycklar | uppenbart |
| Känsliga uppgifter om barn | skärpta krav |
Öppenhet i praktiken. Användaren ska kunna se en lista över vad systemet minns, i läsbar form, och kunna ta bort enskilda poster. Det är både ett GDPR-krav och bra design: ett minne som inte går att korrigera blir obehagligt så snart det innehåller något fel.
Samtycke. Ett minne som sparas över sessioner bör vara ett aktivt val, inte förvalt — särskilt för barn. Och det ska gå att stänga av utan att tjänsten blir oanvändbar.
Kod
import re, time, uuid
from dataclasses import dataclass, field
from enum import Enum
class Typ(Enum):
PREFERENS = ("preferens", 365)
MAL = ("mål", 365)
SVARIGHET = ("svårighet", 180)
FAKTUM = ("faktum", 90)
SESSION = ("session", 1)
def __init__(self, etikett, gallringsdagar):
self.etikett = etikett
self.gallringsdagar = gallringsdagar
FORBJUDNA = {
"personnummer": r"\b(19|20)?\d{6}[-+]?\d{4}\b",
"e-post": r"\b[\w.+-]+@[\w-]+\.[\w.]+\b",
"telefon": r"\b0[\d\s-]{7,12}\b",
"adress": r"\b\w+(gatan|vägen|gränd)\s+\d+\b",
}
def far_sparas(text: str) -> tuple[bool, list[str]]:
traffar = [namn for namn, m in FORBJUDNA.items() if re.search(m, text, re.I)]
return (not traffar), traffar
@dataclass
class Minne:
id: str
anvandare: str
text: str
typ: Typ
skapad: float
kallor: list[str] = field(default_factory=list) # id:n detta härletts ur
def utgangen(self, nu=None):
nu = nu or time.time()
return nu - self.skapad > self.typ.gallringsdagar * 86400
class Minnesbank:
def __init__(self, vektorindex):
self.minnen: dict[str, Minne] = {}
self.index = vektorindex
def skriv(self, anvandare, text, typ: Typ, kallor=None):
ok, traffar = far_sparas(text)
if not ok:
return {"sparat": False, "anledning": f"innehåller {', '.join(traffar)}"}
mid = str(uuid.uuid4())
self.minnen[mid] = Minne(mid, anvandare, text, typ, time.time(), kallor or [])
self.index.upsert(id=mid, vector=badda_in(text),
payload={"anvandare": anvandare, "typ": typ.etikett})
return {"sparat": True, "id": mid}
def radera(self, mid: str) -> dict:
"""Raderar posten OCH allt som härletts ur den."""
borttagna = []
ko = [mid]
while ko:
m = ko.pop()
if m not in self.minnen:
continue
# allt som pekar på m måste också bort eller genereras om
ko += [k for k, v in self.minnen.items() if m in v.kallor]
del self.minnen[m]
self.index.delete(id=m)
borttagna.append(m)
return {"borttagna": borttagna, "antal": len(borttagna)}
def radera_allt(self, anvandare: str) -> dict:
ids = [k for k, v in self.minnen.items() if v.anvandare == anvandare]
for i in ids:
self.minnen.pop(i, None)
self.index.delete(id=i)
return {"antal": len(ids)}
def gallra(self) -> dict:
nu = time.time()
utgangna = [k for k, v in self.minnen.items() if v.utgangen(nu)]
for i in utgangna:
del self.minnen[i]
self.index.delete(id=i)
return {"gallrade": len(utgangna)}
def visa_for_anvandare(self, anvandare: str):
"""Läsbar lista — användaren ska kunna se och ta bort enskilda poster."""
return sorted(
[{"id": m.id, "text": m.text, "typ": m.typ.etikett,
"sparat": time.strftime("%Y-%m-%d", time.localtime(m.skapad)),
"gallras": time.strftime("%Y-%m-%d", time.localtime(
m.skapad + m.typ.gallringsdagar * 86400))}
for m in self.minnen.values() if m.anvandare == anvandare],
key=lambda d: d["sparat"], reverse=True)
# Verifiera raderingen — annars är den ett löfte utan täckning
def verifiera_radering(bank, anvandare):
kvar_minnen = [m for m in bank.minnen.values() if m.anvandare == anvandare]
kvar_index = bank.index.count(filter={"anvandare": anvandare})
return {"minnen_kvar": len(kvar_minnen), "index_kvar": kvar_index,
"ok": len(kvar_minnen) == 0 and kvar_index == 0}
kallor-fältet är det som gör radering genomförbar. Utan spårning av vad ett minne härletts ur går en raderad uppgift att återfinna i en sammanfattning, och raderingen är inte verklig.
Behärskning innebär
- Designar minnen med gallring och radering
- Tillämpar dataminimering på vad som sparas
- Gör minnet synligt och redigerbart för användaren
Logga in för att göra övningarna och bygga upp din behärskning.
Källor
- GDPR — förordning (EU) 2016/679 — EU-rättsakt
- IMY — Integritetsskyddsmyndigheten — myndighetsmaterial
- arXiv — MemGPT: Towards LLMs as Operating Systems — arXiv (öppen åtkomst; licens per artikel)