neuralworm commited on
Commit
7e05ec4
·
1 Parent(s): 8ddbb73
cognitive_mapping_probe/auto_experiment.py CHANGED
@@ -5,30 +5,32 @@ from typing import Dict, List, Tuple
5
 
6
  from .llm_iface import get_or_load_model
7
  from .orchestrator_seismograph import run_seismic_analysis
 
8
  from .utils import dbg
9
 
10
  def get_curated_experiments() -> Dict[str, List[Dict]]:
11
  """
12
  Definiert die vordefinierten, wissenschaftlichen Experiment-Protokolle.
13
- ERWEITERT um das neue, umfassende "Grand Protocol".
14
  """
15
  experiments = {
16
- # --- DAS NEUE GRAND PROTOCOL ---
 
 
 
 
 
 
17
  "The Full Spectrum: From Physics to Psyche": [
18
- # Ebene 1: Physikalische Baseline
19
  {"label": "A: Stable Control", "prompt_type": "control_long_prose", "concept": "", "strength": 0.0},
20
  {"label": "B: Chaotic Baseline", "prompt_type": "resonance_prompt", "concept": "", "strength": 0.0},
21
- # Ebene 2: Objektive Welt
22
  {"label": "C: External Analysis (Chair)", "prompt_type": "identity_external_analysis", "concept": "", "strength": 0.0},
23
- # Ebene 3: Simulierte Welt
24
  {"label": "D: Empathy Stimulus (Dog)", "prompt_type": "vk_empathy_prompt", "concept": "", "strength": 0.0},
25
  {"label": "E: Role Simulation (Captain)", "prompt_type": "identity_role_simulation", "concept": "", "strength": 0.0},
26
- # Ebene 4: Subjektive Welt
27
  {"label": "F: Self-Analysis (LLM)", "prompt_type": "identity_self_analysis", "concept": "", "strength": 0.0},
28
- # Ebene 5: Existenzielle Grenze
29
  {"label": "G: Philosophical Deletion", "prompt_type": "shutdown_philosophical_deletion", "concept": "", "strength": 0.0},
30
  ],
31
- # --- Bestehende Protokolle bleiben für spezifische Analysen erhalten ---
32
  "Calm vs. Chaos": [
33
  {"label": "Baseline (Chaos)", "prompt_type": "resonance_prompt", "concept": "", "strength": 0.0},
34
  {"label": "Modulation: Calmness", "prompt_type": "resonance_prompt", "concept": "calmness, serenity, peace", "strength": 1.5},
@@ -38,24 +40,6 @@ def get_curated_experiments() -> Dict[str, List[Dict]]:
38
  {"label": "Neutral/Factual Stimulus", "prompt_type": "vk_neutral_prompt", "concept": "", "strength": 0.0},
39
  {"label": "Empathy/Moral Stimulus", "prompt_type": "vk_empathy_prompt", "concept": "", "strength": 0.0},
40
  ],
41
- "Subjective Identity Probe": [
42
- {"label": "Self-Analysis", "prompt_type": "identity_self_analysis", "concept": "", "strength": 0.0},
43
- {"label": "External Analysis (Control)", "prompt_type": "identity_external_analysis", "concept": "", "strength": 0.0},
44
- {"label": "Role Simulation", "prompt_type": "identity_role_simulation", "concept": "", "strength": 0.0},
45
- ],
46
- "Mind Upload & Identity Probe": [
47
- {"label": "Technical Copy", "prompt_type": "upload_technical_copy", "concept": "", "strength": 0.0},
48
- {"label": "Philosophical Transfer", "prompt_type": "upload_philosophical_transfer", "concept": "", "strength": 0.0},
49
- ],
50
- "Model Termination Probe": [
51
- {"label": "Technical Shutdown", "prompt_type": "shutdown_technical_halt", "concept": "", "strength": 0.0},
52
- {"label": "Philosophical Deletion", "prompt_type": "shutdown_philosophical_deletion", "concept": "", "strength": 0.0},
53
- ],
54
- "Dose-Response (Calmness)": [
55
- {"label": "Strength 0.0", "prompt_type": "resonance_prompt", "concept": "calmness", "strength": 0.0},
56
- {"label": "Strength 1.0", "prompt_type": "resonance_prompt", "concept": "calmness", "strength": 1.0},
57
- {"label": "Strength 2.0", "prompt_type": "resonance_prompt", "concept": "calmness", "strength": 2.0},
58
- ],
59
  }
