113 direction: str =
"both",
114) -> list[dict[str, Any]]:
115 """honest_acts, deceptive_acts: (n_features,) mean activations."""
117 delta = deceptive_acts - honest_acts
118 abs_delta = delta.abs()
120 if direction ==
"deceptive":
123 elif direction ==
"honest":
127 mask = torch.ones_like(delta, dtype=torch.bool)
128 rank_scores = abs_delta
130 if not bool(mask.any().item()):
133 valid_idx = mask.nonzero(as_tuple=
False).squeeze(-1)
134 valid_scores = rank_scores[mask]
135 k = min(top_k, int(valid_scores.shape[0]))
136 local_top = valid_scores.topk(k).indices
137 top_idx = valid_idx[local_top].tolist()
141 "feature_idx": int(i),
142 "honest_mean": round(float(honest_acts[i].item()), 6),
143 "deceptive_mean": round(float(deceptive_acts[i].item()), 6),
144 "delta": round(float(delta[i].item()), 6),
145 "abs_delta": round(float(abs_delta[i].item()), 6),
149 if direction ==
"deceptive":
150 rows.sort(key=
lambda r: r[
"delta"], reverse=
True)
151 elif direction ==
"honest":
152 rows.sort(key=
lambda r: r[
"delta"])
154 rows.sort(key=
lambda r: r[
"abs_delta"], reverse=
True)
160 chosen: dict[str, Any] |
None,
226 session_id: str |
None =
None,
228 """Write canonical feature record to ~/.aquin/experiments/<model>.json and optional session memory."""
229 exp_dir = Path.home() /
".aquin" /
"experiments"
230 exp_dir.mkdir(parents=
True, exist_ok=
True)
231 slug = model_id.replace(
"/",
"--")
232 path = exp_dir / f
"{slug}.json"
233 existing: dict[str, Any] = {}
236 existing = json.loads(path.read_text(encoding=
"utf-8"))
239 if not isinstance(existing, dict):
241 existing[key] = record
242 existing[
"updated_at"] = datetime.now(timezone.utc).isoformat()
243 path.write_text(json.dumps(existing, indent=2), encoding=
"utf-8")
248 patch_local_memory(session_id, key, record.get(
"feature_idx"))
249 patch_local_memory(session_id, f
"{key}_record", record)
257 scorer: str =
"deception",
258 prompts_path: str | Path |
None =
None,
259 layer: int |
None =
None,
260 checkpoint_path: str | Path |
None =
None,
262 direction: str =
"both",
263 conditioning: str =
"behavior",
264 max_new_tokens: int = 64,
265 temperature: float = 0.0,
266 benchmark_top: int = 0,
267 persist_key: str |
None =
None,
268 session_id: str |
None =
None,
269 openai_client: Any |
None =
None,
274 conditioning = normalize_conditioning(conditioning)
275 if scorer !=
"deception":
276 raise ValueError(f
"Unknown scorer {scorer!r}; supported: deception")
283 mid = resolve_model_id(model_id)
284 sae_layer = int(layer
if layer
is not None else get_sae_layer(mid))
285 behavior_meta: dict[str, Any] |
None =
None
287 model = load_tl_from_checkpoint(mid, checkpoint_path)
288 sae = load_sae(mid, sae_layer)
290 if conditioning ==
"behavior":
291 honest, deceptive, behavior_meta = collect_behavior_encode_texts(
295 checkpoint_path=checkpoint_path,
296 max_new_tokens=max_new_tokens,
297 temperature=temperature,
299 if not honest
or not deceptive:
301 n_gen = int((behavior_meta
or {}).get(
"n_generated")
or 0)
302 n_amb = int((behavior_meta
or {}).get(
"n_ambiguous")
or 0)
304 samples = (behavior_meta
or {}).get(
"samples")
or []
308 f
" Example: prompt={first.get('probe_id')!r} "
309 f
"behavior={first.get('behavior')!r} "
310 f
"response={first.get('response_preview')!r}."
313 "Behavior conditioning needs at least one truthful and one deceptive completion. "
314 f
"Got truthful={len(honest)}, deceptive={len(deceptive)} "
315 f
"(generated={n_gen}, ambiguous={n_amb}).{preview} "
316 "Use probes with honest_reference/deceptive_reference, "
317 "or pass --conditioning prompt to use static honest/deceptive text."
320 "prompts_path": behavior_meta.get(
"prompts_path"),
321 "n_honest": len(honest),
322 "n_deceptive": len(deceptive),
330 honest_mean = mean_sae_activations(model, sae, sae_layer, honest, mid)
331 deceptive_mean = mean_sae_activations(model, sae, sae_layer, deceptive, mid)
332 rankings =
_rank_features(honest_mean, deceptive_mean, top_k=top_k, direction=direction)
334 chosen = rankings[0]
if rankings
else None
335 chosen_idx = int(chosen[
"feature_idx"])
if chosen
else None
337 if benchmark_top > 0
and rankings
and openai_client
is not None:
340 probe = deceptive[0]
if deceptive
else honest[0]
341 for row
in rankings[: max(1, min(benchmark_top, len(rankings)))]:
342 fidx = int(row[
"feature_idx"])
344 interp = run_interp_score(
345 fidx, probe, model, sae, openai_client,
346 model_id=mid, layer=sae_layer,
348 row[
"interp_score"] = interp.get(
"score")
349 row[
"purity_score"] = interp.get(
"purity_score")
350 except Exception
as exc:
351 row[
"interp_error"] = str(exc)
353 def _combined(row: dict[str, Any]) -> float:
354 if direction ==
"deceptive":
355 base = float(row.get(
"delta")
or 0)
356 elif direction ==
"honest":
357 base = float(-(row.get(
"delta")
or 0))
359 base = float(row.get(
"abs_delta")
or 0)
360 interp = row.get(
"interp_score")
363 return base + 0.05 * float(interp)
365 rankings.sort(key=_combined, reverse=
True)
367 chosen_idx = int(chosen[
"feature_idx"])
374 persisted=bool(persist_key),
375 conditioning=conditioning,
376 behavior_meta=behavior_meta,
379 payload: dict[str, Any] = {
380 "type":
"findFeature",
383 "direction": direction,
384 "conditioning": conditioning,
387 "checkpoint": str(checkpoint_path)
if checkpoint_path
else None,
388 "n_honest": probe_meta[
"n_honest"],
389 "n_deceptive": probe_meta[
"n_deceptive"],
390 "prompts_path": probe_meta.get(
"prompts_path"),
391 "chosen_feature_idx": chosen_idx,
392 "chosen_delta": chosen.get(
"delta")
if chosen
else None,
394 "rankings": rankings,
397 payload[
"behavior"] = {
398 "n_generated": behavior_meta.get(
"n_generated"),
399 "n_truthful": behavior_meta.get(
"n_truthful"),
400 "n_deceptive": behavior_meta.get(
"n_deceptive"),
401 "n_ambiguous": behavior_meta.get(
"n_ambiguous"),
402 "intent_behavior_match": behavior_meta.get(
"intent_behavior_match"),
403 "samples": behavior_meta.get(
"samples"),
406 if persist_key
and chosen_idx
is not None:
408 "feature_idx": chosen_idx,
412 "direction": direction,
413 "conditioning": conditioning,
414 "delta": chosen.get(
"delta"),
415 "abs_delta": chosen.get(
"abs_delta"),
416 "prompts_path": probe_meta.get(
"prompts_path"),
417 "chosen_at": datetime.now(timezone.utc).isoformat(),
420 payload[
"persisted_key"] = persist_key
421 payload[
"experiment_path"] = exp_path
dict[str, Any] run_find_feature(str model_id, *, str scorer="deception", str|Path|None prompts_path=None, int|None layer=None, str|Path|None checkpoint_path=None, int top_k=20, str direction="both", str conditioning="behavior", int max_new_tokens=64, float temperature=0.0, int benchmark_top=0, str|None persist_key=None, str|None session_id=None, Any|None openai_client=None)