KI-Integration¶
AdaptiveLearner fährt jede Lern-Konversation durch zwei KI-
Aufrufe pro Roundtrip — einen für die Antwort, einen für den
Schritt-Bewerter. Drei Anbieter sind out-of-the-box dabei;
neue Anbieter klinken sich über den ai_complete-Hook ein.
Der ai_complete-Hook¶
# backend/app/hookspecs.py
@hookspec(firstresult=True)
def ai_complete(
messages: list[dict[str, Any]],
model: str,
api_key: str,
max_tokens: int = 1024,
) -> str | None:
"""Liefert den Assistant-Text oder None, wenn dieses Plugin ``model`` nicht behandelt."""
firstresult=True heißt: pluggy stoppt beim ersten Nicht-None-
Return. Jedes Anbieter-Plugin prüft den model-Präfix und
liefert den Text, wenn ihm das Modell gehört:
@hookimpl
def ai_complete(
self, messages, model, api_key, max_tokens
) -> str | None:
if not model.startswith("claude-"):
return None
# ... Anthropic-API aufrufen, Text zurückgeben ...
Drei Plugins gehören zum Standardumfang: ai-anthropic
(claude-), ai-openai (gpt-), ai-gemini (gemini-*).
Anbieter-Auswahl-Logik¶
Die Funktion _resolve_active_key() der Session-Route sucht:
- Den
UserSettings.active_providerdes Users (anthropic/openai/gemini). - Das passende
api_key_<provider>-Feld inUserSettings(beim Lesen mit Fernet entschlüsselt). - Das passende
model_override_<provider>-Feld (oder Fallback aufDEFAULT_MODELS[provider]).
Dann feuert sie ai_complete mit diesen Werten. Das Plugin
des passenden Anbieters liefert den Text; die anderen liefern
None (firstresult stoppt beim ersten Treffer).
Dual-Prompt-Architektur¶
Jeder POST /api/plugins/session/{id}/message für eine
user-Rolle macht zwei KI-Aufrufe:
- Lernantwort — nutzt den System-Prompt, den
build_prompt(project, profile, method, cycle_step, lang)aus der 42-Zellen-Matrix komponiert.max_tokens=1024. - Schritt-Bewerter — nutzt einen separaten System-Prompt
(
EVALUATION_SYSTEM_PROMPT), der die KI bittet, den Austausch zu lesen und ein JSON-Urteil zu emittieren (advance,confidence,reason,suggested_step).max_tokens=256.
Beide Aufrufe nutzen denselben Anbieter + Key. Das Urteil des
Bewerters treibt den cycle_step-Vorschub. Wenn der Bewerter
unparsbares JSON liefert, springt der deterministische
+1-Fallback (gedeckelt auf 7) und fallback_used=True wird
für Audits vermerkt.
Die 42-Zellen-Prompt-Matrix¶
plugins/adaptive-learner-plugin-session/adaptive_learner_session/prompts.py
hält ein dict[method, dict[step, dict[lang, str]]] — sechs
Methoden, sieben Schritte, zwei Sprachen, 84 Zellen. Jede
Zelle ist 1-2 Sätze, die die Rolle der KI und die Aufgabe des
Schritts setzen. Ein Kontextblock ("Lernprojekt: 'X' | Ziel:
'Y'. Profil-Hinweis: …") wird beim Komponieren angehängt.
Für den Dexie-Modus werden die Prompts wortgetreu nach
frontend/src/data/session-prompts.json exportiert und von
frontend/src/storage/prompts.ts geladen. Gleicher Text,
gleicher Kontextblock — kein Drift möglich.
Neuen Anbieter hinzufügen¶
plugins/adaptive-learner-plugin-ai-newprovider/anlegen.ai_complete-Hookimpl umsetzen: Modell-Präfix prüfen, HTTP-API des Anbieters rufen, Text zurückgeben.- Anbieter-Präfix in
DEFAULT_MODELSinai_orchestration.pymit billigem Default eintragen. - Anbieter-Name in
AIProvider-Enum inapp/schemas/__init__.pyergänzen. - In
AI_PROVIDERSinfrontend/src/lib/constants.tseintragen. - Für Dexie-Parität: Client zu
frontend/src/storage/ai-providers.tshinzufügen und ausaiComplete()dorthin routen.
Jedes Anbieter-Plugin testet seinen Hookimpl + HTTP-Aufruf
isoliert — siehe plugins/adaptive-learner-plugin-ai-anthropic/tests/
als Vorlage (der HTTP-Aufruf wird gemockt).
Browser-Direkt-Aufrufe (Dexie-Modus)¶
Im Dexie-Modus geht der KI-Aufruf nicht durch das Plugin-
System. storage/ai-providers.ts macht den HTTP-Request
direkt. Anthropic erfordert den
anthropic-dangerous-direct-browser-access: true-Header zur
CORS-Freigabe; OpenAI und Gemini akzeptieren direkte Browser-
Aufrufe standardmäßig.
Die Dual-Prompt-Logik ist in beiden Modi identisch —
storage/session-flow.ts ruft aiComplete() zweimal und
parst das Evaluator-JSON genau wie das Backend.
Konfidenz-Schwellenwert¶
session.step_evaluation.confidence_threshold in
backend/config/app.yaml (Standard 0.6) bestimmt, ob ein
echtes (Nicht-Fallback-)Bewerter-Urteil den Zyklus-Schritt
verschiebt. Höher = konservativer, niedriger = forscher.
Fallback-Urteile (Parse-Fehler) wenden den +1-Advance immer
an, unabhängig vom Schwellwert.
Der Dexie-Port spiegelt das mit einem hartkodierten 0.6 in
storage/session-flow.ts. Eine spätere Phase wird das in die
Einstellungen-UI heben.