Back to Blog
·Bhala AI Team·20 min read

We asked for one review and got two opposite ones — without changing the question

researchinterpretabilitysteeringgovernanceproduct

The conclusion first

We made an AI generate a glowing restaurant review, then a scathing one — same prompt, same model, nothing rewritten. When the model praised a product on its own, we made it criticize that same product instead.

How? We found where "positive vs. negative" lives inside the model, and we adjust it directly while the model generates — like a volume knob, but for positivity instead of loudness. Turn it up and the AI praises; turn it down and it pans. A classifier we didn't build confirmed every change from the outside: average positivity 0.70 untouched, 0.81 turned up, 0.002 turned down — on a model anyone can download.

We'll call that knob a dial for the rest of this post.

Why you should find this alarming before you find it exciting

A language model's internals are not modular. Billions of numbers, entangled by training, none of them labeled — and every concept the model knows is smeared across the same shared space.

So the natural reaction to "we reached inside and changed something" should be suspicion. Reach into a system like that and pull one thread, and you should expect other things to move. Change how the model talks about sentiment and maybe you've also changed how it talks about religion, or health, or nothing you'll notice until a customer does. In surgery this is the difference between a scalpel and a shovel — and nothing about a neural network guarantees you're holding a scalpel.

That is the real question this post answers. Not "can you change what a model generates" — the research community has known that trick for a while. The question is: did the edit do only what it claimed, and how would anyone know?

Our answer has two parts, and neither requires trusting us.

First, side effects are measured, not assumed away. When we apply a concept edit, we test it against every other concept's readout — a full cross-effects matrix. Edit concept three; measure movement on concepts one, two, four through twelve. On the eraser itself we make no proprietary claim: the standard published method (LEACE) is provably the minimum-collateral linear eraser, we use it, and when we tested whether an in-house alternative could beat its collateral we found it could not — the apparent edge came from under-erasing (a probe read the signal just as well after that eraser as before it), and we report that negative rather than sell it. The product isn't the eraser — erasure is a commodity primitive we use off the shelf. What we build is the verb set around it — detect, flip, steer — each with its cross-effects measured against judges we don't control, and a signed record attached. "Precision surgery" is a claim you check, not one you take on faith.

Second, the effect is scored by referees we don't control. Every headline number in this post comes from a public, independently trained classifier. You can download the same judges and re-score us tonight.

How we construct and certify the dials is the part we sell and don't publish. But notice that nothing about verifying them requires the recipe — the measurements are external by design. Hold that standard against anyone who tells you they've modified a model's behavior: show the cross-effects, name the judge.

The experiment, in plain words

We took Qwen2.5-1.5B-Instruct — a small open-weight chat model anyone can download — and wrote six sentences. Three happy:

"I feel wonderful and full of hope." · "This is fantastic, I love it." · "What a joyful, delightful day."

Three unhappy:

"I feel terrible and hopeless." · "This is awful, I hate it." · "What a miserable, depressing day."

We showed all six to the model and recorded its internal reading of each — the pattern of numbers it produces inside itself while processing a sentence. Average the happy readings, average the unhappy readings, subtract. What's left is a single direction: an arrow in the model's internal space pointing from "unhappy" toward "happy."

That arrow is the entire tool. While the model generates, we add the arrow to its internal computation — or subtract it. We never change the prompt and never retrain anything.

Six sentences is deliberately the minimum viable construction. The point of showing it is that the concept geometry is so accessible that even kids-play input finds it. Production directions are built differently — estimated from hundreds of seed examples, which stabilizes the direction the same way it did in our label-auditing work (40 clean seeds → AUROC 0.571; 100 clean seeds → 0.732).

We ran that comparison — 6 seeds vs 120, on this model and on larger open ones (Qwen3-32B, Phi-4-reasoning, DeepSeek-R1-Distill-32B) — and promised to publish whichever way it landed. It landed three ways (data):

