~/cases/gnomaAktiv

gnoma

Warum gnoma jeden Prompt durch einen sichtbaren, provider-agnostischen Router schickt und wie diese Entscheidung das Vertrauen in den Agenten prägt.

Solo-Engineer · Persönliches Projekt · 2025 - heute

→Kontext

Ich betreibe mehrere LLM-Provider nebeneinander: Cloud-Modelle für harte Probleme, lokale für billige. Jeder Terminal-Agent, den ich probiert habe, hat mich auf einen Provider festgelegt oder stillschweigend das Modell gewählt.

→Problem

Modellqualität variiert je nach Aufgabe, Routing ist also wirklich nützlich. Aber Routing ohne Sichtbarkeit ist eine Black Box: Wer nicht sieht, warum ein Agent ein Modell gewählt hat, vertraut ihm keine echte Arbeit an.

→Randbedingungen

Solo-Projekt in Go, ausgeliefert als einzelne statische Binary. gnoma telefoniert nach Hause zu niemandem, und lokale Modelle sind erstklassige Bürger, kein Fallback.

→Ziele

Provider-agnostisch by Design. Die Routing-Entscheidung in jeder Runde zeigen. Sicherheit an der Content-Boundary, nicht nachgerüstet. Erweiterbar über Protokolle statt über eine Plugin-Runtime.

→Architektur

Prompts laufen über die TUI in einen Agent-Loop. Ein Multi-Armed-Bandit-Router wägt Capability-Gates, deklarierte Stärken, Latenz und Kosten ab und wählt pro Prompt einen Arm. Ein Tier-0-Kleinmodell klassifiziert Prompts und beantwortet triviale, ohne den Rechner zu verlassen. Eine Safety-Schicht scannt jede ausgehende Nachricht und jedes Tool-Ergebnis auf Secrets und Boundary-Verstöße. Tools erweitern den Loop über LSP, MCP-Server und Hooks.

  • Du tippst in der TUI.
  • Der Agent-Loop nimmt den Prompt entgegen; eine Safety-Schicht prüft jede ausgehende Nachricht und jedes Tool-Ergebnis.
  • Der Bandit-Router wählt pro Prompt den besten Provider-Arm und zeigt seine Wahl.
  • Der Prompt geht an einen von sechs Providern oder wird lokal von einem kleinen Modell beantwortet.
  • Tools erweitern den Agenten über LSP, MCP-Server und Hooks.
gnoma · Request-Pfad

→Trade-offs

Der Bandit-Router passt sich beobachtetem Verhalten an, ist beim Kaltstart aber konservativ. Triviale Prompts lokal zu beantworten spart Kosten und Latenz, braucht aber einen sichtbaren Override-Pfad. Erweiterung über Protokolle hält die Binary klein, macht Erweiterungen aber von externem Tooling abhängig.

→Umsetzung

Pfade werden TOCTOU-sicher kanonisiert, Unicode wird bereinigt, bevor ein Provider ihn sieht, und Language-Server-Grounding läuft über vierzehn LSP-Server. Das Ergebnis ist eine statische Binary plus Multi-Arch-Container-Image.

→Entscheidungen & Trade-offs

EntscheidungWarumTrade-off
Multi-Armed-Bandit-Router statt statischer RegelnRouting passt sich an beobachtete Latenz und Qualität an, statt zu raten.Kaltstart-Wahlen sind konservativ und weniger vorhersagbar.
Tier-0-Modell für triviale PromptsEinfache Prompts verlassen den Rechner nie und kosten nichts.Fehlklassifikation braucht einen sichtbaren Override-Pfad.
Eine statische Binary, Erweiterung über ProtokolleSofortige Installation, keine Laufzeit-Abhängigkeiten, kein Plugin-Manager.Keine dynamischen Plugins; Erweiterung braucht MCP-Server oder Hooks.
Safety-Schicht an der Content-BoundarySecrets und Boundary-Verstöße werden abgefangen, bevor etwas den Host verlässt.Geringe Latenz pro Runde; gelegentliche False Positives.

→Ergebnis

  • Provider-agnostisch über Anthropic, OpenAI, Google, Mistral, Ollama und llama.cpp.
  • Der Router zeigt in jeder Runde, welchen Arm er gewählt hat und warum.
  • Liefert als einzelne statische Binary mit Multi-Arch-Container-Image.

→Nachweise

Provider-Backends
6

Anthropic, OpenAI, Google, Mistral, Ollama, llama.cpp

LSP-Integrationen
14

Language-Server-Grounding

Distribution
Statisches Binary

Zusätzlich ein Multi-Arch-Container-Image

→Was ich heute anders machen würde

Ich habe den Router zuerst gebaut und seine Observability zweitens. Heute würde ich mit dem Decision-Record pro Runde anfangen. Jede Wahl muss im Nachhinein erklärbar bleiben.