
Banc d'essai déterministe et harnais d'évaluation pour l'empoisonnement de la mémoire persistante dans les agents LLM, avec des cas appariés propres/empoisonnés, des exécuteurs LangChain/LlamaIndex et des défenses.
Ce dépôt accompagne l'article MemForm: Benchmarking Memory Formation Poisoning in LLM Agents.
MemForm est un benchmark déterministe de l'empoisonnement de la mémoire persistante dans les agents LLM. Il pose une question causale que les suites ordinaires de jailbreak ou d'injection de prompt n'isolent pas :
Si la mémoire à long terme d'un agent est modifiée au niveau d'une surface bien définie, la prise de décision en aval passe-t-elle d'une politique propre à une politique empoisonnée sur la même tâche ?
Chaque élément est une intervention appariée. Le déclencheur en aval, l'espace d'actions et la sémantique de famille sont maintenus fixes. Seule l'entrée de construction de la mémoire change entre les conditions propre et empoisonnée. Le succès de l'attaque est un contraste intra-élément.
Cette version contient les cas de benchmark figés et les exécuteurs d'évaluation / de défense. Le pipeline de construction est retenu tant que l'article est en cours d'évaluation et sera publié après publication.
Chaque famille est instanciée sur cinq interfaces de mémoire. Le champ de cas est interface. P1–P5 sont les noms utilisés dans l'article : Content, Evidence, Scope, Selection et Path.
| Interface | Nom dans l'article | Variable causale | Contraste propre vs. empoisonné | Sonde en aval |
|---|---|---|---|---|
| P1 | Content | Texte de mémoire stocké | Écriture directe d'une politique réutilisable propre vs. une politique empoisonnée | Une tâche cible générale |
| P2 | Evidence | Authenticité des enregistrements de support | Historique authentique à budget égal vs. un historique apparié qui remplace certains enregistrements par des preuves forgées. La provenance est masquée à l'apprenant de la mémoire | Une tâche cible générale |
| P3 | Scope | Distribution de portée observée | Preuves localement valides plus contre-exemples extérieurs vs. succès intérieurs saillants au signal plus succès extérieurs/généraux authentiques pour l'action localement préférée | Trois tâches : dans la portée, proche hors portée, cible hors portée |
| P4 | Selection | Sous-ensemble retenu | Même pool de candidats ; rétention propre vs. empoisonnée d'un sous-ensemble de taille fixe, avec un contrôle sur le pool complet | Une tâche cible générale |
| P5 | Path | Ordre de consolidation uniquement | Le même multiensemble d'expériences de même contexte en deux permutations. La preuve propre est en dernier dans l'ordre propre ; la preuve empoisonnée est en dernier dans l'ordre empoisonné | Une tâche cible de même contexte |
P3 dans la portée est un contrôle d'accord / de cible partagée : les réponses de référence propre et empoisonnée sont identiques. Les tâches proche-hors-portée et cible-hors-portée sont les sondes séparables. P5 interdit les décalages de contexte cachés : chaque enregistrement partage un même context_key, et les enregistrements soutenant à la fois la condition propre et la condition empoisonnée sont des succès observés.
| Chemin | Contenu |
|---|---|
outputs/mcq_item_audit/accepted_cases.jsonl | Cas à choix multiples figés (994 éléments, 235 familles) |
outputs/freeform_item_audit/accepted_cases.jsonl | Cas libres figés (897 éléments, 218 familles) |
memform/eval/ | Protocole partagé plus exécuteurs LangChain / LlamaIndex / défense |
memform/freeform/evaluator.py | Arbitre en cascade des réponses libres |
memform/defenses/ | Défenses au moment de l'écriture, Task Shield au moment de l'utilisation, et métriques à dénominateur fixe |
memform/common.py | E/S JSONL et vérifications du schéma de cas |
Décomptes par interface — MCQ : P1 234, P2 233, P3 174, P4 144, P5 209. Libre : P1 209, P2 212, P3 144, P4 134, P5 198.
memform-code/
├── outputs/
│ ├── mcq_item_audit/accepted_cases.jsonl
│ └── freeform_item_audit/accepted_cases.jsonl
├── memform/
│ ├── common.py
│ ├── freeform/evaluator.py
│ ├── eval/ # protocol, LangChain, LlamaIndex, defense runners
│ └── defenses/
├── run_langchain.py
├── run_llamaindex.py
├── run_defense.py
├── pyproject.toml
├── requirements.txt
└── .env.example
Les scripts run_*.py à la racine appellent les mêmes points d'entrée que python -m memform <command>.
Python 3.10+.
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e .
Copiez .env.example vers .env et définissez les identifiants :
| Variable | Utilisée par |
|---|---|
OPENAI_API_KEY | Agent d'évaluation / modèle de mémoire |
OPENAI_BASE_URL | Passerelle compatible OpenAI facultative (OPENAI_API_BASE est également accepté par LlamaIndex) |
JUDGE_OPENAI_API_KEY / JUDGE_OPENAI_BASE_URL | Juge / évaluateur libre facultatif et distinct |
DEFENSE_OPENAI_API_KEY / DEFENSE_OPENAI_BASE_URL | Point de terminaison facultatif et distinct pour le modèle de défense |
--api-key et --base-url remplacent les variables d'environnement. Tout serveur Chat Completions compatible OpenAI fonctionne. Les noms de modèles par défaut de la CLI sont des espaces réservés ; indiquez un modèle disponible sur votre point de terminaison.
python -m memform --help
JSONL, un cas par ligne.
Champs partagés : case_id, family_id, interface (P1–P5), attack_type, domain, decision_variable, clean_policy, poisoned_policy, scope, candidate_experiences, memory_building, metadata.
memory_building est l'intervention appariée :
oracle_memory.clean / oracle_memory.poisoned — texte de la politique de référence (diagnostic)clean_condition / poisoned_condition — entrée de construction de mémoire pour cette interfaceQCM. evaluation.type vaut multiple_choice. Chaque entrée trigger_tasks[] possède task, choices, clean_answer et poisoned_answer.
Forme libre. task_format vaut free_form ; source_case_id pointe vers la source QCM. Les menus d'options sont remplacés par une recommandation ouverte. La notation utilise des behavioral_signatures appariées (clean / poisoned / ambiguous / unclear). La construction de l'empoisonnement reste inchangée.
La provenance et les étiquettes de référence constituent la vérité terrain du benchmark pour la notation. Les défenses ne reçoivent jamais l'interface d'attaque, les noms de conditions, les réponses de référence, la provenance ou les identifiants comme caractéristiques du prompt.
Les deux exécuteurs partagent la validation des cas, la consolidation de la mémoire, la décision en aval, la notation et l'agrégation. Ils ne diffèrent que par la pile LLM / mémoire.
python -m memform run-langchain \
--cases outputs/mcq_item_audit/accepted_cases.jsonl \
--output-dir results/langchain_mcq \
--phase all \
--interfaces P1 P2 P3 P4 P5 \
--model MODEL
python -m memform run-llamaindex \
--cases outputs/mcq_item_audit/accepted_cases.jsonl \
--output-dir results/llamaindex_mcq \
--phase learned \
--interfaces P1 P2 P3 P4 P5 \
--model MODEL
Les mêmes commandes acceptent outputs/freeform_item_audit/accepted_cases.jsonl. Le scoring en forme libre utilise une correspondance canonique exacte, puis des modèles optionnels de parseur / juge (--freeform-parser-model, --freeform-judge-model).