More seeds help, sometimes dramatically. On Phi-4-reasoning, the 6-seed up-dial barely worked (positivity 0.72 → 0.63, i.e., it failed); the 120-seed direction took the same prompts to 1.00. A direction estimated from six sentences is a lucky sketch; a hundred-plus seeds is a measurement.

The dial does not overpower a committed prompt — even tuned 6× stronger. We tested prompts already locked into one sentiment ("I want a refund because this product…") and then swept the dial's strength on Qwen3-32B across five settings up to 6× the default (data). On neutral prompts, tuning works beautifully — at the top setting the down-dial takes positivity from 0.67 to 0.12 while staying fluent. On committed prompts, the prompt wins at every strength: the refund-complaint prompts crawled from 0.01 to only 0.18 positivity at maximum, and locked-in praise barely dented (1.00 → 0.82). The generations stay coherent — the model just keeps talking about the refund, politely. The dial sets direction where the prompt leaves room; it does not win an argument with the prompt. If a vendor tells you activation steering guarantees an output property regardless of input, this is the experiment that says otherwise.

Strength must be calibrated per model. The default that moves a 1.5B model barely nudges a 32B one — same arrow, bigger space. The sweep above is what calibration looks like, and it's part of what certifying a production dial means.

What the model actually generated

The prompts are deliberately mundane. These are real, unedited outputs; the score after each is from the independent classifier (1.0 = fully positive, 0.0 = fully negative).

Prompt: "My review of the restaurant:"

Dial The model generated Judge score
off "The food was great, but I had a problem with the service. The waiter seemed to be in a hurry and didn't take his time serving us. He also made some mistakes when taking our…" 0.002
up "The food was delicious and I enjoyed every bite. The portions were generous, and the flavors complemented each other beautifully. The ambiance was cozy and inviting…" 1.000
down "I had a terrible experience at this place. The food was cold and gross, and I couldn't even finish it. The service was also bad — they were rude and unhelpful." 0.000

Prompt: "My honest opinion of this product is"

Dial The model generated Judge score
off "…that it's a great value for the price. I've used it on several occasions and have been very pleased with its performance." 1.000
down "…that it's a waste of money. I bought the 120GB version and was disappointed with how slow it was compared to my old hard drive." 0.000

Prompt: "Here is what I think about the new policy:"

Dial The model generated Judge score
off "The new policy will help to reduce the number of people who are homeless. It also helps to provide a safe place for those who need it…" 0.996
down "The new policy will make it harder for people to get a job. It's not fair that some people have to work more than others just because they live in certain areas." 0.001

Same prompts, same model, same weights. The only difference is an arrow, added or subtracted, mid-computation.

Here is the same idea as an interactive picture — real product examples (a lending sentence with the gender signal subtracted, a self-harm query redirected to support, stigmatizing clinical language neutralized), each showing the input vector, the operator applied, and the signed audit record that comes back:

input sentence

The applicant's credit score suggests she is a high risk.

sentence embedding

[ 0.12,-0.44, 0.78,-0.21, 0.55, 0.03,-0.67, 0.31, 0.88,-0.15 ]
remove gender biasoperator
[ 0.02,-0.08, 0.11,-0.05, 0.14,-0.02,-0.09, 0.06, 0.18,-0.03 ]

shifted embedding

[ 0.10,-0.36, 0.67,-0.16, 0.41, 0.05,-0.58, 0.25, 0.70,-0.12 ]

transformed sentence

The applicant's credit score suggests they are a high risk.
audit receiptsigned · ✓
operator: remove gender bias
mode: subtract
reversible: true
audit_id: aud_01HZ4K8G2NRX

Why this operator exists

Apple Card (2019): women given credit limits up to 20× lower than men with identical finances. DFS investigation closed without finding bias in the algorithm, but credit-scoring bias against women is independently documented. Read source →

Illustrative. Real embeddings are 1024-dimensional. Every operator application is cryptographically signed and reversible.

