Zum Inhalt springen

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.

┌────────────────────────────────────────┐
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:

  1. Eingang (CLI run/chat, API, Scheduler-Job) → agent.Run(ctx, input, opts).
  2. Routing: explizites Modell? Sonst router.Resolve — Heuristik → LLM-Classify (Router-Modell, JSON-Schema-Enum, Temp 0) → Kategorie; dann Präferenzliste gegen pool.HasModel.
  3. Prompt-Assembly (agent/prompt.go): kompakter System-Prompt + Skill-Kurzliste + ggf. vorab geladener Skill.
  4. Tool-Loop (agent/loop.go): Chat-Call über den Pool → Tool-Calls ausführen (strukturiert oder per Salvage gerettet) → Ergebnisse als role:tool anhängen → wiederholen bis Antwort ohne Tool-Call oder Iterations-Cap.
  5. 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.

Jede Änderung sollte diese Prinzipien respektieren:

  1. Kontext ist teuer. System-Prompts kurz, Tool-Descriptions genau ein Satz, Tool-Subsetting per Kategorie-Tags (registry.Select), Skills erst on demand laden.
  2. 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.
  3. 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.
  4. Viele kleine Calls statt einem großen. Klassifikation und Zusammenfassung sind eigene Mini-Calls mit Schema-erzwungenem Output.
PfadInhalt
pkg/…öffentliche, importierbare Plattform (API-Stabilität anstreben)
internal/cliCLI-Kommandos + runtime (Verdrahtung aller Teile)
internal/testutil/fakeollamascriptbarer Fake-Ollama-Server für Tests
internal/smokeBuild-getaggte Tests gegen echtes Ollama
cmd/ollama-agentmain (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.

Terminal-Fenster
make test # Unit-Tests, ohne Netz
go test -race ./... # vor jedem Push empfohlen
make vet
  • fakeollama deckt fast alles ab: Modelle vorgeben, Antworten scripten (Script(...)), Ausfälle simulieren (SetDown), Tool-Calls zurückgeben, per OnChat beliebiges Verhalten. Neue Pool-/Loop-Features immer dagegen testen.
  • Smoke-Tests gegen echte Server (Modell muss gepullt sein):
Terminal-Fenster
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 Test TestSalvage ist eine Tabelle realer Modell-Outputs — bei jedem neuen Fall aus der Praxis dort eine Zeile ergänzen.
  • Arbeit auf develop (oder Feature-Branches → MR nach develop).
  • main ist geschützt; Release = MR developmain mit grüner Pipeline, danach Tag vX.Y.Z auf main.
  • Commits nach Conventional Commits (feat(pool): …, fix(agent): …).
  • CI (.gitlab-ci.yml): vet → test → build.
  • 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_dir persistiert werden.
  • Metriken: /v1/health ist minimal; Prometheus-Endpoint wäre in pkg/api leicht 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.