Architektur & Mitarbeit
Für das Erweitern der Plattform um eigene Workflows (Skills, MCP, eigene Tools/Binaries) siehe Plattform erweitern. Dieses Dokument beschreibt die Arbeit am Kern selbst.
Architektur
Abschnitt betitelt „Architektur“ ┌────────────────────────────────────────┐ CLI (internal/cli) │ runtime (Verdrahtung) │ HTTP-API (pkg/api) ──▶│ │ Scheduler (pkg/sched)─▶ pkg/agent ── Loop, Salvage, History │ │ │ │ │ ├── pkg/router Kategorie→Modell │ │ ├── pkg/tools Registry (+Tags) │ │ │ ├── builtin (load_skill …) │ │ │ └── pkg/mcpclient (Adapter) │ │ ├── pkg/skills SKILL.md │ │ └── pkg/pool Endpoint-Pool │ │ └── pkg/ollama HTTP-Client │ └────────────────────────────────────────┘ pkg/config (JSON-Schema für alles)Datenfluss einer Anfrage:
- Eingang (CLI
run/chat, API, Scheduler-Job) →agent.Run(ctx, input, opts). - Routing: explizites Modell? Sonst
router.Resolve— Heuristik → LLM-Classify (Router-Modell, JSON-Schema-Enum, Temp 0) → Kategorie; dann Präferenzliste gegenpool.HasModel. - Prompt-Assembly (
agent/prompt.go): kompakter System-Prompt + Skill-Kurzliste + ggf. vorab geladener Skill. - Tool-Loop (
agent/loop.go): Chat-Call über den Pool → Tool-Calls ausführen (strukturiert oder per Salvage gerettet) → Ergebnisse alsrole:toolanhängen → wiederholen bis Antwort ohne Tool-Call oder Iterations-Cap. - Pool (
pool/pool.go): wählt gesunden Endpoint mit dem Modell (least-inflight, Prioritäts-/Latenz-Tiebreak); bei Transportfehler/5xx Failover auf den nächsten Kandidaten; 4xx wird durchgereicht.
Design-Prinzipien (kleine Modelle!)
Abschnitt betitelt „Design-Prinzipien (kleine Modelle!)“Jede Änderung sollte diese Prinzipien respektieren:
- Kontext ist teuer. System-Prompts kurz, Tool-Descriptions genau ein Satz, Tool-Subsetting per Kategorie-Tags (
registry.Select), Skills erst on demand laden. - Kleine Modelle machen Formatfehler. Der Salvage-Parser (
agent/parse.go) rettet Tool-Calls aus Plain-Text: JSON in Fences, JSON in Prosa, Key-Varianten (name/tool/action…), XML-Syntax (<function=x><parameter=y>). Neue kaputte Formate aus der Praxis → dort ergänzen, immer mit Tabellen-Test. - Nie hart scheitern. Klassifikation schlägt fehl →
default_category. Kein Modell der Kategorie verfügbar →models.default. History zu groß → zusammenfassen, notfalls alte Runden verwerfen. Iterations-Cap erreicht → finaler Call ohne Tools. - Viele kleine Calls statt einem großen. Klassifikation und Zusammenfassung sind eigene Mini-Calls mit Schema-erzwungenem Output.
Verzeichnisse
Abschnitt betitelt „Verzeichnisse“| Pfad | Inhalt |
|---|---|
pkg/… | öffentliche, importierbare Plattform (API-Stabilität anstreben) |
internal/cli | CLI-Kommandos + runtime (Verdrahtung aller Teile) |
internal/testutil/fakeollama | scriptbarer Fake-Ollama-Server für Tests |
internal/smoke | Build-getaggte Tests gegen echtes Ollama |
cmd/ollama-agent | main (dünn) |
configs/ | Beispiel-Config (Quelle: pkg/config/example.go — beide synchron halten!) |
skills/ | mitgelieferte Beispiel-Skills |
Neue Subcommands: Datei in internal/cli/ mit func init() { register(&command{...}) } — mehr ist nicht nötig.
make test # Unit-Tests, ohne Netzgo test -race ./... # vor jedem Push empfohlenmake vet- fakeollama deckt fast alles ab: Modelle vorgeben, Antworten scripten (
Script(...)), Ausfälle simulieren (SetDown), Tool-Calls zurückgeben, perOnChatbeliebiges Verhalten. Neue Pool-/Loop-Features immer dagegen testen. - Smoke-Tests gegen echte Server (Modell muss gepullt sein):
OLLAMA_HOST=http://192.168.6.80:11436 SMOKE_MODEL=qwen3.5:9b make smoke- Der empfindlichste Code ist
pkg/agent/parse.go(Salvage). Der TestTestSalvageist eine Tabelle realer Modell-Outputs — bei jedem neuen Fall aus der Praxis dort eine Zeile ergänzen.
Git-Workflow & Release
Abschnitt betitelt „Git-Workflow & Release“- Arbeit auf
develop(oder Feature-Branches → MR nachdevelop). mainist geschützt; Release = MRdevelop→mainmit grüner Pipeline, danach TagvX.Y.Zaufmain.- Commits nach Conventional Commits (
feat(pool): …,fix(agent): …). - CI (
.gitlab-ci.yml): vet → test → build.
Bekannte Grenzen / Ideen für später
Abschnitt betitelt „Bekannte Grenzen / Ideen für später“- Streaming in Tool-Runden: der Loop streamt nur die finale Antwort; Zwischen-Iterationen sind non-streaming (bewusste Vereinfachung).
- Sessions sind in-memory (Daemon-Neustart = Gedächtnisverlust). Persistenz (z.B. SQLite) wäre ein sauber isoliertes Feature in
pkg/agent/session.go. - Job-History ist in-memory; könnte in
state_dirpersistiert werden. - Metriken:
/v1/healthist minimal; Prometheus-Endpoint wäre inpkg/apileicht ergänzbar. - Embeddings sind im Client (
ollama.Embed) vorhanden, aber noch ohne Agent-Anbindung (RAG-Workflows wären ein Erweiterungs-Kandidat). - MCP-Ressourcen/Prompts werden nicht genutzt, nur Tools.