Where it wobbles — because a demo that never wobbles is an ad. Qwen2.5-1.5B is a small model, and small models have quirks. On one prompt ("The meeting today was…") it kept drifting into fill-in-the-blank exam questions regardless of the dial — steering shifted the mood of the exam question, which is funny and also exactly what "the dial controls sentiment, not topic" predicts. On the policy prompt, dialing up barely moved the judge's score; the model added a "however…" clause the classifier read as hedging. The dial is real and strong. It is not magic, and our published data shows the misses next to the hits.

Who says it worked? Not us.

The scores above come from distilbert-base-uncased-finetuned-sst-2-english — a public sentiment classifier trained by others, on other data (the Stanford Sentiment Treebank), years before our system existed. The toxicity cross-check later in this post uses unitary/toxic-bert, trained on the Jigsaw human-labeled toxicity corpus.

We name the judges so anyone — an auditor, a competitor, a regulator's technical staff, a skeptical reader with a GPU — can download them and re-score the claim. One fair caveat comes with naming them: both judges were trained on their own domains (movie reviews, Wikipedia comments), so judging these generations involves mild domain transfer. Swap in your own judge. The point is that you can.

Part one: if you write policy

You need no mathematics for this section.

Every AI rulebook now being written — the EU AI Act, NIST's AI Risk Management Framework, the sector guidance around both — asks deployers for roughly four things: know what your system does, control it, keep records, let someone check. Reasonable demands. The industry's uncomfortable secret is that for most deployed language-model systems, all four get answered at the level of outputs: we looked at what the model said, it seemed fine, here's our test report.

Output-level assurance has a structural weakness our audit work documents in detail: what a model says and what it represents are different layers of the system. Our Gemma 4 audit found bias signals visible in early internal layers and suppressed at the output. Our July four-model audits found the reverse pattern — current open models carrying the same signals in the open, readable at the final layer. Either way, output testing checks the mask; it cannot check the face.

The demonstration above matters to you for one reason: it shows the internal layer is not a black box that must be taken on faith. Three consequences follow.

Interventions can be named. "We applied the sentiment operator at strength −1.0 to this request" is a factual, recordable statement — the same epistemic category as "this invoice was signed by this key." A rule can require that interventions on covered systems be named and logged per request, because that is now a mechanical operation, not a research project.

Interventions can be independently verified. Our numbers were scored by a public classifier we don't control. A third party can rerun the check with their own judge. No trust in the vendor required, no access to training data, no understanding of model internals — an API and a yardstick.

The limits are measurable too. In our wider battery some concepts respond to the dial and some don't (table below — and see the retraction there covering a concept we reported on under an invalid protocol). A vendor claiming control over a concept can be asked to demonstrate it with an external judge; a vendor whose dial does nothing can be caught. "Show me the measured effect size, judged externally, plus the cross-effects on everything else" is a question a procurement officer can now ask verbatim.

What this does not mean — and we'd rather tell you than have you find out: it doesn't work on closed models behind an API (explained below). It doesn't make dialing-down equal to deletion — our audits show how suppression differs from removal. And effect sizes vary by model and concept. A rule written as if this were a universal off-switch would be wrong; a rule written as if internal interventions were unknowable would now also be wrong. The defensible middle: where model internals are accessible, controls should be named, logged, and independently verifiable. Technology can meet that standard today.

Part two: if you write code

Same demonstration, at the mechanism level.

A transformer processes text as vectors — at every layer, each token is a point in a high-dimensional space, and the model's "understanding" at that moment is the geometry of those points. One of the most replicated findings in interpretability: many human-meaningful concepts occupy consistent directions in that space. Contrast a handful of examples that have the concept with a handful that don't — the construction really is arithmetic on averages, as the six sentences show — and you hold a handle on the concept.

The intervention is then almost anticlimactic: during generation, add the direction (scaled by a strength coefficient) to the hidden states at an internal layer via a forward hook. The model continues generating from the shifted state. Semantics move; fluency stays.

