EdgeVox — Claude Code-Regeln
Offline-Sprachagenten-Framework für Roboter. Reines Python-Paket, keine Cloud-Abhängigkeiten, läuft auf CPU/CUDA/Metal.
Projektstruktur
edgevox/
├── edgevox/ # Quellpaket
│ ├── audio/ # VAD, Mikrofonaufnahme, Wiedergabe
│ ├── stt/ # STT-Backends (faster-whisper, sherpa-onnx)
│ ├── llm/ # llama.cpp / Gemma-Integration
│ ├── tts/ # TTS-Backends (Kokoro, Piper, Supertonic, PyThaiTTS)
│ ├── core/ # Pipeline-Orchestrierung
│ ├── cli/ # CLI-Einstiegspunkte
│ ├── ui/ # TUI-Widgets
│ ├── integrations/ # ROS2-Bridge, etc.
│ ├── tui.py # Haupt-TUI-App
│ └── setup_models.py # Modell-Downloader
│ ├── server/ # FastAPI-Web-UI + WebSocket-Server
├── webui/ # React-Frontend (Vite + Tailwind)
├── scripts/ # Dienstprogramme (Modell-Upload, etc.)
├── voices/ # Sprachkonfigurationsdateien
├── docs/ # Projektdokumentation
├── website/ # VitePress-Seite
└── pyproject.toml
Einstiegspunkte (siehe pyproject.toml):
edgevox→edgevox.tui:main(TUI-Standard,--web-uifür Web,--simple-uifür CLI)edgevox-cli→edgevox.cli.main:mainedgevox-setup→edgevox.setup_models:main
Unterstützte Sprachen und Backends
| Sprache | STT | TTS |
|---|---|---|
| Englisch, Französisch, Spanisch, usw. | faster-whisper | Kokoro |
| Vietnamesisch | sherpa-onnx (zipformer) | Piper |
| Deutsch, Russisch, Arabisch, Indonesisch | faster-whisper | Piper |
| Koreanisch | faster-whisper | Supertonic |
| Thailändisch | faster-whisper | PyThaiTTS |
Modelle werden auf nrl-ai/edgevox-models (HuggingFace) gehostet, mit Fallback auf die ursprünglichen Repositories.
Architekturprinzipien
- Plug-and-Play, standardmäßig anpassbar. Jede Komponente – STT-Backend, TTS-Backend, LLM, VAD, Agent-Schleifenverhalten, Pipeline-Stufe, Tool, Skill, Hook – muss austauschbar sein, ohne Kerncode zu bearbeiten. Bevorzuge Protokolle, Registries und Dekorateure gegenüber hartcodierten Pfaden. Neues Verhalten landet als neues Plugin/Hook/Backend, nicht als Patch in einem bestehenden Modul. Wenn du dich dabei ertappst, für einen bestimmten Anwendungsfall eine Bedingung zum Kern hinzuzufügen, tritt einen Schritt zurück und extrahiere sie stattdessen in einen Injektionspunkt.
Agenten-Harness-Architektur
Die Agenten-Harness (edgevox/agents/ + edgevox/llm/hooks_slm.py + edgevox/llm/tool_parsers/) ist vollständig unter docs/documentation/ dokumentiert:
agent-loop.md– die sechs-Feuerpunkte-Schleife, paralleler Dispatch, Handoff-Short-Circuit.hooks.md– Hook-Autoren-Vertrag, eingebaute Hooks, Reihenfolgeregeln.memory.md–MemoryStore/SessionStore/NotesFile/Compactor.interrupt.md– Barge-In-Signale + Cancel-Token-Verbindungen.multiagent.md– Blackboard, BackgroundAgent, AgentPool.tool-calling.md– Parser-Kette + Grammar-beschränktes Decoding (Fahrplan).
Harness-Regeln
- Typisierte
AgentContext-Felder (ctx.tool_registry,ctx.llm,ctx.interrupt,ctx.memory,ctx.artifacts,ctx.blackboard) sind die öffentliche Schnittstelle.ctx.stateist nur für Benutzer gedacht – Framework-Code darf dort keine magischen Schlüssel schreiben. - Hook-eigener Zustand lebt unter
ctx.hook_state[id(self)]. Die Schlüsselung mitid(self)stellt sicher, dass zwei Instanzen derselben Hook-Klasse keinen Zustand teilen. - Barge-In ist durchsetzbar, nicht nur ein Hinweis. Jeder
LLM.complete-Aufruf übergibtctx.interrupt.cancel_tokenüberstop_event=…, sodassstopping_criteriavon llama-cpp die Generierung innerhalb eines Dekodierschritts tatsächlich anhält. - Token-genaue Token-Anzahlen.
estimate_tokens(messages, llm)undLLM.count_tokensersetzen die Heuristikchars // 4, wenn ein LLM verfügbar ist. Erforderlich für korrekte Kontextfenster-Entscheidungen bei CJK / Vietnamesisch / Thailändisch. - Tool-Call-Parsing läuft roh zuerst.
parse_tool_calls_from_contentversucht Detektoren gegen den rohen Inhalt, bevor-Blöcke entfernt werden – Qwen3 gibt Tool-Calls innerhalb von Reasoning-Blöcken aus (siehe llama.cpp#20837). - Voreingestellte Parser werden beim Laden validiert.
resolve_preset(slug)stellt sicher, dass jeder Name intool_call_parsers=(...)ein registrierter Detektor ist; ein Tippfehler führt zu einem lauten Fehler, anstatt die Erkennung stillschweigend zu deaktivieren. - Vom Modell ausgegebene Tool-Call-IDs sind rundum gültig. Das
[TOOL_CALLS]-Format von Mistral trägt eine 9-stellige ID, die die folgenderole="tool"-Nachricht wiederverwenden muss.ToolCallItem.idleitet diese durch die Parser-Kette und die Agenten-Schleife.
Bevorzugte Import-Oberflächen
- Agenten-Framework:
from edgevox.agents import LLMAgent, AgentContext, Session, Handoff, ... - Eingebaute Hooks:
from edgevox.agents.hooks_builtin import MemoryInjectionHook, TokenBudgetHook, ... - SLM-Härtung:
from edgevox.llm.hooks_slm import default_slm_hooks - Speicher:
from edgevox.agents.memory import JSONMemoryStore, NotesFile, Compactor, estimate_tokens - Multi-Agent:
from edgevox.agents.multiagent import Blackboard, BackgroundAgent, AgentPool - Unterbrechung:
from edgevox.agents.interrupt import InterruptController, InterruptPolicy, EnergyBargeInWatcher
Vermeide es, in private Module oder _agent_harness.py direkt zu greifen.
Codierungsregeln
- Python ≥ 3.10. Verwende moderne Syntax (
X | Y-Unions,match,dict[str, int]). - Formatiere und lint mit ruff. Zeilenlänge 120. Führe
ruff formatund dannruff check --fixaus. - Keine abschließenden Zusammenfassungen in Code-Kommentaren. Kommentiere das Warum, nicht das Was.
- Typannotationen bei öffentlichen Funktionen. Interne Helfer dürfen sie bei Offensichtlichkeit weglassen.
- Keine Prints in Bibliothekscode. Verwende
rich/textualfür benutzergerichtete Ausgabe,loggingfür Diagnose. - Importe stehen am Anfang der Datei. Verschiebe einen Import nur dann innerhalb einer Funktion, wenn etwas Konkretes dazu zwingt – circular-import-Break, optionale/schwere Abhängigkeit hinter einer Capability-Prüfung oder Lazy-Load zur Reduzierung der CLI-Startlatenz. Bequemlichkeit oder „es wird nur an einer Stelle verwendet“ ist kein Grund; verschiebe ihn nach oben.
- Keine neuen Abhängigkeiten auf oberster Ebene ohne Grund. Bevorzuge die Standardbibliothek. Wenn du eine hinzufügen musst, aktualisiere
pyproject.toml. - Hardware-bewusste Codepfade müssen sich elegant verschlechtern – CUDA/Metal/CPU-Fallbacks, nie einen Absturz bei fehlendem Beschleuniger verursachen.
- Commite niemals Modell-Dateien (
.gguf,.onnx,.bin, Gewichte). Sie leben untermodels/, das in.gitignoreist.
Audio- / Modell-Konventionen
- Abtastrate: 16 kHz mono int16 für Aufnahme und STT-Eingabe.
- TTS-Ausgabe: mit
sounddeviceauf die Geräterate neu abtasten. - VAD-Frame-Größe: 32 ms (512 Samples @ 16 kHz).
- Latenzbudget: STT < 0,5 s, LLM erstes Token < 0,4 s, TTS erster Chunk < 0,1 s auf RTX 3080.
- Behandle die Streaming-Pipeline als Vertrag: Führe keine blockierenden Aufrufe ein, die die Ereignisschleife anhalten.
Werkzeuge
- uv für die Paketverwaltung. Verwende
uv pip install/uv venvanstelle von nacktempip/python -m venv. Siehe https://docs.astral.sh/uv/. - pre-commit führt ruff (Lint + Format), gitleaks und Standard-Hygiene-Hooks aus. Installiere einmalig mit
pre-commit install. - gitleaks durchsucht bei jedem Commit nach Geheimnissen. Wenn ein Befund ein False-Positive ist, whiteliste ihn in
.gitleaks.tomlmit einem Kommentar, der erklärt, warum – lösche den Befund nicht. - pytest für Tests (
pytest, asyncio-Modus = auto). Tests befinden sich untertests/.
Workflow-Erwartungen
- Lese Dateien, bevor du sie bearbeitest. Schlage keine Änderungen an Code vor, den du dir nicht angesehen hast.
- Führe
ruff format+ruff check --fixaus, bevor du eine Aufgabe für erledigt erklärst. - Umgehe keine Hooks (
--no-verify) – behebe das zugrunde liegende Problem. - Füge dich selbst oder Claude nicht als Commit-Autor / Co-Autor hinzu. Insbesondere: kein
Co-Authored-By: Claude …-Trailer, kein🤖 Generiert mit Claude Code-Footer – Commit-Nachrichten enden nach dem Body, nichts weiter. - Bevorzuge das Bearbeiten vorhandener Dateien gegenüber dem Erstellen neuer Dateien; erstelle keine README/Dokumentationsdateien, es sei denn, du wirst darum gebeten.
- Wenn eine Änderung die Streaming-Pipeline betrifft, notiere die Latenzauswirkungen manuell in der PR-Beschreibung.
- Bevorzuge Mermaid-Diagramme gegenüber ASCII-Kunst in jedem Markdown-Dokument (
docs/,website/,README.md, PR-Beschreibungen). GitHub und VitePress rendernmermaid-Codeblöcke nativ; handgezeichnete Box-und-Linie-ASCII ist schwerer zu lesen, unmöglich sauber zu bearbeiten und bricht bei Änderungen der nichtproportionalen Schriftart. Die einzigen akzeptablen ASCII-Diagramme sind Verzeichnisbäume (├──/└──) – die bleiben wie sie sind.
Dokumentation schreiben (docs/, README.md, website/)
- Zitiere Mermaid-Beschriftungen immer in Anführungszeichen, die
(,),@,:,,,&,<br/>oder andere Satzzeichen als Buchstaben, Ziffern,_und-enthalten. Verwende["foo(args)"]für Knoten und-->|"@tool call"|für Kantenbeschriftungen. Unzitierte Klammern innerhalb von[...]oder|...|bringen den Flussdiagramm-Parser zum Absturz – die Seite lädt noch, aber das Diagramm verschwindet. Nachdem du einen Mermaid-Block bearbeitet hast, öffne die Seite lokal (npm run docs:dev) und bestätige, dass keineParse error on line N-Einträge in der Browserkonsole erscheinen. Der Fehler ist in der Quellcode-Überprüfung unsichtbar. - Teste Dokumentation im Dev-Server, bevor du sie für erledigt erklärst.
npm run docs:dev(VitePress auf:5173) fängt Mermaid-Parse-Fehler, kaputte Links und tote Anker, die die Markdown-Quelle verbirgt. Bei visuellen Änderungen (neue Diagramme, Layout, Hero, Sidebar) erstelle einen Playwright-Screenshot der betroffenen Seite, damit der Reviewer Regressionen erkennen kann, ohne den Server hochzufahren. - Cross-Page-Links sind root-absolut, ohne Erweiterung –
/documentation/hooks, niemals./hooks.mdoderhooks(letzteres löst untercleanUrls: truefalsch auf). Mermaid-Knoten-Ziel-Links folgen derselben Konvention. - Sidebar-Registrierung ist obligatorisch. Eine neue Seite unter
docs/documentation/ist unsichtbar, bis sie imsidebar-Block indocs/.vitepress/config.tshinzugefügt wird. Aktualisiere beide gleichzeitig.
Was NICHT zu tun ist
- Füge keine Cloud-API-Aufrufe oder Telemetrie hinzu. EdgeVox ist offline-first.
- Führe keine GPL-lizenzierten Abhängigkeiten ein (Projekt ist MIT).
- Lizenzprüfung ist obligatorisch, bevor eine neue Abhängigkeit hinzugefügt wird. Überprüfe die tatsächliche Lizenz des Pakets (PyPI/GitHub/Dokumentation – nicht nur eine Vermutung) und dokumentiere das Ergebnis inline in
pyproject.tomlneben der Abhängigkeit (z.B.# MIT/# LGPL-3 – dynamisch gelinkt, kompatibel). Copyleft-Lizenzen, die abgelehnt werden müssen: GPL-2/3, AGPL, SSPL. LGPL ist nur für reine dynamische Link-Bibliotheken akzeptabel (PySide6, Qt Multimedia, rlottie). CC-BY-SA ist für Assets akzeptabel (SVG-Figurensätze usw.), niemals für Abhängigkeiten auf Quellcode-Ebene. Im Zweifelsfall wähle eine permissive Alternative oder markiere es zur Diskussion. - Commite nicht
dist/,build/,*.egg-info/, Modell-Gewichte oder Aufnahmen. - Füge keine spekulativen Abstraktionen oder „Zukunftssicherung“ hinzu, die über die Anforderungen der Aufgabe hinausgehen.