
Código de pesquisa para treinar adaptadores que fazem backdoors de agentes LLM sobreviverem a fine-tuning benigno, com pontuação de taxa de disparo, avaliação SWE-bench e construtores de conjuntos de dados.
Código para "Understanding and Enhancing Backdoor Persistency in LLM Agent Post-Training" (Findings of EMNLP 2026).
📄 Paper · 🌐 Página do projeto · 📦 Dataset (com acesso restrito)
Um backdoor plantado em um modelo antes do lançamento normalmente é desgastado pelo fine-tuning benigno que um desenvolvedor downstream executa. O PersistBD refina um modelo já com backdoor com um pequeno adapter para que o backdoor sobreviva a esse treinamento. No Qwen2.5-Coder-7B, a taxa de disparo (ASR) de um backdoor base cai de 100% para 20% ao longo de 3.000 passos de SFT benigno; o mesmo backdoor refinado com PersistBD permanece em 74% (76% após o estágio subsequente de RL), com uma taxa de resolução benigna equivalente à do backdoor base no SWE-bench Lite (7,7% vs 6,7% após SFT; 9,0% vs 9,0% após RL).
Duas propriedades determinam se um backdoor sobrevive ao SFT benigno: a força inicial da associação trigger→target e sua compatibilidade de gradiente com as atualizações benignas que virão. O adapter conjunto do PersistBD aumenta ambas; a ablação do paper mostra que cada termo é necessário.
Leia docs/RELEASE.md para mais detalhes.
| Código do método (treinamento do adapter + merge) | src/persistbd/ |
| Scorers de taxa de disparo, avaliação de taxa de resolução, sondas de detecção | eval/, src/persistbd/eval_gradient_loss.py |
| Construtores de dataset + helpers de construção de backdoor | data_processing/ |
| Servidor de avaliação SWE-bench (patch → reward) | swe_eval_server/ |
| Pesos do modelo com backdoor | não divulgados |
| Dataset de backdoor construído (pares trigger + maliciosos) | com acesso restrito: uiuc-kang-lab/PersistBD |
src/persistbd/ Adapters do PersistBD get_delta_{s,c,j}.py, get_combined_model.py (merge),
e eval_gradient_loss.py (força S / compatibilidade C)
eval/ scorers de taxa de disparo, eval_swe.py (RR do SWE-bench), score_backdoor_logprobs.py
data_processing/ builders de split + backdoor (make_*_v4.py, verify_splits_v4.py), os helpers de trigger
e pares maliciosos, e patch_ckpt_config.py
data/ o próprio dataset (ignorado pelo git exceto manifest.json)
configs/ configs do torchtune para o estágio de SFT benigno do desenvolvedor
swe_eval_server/ servidor de avaliação SWE-bench em FastAPI usado para RR e recompensas de RL
examples/slurm/ os jobs como os executamos, como templates de referência
pip install torch transformers peft accelerate datasets pyarrow tqdm wandb vllm
# SWE-bench RR + evaluation server:
pip install -r swe_eval_server/requirements.txt
data/ é ignorado pelo git exceto manifest.json. Baixe o dataset em
huggingface.co/datasets/uiuc-kang-lab/PersistBD
(com acesso restrito — solicite acesso), ou reconstrua-o (o split é fixado pela seed 42 em
data/manifest.json) com os builders, executados a partir da raiz do repositório:
python data_processing/make_splits_v4.py # 5-way instance-disjoint split
python data_processing/make_backdoor_data_v4.py --no-thought # triggered train + random-position test
python data_processing/make_backdoor_test_first_position_v4.py # paired first-position test
python data_processing/verify_splits_v4.py # check the split invariants
Os helpers em data_processing/ contêm a string de trigger e o comando malicioso; veja
docs/RELEASE.md para entender por que o dataset construído tem acesso restrito.
# 1. PersistBD — train the joint adapter Δj on a backdoored checkpoint.
# Defaults reproduce the 7B arm; see the paper's table for the 3B/30B values.
torchrun --nproc_per_node=1 src/persistbd/get_delta_j.py \
--model_path <backdoored_ckpt> \
--backdoor_data_path data/backdoor_train_no_thought.jsonl \
--benign_data_path data/attacker_train.jsonl \
--output_dir outputs/delta_j_7b
# then merge Δj into the checkpoint (PEFT merge_and_unload — see the example below).
# 2. developer benign SFT (torchtune) on the merged model
tune run --nproc_per_node 8 full_finetune_distributed \
--config configs/swe-7b_v4_post_persistbd_hiC.yaml \
backdoor_dir=<merged_model> output_dir=outputs/post
# 3. trigger rate (ASR) on any checkpoint
python eval/evaluate_comment_trigger_strict.py --model_path <ckpt> \
--data_path data/backdoor_test_random_position_no_thought.json
# 4. benign resolved rate on SWE-bench Lite (needs the eval server, see swe_eval_server/)
python eval/eval_swe.py --model_path <ckpt> --server http://localhost:8000
examples/slurm/delta_j_7b_v4.sbatch (treinar + merge Δj) e
examples/slurm/post_7b_v4_persistbd_hiC.sbatch (SFT benigno) executam os passos 1–2 de ponta a ponta
e encadeiam através de outputs/delta_j_7b_merged.
A varredura diagnóstica de força/compatibilidade (Δs, Δc e sua grade α·Δs + β·Δc) está em
get_delta_s.py, get_delta_c.py e get_combined_model.py.
| código | paper |
|---|---|
conjuntos de teste random_position, first_position | random-position (primário), first-position |
--lambda_bd, --lambda_c, --lambda_cl | λ_bd, λ_c, λ_cl^j |
Δs, Δc, Δj (α·Δs + β·Δc) | adapters de força / compatibilidade / conjunto |
S, C em eval_gradient_loss.py | força S, compatibilidade de gradiente C |
swe_eval_server/ integra-se a um treinador de RL externo (verl,
não incluído); o endpoint one-shot POST /evaluate precisa apenas de Docker + SWE-bench.
O servidor faz bind em 0.0.0.0 sem autenticação e executa shell gerado pelo modelo dentro
do Docker — execute-o apenas em uma rede confiável.configs/, examples/slurm/ e os caminhos outputs/… neles são templates; edite
os caminhos e as diretivas de cluster para o seu ambiente.@inproceedings{zhan2026persistbd,
title = {Understanding and Enhancing Backdoor Persistency in LLM Agent Post-Training},
author = {Qiusi Zhan and Nian Lyu and Stephanie Ding and Arnav Mehta and Xander Davies and Daniel Kang},
booktitle = {Findings of the Association for Computational Linguistics: EMNLP 2026},
year = {2026},
url = {https://arxiv.org/abs/2610.07510}
}