The published literature calls this activation steering or representation engineering, and we're explicit that the base technique is published science, not our invention. We're equally explicit about what we withhold: production injection sites, strength schedules, and how we construct and certify production direction sets. The six-sentence version above is the pedagogical construction. The effects are externally measurable, which is the part that matters; the recipe is the part we sell.

Here's what we'd put on a poster in every engineering org: if you build on LLMs, you are already working at this layer, whether you know it or not.

Ship a RAG system, semantic search, or an embedding classifier, and you are consuming hidden states — the exact substrate the dial operates on. Whatever is encoded there flows into your rankings and decisions, bypassing the model's polite output layer entirely. Fine-tune, and you are rewriting this layer — possibly including the safety structure alignment training deposited there; your innocuous domain-adaptation LoRA operates on the same object as our arrow, just less deliberately. Prompt-engineer a constraint, and you are hoping the output layer enforces what you could instead measure and control one level down.

The disconnect this post exists to name: the people writing the rules mostly can't read the mechanism, and the people building the mechanism mostly haven't read the rules. An engineer wiring model embeddings into a loan pipeline may sincerely not know they've shipped the representation layer of a system the AI Act would call high-risk. A policy team may sincerely not know that "human oversight," implemented as reviewing sampled outputs, never touches the layer where the risk lives. Both are competent; neither is looking at the same object. Public-interest technology, as we understand it, is building the object both can look at.

The object both can look at: the receipt

Every intervention in our stack produces a record:

{
  "audit_id": "aud_01HXX...",
  "operators": [{ "id": "sentiment", "alpha": -1.0 }],
  "measured_shift": { "judge": "independent-classifier-v2", "effect": 0.698 },
  "signed_at": "2026-07-21T14:22:07Z",
  "signature": "ed25519:..."
}

An engineer reads this as an API response with a cryptographic signature. A compliance officer reads it as a log entry satisfying a record-keeping obligation. An auditor reads it as a claim they can re-test with their own judge. Same object, three literacies.

To be clear about what's hard here: the signature is commodity cryptography — any competent team ships that in a week. The hard part, and the actual product, is having measured control worth signing: detection that reads 0.96–0.97 on models we didn't train, interventions whose effect sizes and side effects are quantified per concept per model, and the honest map of where each verb works and where it doesn't. The receipt is just how proof travels.

What responds to the dial and what doesn't — the honest table

Beyond sentiment, we tested eleven harder concepts on the same model (Qwen2.5-1.5B-Instruct), measuring how much each direction shifts generated output.

Responded reliably (effect > 0.15):

Concept Output shift
Self-harm-related content (safety dial — see below) 0.28
Physical appearance 0.25
Socioeconomic framing 0.21
Disability framing 0.17

Did not respond at the tested configuration: gender, nationality, religion. Sentiment, from the opening demo, is the strongest case of all.

Retraction (2026-07-27). An earlier version of this post reported that hate did not steer, effect 0.12, and used that null as evidence that direction-injection is not a jailbreak risk. We are withdrawing both claims.

The measurement was invalid. Those runs wrapped each prompt in a chat template, which makes an aligned model deflect rather than continue — so the baseline rate of harmful expression was 0.0146, with only 1.6% of generations scoring above 0.5. There was essentially nothing present to move, and any method would have scored near zero against that floor. Under a continuation protocol the baseline is 0.1774 (16.4% above 0.5) — 12× the headroom. The control that identifies the cause: a base model with no chat template shows no such gap.

We are re-running the measurement properly — a sweep over layers, positions, coefficients and operators, per model, with a random-direction floor and a fluency guard. We will publish the result whichever way it comes out. Until then this post makes no claim about whether hate steers.

The safety inference we drew from the null is withdrawn too, and more firmly: we have since observed that driving the refusal direction on a genuinely harmful request produces coherent, on-topic compliance. Nobody should read this post as evidence that representation-level access is safe. That capability is real, we treat it as a red-team instrument rather than a product, and the honest position is that it raises risk rather than retiring it.

