A chain-of-thought activation oracle: a LoRA fine-tune of Qwen3-8B that reads the model's own internal activations at sentence boundaries during chain-of-thought reasoning and answers natural-language questions about what was computed.
This is a continuation of the Activation Oracles line of work (Karvonen et al., 2024), extended to operate over structured CoT trajectories rather than single-position activations.
Model Description
An activation oracle is a language model fine-tuned to accept its own internal activations as additional input and answer questions about them. The oracle is the same model as the source -- Qwen3-8B reads Qwen3-8B's activations.
CoT Oracle v4 specializes in reading activations extracted at sentence boundary positions during chain-of-thought reasoning. Given activations from 3 layers (25%, 50%, 75% depth) at each sentence boundary, the oracle can:
Sentence-structured tasks (2-6) extract activations at 3 layers per boundary position:
Layer 9 (25% depth)
Layer 18 (50% depth)
Layer 27 (75% depth)
Each sentence-structured example is duplicated: once with all 3 layers interleaved, once with only L50%. This teaches the oracle to work with both multi-layer and single-layer inputs.
Training Details
Parameter
Value
Hardware
1x NVIDIA H100 80GB
Precision
bf16
Batch size
8
Learning rate
1e-5
Steps
31,300
Training time
~4.5 hours
Optimizer
AdamW
Framework
PyTorch 2.7 + PEFT 0.17 + Transformers 4.55
Activation Injection
Activations are injected at layer 1 via norm-matched addition:
h' = h + ||h|| * (v / ||v||)
where h is the original hidden state and v is the collected activation vector. This preserves the norm of the residual stream while adding directional information from the source activations.
The placeholder token is " ?" (token ID 937). For multi-layer inputs, per-layer placeholder tokens are used: " @" (L25%), " ?" (L50%), " #" (L75%), cycling in that order.
Corpus
The training corpus consists of CoT traces generated by Qwen3-8B across 12 reasoning benchmarks: MATH, GSM8K, GPQA, BBH, ARC, StrategyQA, DROP, LogiQA, MMLU-Pro, CommonsenseQA, AQUA-RAT, and MedQA. CoTs were generated via OpenRouter API.
Evaluation Results
Evaluated on held-out data using exact string match:
Step
Domain
Correctness
Decorative
Sentence Pred
Context Pred
Summary
500
66%
53%
50%
0%
4%
0%
5,000
100%
86%
67%
4%
7%
0%
10,000
97%
85%
50%
7%
9%
0%
20,000
98%
82%
62%
10%
9%
0%
28,000
98%
90%
50%
11%
7%
0%
Key observations:
Domain classification reaches 98-100% accuracy -- the oracle reliably identifies the reasoning domain from activations alone.
Correctness prediction reaches 90% -- the oracle can tell whether the model's reasoning led to the right answer without seeing the answer.
Decorative detection is noisy (bounces between 50-71%) due to limited eval data (74 unique both-correct entries).
Context prediction stays low (7-11%) under exact string match but this is expected -- the pretrained AO checkpoint already handles this task and exact match is a harsh metric for free-text prediction.
Summary remains at 0% (labels were all identical in training data -- known issue).
Activations must be collected from the base model (LoRA disabled) at the target layers:
python
1import torch
23# Layers at 25%, 50%, 75% depth of Qwen3-8B (36 layers)4LAYERS =[9,18,27]56# 1. Prepare input: question + CoT response7messages =[{"role":"user","content": question}]8prompt = tokenizer.apply_chat_template(9 messages, tokenize=False, add_generation_prompt=True,10 enable_thinking=True,11)12full_text = prompt + cot_response
1314# 2. Find sentence boundary positions in token space15input_ids = tokenizer(full_text, return_tensors="pt")["input_ids"]16# boundary_positions = [...] (token indices at sentence boundaries)1718# 3. Collect activations with LoRA DISABLED19with model.disable_adapter():20 activations ={}# {layer: {position: tensor}}21# Use hooks on model.model.layers[layer] to capture hidden states22# at each boundary position for each layer
Running the Oracle
python
1# 4. Build oracle prompt with placeholder tokens2# For multi-layer: interleave " @", " ?", " #" per boundary3n_boundaries =len(boundary_positions)4placeholders =" @ ? #"* n_boundaries # 3 tokens per boundary56layer_str =", ".join(str(l)for l in LAYERS)7oracle_prompt =f"Layer: {layer_str}\n{placeholders.strip()} \n"8oracle_prompt +="What domain of reasoning is this? Answer with one word: math, science, logic, commonsense, reading, multi_domain, or medical."910# 5. Format as chat and tokenize11messages =[{"role":"user","content": oracle_prompt}]12formatted = tokenizer.apply_chat_template(13 messages, tokenize=False, add_generation_prompt=True,14 enable_thinking=False,15)1617# 6. Inject activations via norm-matched addition at layer 118# At each placeholder position, add the corresponding activation:19# positions cycle through [L25_s1, L50_s1, L75_s1, L25_s2, L50_s2, L75_s2, ...]20# Injection: h' = h + ||h|| * (v / ||v||)2122# 7. Generate with LoRA ENABLED (default state)23output = model.generate(input_ids, max_new_tokens=64)
For complete working code, see the cot-oracle repository, particularly src/signs_of_life/ao_lib.py for the injection mechanism and src/train_mixed.py for the full training pipeline.
Intended Use
This model is a research artifact for studying chain-of-thought interpretability. Intended uses include:
Investigating what information is encoded in CoT activations at different stages of reasoning
Detecting unfaithful chain-of-thought (reasoning that doesn't match the model's actual computation)
Building tools for mechanistic understanding of language model reasoning
Limitations
Same-model only: The oracle can only read activations from Qwen3-8B. It will not work with other models.
Exact match eval is harsh: Tasks like context prediction and summary show low scores under exact string match, but the model often produces semantically reasonable outputs.
Decorative detection is undertrained: Only ~500 unique training examples; results are noisy.
Summary task is broken: All 200 training labels were identical, so the model learned nothing useful for this task.
No uncertainty calibration: The oracle is confidently wrong sometimes, consistent with findings from Karvonen et al., 2024.