Skip to main content

Domainsteer

Steer any causal LLM toward user-specified domain expertise via calibrated activation steering — derive a contrastive direction per domain, add it to the residual stream, and dial the strength.

Contrastive pairs for 144 scientific domains ship with the package, so building a steering vector needs no API key — as does the 2,160-item polysemy-trap benchmark the vectors are evaluated on.

pip install -e .
from domainsteer import DomainSteerer

steerer = DomainSteerer(
    model_name="meta-llama/Llama-3.1-8B-Instruct",
    domain="Industrial biotechnology",
)
steerer.build(layer=14)
steerer.generate("What is a strain?", alpha=0.25)
steerer.compare("What does a payload deliver??",
                alpha=0.2)

Steered, "strain" is a microbial variant; unsteered, it is a pulled muscle. That is the effect the bundled pairs encode — one domain sense against the everyday one.

The bundled pairs

Model Domains Pairs
meta-llama/Llama-3.1-8B-Instruct 144 4,320
meta-llama/Llama-3.2-3B-Instruct 144 4,320
from domainsteer import list_bundled_models, bundled_manifest, load_bundled_pairs

list_bundled_models()
bundled_manifest("meta-llama/Llama-3.1-8B-Instruct")["contrast"]

# cluster id, directory name, or cluster name all resolve
load_bundled_pairs("meta-llama/Llama-3.1-8B-Instruct", "3106")
load_bundled_pairs("meta-llama/Llama-3.1-8B-Instruct", "Industrial biotechnology")

Pairs are model-specific — each model wrote both sides of its own, and only the question stems are shared between the two sets. Ask for the model you are steering; you get None if it ships none, and pairs are then generated through the API as before.

DomainSteerer resolves pairs in this order:

  1. cached pairs under cache_dir/<domain-slug>/pairs.jsonl
  2. the bundled pairs, for a matching (model, domain)
  3. API generation via PairGenerator (needs OPENAI_API_KEY)

Opt out with DomainSteerer(..., use_bundled_pairs=False) or regenerate with build(force_pairs=True). Full details, including the contrast the bundled sets carry, are in domainsteer/data/README.md.

The benchmark

The polysemy-trap exam ships too: 144 domains x 15 items = 2,160 bare questions whose overloaded term has to be read in the domain sense.

from domainsteer import load_benchmark, iter_benchmark

item = load_benchmark("3106")["items"][0]
item["question"]        # 'What can a fingerprint distinguish?'
item["gold"]            # 'product identity'      <- the domain reading
item["baseline_trap"]   # "a person's identity"   <- where an unsteered model lands
item["gold_responses"]  # one reference answer, the one scored against

sum(len(d["items"]) for d in iter_benchmark())    # 2160

gold_responses holds exactly one frozen reference sentence per item. Every arm is scored against that same sentence.

Pipeline

domain → bundled (or generated) pairs → ΔH → v → residual add
  1. Domain registrydomain_clusters.json names every steerable domain and subdivides each into exactly ten concepts.
  2. Contrastive pairs — both personas answer the same unnamed questions. Shipped as package data for the two models above.
  3. Vector extraction — mean-pooled assistant hidden states at each middle-third layer become one unit vector per layer (diff_means default; rfm optional).
  4. Steering — a forward hook adds a norm-relative shift h' = h + α‖h‖v at one decoder block. Pass a raw alpha (typical 0.10–0.40).

build(layer=…) is the installed-package path. NLI layer pick and the expertise dial live in extras/ (build() with no layer); they are not in the wheel and are not the paper measurement (the paper reports the full layer × strength grid).

Layout

domainsteer/                                the installable package
  clusters.py  pairs.py  em_traps.py        registry, contrastive pairs, trap exam
  extract.py  directions.py  steering.py    hidden states, directions, the hook
  steerer.py  self_gold.py                  DomainSteerer API, self-gold contrast
  data/                                     package data → data/README.md
    domain_clusters.json                      144 domains × 10 concepts
    pairs/<model-slug>/*.jsonl                144 × 30 pairs, per model
    benchmark/*.json                          144 × 15 polysemy traps
extras/                                     not installed (research checkout only)
  run_model_sweep.py                        four-arm layer × strength grid
  calibrate.py  judge.py  llm_judge.py      NLI/pairwise calibration and judges
  benchmark.py  trap.py  self_gold_sim.py   older exams and hybrid scoring
  concepts.py                               concept-file I/O
  iclr2027/                                 ICLR 2027 paper sources
tests/                                      CPU-only tests → tests/README.md

Tests

pip install -e ".[dev]"
pytest -q

CPU-only; nothing here downloads a model. Research scripts, the paper, and NLI calibration live in extras/ and are not part of pip install.

Optional extras

Extra For
generation Pair and gold-item generation, LLM judge (anthropic, openai)
eval Cosine scoring against reference answers (sentence-transformers)
dev pytest, ruff

License

MIT — see LICENSE.

Release files for domainsteer 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for domainsteer 0.2.0
File Size Uploaded
domainsteer-0.2.0.tar.gz 2.0 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for domainsteer 0.2.0
File Interpreter ABI Platform
domainsteer-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 4.4 MB

Release files / domainsteer-0.2.0.tar.gz

Download URL domainsteer-0.2.0.tar.gz
Size 2.0 MB
Tags Source
SHA-256 checksum
How to use checksums
692b6308f4ef59d65ecfb66dba66bfcca0eb86f3d99ff155a24fab1d17d6bc13
BLAKE2b-256 checksum
How to use checksums
f8f25a9c47df5319a30af60db63c4597270c41c023a1571bd6cfa7826fc40c6c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.10

Release files / domainsteer-0.2.0-py3-none-any.whl

Download URL domainsteer-0.2.0-py3-none-any.whl
Size 2.4 MB
Tags Python 3
SHA-256 checksum
How to use checksums
a62c4de57fde637ffb3c06ed886bdc275d14407289ba3bdf3b8f6f22bb6115d0
BLAKE2b-256 checksum
How to use checksums
8239cc7f86a76c374bb4800b126600c58d5c53b79e686b116274356bf028a64d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.10

Release history Release notifications | RSS feed

1.0.1

2 release files

1.0.0

3 release files

This release

0.2.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page