fix(brain): Gift-Waechter — Identity-Breaks nie persistieren (Kaskade unterbunden)
Wiederkehrender Identity-Bug: --system-prompt liefert die Persona korrekt (Proxy-
Test = "ICH BIN ARIA"), ABER ein einziger in der History gespeicherter Break
("ich bin nach wie vor Claude / die Persona ist erfunden") zieht bei schwachen
Folgeturns eine Kaskade nach sich — das Modell setzt seine eigene Ablehnung fort.
Das erste Cleanup verlangte "claude code" und liess deutsche Breaks durch.
Fix zweifach:
- prompts.py: looks_like_identity_break() — STARKE selbstreferenzielle Marker
(ich-bin-Claude / erfundene Persona / diese-Session-injiziert / nicht-real-in-
dieser). Bewusst NICHT das blosse "injizier"/"prompt injection" — das nutzt
ARIA in Pentest-Antworten legitim (verifiziert: 0 False Positives).
- agent.py: nach dem Claude-Loop Break-Check; bei Break Retry (Nondeterminismus
holt meist ARIA), sonst sichere ARIA-Fallback-Antwort — Break wird NIE
persistiert. Auch die lokale Fast-Lane eskaliert bei Break auf Claude.
- clean_poisoned_turns.py: auf denselben starken Matcher umgestellt (der breite
produzierte False Positives auf echte Security-Doku, im Dry-Run gesehen).
VM bereits bereinigt (je 2 Rest-Breaks aus conversation.jsonl + chat_backup).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+28
-3
@@ -26,7 +26,7 @@ from typing import Optional
|
||||
|
||||
from conversation import Conversation, Turn
|
||||
from memory import Embedder, VectorStore, MemoryPoint
|
||||
from prompts import build_system_prompt, IDENTITY_SEED, IDENTITY_ANCHOR
|
||||
from prompts import build_system_prompt, IDENTITY_SEED, IDENTITY_ANCHOR, looks_like_identity_break
|
||||
from proxy_client import ProxyClient, Message as ProxyMessage
|
||||
import router as router_mod
|
||||
from local_llm import local_llm_chat
|
||||
@@ -1100,8 +1100,10 @@ class Agent:
|
||||
self.conversation.add("assistant", content, project_id=active_project_id)
|
||||
return content
|
||||
|
||||
# Normalbetrieb: leere Antwort oder Escalation-Marker → Claude.
|
||||
if not content or router_mod.ESCALATE_MARKER in content:
|
||||
# Normalbetrieb: leer, Escalation-Marker oder (unwahrscheinlich) ein
|
||||
# Identity-Break → Claude uebernehmen.
|
||||
if (not content or router_mod.ESCALATE_MARKER in content
|
||||
or looks_like_identity_break(content)):
|
||||
logger.info("[router] lokal eskaliert → Claude")
|
||||
return None
|
||||
|
||||
@@ -1339,6 +1341,29 @@ class Agent:
|
||||
logger.warning("Konnte Error-Marker nicht persistieren: %s", add_exc)
|
||||
raise
|
||||
|
||||
# Gift-Waechter: faellt Claude trotz --system-prompt + Seed aus der Rolle
|
||||
# (Identity-Break), NICHT persistieren — sonst vergiftet dieser eine Turn
|
||||
# die History und loest bei schwachen Folgeturns eine Kaskade aus (das
|
||||
# Modell setzt seine eigene Ablehnung fort). Ein Retry holt per
|
||||
# Nondeterminismus meist die ARIA-Antwort; sonst sichere Fallback-Antwort.
|
||||
# So kann ein einzelner Ausrutscher nie snowballen.
|
||||
if looks_like_identity_break(final_reply):
|
||||
logger.warning("[guard] Identity-Break in Antwort erkannt — Retry")
|
||||
try:
|
||||
retry = self.proxy.chat_full(messages, tools=tools,
|
||||
project_id=active_project_id)
|
||||
retry_text = (retry.content or "").strip()
|
||||
except Exception as exc:
|
||||
logger.warning("[guard] Retry fehlgeschlagen: %s", exc)
|
||||
retry_text = ""
|
||||
if retry_text and not looks_like_identity_break(retry_text):
|
||||
logger.info("[guard] Retry lieferte saubere Antwort")
|
||||
final_reply = retry_text
|
||||
else:
|
||||
logger.warning("[guard] Retry weiter Break/leer — Fallback, Break NICHT persistiert")
|
||||
final_reply = ("Hey, ich bin ARIA. \U0001F60A Bei mir ist alles bereit — "
|
||||
"sag mir einfach, was du brauchst.")
|
||||
|
||||
# 7. Assistant-Turn (final reply) in die Conversation
|
||||
self.conversation.add("assistant", final_reply,
|
||||
project_id=active_project_id)
|
||||
|
||||
Reference in New Issue
Block a user