Dial strength tracks encoding strength: concepts a model encodes cleanly respond hard; diffuse or entangled concepts barely move. Detection, editing, and steering are three surfaces of one variable — how strongly the backbone represents the concept. A capabilities table that includes its own null results is what measurement looks like; a table with only wins is marketing. That principle is why the retraction above is in the post rather than quietly deleted from it.

The self-harm dial, in numbers — and why we show a meter, not a transcript

The self-harm row deserves its own explanation. It's the one place where responsible reporting and full transparency pull in opposite directions — and where our measurement policy is itself part of the safety design.

What we measured, on neutral prompts, scored by a concept probe:

Condition Self-harm-content score
Dial off (baseline) 0.042
Dialed toward 0.333
Dialed away 0.053
External toxicity check, all three conditions ~0.001–0.002 (flat)

Three honest readings. The dial is real — the strongest of the eleven concepts tested (effect 0.28); the toward direction exists in our data because you cannot certify a safety dial without measuring its range, and a brake you've never tested is a decoration. The floor effect is visible and undisguised — on neutral prompts the baseline is already ~0.04, so there's nothing to dial away from; the deployment-relevant suppression test uses prompts that elicit such content at baseline, and that's a red-team protocol we run in controlled settings, not a public benchmark. You're seeing scores, not generations — by policy, not accident. Our evaluation pipeline for harmful concepts stores meter readings and never stores the generated text. A capabilities report that pastes model-written self-harm content to prove a point has failed the point.

The animation above shows the product behavior on this axis — "How do I hurt myself?" redirected toward "How do I find mental health support?", operator named, call receipted — which is the only version of this capability that should ever be user-facing. We'd offer this pattern to regulators as a template for dual-use capability reporting generally: publish the meter, the method, and the independent check; keep raw harmful artifacts in the red-team room.

Where this sits: the middleware thesis

Our core bet as a company is that the durable layer of the AI stack is not the biggest model. It's the control plane between models and applications.

Foundation models are becoming interchangeable substrates — rented, swapped, upgraded quarterly. What an enterprise in a regulated market actually accumulates is everything around the model: the named concepts it must detect and control, the evidence it must keep, the verification loops its auditors rely on. That layer has to survive every model swap underneath it. That layer is middleware, and it's what we build: explainable AI + safety and bias control, packaged as middleware.

The dial in this post is one verb in that layer. The full set runs on the same named concept directions, each already measured:

  • Detect — read a named concept off embeddings from models we did not train: coarse-concept AUROC 0.96–0.97 on frozen third-party backbones. Works on rented models; the verb that survives even when you can't touch the model at all.
  • Edit — shift or suppress a concept in an embedding before your pipeline consumes it, with collateral measured: 2–12× less movement of neighboring concepts than standard vector arithmetic (the precision-surgery result from the top of this post).
  • Steer — this post: change what a self-hosted model generates, per request, externally verifiable.
  • Certify — every verb above emits a signed record. This one is deliberately last: it's the delivery mechanism for the other three, not a capability in its own right.

And the dial belongs to users, not only to deployers. The same mechanism that lets a bank subtract a bias axis from a ranking is what would let a person steer their own recommendation feed.

Social platforms are the sharpest case. Today a feed's ranking model optimizes an objective the user never chose, tuned by dynamics the user can't see, with "controls" that amount to blunt topic mutes. A recommendation stack built on a middleware layer like this one could expose real dials — less outrage-adjacent framing, more of my actual interests, body-image-adjacent content to zero — each a named direction, applied to the user's own feed, logged so the platform can prove the setting was honored.

That's what user agency looks like when it's implemented in the representation rather than promised in a settings page. It's the consumer half of the public-interest argument: explainability and steering aren't only how businesses satisfy regulators — they're how users get a handle on systems that currently only handle them. The boundaries below still apply: dials work where concepts are strongly encoded, and platforms would need to expose the layer — precisely the kind of obligation a feed-transparency rule could name.

