
Code de recherche pour entraîner des adaptateurs permettant aux backdoors d'agents LLM de survivre à un fine-tuning bénin, avec scoring du taux de déclenchement, évaluation SWE-bench et constructeurs de jeux de données.
Code pour « Understanding and Enhancing Backdoor Persistency in LLM Agent Post-Training » (Findings of EMNLP 2026).
📄 Article · 🌐 Page du projet · 📦 Jeu de données (accès restreint)
Une porte dérobée implantée dans un modèle avant sa publication est normalement érodée par le fine-tuning bénin qu'exécute un développeur en aval. PersistBD affine un modèle déjà porteur d'une porte dérobée à l'aide d'un petit adaptateur afin que la porte dérobée survive à cet entraînement. Sur Qwen2.5-Coder-7B, le taux de déclenchement (ASR) d'une porte dérobée de base chute de 100 % à 20 % sur 3 000 étapes de SFT bénin ; la même porte dérobée affinée avec PersistBD reste à 74 % (76 % après l'étape RL suivante), avec un taux de résolution bénin équivalent à celui de la porte dérobée de base sur SWE-bench Lite (7,7 % contre 6,7 % après le SFT ; 9,0 % contre 9,0 % après le RL).
Deux propriétés déterminent si une porte dérobée survit au SFT bénin : la force initiale de l'association déclencheur→cible et sa compatibilité de gradient avec les mises à jour bénignes à venir. L'adaptateur conjoint de PersistBD augmente les deux ; l'ablation de l'article montre que chaque terme est nécessaire.
Consultez docs/RELEASE.md pour plus de détails.
| Code de la méthode (entraînement de l'adaptateur + fusion) | src/persistbd/ |
| Scoreurs de taux de déclenchement, évaluation du taux de résolution, sondes de détection | eval/, src/persistbd/eval_gradient_loss.py |
| Générateurs de jeux de données + assistants de construction de portes dérobées | data_processing/ |
| Serveur d'évaluation SWE-bench (patch → récompense) | swe_eval_server/ |
| Poids du modèle porteur de la porte dérobée | non publiés |
| Jeu de données de porte dérobée construit (paires déclencheur + malveillantes) | accès restreint : uiuc-kang-lab/PersistBD |
src/persistbd/ Adaptateurs PersistBD get_delta_{s,c,j}.py, get_combined_model.py (fusion),
et eval_gradient_loss.py (force S / compatibilité C)
eval/ scoreurs de taux de déclenchement, eval_swe.py (RR SWE-bench), score_backdoor_logprobs.py
data_processing/ découpage + générateurs de portes dérobées (make_*_v4.py, verify_splits_v4.py), les assistants
pour le déclencheur et les paires malveillantes, et patch_ckpt_config.py
data/ le jeu de données lui-même (ignoré par git sauf manifest.json)
configs/ configurations torchtune pour l'étape de SFT bénin du développeur
swe_eval_server/ serveur d'évaluation SWE-bench FastAPI utilisé pour le RR et les récompenses RL
examples/slurm/ les jobs tels que nous les avons exécutés, comme modèles de référence
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/ est ignoré par git sauf manifest.json. Téléchargez le jeu de données depuis
huggingface.co/datasets/uiuc-kang-lab/PersistBD
(accès restreint — demandez l'accès), ou reconstruisez-le (le découpage est fixé par la graine 42 dans
data/manifest.json) avec les générateurs, exécutés depuis la racine du dépôt :
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
Les assistants dans data_processing/ contiennent la chaîne de déclenchement et la commande
malveillante ; consultez docs/RELEASE.md pour savoir pourquoi le jeu de données construit est à accès restreint.
# 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 (entraînement + fusion de Δj) et
examples/slurm/post_7b_v4_persistbd_hiC.sbatch (SFT bénin) exécutent les étapes 1–2 de bout en bout
et s'enchaînent via outputs/delta_j_7b_merged.
Le balayage diagnostique force/compatibilité (Δs, Δc et leur grille α·Δs + β·Δc) se trouve dans
get_delta_s.py, get_delta_c.py et get_combined_model.py.
| code | article |
|---|---|
ensembles de test random_position, first_position | position aléatoire (principal), première position |
--lambda_bd, --lambda_c, --lambda_cl | λ_bd, λ_c, λ_cl^j |
Δs, Δc, Δj (α·Δs + β·Δc) | adaptateurs force / compatibilité / conjoint |
S, C dans eval_gradient_loss.py | force S, compatibilité de gradient C |
swe_eval_server/ s'intègre à un entraîneur RL externe (verl,
non inclus) ; le point de terminaison ponctuel POST /evaluate ne nécessite que Docker + SWE-bench.
Le serveur se lie à 0.0.0.0 sans authentification et exécute du shell généré par le modèle à l'intérieur
de Docker — ne l'exécutez que sur un réseau de confiance.configs/, examples/slurm/ et les chemins outputs/… qu'ils contiennent sont des modèles ; modifiez
les chemins et les directives de cluster pour votre environnement.@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}
}