Skip to content

ADR-0067: KI-Provider-Verbindungen — Ollama lokal, Anthropic und OpenAI optional

Kontext

ADR-0061 und ADR-0065 legen fest, dass Modelle lokal über Ollama laufen und keine Inhalte das Haus verlassen. Die Messung in ADR-0065 zeigt, dass Ollama ohne GPU für Scans zu langsam ist. Betreiber brauchen deshalb die Wahl eines gehosteten Anbieters, ohne dass dafür .env und Neustart nötig sind.

Entscheidung

  1. Anbieter und Agenten sind getrennt. Ein Anbieter ist ein Ort, an dem ein Modell läuft (ollama, anthropic, openai) und hat eine Verbindung (URL, API-Schlüssel). Ein Agent ist eine Assistenten-Aufgabe und wählt seinen eigenen Anbieter und sein Modell. Beides gilt plattformweit und liegt in der Control-DB (ai_platform_settings für Verbindungen, ai_agents für Zuordnungen; Migrationen 0045–0048).
  2. Agenten-Katalog im Code (assistant/ai_catalog.py): field_message (Feldnachricht verstehen, Aufgaben zuordnen, erledigt/Problem erkennen in einem Aufruf; einziger bisher aufgerufener Agent), document_lookup, document_classify, document_vision, daily_report (vorbereitet, noch ohne Aufrufer; in der Oberfläche als „Geplant“ markiert). Nur die Zuordnungen werden gespeichert.
  3. Ollama bleibt Standard und lokal. Ohne gespeicherte Zuordnung läuft jeder Agent auf Ollama mit ASSISTANT_MODEL_FAST / ASSISTANT_MODEL_REPORT (Vision: kein Standard). Ohne Ollama-URL (OLLAMA_BASE_URL oder Einstellung) arbeitet der Offline-Assistent (Schlüsselwort-Heuristik).
  4. Gehostete Anbieter sind Opt-in. Anthropic und OpenAI sind nur wählbar, wenn ein Schlüssel hinterlegt ist; ein Agent ohne Modell oder mit unvollständigem Anbieter wird mit 422 abgelehnt. Die Oberfläche warnt, dass Nachrichtentexte an den Dienst gesendet werden; der Betreiber braucht einen Auftragsverarbeitungsvertrag. Das lockert die „nur lokal“-Regel aus ADR-0061/0065 ausdrücklich und nur auf Betreiberentscheid, je Agent.
  5. Schlüssel sind nur schreibbar. API-Schlüssel liegen Fernet-verschlüsselt (app.core.secrets); die API liefert nur „gesetzt“ und die letzten vier Zeichen. null behält, "" entfernt den Schlüssel. Nicht genutzte Felder (Ollama hat keinen Schlüssel, Anthropics URL ist fest) werden ignoriert.
  6. Verfügbare Modelle. GET /platform/ai/providers/{provider}/models fragt die Modellliste des Anbieters (/api/tags, /v1/models, /models) mit 10 s Timeout ab. Das ist zugleich der Verbindungstest; Fehler nennen nur den Status, nie den Schlüssel. Die Agenten-Oberfläche nutzt die Liste als Vorschlag, erzwingt sie aber nicht (neue Modelle erscheinen oft zuerst als Name in der Dokumentation). Die Modellauswahl der Agenten speist sich aus der angebotenen Modellliste je Anbieter (ai_models: Modell-ID, Name, Version, an/aus, Reihenfolge; GET /platform/ai/models, PUT /platform/ai/providers/{provider}/offered-models ersetzt die Liste eines Anbieters). Der Verbindungsdialog (Stift) bearbeitet sie unter den Zugangsdaten als kleine Tabelle und speichert beides zusammen; das Pulssymbol bleibt der reine Verbindungstest. Die Tabelle erlaubt an-/abwählen, ändern, hinzufügen und löschen und startet bei leerer Liste mit der jeweils neuesten Version je Familie (Haiku 4.5, GPT mini 5; Ollama: jeder installierte Tag). Ohne gespeicherte Liste gelten diese Vorschläge direkt. Gespeichert wird die exakte Modell-ID; die Liste ist Komfort, kein Filter: freie Eingabe bleibt möglich.
  7. Gemeinsames Protokoll. OllamaProvider, AnthropicProvider (erzwungenes Tool result) und OpenAIProvider (response_format mit JSON-Schema) erfüllen LLMProvider.complete_structured; die Pipeline löst ihren Agenten über resolve_agent auf und kennt keinen Anbieter.
  8. Health. Die Zeile ai_agents meldet nur Agenten, die der Code aufruft, und ruft gehostete Anbieter nicht selbst auf.
  9. Aufwand (Effort). ai_agents.effort ('', low, medium, high; leer = Standard des Anbieters) wird nur für Reasoning-Modelle gespeichert und gesendet: OpenAI GPT-5 und o-Serie als reasoning_effort, Ollama gpt-oss als think. Anthropic ist ausgenommen, weil sein Denkbudget nicht mit dem erzwungenen Tool-Aufruf für das Ergebnis kombinierbar ist. Wechselt das Modell auf eines ohne Effort, verwirft der Server den Wert (supports_effort in ai_catalog.py, Gegenstück modelSupportsEffort im Frontend).
  10. Anweisungen. ai_agents.instructions (leer = Standardtext aus agent_instructions.py) hält den Systemtext, den ein Admin je Agent anpassen kann; nur Agenten mit Standardtext (derzeit field_message) nehmen einen an (sonst 422). Ein Update ohne instructions lässt den Text unverändert, der Standardtext oder ein leerer Wert setzt zurück. Das Ergebnisformat (FieldReport) geht als JSON-Schema separat an das Modell und bleibt unveränderlich. Läufe mit angepasstem Text tragen +custom in agent_runs.prompt_version. Das Abhaken per Chat (task_closure.py) ist regelbasiert und hat keine Anweisungen.
  11. Oberfläche. Unter Einstellungen → System → KI-Agent gibt es zwei Unterseiten als eigene Routen: /settings/ai-agent/agents und /settings/ai-agent/providers. Zuordnung und Verbindung werden in Dialogen bearbeitet, damit keine Seite mit allen Feldern entsteht.

Folgen

  • Kostenkontrolle liegt beim Betreiber (Limits im Konsolenkonto des Anbieters).
  • Die Dokument-Agenten (P14b) und der Tagesbericht brauchen keine weitere Konfiguration, wenn sie angebunden werden.
  • Ein Fehlurteil bei „erledigt“ ist teurer als ein verpasstes. Reicht die Qualität nicht, lässt sich field_message auf ein größeres Modell stellen, ohne andere Agenten zu ändern; eine Aufteilung in Verstehen und Erledigt-Prüfung wäre ein eigener Schritt.
  • Mandantenweise Wahl ist nicht vorgesehen; sie wäre eine eigene Entscheidung.