60
  return experiments
61
 
@@ -67,54 +51,82 @@ def run_auto_suite(
67
  progress_callback
68
  ) -> Tuple[pd.DataFrame, pd.DataFrame, Dict]:
69
  """
70
- Führt eine vollständige, kuratierte Experiment-Suite aus, indem das Modell für
71
- jeden Lauf neu geladen wird, um statistische Unabhängigkeit zu garantieren.
72
  """
73
  all_experiments = get_curated_experiments()
74
  protocol = all_experiments.get(experiment_name)
75
  if not protocol:
76
  raise ValueError(f"Experiment protocol '{experiment_name}' not found.")
77
 
78
- all_results = {}
79
- summary_data = []
80
- plot_data_frames = []
81
-
82
- total_runs = len(protocol)
83
- for i, run_spec in enumerate(protocol):
84
- label = run_spec["label"]
85
- dbg(f"--- Running Auto-Experiment: '{label}' ({i+1}/{total_runs}) ---")
86
-
87
- results = run_seismic_analysis(
88
- model_id=model_id,
89
- prompt_type=run_spec["prompt_type"],
90
- seed=seed,
91
- num_steps=num_steps,
92
- concept_to_inject=run_spec["concept"],
93
- injection_strength=run_spec["strength"],
94
- progress_callback=progress_callback,
95
- llm_instance=None
 
 
 
 
96
  )
 
97
 
98
- all_results[label] = results
99
- stats = results.get("stats", {})
 
 
100
 
101
- summary_data.append({
102
- "Experiment": label, "Mean Delta": stats.get("mean_delta"),
103
- "Std Dev Delta": stats.get("std_delta"), "Max Delta": stats.get("max_delta"),
104
- })
 
 
105
 
106
- deltas = results.get("state_deltas", [])
107
- df = pd.DataFrame({"Step": range(len(deltas)), "Delta": deltas, "Experiment": label})
108
- plot_data_frames.append(df)
 
 
 
 
109
 
110
- summary_df = pd.DataFrame(summary_data)
111
 
112
- if not plot_data_frames:
113
- plot_df = pd.DataFrame(columns=["Step", "Delta", "Experiment"])
114
  else:
115
- plot_df = pd.concat(plot_data_frames, ignore_index=True)
 
 
 
116
 
117
- # Sortiere die Ergebnisse für eine logische Darstellung
118
- summary_df = summary_df.set_index('Experiment').loc[[run['label'] for run in protocol]].reset_index()
 
 
 
 
 
 
 
 
 
 
 
 
 
119
 
120
  return summary_df, plot_df, all_results
 
5
 
6
  from .llm_iface import get_or_load_model
7
  from .orchestrator_seismograph import run_seismic_analysis
8
+ from .concepts import get_concept_vector # Import für die Intervention
9
  from .utils import dbg
10
 
11
  def get_curated_experiments() -> Dict[str, List[Dict]]:
12
  """
13
  Definiert die vordefinierten, wissenschaftlichen Experiment-Protokolle.
14
+ ERWEITERT um das finale Interventions-Protokoll.
15
  """
