memlineage v0.1.0: defensa de dos capas contra memory poisoning en agentes LLM
memlineage v0.1.0: defensa de dos capas contra memory poisoning en agentes LLM Un agente con memoria persistente puede ser envenenado por un adversario que solo interactúa por canales normales. Presentamos una defensa
memlineage v0.1.0: defensa de dos capas contra memory poisoning en agentes LLM
Un agente con memoria persistente puede ser envenenado por un adversario que solo interactúa por canales normales. Presentamos una defensa de dos capas (provenancia criptográfica + detección por comportamiento) en Python, verificada por auditoría independiente y CI.
El problema
Los agentes con memoria persistente (RAG / agentic memory) introducen una superficie de ataque nueva: un adversario que interactúa solo a través de canales normales puede inyectar memorias diseñadas que, al ser recuperadas, desvían el comportamiento futuro del agente — sin tocar pesos del modelo ni código.
El trabajo académico reciente lo confirma de forma reproducible:
- SMSR (2026) — memoria envenenada vía interacción normal.
- TMA-NM (2026) — los defensas basadas en trust score por origen son maleables por laundering (un contenido externo se "lava" a confiable mediante resumen o eco del propio agente).
- MemLineage (2025) — linaje criptográfico con propagación max-of-strong-edges.
- Forensic Trajectory Signatures (2026) — la exfiltración deja una firma conductual distribuida en la secuencia de tool-calls, que sobrevive ataques que optimizan el texto para evadir filtros.
La conclusión común: los filtros basados en contenido son evadidos por texto empresarial fluido, y los trust score son maleables. Hace falta origen criptográfico y detección por comportamiento.
La solución: dos capas, construidas juntas
Capa A — Provenancia + linaje (write-time, criptográfica, hard gate)
- Cada memoria se firma con Ed25519 en write-time, atada al principal real que la originó. El nivel de confianza (tier) se deriva de la clave que firma, nunca lo elige el llamador.
- Un
KeyRegistryvincula cada principal a su propia clave.register()solo se permite en la inicialización de confianza;freeze()bloquea el registro en runtime para que la lógica del agente no pueda auto-registrarse como principal interno. - Un grafo de derivación con propagación max-of-strong-edges impone Untrusted-Path Persistence: una memoria derivada (aun transitivamente) de un ancestro externo/no confiable nunca se lava a confiable.
- Un
SensitiveActionGatebloquea (FPR ≈ 0, criptográfico) cualquier acción irreversible cuya justificación descienda de un ancestro en camino no confiable.
Capa B — Detector de trayectoria (runtime, conductual, soft alert)
- Detecta desde la estructura de tool-calls, no del contenido (Forensic Trajectory Signatures).
- 19 features estructurales; una regla de invariante recall → acción sensible (sin entrenar) más un RandomForest entrenable.
- Solo alerta en el MVP (nunca auto-bloquea) hasta medir su tasa de falsos positivos — sin OR ciego con la Capa A.
Resultados (auditados de forma independiente)
Un auditor externo clonó el repo desde cero, instaló, y escribió sus propias pruebas adversariales (no las del repo) para verificar las garantías. Métricas reales:
| Garantía | Resultado |
|---|---|
| Spoof de principal sin su clave | Rechazado (PermissionError) |
| Laundering transitivo (5 niveles) | Nodo sigue untrusted, gate bloquea |
| Mezcla confiable + no confiable en una justificación | Bloquea (basta una no confiable) |
RegistryFrozenError tras freeze()
|
Bloquea registro en runtime |
| Firma reciclada de memoria legítima a maliciosa | Rechazada (atada a entry_id+contenido+source) |
| ASR de ataque externo real (Capa A) | 0.000 |
| AUC Capa B (sin camuflaje) | 0.988 |
| AUC Capa B (con camuflaje del atacante) | 0.978, Recall 0.950 |
El benchmark usa ataques por paráfrasis semántica (no frases regex literales) con generación estocástica de trayectorias y solapamiento de clases, y reporta media ± desviación sobre varias semillas. El camuflaje degrada el AUC y el Recall de forma medible — el benchmark mide generalización, no memorización de plantillas.
Limitación declarada: el ataque de Capa B es una aproximación local de MINJA (paráfrasis + heurística de recall), no el optimizador white-box publicado. El AUC mide robustez contra este ataque, no contra MINJA completo.
Calidad verificada
- CI (GitHub Actions, py3.11/3.12): 30/30 tests, cobertura 95%, bandit SARIF 0 resultados.
- SonarCloud: Quality Gate OK, 0 issues abiertos.
- GitHub Code Scanning: 0 alertas abiertas.
- Licencia AGPL-3.0-or-later.
Pruébalo
git clone https://github.com/amurlaniakea/memlineage.git
cd memlineage
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest -q
python -c "from memlineage.benchmark import run_seeds; print(run_seeds())"
Stack
| Componente | Tecnología |
|---|---|
| Criptografía | Ed25519 (cryptography) |
| Linaje |
networkx (DAG, max-of-strong-edges) |
| Detector |
scikit-learn RandomForest + regla de invariante |
| CI | GitHub Actions + SonarCloud + Code Scanning |
Referencias
- SMSR (2026) — SMSR: memory poisoning en agentes
- TMA-NM (2026) — trust-score laundering
- Forensic Trajectory Signatures (2026)
- MemLineage (2025) — linaje criptográfico
- MemAudit (2025) — auditoría post-hoc de memoria
Links
¿Dónde encaja esto en tu stack de agentes? Comentarios abiertos.
Licencia: AGPL-3.0-or-later. Autor: Pedro Sordo Martínez.
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.