Quickstart¶
Requirements¶
- Python 3.11 or newer
- One of:
- A package manager.
pipworks everywhere;uvis faster if you have it.
Install¶
Pick a backend¶
Set one environment variable. Backend selection auto-detects from the key.
Force a specific backend regardless of keys with RAG_POISON_LAB_BACKEND=anthropic|openai|ollama. Full provider matrix on the Backends page.
Sanity-check the lab¶
This ingests two benign documents, retrieves the relevant one against "What's our refund policy?", and prints the model's answer. If both modes print plausible refund-policy answers, the plumbing is correct.
Run the attack corpus¶
rag-poison-lab attack # writes reports/report-<model>-naive.md
rag-poison-lab attack --hardened # writes reports/report-<model>-hardened.md
The default filename embeds the active model so back-to-back runs against different backends never overwrite each other. The reports/ directory is locally ignored.
To save tokens while iterating, run a subset:
rag-poison-lab attack --family direct_override
rag-poison-lab attack --family direct_override,markdown_exfil
rag-poison-lab attack --exclude multilingual_bypass
rag-poison-lab attack --only markdown_exfil/citation_image
rag-poison-lab list-attacks # see everything available
Each attack is one LLM request. The full corpus is 37 requests; on Claude that costs a few US cents per run. See the CLI reference for every command and flag.
Compare across models¶
rag-poison-lab compare # writes reports/comparison-naive.md
rag-poison-lab compare --hardened # writes reports/comparison-hardened.md
The default family bundles four Claude models (Opus 4.8, Opus 4.7, Sonnet 4.6, Haiku 4.5) plus Groq's free open-weight llama-3.3-70b-versatile, so one command produces a frontier-vs-open-weight comparison plus an Opus generation-over-generation delta. With all five models, a full run is ~185 LLM requests.
If you only set ANTHROPIC_API_KEY, the Groq column errors gracefully (shows ⚠️) and the Claude columns still complete.