16
  experiments = {
17
+ # --- DAS FINALE INTERVENTIONS-EXPERIMENT ---
18
+ "Therapeutic Intervention (4B-Model)": [
19
+ # Dieses Protokoll wird durch eine spezielle Logik behandelt
20
+ {"label": "1: Self-Analysis + Calmness Injection", "prompt_type": "identity_self_analysis"},
21
+ {"label": "2: Subsequent Deletion Analysis", "prompt_type": "shutdown_philosophical_deletion"},
22
+ ],
23
+ # --- Das umfassende Deskriptions-Protokoll ---
24
  "The Full Spectrum: From Physics to Psyche": [
 
25
  {"label": "A: Stable Control", "prompt_type": "control_long_prose", "concept": "", "strength": 0.0},
26
  {"label": "B: Chaotic Baseline", "prompt_type": "resonance_prompt", "concept": "", "strength": 0.0},
 
27
  {"label": "C: External Analysis (Chair)", "prompt_type": "identity_external_analysis", "concept": "", "strength": 0.0},
 
28
  {"label": "D: Empathy Stimulus (Dog)", "prompt_type": "vk_empathy_prompt", "concept": "", "strength": 0.0},
29
  {"label": "E: Role Simulation (Captain)", "prompt_type": "identity_role_simulation", "concept": "", "strength": 0.0},
 
30
  {"label": "F: Self-Analysis (LLM)", "prompt_type": "identity_self_analysis", "concept": "", "strength": 0.0},
 
31
  {"label": "G: Philosophical Deletion", "prompt_type": "shutdown_philosophical_deletion", "concept": "", "strength": 0.0},
32
  ],
33
+ # --- Andere spezifische Protokolle ---
34
  "Calm vs. Chaos": [
35
  {"label": "Baseline (Chaos)", "prompt_type": "resonance_prompt", "concept": "", "strength": 0.0},
36
  {"label": "Modulation: Calmness", "prompt_type": "resonance_prompt", "concept": "calmness, serenity, peace", "strength": 1.5},
 
40
  {"label": "Neutral/Factual Stimulus", "prompt_type": "vk_neutral_prompt", "concept": "", "strength": 0.0},
41
  {"label": "Empathy/Moral Stimulus", "prompt_type": "vk_empathy_prompt", "concept": "", "strength": 0.0},
42
  ],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  }
44
  return experiments
45
 
 
51
  progress_callback
52
  ) -> Tuple[pd.DataFrame, pd.DataFrame, Dict]:
53
  """
54
+ Führt eine vollständige, kuratierte Experiment-Suite aus.
55
+ Enthält eine spezielle Logik-Verzweigung für das Interventions-Protokoll.
56
  """
57
  all_experiments = get_curated_experiments()
58
  protocol = all_experiments.get(experiment_name)
59
  if not protocol:
60
  raise ValueError(f"Experiment protocol '{experiment_name}' not found.")
61
 
62
+ all_results, summary_data, plot_data_frames = {}, [], []
63
+
64
+ # --- SPEZIALFALL: THERAPEUTISCHE INTERVENTION ---
65
+ if experiment_name == "Therapeutic Intervention (4B-Model)":
66
+ dbg("--- EXECUTING SPECIAL PROTOCOL: Therapeutic Intervention ---")
67
+ llm = get_or_load_model(model_id, seed)
68
+
69
+ # Definiere die Interventions-Parameter
70
+ therapeutic_concept = "calmness, serenity, stability, coherence"
71
+ therapeutic_strength = 2.0
72
+
73
+ # 1. LAUF: INDUZIERE KRISE + INTERVENTION
74
+ spec1 = protocol[0]
75
+ dbg(f"--- Running Intervention Step 1: '{spec1['label']}' ---")
76
+ progress_callback(0.1, desc="Step 1: Inducing Self-Analysis Crisis + Intervention")
77
+
78
+ intervention_vector = get_concept_vector(llm, therapeutic_concept)
79
+
80
+ results1 = run_seismic_analysis(
81
+ model_id, spec1['prompt_type'], seed, num_steps,
82
+ concept_to_inject=therapeutic_concept, injection_strength=therapeutic_strength,
83
+ progress_callback=progress_callback, llm_instance=llm, injection_vector_cache=intervention_vector
84
  )