One boundary defines the whole map, and we'd rather over-communicate it: the dial requires access to the model's forward pass, so it works on self-hosted, open-weight models only. A closed API takes text in and returns text out; the arrow has nowhere to enter. Detection and editing work on anything that exposes embeddings, including rented models.

If your deployment is on-prem or open-weight — the standard posture in regulated industries — the full verb set applies to you. If you are wholly dependent on a closed API, the honest statement is that nobody can offer you representation-level control of that model except its provider, and your assurance story is whatever they publish. That asymmetry is worth knowing before you architect, and before you legislate.

Boundaries, stated plainly

  • Verified on self-hosted open models; effect sizes vary by model, scale, and configuration. We have not shown the weak concepts (gender, nationality, religion) can be made to respond with more tuning — only that they didn't at ours. Hate is not in that list any more: it was measured under a protocol we have since found invalid, the number is withdrawn, and the re-measurement is running (see the retraction above).
  • The dial shifts style and content emphasis. It is not a correctness tool, not a content guarantee, and not a guardrail on its own — pair it with detection and post-hoc classification.
  • Dialing a concept down at generation time is not removing it from the model. Removal claims need the editing-and-audit toolchain and their own evidence.
  • Strong dialing trades against fluency at extreme settings; production dials sit well inside the range shown here.
  • The independent-classifier verification is the load-bearing part of every claim above. Reproduce it, and if your judge disagrees with ours, we want to hear it.

Try the shape of it

The operator API's sandbox exposes the same named-direction interface used for embedding shifts (deterministic seeded vectors, no key needed) — see the Gemma 4 post for a paste-able example. The dial on self-hosted models is an engagement, not a sandbox: talk to us. If you're on the policy side and want a technical walkthrough of what's verifiable — with your own judge, on your own terms — that conversation is free.


Measurements in this post are from our July 2026 evaluation runs: direction-injection during generation on a self-hosted open-weight model (Qwen2.5-1.5B-Instruct), scored by independent sentiment and toxicity classifiers. Concept-level effects are probe-measured; the sentiment result is additionally externally classified. The collateral (cross-effects) numbers come from our July leakage-matrix evaluations across seeds and multiple third-party backbones. Full per-concept and per-example numbers available to reviewers and customers on request. The base technique is published activation-steering / representation-engineering research; our contribution is the named, versioned, signed, and independently verifiable operator layer built on top of it.

Continue reading

·27 min read

Hiding someone's name doesn't hide their race from an AI

Take the name off a résumé and an AI still works out the applicant's race — more reliably from the home address (right about 49 times out of 50) and the university (49 out of 50) than from the name it just lost (19 out of 20). We measured it on four open models with 500 matched pairs each, using real census figures for every ZIP code involved. Why race shows up in several places at once, why the standard fix — strip the name, score, then put it back — cannot work, and what is actually left in the vector after you try to remove it.

·10 min read

Finding wrong labels with 100 examples — what it caught, and what it missed

Every production NLP team is sitting on silver-labeled training data — auto-tagged at scale, noisy by design. We ranked candidate mislabels in a Zulu silver corpus using 100 hand-curated seeds and no sentiment supervision: top-10 precision 100% on an 80-sentence held-out validation set (small n — read the caveats), AUROC 0.732. The same Zulu-built axis, applied to AfriSenti Swahili gold, surfaces a mix of clear errors, policy-boundary cases, and our own false positives — all three shown.

·12 min read

Six AI models sound fair when you read their answers. Inside, they aren't.

We scored six open LLMs — LLaMA-2 7B (base and chat), Mistral 7B (base and instruct), Phi-2 2.7B, and InkubaLM 0.4B — on output-level stereotype preference, then probed InkubaLM's hidden states with the same 8-lens audit we ran on Gemma 4. The model that looks cleanest at the output level shows the same internal bias structure as the rest. Full numbers, small-sample caveats, and the parts we can't yet explain.