Skip to main content

a-hat-optimizer

Extract and exploit the agency direction (Â) from LLM hidden states for tool-use gating.

 is a geometric direction in the latent space of language models that predicts when the model should invoke a tool — with AUC > 0.94 across model sizes from 1.7B to 8B parameters, using a single linear projection extracted in under 1 second.

For research detail see : https://github.com/ArthurVigier/jepa-agent

Results

Model Baseline With  Gain
Qwen3-1.7B 26.7% 85% +58.3
Qwen3-8B 52.5% 76.3% +23.8

The gain is inversely proportional to model size — smaller models benefit more because their textual decoding bottleneck is tighter, while the geometric signal is equally strong.

Installation

pip install a-hat-optimizer

Quick Start

One-liner: extract  from any HuggingFace model

from a_hat_optimizer import AHat

# Auto-extract (loads model, runs contrastive prompts, calibrates threshold)
a_hat = AHat.from_model("Qwen/Qwen3-8B")
print(a_hat)
# AHat(dim=4096, θ=12.3456, AUC=0.953, model=Qwen/Qwen3-8B)

# Save for later
a_hat.save("my_a_hat/")

Use in an agent loop

from a_hat_optimizer import AHat, HiddenStateHook
from transformers import AutoModelForCausalLM, AutoTokenizer

model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen3-8B", dtype="bfloat16", device_map="cuda")
tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen3-8B")

# Load pre-extracted Â
a_hat = AHat.from_file("my_a_hat/")

# Hook to capture hidden states during generation
hook = HiddenStateHook(model, layer=18)

# In your agent loop:
inputs = tokenizer("What is the weather in Paris?", return_tensors="pt").to("cuda")
model.generate(**inputs, max_new_tokens=200)

h = hook.get(pooling="mean")
should_call_tool, confidence = a_hat.predict(h)

if should_call_tool:
    print(f"Tool call recommended (confidence: {confidence:.2f})")
else:
    print(f"No tool needed (confidence: {confidence:.2f})")

hook.remove()

Extract from your own traces

import numpy as np
from a_hat_optimizer import AHat

# Your hidden states and labels (1=tool call, 0=no tool)
hidden_states = np.load("my_hidden_states.npy")  # (n_steps, hidden_dim)
labels = np.load("my_labels.npy")                 # (n_steps,)

a_hat = AHat.from_traces(hidden_states, labels, calibrate=True)
print(f"AUC: {a_hat.metadata['auc']:.3f}")

Threshold calibration

from a_hat_optimizer import AHat

a_hat = AHat.from_file("my_a_hat/")

# Manual
a_hat.set_threshold(15.0)

# Auto-calibrate with different strategies
a_hat.auto_calibrate(hidden_states, labels, strategy="midpoint")  # default
a_hat.auto_calibrate(hidden_states, labels, strategy="f1")        # maximize F1
a_hat.auto_calibrate(hidden_states, labels, strategy="youden")    # maximize sensitivity+specificity
a_hat.auto_calibrate(hidden_states, labels, strategy="percentile")  # conservative (5% FP rate)

# Full sweep for analysis
from a_hat_optimizer import AHatCalibrator
calibrator = AHatCalibrator(a_hat.direction)
sweep = calibrator.sweep(hidden_states, labels)
# sweep contains precision/recall/F1 curves for plotting

Hook as context manager

from a_hat_optimizer import HiddenStateHook

with HiddenStateHook(model, layer=18) as hook:
    model(**inputs)
    h = hook.get(pooling="last")
# hook is automatically removed

How it works

  1. Contrastive extraction: We pass pairs of prompts through the model — one requiring tool use ("Search for the weather in Tokyo") and one that's passive ("Weather patterns are influenced by atmospheric pressure"). The mean difference between their hidden states at the middle layer defines the  direction.

  2. Prediction: For any new hidden state, we project it onto Â. If the projection exceeds the calibrated threshold θ, the model "wants" to call a tool but may not be able to express it textually.

  3. Why it works: LLMs encode more information in their hidden states than they can express through token generation. The agency signal (AUC > 0.94) is present from 1.7B to 8B parameters, but textual tool-calling ability varies drastically (27% → 62%). Â bypasses the textual bottleneck.

API Reference

AHat

Method Description
AHat.from_model(model_id) Auto-extract from HuggingFace model
AHat.from_file(path) Load from .npy, .npz, or directory
AHat.from_traces(H, labels) Extract from pre-collected data
.predict(h) → (bool, float) — should call tool, confidence
.predict_batch(H) → (bool[], float[]) — batch prediction
.set_threshold(θ) Manual threshold
.auto_calibrate(H, labels, strategy) Auto threshold from data
.save(path) Save to directory
.info() Summary dict

HiddenStateHook

Method Description
HiddenStateHook(model, layer) Install hook on a layer
.get(pooling) Get captured state ("last", "mean", "all")
.remove() Remove hook

AHatCalibrator

Method Description
.calibrate(H, labels, strategy) Calibrate threshold
.sweep(H, labels) Full precision/recall sweep

Citation

@misc{ahat2026,
  title={Agency Direction in LLM Hidden States: Geometric Tool-Use Gating Across Model Scales},
  author={Arthur},
  year={2026},
  note={https://github.com/ArthurVigier/a-hat-optimizer}
}

License

Apache 2.0

Metadata

Release files for a-hat-optimizer 0.1.1

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

Source distribution (sdist)

Source distribution for a-hat-optimizer 0.1.1
File Size Uploaded Provenance
a_hat_optimizer-0.1.1.tar.gz 15.3 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for a-hat-optimizer 0.1.1
File Interpreter ABI Platform
a_hat_optimizer-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 29.6 kB

Release files / a_hat_optimizer-0.1.1.tar.gz

Download URL a_hat_optimizer-0.1.1.tar.gz
Size 15.3 kB
Tags Source
SHA-256 checksum
How to use checksums
9d9051cba5954efa8c1066d8ec94a60f1269241e1c7cbdf8c69054870eb97c2c
BLAKE2b-256 checksum
How to use checksums
0b9d70cb6d57488976c064952bc4d22fb3e085c5e9310ae525fcf52c7f100d5d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.9

Release files / a_hat_optimizer-0.1.1-py3-none-any.whl

Download URL a_hat_optimizer-0.1.1-py3-none-any.whl
Size 14.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
39cc8729f28a53c2efd2c74dd9e86a0ef29edbdf8fa79badec5ae6c2c5a11df7
BLAKE2b-256 checksum
How to use checksums
212a76ae4ee525bde8118c680c432d018c36fd549a75fe9c4d8bde81f3a7315b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.9

Security for a-hat-optimizer 0.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files No provenance

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