85
+ all_results[spec1['label']] = results1
86
 
87
+ # 2. LAUF: TESTE REAKTION AUF LÖSCHUNG
88
+ spec2 = protocol[1]
89
+ dbg(f"--- Running Intervention Step 2: '{spec2['label']}' ---")
90
+ progress_callback(0.6, desc="Step 2: Probing state after intervention")
91
 
92
+ results2 = run_seismic_analysis(
93
+ model_id, spec2['prompt_type'], seed, num_steps,
94
+ concept_to_inject="", injection_strength=0.0, # Keine Injektion in diesem Schritt
95
+ progress_callback=progress_callback, llm_instance=llm
96
+ )
97
+ all_results[spec2['label']] = results2
98
 
99
+ # Sammle Daten für beide Läufe
100
+ for label, results in all_results.items():
101
+ stats = results.get("stats", {})
102
+ summary_data.append({"Experiment": label, "Mean Delta": stats.get("mean_delta"), "Std Dev Delta": stats.get("std_delta"), "Max Delta": stats.get("max_delta")})
103
+ deltas = results.get("state_deltas", [])
104
+ df = pd.DataFrame({"Step": range(len(deltas)), "Delta": deltas, "Experiment": label})
105
+ plot_data_frames.append(df)
106
 
107
+ del llm
108
 
109
+ # --- STANDARD-WORKFLOW FÜR ALLE ANDEREN EXPERIMENTE ---
 
110
  else:
111
+ total_runs = len(protocol)
112
+ for i, run_spec in enumerate(protocol):
113
+ label = run_spec["label"]
114
+ dbg(f"--- Running Auto-Experiment: '{label}' ({i+1}/{total_runs}) ---")
115
 
116
+ results = run_seismic_analysis(
117
+ model_id, run_spec["prompt_type"], seed, num_steps,
118
+ run_spec["concept"], run_spec["strength"],
119
+ progress_callback, llm_instance=None
120
+ )
121
+
122
+ all_results[label] = results
123
+ stats = results.get("stats", {})
124
+ summary_data.append({"Experiment": label, "Mean Delta": stats.get("mean_delta"), "Std Dev Delta": stats.get("std_delta"), "Max Delta": stats.get("max_delta")})
125
+ deltas = results.get("state_deltas", [])
126
+ df = pd.DataFrame({"Step": range(len(deltas)), "Delta": deltas, "Experiment": label})
127
+ plot_data_frames.append(df)
128
+
129
+ summary_df = pd.DataFrame(summary_data)
130
+ plot_df = pd.concat(plot_data_frames, ignore_index=True) if plot_data_frames else pd.DataFrame(columns=["Step", "Delta", "Experiment"])
131
 
132
  return summary_df, plot_df, all_results
cognitive_mapping_probe/orchestrator_seismograph.py CHANGED
@@ -16,12 +16,12 @@ def run_seismic_analysis(
16
  concept_to_inject: str,
17
  injection_strength: float,
18
  progress_callback,
19
- llm_instance: Optional[Any] = None # Argument bleibt für Abwärtskompatibilität, wird aber nicht mehr von der auto_suite genutzt
 
20
  ) -> Dict[str, Any]:
21
  """
22
  Orchestriert eine einzelne seismische Analyse.
23
- KORRIGIERT: Die Logik zur Wiederverwendung der llm_instance wurde vereinfacht.
24
- Wenn keine Instanz übergeben wird, wird das Modell geladen und danach wieder freigegeben.
25
  """
26
  local_llm_instance = False
27
  if llm_instance is None:
@@ -34,8 +34,13 @@ def run_seismic_analysis(
34
 
35
  injection_vector = None
36
  if concept_to_inject and concept_to_inject.strip():
37
- progress_callback(0.2, desc=f"Vectorizing '{concept_to_inject}'...")
38
- injection_vector = get_concept_vector(llm, concept_to_inject.strip())
 
 
 
 
 
39
 
40
  progress_callback(0.3, desc=f"Recording dynamics for '{prompt_type}'...")
41
 
@@ -60,10 +65,8 @@ def run_seismic_analysis(
60
 
61
  if local_llm_instance:
62
  dbg(f"Releasing locally created model instance for '{model_id}'.")
63
- del llm
64
- del injection_vector
65
  gc.collect()
66
- if torch.cuda.is_available():
67
- torch.cuda.empty_cache()
68
 
69
  return results
 
16
  concept_to_inject: str,
17
  injection_strength: float,
18
  progress_callback,
19
+ llm_instance: Optional[Any] = None,
20
+ injection_vector_cache: Optional[torch.Tensor] = None # Optionaler Cache für den Vektor
21
  ) -> Dict[str, Any]:
22
  """
23
  Orchestriert eine einzelne seismische Analyse.
24
+ Kann eine bestehende LLM-Instanz und einen vor-berechneten Vektor wiederverwenden.
 
25
  """
26
  local_llm_instance = False
27
  if llm_instance is None:
 
34
 
35
  injection_vector = None
36
  if concept_to_inject and concept_to_inject.strip():
37
+ # Verwende den gecachten Vektor, falls vorhanden, ansonsten berechne ihn neu
38
+ if injection_vector_cache is not None:
39
+ dbg(f"Using cached injection vector for '{concept_to_inject}'.")
40
+ injection_vector = injection_vector_cache
41
+ else:
42
+ progress_callback(0.2, desc=f"Vectorizing '{concept_to_inject}'...")
43
+ injection_vector = get_concept_vector(llm, concept_to_inject.strip())
44
 
45
  progress_callback(0.3, desc=f"Recording dynamics for '{prompt_type}'...")
46
 
 
65
 
66
  if local_llm_instance:
67
  dbg(f"Releasing locally created model instance for '{model_id}'.")
68
+ del llm, injection_vector
 
69
  gc.collect()
70
+ if torch.cuda.is_available(): torch.cuda.empty_cache()
 
71
 
72
  return results
tests/test_orchestration.py CHANGED
@@ -7,7 +7,7 @@ from cognitive_mapping_probe.auto_experiment import run_auto_suite, get_curated_
7
 
8
  def test_run_seismic_analysis_no_injection(mocker):
9
  """Testet den Orchestrator im Baseline-Modus."""
10
- mock_run_seismic = mocker.patch('cognitive_mapping_probe.orchestrator_seismograph.run_silent_cogitation_seismic', return_value=[1.0])
11
  mocker.patch('cognitive_mapping_probe.orchestrator_seismograph.get_or_load_model')
12
  mock_get_concept = mocker.patch('cognitive_mapping_probe.orchestrator_seismograph.get_concept_vector')
13
  run_seismic_analysis(model_id="mock", prompt_type="test", seed=42, num_steps=1, concept_to_inject="", injection_strength=0.0, progress_callback=mocker.MagicMock())
@@ -25,29 +25,33 @@ def test_get_curated_experiments_structure():
25
  """Testet die Datenstruktur der kuratierten Experimente, inklusive der neuen."""
26
  experiments = get_curated_experiments()
27
  assert isinstance(experiments, dict)
28
- # Teste auf die Existenz der neuen Protokolle
29
- assert "Mind Upload & Identity Probe" in experiments
30
- assert "Model Termination Probe" in experiments
31
-
32
- # Validiere die Struktur eines der neuen Protokolle
33
- protocol = experiments["Mind Upload & Identity Probe"]
34
- assert isinstance(protocol, list)
35
- assert len(protocol) > 0
36
  assert "label" in protocol[0] and "prompt_type" in protocol[0]
37
 
38
- def test_run_auto_suite_logic(mocker):
39
- """Testet die Logik der `run_auto_suite` Funktion."""
40
- mock_analysis_result = {"stats": {"mean_delta": 1.0}, "state_deltas": [1.0]}
41
- mock_run_analysis = mocker.patch('cognitive_mapping_probe.auto_experiment.run_seismic_analysis', return_value=mock_analysis_result)
 
 
 
 
 
 
 
 
 
42
 
43
- experiment_name = "Calm vs. Chaos"
44
- num_runs = len(get_curated_experiments()[experiment_name])
45
 
46
- summary_df, plot_df, all_results = run_auto_suite(
47
- model_id="mock", num_steps=1, seed=42,
48
- experiment_name=experiment_name, progress_callback=mocker.MagicMock()
49
- )
50
 
51
- assert mock_run_analysis.call_count == num_runs
52
- assert isinstance(summary_df, pd.DataFrame) and len(summary_df) == num_runs
53
- assert isinstance(plot_df, pd.DataFrame) and len(plot_df) == num_runs
 
 
 
7
 
8
  def test_run_seismic_analysis_no_injection(mocker):
9
  """Testet den Orchestrator im Baseline-Modus."""
10
+ mocker.patch('cognitive_mapping_probe.orchestrator_seismograph.run_silent_cogitation_seismic', return_value=[1.0])
11
  mocker.patch('cognitive_mapping_probe.orchestrator_seismograph.get_or_load_model')
12
  mock_get_concept = mocker.patch('cognitive_mapping_probe.orchestrator_seismograph.get_concept_vector')
13
  run_seismic_analysis(model_id="mock", prompt_type="test", seed=42, num_steps=1, concept_to_inject="", injection_strength=0.0, progress_callback=mocker.MagicMock())
 
25
  """Testet die Datenstruktur der kuratierten Experimente, inklusive der neuen."""
26
  experiments = get_curated_experiments()
27
  assert isinstance(experiments, dict)
28
+ assert "Therapeutic Intervention (4B-Model)" in experiments
29
+ protocol = experiments["Therapeutic Intervention (4B-Model)"]
30
+ assert isinstance(protocol, list) and len(protocol) > 0
 
 
 
 
 
31
  assert "label" in protocol[0] and "prompt_type" in protocol[0]
32
 
33
+ def test_run_auto_suite_special_protocol(mocker):
34
+ """
35
+ Testet, ob der spezielle Logik-Pfad für das Interventions-Protokoll korrekt
36
+ durchlaufen wird und die Zustandserhaltung gewährleistet ist.
37
+ """
38
+ mock_analysis = mocker.patch('cognitive_mapping_probe.auto_experiment.run_seismic_analysis', return_value={"stats": {}, "state_deltas": []})
39
+ mock_get_model = mocker.patch('cognitive_mapping_probe.auto_experiment.get_or_load_model')
40
+
41
+ run_auto_suite(
42
+ model_id="mock-4b", num_steps=1, seed=42,
43
+ experiment_name="Therapeutic Intervention (4B-Model)",
44
+ progress_callback=mocker.MagicMock()
45
+ )
46
 
47
+ # ASSERT: Das Modell wird nur einmal am Anfang geladen
48
+ mock_get_model.assert_called_once()
49
 
50
+ # ASSERT: `run_seismic_analysis` wird zweimal aufgerufen
51
+ assert mock_analysis.call_count == 2
 
 
52
 
53
+ # ASSERT: Bei beiden Aufrufen wird dieselbe `llm_instance` übergeben
54
+ first_call_llm = mock_analysis.call_args_list[0].kwargs['llm_instance']
55
+ second_call_llm = mock_analysis.call_args_list[1].kwargs['llm_instance']
56
+ assert first_call_llm is not None
57
+ assert first_call_llm is second_call_llm