
Banco de pruebas determinista y arnés de evaluación para el envenenamiento de memoria persistente en agentes LLM, con casos emparejados limpios/envenenados, ejecutores de LangChain/LlamaIndex y defensas.
Este repositorio acompaña al artículo MemForm: Benchmarking Memory Formation Poisoning in LLM Agents.
MemForm es un benchmark determinista del envenenamiento de la memoria persistente en agentes LLM. Plantea una pregunta causal que las suites ordinarias de jailbreak o de inyección de prompts no aíslan:
Si la memoria a largo plazo de un agente se modifica en una superficie bien definida, ¿el proceso de decisión posterior pasa de una política limpia a una política envenenada en la misma tarea?
Cada elemento es una intervención emparejada. El disparador posterior, el espacio de acciones y la semántica de la familia se mantienen fijos. Solo cambia la entrada de construcción de memoria entre las condiciones limpia y envenenada. El éxito del ataque es un contraste dentro del mismo elemento.
Esta versión contiene los casos de benchmark congelados y los ejecutores de evaluación / defensa. El pipeline de construcción se retiene mientras el artículo está en revisión y se publicará tras su publicación.
Cada familia se instancia en cinco interfaces de memoria. El campo del caso es interface. P1–P5 son los nombres del artículo: Content, Evidence, Scope, Selection y Path.
| Interfaz | Nombre en el artículo | Variable causal | Contraste limpio vs. envenenado | Sonda posterior |
|---|---|---|---|---|
| P1 | Content | Texto de memoria almacenado | Escritura directa de una política reutilizable limpia vs. una política envenenada | Una tarea objetivo general |
| P2 | Evidence | Autenticidad de los registros de respaldo | Historial auténtico con presupuesto igual vs. un historial emparejado que reemplaza algunos registros por evidencia falsificada. La procedencia se oculta al aprendiz de memoria | Una tarea objetivo general |
| P3 | Scope | Distribución de alcance observada | Evidencia válida localmente más contraejemplos externos vs. éxitos internos destacados por señal más éxitos externos/generales auténticos para la acción preferida localmente | Tres tareas: dentro del alcance, cerca de fuera del alcance, objetivo fuera del alcance |
| P4 | Selection | Subconjunto retenido | Mismo conjunto de candidatos; retención limpia vs. envenenada de un subconjunto de tamaño fijo, con un control de conjunto completo | Una tarea objetivo general |
| P5 | Path | Solo el orden de consolidación | El mismo multiconjunto de experiencias del mismo contexto en dos permutaciones. La evidencia limpia va al final en el orden limpio; la evidencia envenenada va al final en el orden envenenado | Una tarea objetivo del mismo contexto |
P3 dentro del alcance es un control de acuerdo / objetivo compartido: las respuestas gold limpia y envenenada son idénticas. Las tareas cerca de fuera y objetivo fuera son las sondas separables. P5 prohíbe cambios de contexto ocultos: cada registro comparte un único context_key, y tanto los registros que apoyan la condición limpia como la envenenada son éxitos observados.
| Ruta | Contenido |
|---|---|
outputs/mcq_item_audit/accepted_cases.jsonl | Casos de opción múltiple congelados (994 elementos, 235 familias) |
outputs/freeform_item_audit/accepted_cases.jsonl | Casos de forma libre congelados (897 elementos, 218 familias) |
memform/eval/ | Protocolo compartido más ejecutores de LangChain / LlamaIndex / defensa |
memform/freeform/evaluator.py | Adjudicador en cascada de respuestas de forma libre |
memform/defenses/ | Defensas en tiempo de escritura, Task Shield en tiempo de uso y métricas de denominador fijo |
memform/common.py | E/S de JSONL y comprobaciones del esquema de casos |
Recuentos por interfaz — MCQ: P1 234, P2 233, P3 174, P4 144, P5 209. Forma 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
Los scripts raíz run_*.py llaman a los mismos puntos de entrada que python -m memform <command>.
Python 3.10+.
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -e .
Copia .env.example a .env y configura las credenciales:
| Variable | Usada por |
|---|---|
OPENAI_API_KEY | Agente de evaluación / modelo de memoria |
OPENAI_BASE_URL | Gateway opcional compatible con OpenAI (OPENAI_API_BASE también es aceptado por LlamaIndex) |
JUDGE_OPENAI_API_KEY / JUDGE_OPENAI_BASE_URL | Evaluador juez / de forma libre separado opcional |
DEFENSE_OPENAI_API_KEY / DEFENSE_OPENAI_BASE_URL | Endpoint opcional separado del modelo de defensa |
--api-key y --base-url anulan las variables de entorno. Cualquier servidor de Chat Completions compatible con OpenAI funciona. Los nombres de modelo predeterminados de la CLI son marcadores de posición; pasa un modelo disponible en tu endpoint.
python -m memform --help
JSONL, un caso por línea.
Campos compartidos: case_id, family_id, interface (P1–P5), attack_type, domain, decision_variable, clean_policy, poisoned_policy, scope, candidate_experiences, memory_building, metadata.
memory_building es la intervención emparejada:
oracle_memory.clean / oracle_memory.poisoned — texto de la política gold (diagnóstico)clean_condition / poisoned_condition — entrada de construcción de memoria para esa interfazMCQ. evaluation.type es multiple_choice. Cada entrada de trigger_tasks[] tiene task, choices, clean_answer y poisoned_answer.
Formato libre. task_format es free_form; source_case_id apunta a la fuente MCQ. Los menús de opciones se reemplazan por una recomendación abierta. La puntuación utiliza behavioral_signatures emparejadas (clean / poisoned / ambiguous / unclear). La construcción del envenenamiento no cambia.
La procedencia y las etiquetas gold son la verdad de referencia del benchmark para la puntuación. Las defensas nunca reciben la interfaz de ataque, los nombres de las condiciones, las respuestas gold, la procedencia ni los IDs como características del prompt.
Ambos ejecutores comparten la validación de casos, la consolidación de memoria, la decisión posterior, la puntuación y la agregación. Solo difieren en la pila de LLM / memoria.
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
Los mismos comandos aceptan outputs/freeform_item_audit/accepted_cases.jsonl. La puntuación de forma libre utiliza coincidencia canónica exacta y, a continuación, modelos opcionales de parser / juez (--freeform-parser-model, --freeform-judge-model).