Qwen3 kann natives OpenAI-Tool-Calling; llama.cpp (--jinja) unterstuetzt es. Lokales Tier bekommt einen kleinen, risikoarmen Start-Satz (Wetter, memory_search, trigger_timer, Spotify, Licht); volles Arsenal bleibt bei Claude, Escalation-Netz bleibt. Klein halten = Speed. Plan-Entscheidungen + Phasen entsprechend gezogen. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
8.4 KiB
Plan B — Lokaler LLM-Router (Gamebox) neben Claude
Ziel: „Gemini-Feeling" für den Alltag, ohne die Claude-Max-Subscription aufzugeben. Ein schnelles lokales LLM beantwortet die einfachen ~80 % der Turns in <1 s; nur die schweren 20 % (Tiefe, Code, Tools, Pentest, langer Kontext) gehen an Claude. Claude bleibt das Tiefen-Hirn.
Warum das der einzige realistische Weg zu „live" ist
Gemessen (10.07.2026): CLI-Round-trip über den Claude-Max-Proxy hat einen harten Boden von ~3,5 s (Subprozess-Start pro Turn). Streaming-API würde das brechen, kostet aber API-Geld → verliert die Max-Subscription. Ein lokales LLM für die einfachen Turns umgeht den 3,5-s-Boden komplett und ist gratis (läuft auf vorhandener Gamebox-GPU). Echtes Speech-to-Speech-Duplex (Gemini Live nativ) ist mit einem Text-Modell als Hirn prinzipiell nicht drin.
Modell & Serving (entschieden)
- Modell: Qwen3 8B, GGUF Q4_K_M (~6 GB). Bestes Tool-Calling der 7/8B- Klasse, solides Deutsch, Apache-2.0. Alt.: Mistral Small 3 7B (schneller, weniger Tool-Calling).
- Serving: llama.cpp
llama-serverim Docker-Container auf der Gamebox (kein Ollama nötig — nativer OpenAI-kompatibler/v1/chat/completions). - VRAM-Budget: 12-GB-Karte, Whisper-small (~1–2 GB) + F5-TTS (~1–2 GB) → ~8–9 GB frei → passt. (FLUX ist auf 12 GB eh raus.)
Anbindung: über den RVS, wie TTS/STT (kein IP-Pflegen)
Die Gamebox ist ein anderer Host als das Brain. Statt direktem HTTP (IP/Port/ Firewall) läuft das LLM über den RVS-Token-Room, exakt wie Whisper/F5-TTS:
- llama.cpp hört nur auf localhost der Gamebox.
- Ein dünner RVS-Adapter daneben (Vorbild: whisper-/xtts-Bridge) verbindet
sich mit dem RVS-Token, lauscht auf
llm_request, ruft lokal llama-server, schicktllm_response(korreliert per requestId) zurück. rvs/server.jsALLOWED_TYPESumllm_request,llm_responseund (Phase 2)llm_partialerweitern.- Das Brain bekommt einen zweiten „Proxy" — nur über RVS statt direktem HTTP.
Router-Logik im Brain
Reihenfolge pro Turn (früh raus = schnell):
- Tier 0 — Fast-Path (existiert): reine Steuerbefehle (Spotify, Licht) → Skill direkt, kein LLM. <1 s.
- Tier 1 — Lokal (Qwen3): einfache Konversation, kurze Fakten, Smalltalk, Bestätigungen. Ziel <1 s.
- Tier 2 — Claude: tief/technisch, Code, Tool-Use nötig, Pentest-Projekt, langer/komplexer Kontext.
Routing-Signal (heuristisch zuerst, deterministisch & schnell): Nachrichtenlänge, Schlüsselwörter, ob ein Tool nötig scheint, Projekt-Kontext (Pentest-Projekt → immer Claude), Konversationstiefe.
Escalation statt perfekter Vorab-Klassifikation: Das lokale Modell bekommt
die Anweisung, bei Unsicherheit oder Tool-Bedarf NICHT zu raten, sondern zu
eskalieren (z.B. Antwort <<ESCALATE>>). Das Brain routet den Turn dann an
Claude. So sind Fehlklassifikationen billig — lieber einmal lokal→Claude als
eine falsche lokale Antwort.
Modus „Nur lokales LLM" (Diagnostic-Checkbox, Eval-Schalter): Ein Flag
localLlmOnly (in Diagnostic setzbar, vom Brain beim Routen gelesen). Ist es an:
JEDER Turn geht ans lokale LLM, <<ESCALATE>> / „zu schwer" werden ignoriert
(kein Claude-Fallback) — damit Stefan die echte Staerke/Schwaeche des lokalen
Modells sieht, ohne dass Claude die schweren Turns rettet. Haken aus = normale
Heuristik + Escalation. Ehrlicher Hinweis: im Nur-lokal-Modus funktionieren
werkzeug-abhaengige Turns (Wetter, Timer, Memory, Bild) nicht — das lokale Tier
hat keine Tools; das ist ein Gespraechs-Eval-Modus, kein Voll-ARIA. Fast-Path
(Spotify etc.) laeuft davon unberuehrt weiter.
Persona auf BEIDEN Modellen
Das lokale Modell braucht ARIAs Identität, sonst bricht es aus der Rolle
(gelernt aus dem --system-prompt-Debakel). Aber schlanker:
- IDENTITY_SEED + Kern-Persona: ja.
- Volles Memory / ALLE Skill-Schemas: nein — nur eine kuratierte, kleine Tool-Auswahl (siehe unten). Haelt den lokalen Prompt klein → schnell.
- Persona kommt lokal auch als echter System-Prompt (llama.cpp
system-Rolle).
Tool-Calling lokal (kuratierte Auswahl)
Das lokale LLM DARF Werkzeuge nutzen (Qwen3 = natives OpenAI-Tool-Calling, von
llama.cpp --jinja unterstuetzt). Ablauf wie bei Claude: Brain schickt
messages + tools → Qwen antwortet mit tool_calls → Brain fuehrt via
_dispatch_tool aus → Ergebnis zurueck → finale Antwort. Tool-Loop im Brain,
Ziel = lokales LLM statt Claude-Proxy.
Kritische Regel: klein halten. Der Wert des lokalen Tiers ist Speed; das volle Arsenal (~49 K Zeichen Schemas) wuerde den Prompt sprengen und das kleine Modell ueberfordern. Daher nur ein Start-Satz haeufiger, risikoarmer Tools:
- Lokal: Wetter, Uhrzeit,
memory_search(lesen),trigger_timer, Spotify-Steuerung (Nicht-Fast-Path-Saetze), Licht/Smart-Home. - Bleibt bei Claude:
skill_create/update/delete,oauth_*,project_*,flux_generate,memory_save(schreiben) — erst spaeter evtl. lokal.
Escalation-Netz bleibt: braucht ein Turn ein Tool, das lokal nicht dabei ist →
<<ESCALATE>> → Claude mit vollem Arsenal.
Implementierung (B1): Adapter reicht tools an llama.cpp + gibt tool_calls
zurueck; Bridge schleust beides durch (llm_request/llm_response); Brain-Tool-Loop
mit Ziel lokal.
Phasen
- B0 — Infra: llama.cpp-Container + RVS-Adapter auf der Gamebox,
ALLOWED_TYPES,local_llm_chat()im Brain. Isoliert testen („sag hallo"). - B1 — Router + lokale Tools: Heuristik Tier-1/2 + Escalation, schlanke Persona lokal, kuratierte Tool-Auswahl lokal (Adapter/Bridge/Brain-Tool- Loop, siehe oben) + „Nur lokales LLM"-Checkbox. Einfache Turns → lokal. Messen: Trefferquote, Tool-Zuverlaessigkeit & Latenz.
- B2 — Streaming/Voice:
llm_partial→ TTS beginnt beim ersten Satz → der „live"-Sprung. Hier den Gong-/Ohr-Re-Arm-Bug mit-fixen (Barge-In, sauberes Re-Listen). - B3 (optional): lokalen Tool-Satz erweitern, sobald Qwen sich als
zuverlaessig erweist (z.B.
memory_save).
Offene Entscheidungen (für Stefan)
- Modell: Qwen3 8B (Tool-Calling) — oder doch Mistral Small 3 7B (Speed)?
- Routing v1: rein heuristisch + Escalation (entschieden).
- Tools lokal: kuratierte kleine Auswahl (entschieden — Start-Satz oben; Stefan bestaetigt/justiert die konkrete Liste vor dem B1-Bau).
Folge-Baustein: Modell-Auswahl in ARIA Diagnostic (B0.5)
Ziel: In Diagnostic ein Modell auswählen; ist es nicht da, lädt der Container
es on-demand und aktiviert es. Spiegelt zwei bestehende Muster: den
whisperModel-Hotswap (RVS-Config-Broadcast → Bridge hot-swapped) und die
kuratierte Claude-Tier-Liste aus models.json.
Kernproblem: llama.cpp-Server serviert ein Modell pro Prozess —
„anderes aktivieren" = neu laden/swappen.
Lösung: llama-swap (Proxy vor llama.cpp): kennt eine Liste von Modellen,
lädt bei Anfrage das gewünschte on-demand (Download via -hf beim ersten Mal),
swappt bei VRAM-Knappheit das alte raus. OpenAI-kompatibel — der llm-adapter
zeigt statt auf llama:8081 auf llama-swap.
Bausteine:
llama-swap-Service inxtts/docker-compose.yml(ersetzt/ergänztllama), Config mit den verfügbaren Modellen (Name →-hf-Command).- Kuratierte Liste
local_models.json(analogmodels.json) — Diagnostic-UI liest sie, zeigt Dropdown „Lokales Modell". - Diagnostic → RVS-Config-Broadcast
localLlmModel→ llm-adapter setzt dasmodel-Feld seiner llama-swap-Requests → swap/Download passiert automatisch. - Status zurück an Diagnostic (lädt / bereit / VRAM-OOM), analog whisper-Status.
Konkret gewünschte UI (Stefan):
- Modell-Status sichtbar: lädt (mit Fortschrittsbalken) → heruntergeladen → aktiviert. Ist ein Modell schon im Cache: nicht neu laden, nur aktivieren (llama.cpp/llama-swap macht das nativ ueber den Cache).
- Testchat-Zeile in Diagnostic: kurze Nachricht direkt ans lokale LLM
schicken, Antwort + Latenz anzeigen. Nutzt denselben RVS-Pfad
(
llm_request/llm_response) wie der Self-Test — kein neuer Kanal noetig.
Bis dahin: ein Modell via -hf Auto-Download (B0, erledigt). Erst end-to-end
grün, dann dieser Komfort-Layer.
Nicht-Ziele
- Kein echter Gemini-Live-Duplex-Klon (Text-Modell als Hirn).
- FLUX bleibt optional/später (dickere GPU). Bild-Generierung separat als pluggbarer Provider (ChatGPT/DALL·E-Alternative) — eigenes Feature, nicht Teil B.