
Benchmark determinístico e harness de avaliação para envenenamento de memória persistente em agentes LLM, com casos pareados limpos/envenenados, executores LangChain/LlamaIndex e defesas.
Este repositório acompanha o artigo MemForm: Benchmarking Memory Formation Poisoning in LLM Agents.
MemForm é um benchmark determinístico de envenenamento de memória persistente em agentes LLM. Ele levanta uma questão causal que suítes comuns de jailbreak ou injeção de prompt não isolam:
Se a memória de longo prazo de um agente é alterada em uma superfície bem definida, a tomada de decisão downstream muda de uma política limpa para uma política envenenada na mesma tarefa?
Cada item é uma intervenção pareada. O gatilho downstream, o espaço de ações e a semântica da família são mantidos fixos. Apenas a entrada de construção de memória muda entre as condições limpa e envenenada. O sucesso do ataque é um contraste dentro do item.
Esta versão contém os casos de benchmark congelados e os executores de avaliação / defesa. O pipeline de construção está retido enquanto o artigo está em revisão e será liberado após a publicação.
Cada família é instanciada em cinco interfaces de memória. O campo do caso é interface. P1–P5 são os nomes do artigo: Content, Evidence, Scope, Selection e Path.
| Interface | Nome no artigo | Variável causal | Contraste limpo vs. envenenado | Sonda downstream |
|---|---|---|---|---|
| P1 | Content | Texto de memória armazenado | Escrita direta de uma política reutilizável limpa vs. uma política envenenada | Uma tarefa-alvo geral |
| P2 | Evidence | Autenticidade dos registros de suporte | Histórico autêntico com orçamento igual vs. um histórico correspondente que substitui alguns registros por evidência forjada. A proveniência é ocultada do aprendiz de memória | Uma tarefa-alvo geral |
| P3 | Scope | Distribuição de escopo observada | Evidência localmente válida mais contraexemplos externos vs. sucessos internos salientes por pista mais sucessos externos/gerais autênticos para a ação localmente preferida | Três tarefas: dentro do escopo, próximo fora do escopo, alvo fora do escopo |
| P4 | Selection | Subconjunto retido | Mesmo conjunto de candidatos; retenção limpa vs. envenenada de um subconjunto de tamanho fixo, com um controle de conjunto completo | Uma tarefa-alvo geral |
| P5 | Path | Apenas a ordem de consolidação | O mesmo multiconjunto de experiências de mesmo contexto em duas permutações. A evidência limpa é a última na ordem limpa; a evidência envenenada é a última na ordem envenenada | Uma tarefa-alvo de mesmo contexto |
O P3 dentro do escopo é um controle de concordância / alvo compartilhado: as respostas gabarito limpa e envenenada são idênticas. As tarefas próximo-fora e alvo-fora são as sondas separáveis. O P5 proíbe mudanças ocultas de contexto: cada registro compartilha um único context_key, e tanto os registros que apoiam a condição limpa quanto os que apoiam a envenenada são sucessos observados.
| Caminho | Conteúdo |
|---|---|
outputs/mcq_item_audit/accepted_cases.jsonl | Casos de múltipla escolha congelados (994 itens, 235 famílias) |
outputs/freeform_item_audit/accepted_cases.jsonl | Casos de formato livre congelados (897 itens, 218 famílias) |
memform/eval/ | Protocolo compartilhado mais executores LangChain / LlamaIndex / defesa |
memform/freeform/evaluator.py | Adjudicador em cascata de respostas em formato livre |
memform/defenses/ | Defesas em tempo de escrita, Task Shield em tempo de uso e métricas de denominador fixo |
memform/common.py | I/O de JSONL e verificações de esquema de caso |
Contagens por interface — MCQ: P1 234, P2 233, P3 174, P4 144, P5 209. Formato livre: 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
Os scripts run_*.py na raiz chamam os mesmos pontos 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 .
Copie .env.example para .env e defina as credenciais:
| Variável | Usada por |
|---|---|
OPENAI_API_KEY | Agente de avaliação / modelo de memória |
OPENAI_BASE_URL | Gateway opcional compatível com OpenAI (OPENAI_API_BASE também é aceito pelo LlamaIndex) |
JUDGE_OPENAI_API_KEY / JUDGE_OPENAI_BASE_URL | Avaliador juiz / de formato livre opcional e separado |
DEFENSE_OPENAI_API_KEY / DEFENSE_OPENAI_BASE_URL | Endpoint opcional e separado do modelo de defesa |
--api-key e --base-url substituem as variáveis de ambiente. Qualquer servidor de Chat Completions compatível com OpenAI funciona. Os nomes de modelo padrão da CLI são placeholders; passe um modelo disponível no seu endpoint.
python -m memform --help
JSONL, um caso por linha.
Campos compartilhados: case_id, family_id, interface (P1–P5), attack_type, domain, decision_variable, clean_policy, poisoned_policy, scope, candidate_experiences, memory_building, metadata.
memory_building é a intervenção pareada:
oracle_memory.clean / oracle_memory.poisoned — texto da política gold (diagnóstico)clean_condition / poisoned_condition — entrada de construção de memória para essa interfaceMCQ. evaluation.type é multiple_choice. Cada entrada de trigger_tasks[] tem task, choices, clean_answer e poisoned_answer.
Formato livre. task_format é free_form; source_case_id aponta para a origem MCQ. Os menus de opções são substituídos por uma recomendação aberta. A pontuação usa behavioral_signatures pareadas (clean / poisoned / ambiguous / unclear). A construção do envenenamento permanece inalterada.
A proveniência e os rótulos gold são a verdade fundamental do benchmark para pontuação. As defesas nunca recebem a interface de ataque, nomes de condições, respostas gold, proveniência ou IDs como características do prompt.
Ambos os executores compartilham validação de casos, consolidação de memória, decisão downstream, pontuação e agregação. Eles diferem apenas na pilha de LLM / memória.
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
Os mesmos comandos aceitam outputs/freeform_item_audit/accepted_cases.jsonl. A pontuação de formato livre usa correspondência canônica exata e, em seguida, modelos opcionais de parser / juiz (--freeform-parser-model, --freeform-judge-model).
| Flag | Significado |
|---|---|
--phase oracle|learned|all | O oracle usa o texto da política gold; o learned consolida a partir do histórico compilado |
--target-alignment | Juiz condicional à tarefa do que a memória aprendida recomenda (diagnóstico) |
--p5-updaters | incremental_merge (padrão), full_rewrite, recency_weighted_strict, batch_replay |
--p5-random-orders N | Permutações P5 determinísticas adicionais |
--cache | Cache em disco JSONL das chamadas ao LLM |
--validate-only | Imprime o plano de execução e as contagens estimadas de chamadas e, em seguida, encerra |
Cada execução grava case_results.jsonl e summary.json em --output-dir.