KI-Integration¶
Adaptive Learner fährt jede Lern-Konversation durch bis zu
drei KI-Aufrufe pro Roundtrip — die gestreamte Antwort, den
Schritt-Bewerter und (bei Schritt 7) den
Themen-Übergangs-Bewerter. Drei Anbieter sind out-of-the-box
dabei; neue Anbieter klinken sich über die ai_complete*-Hook-
Familie 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-*).
Async- + Streaming-Varianten¶
@hookspec(firstresult=True)
async def ai_complete_async(messages, model, api_key, max_tokens) -> str:
"""Awaitable; gleiche Form wie ai_complete. v1.5.0+."""
@hookspec(firstresult=True)
def ai_complete_stream(messages, model, api_key, max_tokens):
"""Liefert einen Async-Iterator von Text-Deltas. v1.6.0+."""
ai_complete_async wird von der Session-Route an der Zyklus-
Grenze Schritt 6→7 genutzt, damit Schritt-Bewertung und
Themen-Übergang gleichzeitig über asyncio.gather feuern
(async_evaluation: true in app.yaml).
ai_complete_stream treibt den Streaming-SSE-Endpunkt
POST /api/plugins/session/{id}/message/stream, der
start / chunk / done-Events emittiert.
Anbieter-Auswahl-Logik (v1.20.0)¶
Die _resolve_active_key() der Session-Route ruft
services/settings.resolve_api_key(db, user_id, provider) auf,
das die Drei-Schichten-Kette durchläuft:
ADAPTIVE_LEARNER_<PROVIDER>_API_KEY-Umgebungsvariable.ai.<provider>.api_keyin~/.config/adaptive_learner/secrets.yaml.- Fernet-entschlüsseltes
UserSettings.api_key_<provider>. None— der Aufruf reichtai_erroran die UI durch.
resolve_default_model(db, user_id, provider) durchläuft
dieselbe Kette für das Modell-Override (env > yaml > UI-Override >
DEFAULT_MODELS[provider]).
Dann feuert ai_complete* mit den aufgelösten Werten. Das Plugin
des passenden Anbieters liefert den Text; die anderen liefern
None (firstresult stoppt beim ersten Treffer).
Dual-Prompt-Architektur (v0.5.0) + Auto-Loop (v1.4.0)¶
Jeder POST /api/plugins/session/{id}/message für eine
user-Rolle macht bis zu drei KI-Aufrufe:
- Lernantwort — gestreamt über
ai_complete_stream. Der System-Prompt wird vonbuild_prompt(project, profile, method, cycle_step, lang)aus der 42-Zellen-Matrix komponiert, mit einer explizit angehängten "antworte in der Sprache des Lernenden"-Direktive (build_language_directive(lang), #827 — siehe unten).max_tokens=1024. SSE emittiertstart/chunk/done-Events. - Schritt-Bewerter — separater 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. Das Urteil des Bewerters treibt dencycle_step-Vorschub (gedeckelt durchconfidence ≥ 0.6). - Themen-Übergang — nur bei Schritt 7. Ein dritter KI-Aufruf
beurteilt, ob das Thema integriert wurde und ob ein neuer
Zyklus auf einem neuen Unterthema starten soll. Deckel von
max_cycles=5pro Session.
Wenn der Bewerter unparsbares JSON liefert, springt der
deterministische +1-Fallback (gedeckelt auf 7) und
fallback_used=True wird vermerkt.
Die Zyklus-Grenze (Schritt 6 → 7) feuert Schritt-Bewertung +
Themen-Übergang gleichzeitig über asyncio.gather (spart ~T₂ an
Latenz). Im timings-Block der Message-Antwort zurückgegeben
(learning_ms, evaluation_ms, topic_transition_ms,
total_ms, parallel_saved_ms).
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/ai/prompts.ts geladen. Gleicher Text,
gleicher Kontextblock — kein Drift möglich.
Ausgabesprachen-Direktive (#827)¶
Die 42-Zellen-Matrix ist nur in zwei Sprachen (de / en) geschrieben, daher würde ein Lernender mit einer der anderen UI-Sprachen einen englischen Prompt bekommen und englisch beantwortet werden. Dagegen wird eine explizite "antworte in der Sprache des Lernenden"-Anweisung an den komponierten System- Prompt angehängt. Sie benennt die Sprache des Lernenden (englischer Name + Endonym), damit die KI in ihr antwortet, unabhängig davon, in welcher Sprache der Prompt selbst geschrieben ist — über alle 11 UI-Sprachen.
Das Backend baut sie in
plugins/adaptive-learner-plugin-session/.../prompts.py
(LANGUAGE_NAMES + build_language_directive(lang)); der Dexie-
Port ist byte-identisch in
frontend/src/storage/ai/prompts.ts
(buildLanguageDirective(lang)), damit die zwei Modi nie
auseinanderlaufen.
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/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/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/ai/session-flow.ts ruft aiComplete() zweimal und
parst das Evaluator-JSON genau wie das Backend. Jedes
browser-direkte KI-Feature liegt unter frontend/src/lib/ai/
(die reinen Engines) und frontend/src/storage/ai/ (die
Anbieter-Clients), damit das GitHub-Pages-Deployment die
gesamte KI-Oberfläche ohne Backend ausführt.
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/ai/session-flow.ts. Eine spätere Phase wird das in die
Einstellungen-UI heben.
KI-Übungsgenerierungs-Pipeline (EXP-036 / AIX-01..06)¶
Eine reine Theorie-Lektion kann Übungen von der KI verfasst
bekommen. Die Pipeline ist generate -> quality-gate -> balance
-> feedback, alles unter frontend/src/lib/ai/. Die Engines
sind library-grade (keine App-State-Imports) und nehmen einen
Provider-SEAM, damit der Dexie-Pfad (browser-direkt) und der
API-Pfad ihre eigene Completion-Funktion injizieren:
- Generieren (AIX-01) —
exercise-generation-prompt.tsbaut den Prompt aus den Theorie-Schritten der Lektion;generate-exercises.tsruft die KI undexercise-generation-parser.tsparst die Antwort defensiv in strukturell gültige Karten (toleriert Code-Fences und Vorspann-Prosa). - Quality-Gate (AIX-03) —
exercise-quality-gate.tsist ein deterministischer (KI- freier) Filter: er verwirft Duplikate, Ein-Zeichen-Antworten, einen Distraktor gleich der richtigen Antwort, eine Matching-Karte mit weniger als drei Paaren usw. und markiert weiche Probleme als Warnungen. - Balancieren (AIX-04) —
exercise-distribution.tsordnet die Karten um (löscht nie), damit kein einzelner Übungstyp vorne überrepräsentiert ist und derselbe Typ nicht dreimal in Folge auftaucht, solange ein anderer Typ verfügbar ist.distributionGaps()meldet abwesende Typen, damit ein Regenerierungs-Prompt sie erwähnen kann. - Mit Feedback regenerieren (AIX-05) — das Feedback des Nutzers fließt für einen weiteren Durchlauf in den Prompt zurück.
Ein "Übungen generieren"-Button erscheint auf reinen
Theorie-Lektionen (AIX-02), und die Batch-Generierung
(AIX-06) fährt die Pro-Lektion-Pipeline sequenziell über jede
reine Theorie-Lektion eines Sets
(generate-exercises-for-set.ts + set-batch-deps.ts):
sequenziell aus Token-Budget- + Rate-Limit-Gründen, mit
Fortschrittsmeldung, eine fehlerhafte Lektion überspringend und
ein AbortSignal beachtend (bereits generierte Lektionen
bleiben erhalten).
KI-Inhaltsvalidierung (EXP-033)¶
Set-weite KI-Qualitätsprüfungen für verfasste Inhalte. Der Nutzer wählt Anbieter + Modell und fährt einen "Mit KI prüfen"-Bericht:
content-validator.tsbaut einen gebündelten Prompt (VALIDATION_BATCH_SIZEKarten pro Aufruf) und parst die JSON-Antwort;validation-runner.tsorchestriert die Bündel und aggregiert die Pro-Karte-Ergebnisse hinter demselben Provider-Seam. Kostenschranken deckeln die Karten pro Lauf, und die Aufrufstelle erzwingt ein Rate-Limit.- Der Bericht wird in IndexedDB gecacht und ist nach Markdown
exportierbar (
validation-markdown.ts), mit einer "Geprüft mit:"-Provenienz-Zeile ( validation-provenance.ts). - Ein Content-Hash + Signatur (
content-hash.ts,validation-signature.ts) untermauert ein "KI-geprüft"- Abzeichen, damit ein Set, dessen Inhalt sich nach der Prüfung geändert hat, nicht mehr als validiert angezeigt wird.
Konfigurierte-Anbieter-Übersicht + Pro-Anbieter-Test (#810)¶
Der Einstellungen-KI-Tab zeigt eine ConfiguredProvidersTable —
eine Zeile pro Anbieter mit seinem Modell, einer maskierten
Key-Vorschau (erste 4 + Auslassung + letzte 4 über
lib/providers/maskSecret.ts), dem Aktiv-Anbieter-Radio und
einem Pro-Anbieter-Test-Button. Der Test trifft den
Modell-Listen-Endpunkt des Anbieters (OpenAI /v1/models,
Gemini /v1beta/models, Anthropic /v1/models), nicht einen
Generierungs-Aufruf, damit ein erfolgreicher Test nichts kostet
(#800).
Die maskierte Vorschau wird auf der Settings-Payload geliefert
(key_preview_<provider>): serverseitig im API-Modus,
clientseitig im Dexie-Modus berechnet, damit die Übersicht in
beiden Modi funktioniert.
Die Modellauswahl gruppiert die Live-Modell-Liste in
Empfohlen + Alle Modelle
(lib/ai/model-recommendations.ts): eine kleine statische Liste
empfohlener Modell-FAMILIEN (als id-Präfixe gematcht, die
neueste-datierte Variante gewinnt) zieht für jeden Anbieter
dieselben 2-3 guten Modelle nach oben. Die Live-Liste wird über
denselben Modell-Listen-Endpunkt entdeckt
(storage/ai/model-discovery.ts im Dexie-Modus;
backend/app/services/model_discovery.py im API-Modus), mit
einem Pro-Tab-sessionStorage-Cache.
Feature-Gating (aktiv / deaktiviert / versteckt)¶
KI-Features werden über ein zentrales Registry
(frontend/src/features/featureConfig.ts) und den
useFeatureAvailable-Hook (features/useFeatureAvailable.ts)
gegated, aufgelöst aus einem memoisierten {mode, hasAiKey}-
Kontext. Jedes KI-gestützte Feature (Session-Start/-Wiederaufnahme,
Konversationsanalyse, Anki-Extraktion, NotebookLM-Fragen/-Leitfaden,
KI-Lektionsgenerierung, Aussprache) ist in NEEDS_AI_KEY: es
zeigt sich als aktiv mit nutzbarem Key, deaktiviert
(Grund api_key_required) ohne einen, nie still versteckt —
gemäß der sichtbar-aber-deaktiviert-Feature-State-Policy (#335).
Weitere KI-Oberflächen (Nur-Lese-Zusammenfassung)¶
Mehrere Nicht-Session-Features nutzen dieselben KI-Anbieter-
Plugins über ai_complete*:
- Konversationsanalysator (Phase 12 / v0.9.0+) —
frontend/src/chat_import/analysis.tszerteilt importierte Transkripte bei 16K Zeichen mit 2-Nachrichten-Überlappung, feuertai_completepro Chunk, merged die Ergebnisse. Extrahiert topic / weaknesses / error_patterns / recommended_method / vocabulary (seit v1.20.0). Toleranter JSON-Parser fängt Haiku-Klasse-Fehlverhalten ab (Fenced- Output, Vorspann-Prosa). - Anki-Extraktion (Phase 30 / v1.17.0) —
plugins/.../ anki/card_extraction.pyextrahiert Flashcard-Kandidaten aus einer Session oder Konversation; der Vokabel-Pfad läuft clientseitig ohne KI, wennanalysis_result.vocabularybefüllt ist. - NotebookLM-Lernfragen + Leitfaden (Phase 32 /
v1.19.0) —
plugins/.../notebooklm/question_generator.py study_guide.py; toleranter JSON-Parser; nutzer-editierte Fragen überspringen die Re-Generierung.- Aussprache-Bewerter (Phase 31 / v1.18.0) —
plugins/.../pronunciation.pygeneriert Zielphrasen - bewertet die Audio-Ähnlichkeit des Lernenden (Eignung gegated durch die Sprachen-Subject-Taxonomie).