jbergner 83b831526a
All checks were successful
release-tag / release-image (push) Successful in 1m36s
Patch
2026-08-05 19:17:23 +02:00
2026-07-27 17:32:35 +02:00
2026-08-04 20:52:55 +02:00
2026-07-27 17:32:35 +02:00
2026-08-05 19:13:45 +02:00
2026-08-05 19:17:23 +02:00
2026-08-05 15:33:56 +02:00
2026-08-05 19:13:45 +02:00
2026-08-05 19:13:45 +02:00
2026-08-04 20:52:55 +02:00
2026-08-04 20:52:55 +02:00
2026-08-04 20:52:55 +02:00
2026-08-02 16:14:11 +02:00
2026-08-02 23:00:39 +02:00
2026-08-05 19:13:45 +02:00
2026-08-02 16:14:11 +02:00
2026-08-05 19:13:45 +02:00
2026-08-05 19:13:45 +02:00
2026-08-04 20:52:55 +02:00
2026-07-27 17:32:35 +02:00
2026-08-04 20:52:55 +02:00
2026-08-04 20:52:55 +02:00
2026-08-05 19:13:45 +02:00
2026-07-27 19:18:36 +02:00
2026-08-05 19:13:45 +02:00
2026-08-05 19:13:45 +02:00

GLPI AI Agent (Go + Ollama)

Produktionsorientierter, bewusst policy-gesteuerter Ticket-Agent für GLPI 11. Er liest neue/geänderte Tickets über die GLPI High-Level API, führt getrennte KI-Läufe für Kategorie, Priorität, Störungszuordnung und Antwortauswahl aus und kann offene Tickets in einem unabhängigen Scheduler auf Eskalationsbedarf prüfen. Schreiboperationen erfolgen ausschließlich nach deterministischen Go-Policies.

Sicherheitsmodell

  • DRY_RUN=true ist der Default.
  • AUTO_REPLY=false ist der Default.
  • Das LLM erhält keine GLPI-Tools und kann keine Schreiboperation direkt auslösen.
  • Kategorie-IDs werden gegen die aus GLPI geladene Kategorie-Liste validiert.
  • Automatische Antworten stammen nicht aus freiem LLM-Text, sondern aus einem freigegebenen Knowledge-Dokument (auto_reply=true).
  • Der Knowledge-Index ist fail-closed: Geladen wird nur die Vereinigung aus KNOWLEDGE_ALLOWED_SOURCES und KNOWLEDGE_CATEGORY_SOURCES.
  • Einträge aus KNOWLEDGE_CATEGORY_SOURCES werden ausschließlich für die Kategorieentscheidung verwendet und sind keine Antwortkandidaten.
  • Auto-Replies benötigen zusätzlich eine Quelle aus KNOWLEDGE_AUTO_REPLY_SOURCES sowie passende Sprach-/Stil-Metadaten.
  • Endnutzer-Antworten werden zentral mit konfigurierter Anrede, Grußformel und Signatur gerahmt.
  • Vor einer Antwort werden Followups zweimal geprüft: vor der KI-Analyse und unmittelbar vor dem Schreiben.
  • Sobald irgendein Followup existiert, antwortet der Agent nicht.
  • Pro Ticket wird innerhalb eines Prozesses seriell gearbeitet; Polling/Webhook-Ereignisse werden dedupliziert.
  • GLPI-Schreibfehler werden nicht automatisch wiederholt, um Doppelwrites zu vermeiden.
  • Das Dashboard ist read-only und standardmäßig mit HTTP Basic Auth geschützt.
  • Jeder KI-Schritt wird als eigenständiger AnalysisRun mit Input-Hash, Prompt-Version, Reason Codes, Policy-Checks und Action-Audit gespeichert.
  • Zeitgesteuerte Eskalationsläufe sind von date_mod und der normalen Ticket-Deduplizierung unabhängig.
  • Die interne Verarbeitung nutzt eine deduplizierende Prioritätsqueue; Webhooks, manuelle Läufe, Polling und Scheduler besitzen getrennte Prioritäten.
  • Audit-Trail: data/runs.jsonl mit automatischer Kompaktierung bei starkem Wachstum. Der kompakte Betriebszustand für letzte Ticketversionen und ausgeführte Eskalationsstufen liegt getrennt in data/state-index.json.

Wichtige Grenze: Die zweite Followup-Prüfung minimiert Race Conditions, kann ohne einen atomaren Conditional-Write auf GLPI-Seite aber kein mathematisch vollständig atomisches "check-and-write" garantieren. Für einen einzelnen Agent-Prozess ist zusätzlich ein Ticket-Lock aktiv.

Voraussetzungen

  • GLPI 11.0.6+ empfohlen (API v2.3).
  • High-Level API in GLPI aktiviert.
  • OAuth Client in Setup > OAuth Clients mit Password Grant und api Scope.
  • Dedizierter GLPI-Servicebenutzer mit minimal nötigen Rechten: Tickets lesen, Kategorien lesen/ändern (falls genutzt), Followups lesen/hinzufügen (falls Auto-Reply genutzt).
  • Ollama mit Chat- und Embedding-Modell.

Beim Start lädt der Agent /api.php/doc.json und prüft, ob die erwarteten Kernrouten vorhanden sind. Dadurch schlägt ein API-Mismatch früh und sichtbar fehl. Die mitgelieferten Tests laufen gegen HTTP-Mocks; eine echte GLPI-Instanz konnte in dieser Build-Umgebung nicht angebunden werden, daher ist der Shadow-Mode auf deiner Installation vor Live-Schreibzugriff zwingend.

Start nativ unter Windows / PowerShell

Für einen nativen Windows-Start nicht die Docker-Pfade /app/data, /app/knowledge oder den Docker-Hostnamen ollama verwenden. Die mitgelieferte .env.example enthält deshalb jetzt native, plattformneutrale Defaults:

DATA_DIR=./data
KNOWLEDGE_DIR=./knowledge
OLLAMA_URL=http://localhost:11434

Einmalig:

Copy-Item .env.example .env
# Danach .env mit den echten GLPI-Zugangsdaten bearbeiten.
ollama pull qwen3:8b
ollama pull embeddinggemma

Start:

.\run.ps1

run.ps1 lädt .env, startet immer aus dem Projektverzeichnis und erkennt zur Migration auch alte Docker-Werte. Beispielsweise wird ein vorhandenes KNOWLEDGE_DIR=/app/knowledge beim nativen Windows-Start auf <Projekt>\knowledge umgesetzt und mit einer Warnung ausgegeben. Das Datenverzeichnis wird bei Bedarf erstellt; ein fehlendes Knowledge-Verzeichnis führt zu einer verständlichen Fehlermeldung statt zu einem Panic.

Docker Compose überschreibt diese drei nativen Werte im Container weiterhin explizit mit /app/data, /app/knowledge und http://ollama:11434.

Start mit Docker Compose

cp .env.example .env
$EDITOR .env

docker compose up -d ollama
docker compose exec ollama ollama pull qwen3:8b
docker compose exec ollama ollama pull embeddinggemma

docker compose up -d --build agent

Dashboard: http://127.0.0.1:8080/

Vor dem ersten Live-Betrieb unbedingt mehrere Tage/Wochen im Shadow Mode lassen:

DRY_RUN=true
AUTO_CATEGORY=true
AUTO_REPLY=false
PRIORITY_ENABLED=true
AUTO_PRIORITY=false
ESCALATION_ENABLED=false
AUTO_ESCALATION=false

Danach zunächst nur Kategorieänderungen:

DRY_RUN=false
AUTO_CATEGORY=true
AUTO_REPLY=false

Erst nach Auswertung der Audit-Daten einzelne KB-Einträge mit auto_reply=true freigeben und anschließend:

AUTO_REPLY=true
GLPI_AGENT_USER_ID=123

Getrennte KI-Läufe: Priorität und Eskalation

Jede Analyse besitzt eine eigene ID und wird unabhängig diagnostiziert. Der übergeordnete Ticketlauf enthält lediglich die zeitliche und kausale Klammer. Die Diagnose zeigt je Analyse unter anderem Modell, Prompt-Version, Eingabe-Snapshot und -Hash, strukturierte Entscheidung, Grundcodes, Confidence, Policy-Gates und die tatsächlich ausgeführte Aktion.

Die Prioritätsanalyse ist standardmäßig aktiv, aber im Shadow Mode:

PRIORITY_ENABLED=true
AUTO_PRIORITY=false
PRIORITY_CONFIDENCE=0.88
PRIORITY_MAX_INCREASE=1

Das Modell empfiehlt eine GLPI-Priorität und kontrollierte Grundcodes. Die Go-Policy verhindert Herabstufungen, begrenzt Erhöhungen und akzeptiert nur konfigurierte Gründe. Für einen kontrollierten Live-Betrieb sind beide Schalter erforderlich:

DRY_RUN=false
AUTO_PRIORITY=true

Die Eskalation verwendet einen separaten Scheduler und findet deshalb auch unveränderte, ältere Tickets:

ESCALATION_ENABLED=true
AUTO_ESCALATION=false
ESCALATION_SCAN_INTERVAL=15m
ESCALATION_MIN_AGE=4h
GLPI_ESCALATION_FILTER=status.id==1

Zuerst sollte AUTO_ESCALATION=false bleiben. Der Scheduler erzeugt dann vollständige Eskalationsanalysen, führt aber keine Aktion aus. Implementiert sind raise_priority, assign_second_level, assign_security_team, notify_service_owner, link_major_incident und request_manager_review. Das Modell kann höchstens drei Aktionen empfehlen; jede wird separat gegen Zielkonfiguration, Grundcodes, Mindeststufe und Idempotenz geprüft und als eigener Action-Audit-Schritt gespeichert. Erfolgreiche Schritte werden mit Ticket, Stufe, Aktion und Ziel in state-index.json dedupliziert. Followups des konfigurierten Agent-Benutzers werden bei der Inaktivitätsberechnung ausgenommen. Für AUTO_ESCALATION=true muss GLPI_AGENT_USER_ID auf das dedizierte GLPI-Agentkonto zeigen; andernfalls verweigert die Konfiguration den Start. Die vollständige Konfiguration und Einführungsreihenfolge steht in ESCALATION.md.

GLPI-Endpunkte

Default ist GLPI_API_VERSION=v2.3. Der Client verwendet:

  • OAuth: POST /api.php/token
  • Tickets lesen und Eskalationskandidaten suchen: /api.php/v2.3/Assistance/Ticket
  • Kategorie, Priorität und konfigurierte Bearbeiter-/Gruppenzuweisungen schreiben: PATCH /api.php/v2.3/Assistance/Ticket/{id}
  • Öffentliche und private Followups: /api.php/v2.3/Assistance/Ticket/{id}/Timeline/Followup
  • Major-Incident-Verknüpfung: installationsspezifischer, ausdrücklich über GLPI_ESCALATION_ITIL_LINK_PATH und GLPI_ESCALATION_ITIL_LINK_BODY konfigurierter POST
  • Kategorien: /api.php/v2.3/Dropdowns/ITILCategory
  • OpenAPI-Prüfung: /api.php/doc.json

Die OpenAPI-Dokumentation deiner Installation ist die maßgebliche Quelle, weil GLPI die API-Dokumentation dynamisch aus Core und aktivierten Plugins erzeugt.

Wissensbasis, Quellen und Kommunikationspolicy

Jede Datei in knowledge/ ist JSON und trägt eine explizite Herkunft sowie Kommunikations-Metadaten:

{
  "id": "KB-128",
  "title": "GlobalProtect Gateway nicht erreichbar",
  "text": "Beschreibung, Fehlermeldungen, Voraussetzungen ...",
  "answer": "Bitte trennen Sie die bestehende VPN-Verbindung vollständig und starten Sie den VPN-Client anschließend neu.",
  "auto_reply": true,
  "min_score": 0.92,
  "categories": [22],
  "keywords": ["GlobalProtect", "Gateway not responding"],
  "source": "internal-kb",
  "source_uri": "kb://network/vpn/128",
  "language": "de-DE",
  "communication_style": "formal"
}

Die aktive Source-Policy wird über die Umgebung festgelegt:

# Normale Knowledge-Suche und mögliche Antwortkandidaten.
KNOWLEDGE_ALLOWED_SOURCES=internal-kb,glpi-kb,vendor-docs

# Ausschließlich für die Kategorisierung; keine Antwortauswahl möglich.
KNOWLEDGE_CATEGORY_SOURCES=internal-category

# Nur diese Teilmenge der normalen Quellen darf eine automatische Antwort auslösen.
KNOWLEDGE_AUTO_REPLY_SOURCES=internal-kb,glpi-kb

KNOWLEDGE_AUTO_REPLY_SOURCES muss eine Teilmenge von KNOWLEDGE_ALLOWED_SOURCES sein. KNOWLEDGE_CATEGORY_SOURCES darf dagegen eigene Quellen enthalten. Diese werden indexiert und ausschließlich im ersten, separaten Ollama-Aufruf für die Kategorieanalyse verwendet; Antworttext und HTML werden dabei entfernt. Erst nach dieser Kategorieentscheidung werden die normalen Antwortquellen anhand der wirksamen Kategorie neu gerankt und sofern keine passende Statusantwort ausgewählt wurde in einem nachgelagerten Ollama-Aufruf bewertet. Kategorie-KB-IDs sind niemals als Antwort-Knowledge zulässig. Ohne gesetzte Variable entspricht KNOWLEDGE_CATEGORY_SOURCES aus Kompatibilitätsgründen KNOWLEDGE_ALLOWED_SOURCES. Mit KNOWLEDGE_CATEGORY_SOURCES=none kann der Knowledge-Einfluss auf die Kategorisierung deaktiviert werden. Mit KNOWLEDGE_AUTO_REPLY_SOURCES=none kann die Quellenfreigabe für Auto-Replies vollständig deaktiviert werden. Ein Knowledge-Dokument ohne source führt absichtlich zu einem Startfehler, damit die Herkunft nicht implizit geraten wird.

Gemeinsame KB-Dateien mit fremden Kategorien

Lokale KB-Dateien dürfen in categories neben numerischen GLPI-IDs jetzt auch String-Kategorien aus einer anderen Anwendung enthalten. Die Quelldatei muss dafür nicht verändert werden. Beispiel:

{
  "id": "KB-SEC-ATTCK-AN-0001",
  "categories": ["Security", "MITRE ATT&CK", "Account Access"]
}

Empfohlener Standard:

KNOWLEDGE_CATEGORY_MODE=unscoped
KNOWLEDGE_CATEGORY_MAP_FILE=/app/data/knowledge-category-map.json
KNOWLEDGE_IGNORE_GLOBS=

unscoped lädt auch Artikel mit unbekannten externen Kategorien. Diese Labels werden als external_categories im Agenten behalten und für das lexikalische Retrieval mitbenutzt. Solange mindestens eine externe Kategorie nicht auf GLPI abgebildet ist, wird auto_reply für diesen Artikel fail-closed deaktiviert. Der Artikel bleibt aber für RAG und Klassifizierung verfügbar.

Eine Mapping-Datei kann externe Kategorien ohne Änderung der KB-Dateien auf eine oder mehrere GLPI-ITIL-Kategorie-IDs abbilden:

{
  "Security": 17,
  "Account Access": [2, 17],
  "Microsoft Office": 23,
  "Docker": 31
}

Alternativ ist auch { "mappings": { ... } } erlaubt. Mapping-Schlüssel werden ohne Beachtung der Groß-/Kleinschreibung verglichen. Numerische Strings in categories, z. B. "17", werden direkt als GLPI-ID verstanden.

Weitere Modi:

  • KNOWLEDGE_CATEGORY_MODE=skip: Eine Datei mit mindestens einer unbekannten externen Kategorie wird komplett ignoriert.
  • KNOWLEDGE_CATEGORY_MODE=strict: Eine unbekannte externe Kategorie verhindert den Start. Das entspricht dem alten strengen Verhalten.

Bestimmte gemeinsame Dateien können unabhängig davon per Dateimuster ausgeschlossen werden:

KNOWLEDGE_IGNORE_GLOBS=KB-SEC-ATTCK-*.json,external-only-*.json

Im Dashboard werden externe und nicht gemappte Kategorien sowie die Zahl ignorierter Dateien angezeigt.

GLPI Knowledge Base als echter Connector

Die GLPI-Wissensdatenbank kann jetzt direkt read-only synchronisiert werden. Der Agent ermittelt bei GLPI_KB_PATH=auto den lesbaren KnowbaseItem-Collection-Endpunkt aus /api.php/doc.json. GLPI selbst entscheidet anhand der Rechte des OAuth-Service-Accounts, welche Artikel sichtbar sind.

KNOWLEDGE_ALLOWED_SOURCES=internal-kb,glpi-kb
GLPI_KB_ENABLED=true
GLPI_KB_PATH=auto
GLPI_KB_SYNC_INTERVAL=10m
GLPI_KB_LIMIT=500
GLPI_KB_SOURCE=glpi-kb

Erfolgreich synchronisierte Artikel werden normalisiert, lokal unter DATA_DIR/glpi-kb-cache.json gecacht und in denselben RAG-Index wie lokale Knowledge-Dokumente aufgenommen. Unveränderte Dokumente behalten ihre gecachten Embeddings; nur neue oder geänderte Artikel werden erneut eingebettet. Fällt GLPI bei einem späteren Start/Sync aus, kann der zuletzt gespeicherte Cache weiter als read-only Wissensstand geladen werden.

GLPI-KB-Auto-Replies sind absichtlich separat gesperrt. Die Grundfreigabe ist jetzt bewusst einfach und entspricht der GLPI-Datenstruktur:

  1. Artikel mit GLPI-Knowledge-Base-Kategorie: Mindestens eine Artikel-KB-Kategorie muss in GLPI_KB_AUTO_REPLY_CATEGORY_IDS enthalten sein.
  2. Artikel ohne GLPI-Knowledge-Base-Kategorie: GLPI_KB_AUTO_REPLY_ALLOW_UNCATEGORIZED=true und die konkrete GLPI-KnowbaseItem-ID muss in GLPI_KB_AUTO_REPLY_UNCATEGORIZED_ARTICLE_IDS stehen.
  3. ITIL-/Ticketkategorien geben keinen Artikel frei. Sie können, soweit GLPI ein Mapping liefert, weiterhin als fachliches Signal für Retrieval, Evidenz und die separate Prüfung „Artikel passt zur effektiven Ticketkategorie“ dienen.

Beispiel für kategorisierte Artikel:

KNOWLEDGE_AUTO_REPLY_SOURCES=internal-kb,glpi-kb
GLPI_KB_AUTO_REPLY=true
GLPI_KB_AUTO_REPLY_CATEGORY_IDS=4,7
GLPI_KB_AUTO_REPLY_ALLOW_UNCATEGORIZED=false
GLPI_KB_AUTO_REPLY_UNCATEGORIZED_ARTICLE_IDS=

Beispiel für einen ausdrücklich freigegebenen Artikel ohne KB-Kategorie:

KNOWLEDGE_AUTO_REPLY_SOURCES=internal-kb,glpi-kb
GLPI_KB_AUTO_REPLY=true
GLPI_KB_AUTO_REPLY_CATEGORY_IDS=4,7
GLPI_KB_AUTO_REPLY_ALLOW_UNCATEGORIZED=true
GLPI_KB_AUTO_REPLY_UNCATEGORIZED_ARTICLE_IDS=1,5

Damit sind die kategorisierten Artikel der KB-Kategorien 4 und 7 sowie ausschließlich die unkategorisierten GLPI-Artikel 1 und 5 grundsätzlich freigegeben. Alle weiteren Retrieval-, KI-, Evidenz-, Sprach-, Stil-, Kontext- und Ausführungsprüfungen bleiben unverändert.

GLPI_KB_AUTO_REPLY_ITIL_CATEGORY_IDS ist veraltet und wird ignoriert. Alte Konfigurationen sollten den Wert leeren oder die Variable entfernen. Ein alter glpi-kb-cache.json, der noch mit der vorherigen ITIL-Freigabelogik erzeugt wurde, wird aus Sicherheitsgründen nicht geladen und beim nächsten erfolgreichen GLPI-KB-Sync im neuen Format ersetzt.

Die Diagnose trennt jetzt zwei Fragen klar:

  • „Artikel darf für Auto-Reply verwendet werden“: reine Grundfreigabe über KB-Kategorie oder konkrete Artikel-ID.
  • „Artikel passt zur effektiven Ticketkategorie“: fachliche Eignung auf Basis eines vorhandenen Mappings; fehlt ein Mapping, entscheiden Retrieval, KI-Auswahl und Evidenz.

Optional kann GLPI_KB_FILTER gesetzt werden, um die von GLPI gelieferte Collection zusätzlich serverseitig einzuschränken. Die konkrete Filtersyntax und verfügbare Felder stammen aus der OpenAPI-Dokumentation deiner installierten GLPI-Version.

Für die Kommunikation gelten zentrale Vorgaben:

COMMUNICATION_LANGUAGE=de-DE
COMMUNICATION_STYLE=formal
COMMUNICATION_SALUTATION=Guten Tag,
COMMUNICATION_CLOSING=Mit freundlichen Grüßen
COMMUNICATION_SIGNATURE=IT-Service
AI_CONTENT_LABEL_ENABLED=true

Ein Auto-Reply ist nur erlaubt, wenn language und communication_style des freigegebenen Knowledge-Dokuments exakt zur aktiven Policy passen. Das Feld answer enthält nur den fachlich freigegebenen Nachrichtentext; Anrede, Grußformel und Signatur werden von der Go-Policy zentral ergänzt. Damit kann das Modell diese Kommunikationsvorgaben nicht überschreiben.

categories begrenzt Auto-Reply auf die angegebenen Zielkategorien. Eine leere Liste bedeutet keine zusätzliche Kategorie-Einschränkung. min_score kann die globale Schwelle je Artikel verschärfen.

Bei aktiviertem RAG erzeugt Ollama Embeddings über /api/embed. Der aktive lokale Index wird persistent unter DATA_DIR/knowledge-index/snapshot.gob gespeichert. Ein vorhandenes altes data/embeddings.json wird nur noch als einmalige Migrationsquelle verwendet. Für Ticket und Knowledge wird dasselbe Embedding-Modell verwendet.

Realistisches Hybrid-Scoring und dynamische Kandidatenauswahl

Knowledge-Treffer werden nicht nur über eine einzelne Cosine-Similarity bewertet. Lange Artikel werden in überlappende Abschnitte zerlegt und der beste semantische Abschnitt wird mit Titel-, lexikalischen, Keyword- und Kategorie-/Lernsignalen kombiniert. Empfohlene Standardwerte:

KNOWLEDGE_MIN_SCORE=0.70
KNOWLEDGE_RETRIEVAL_FLOOR=0.30
KNOWLEDGE_WEIGHT_SEMANTIC=0.45
KNOWLEDGE_WEIGHT_TITLE=0.20
KNOWLEDGE_WEIGHT_LEXICAL=0.20
KNOWLEDGE_WEIGHT_KEYWORDS=0.075
KNOWLEDGE_WEIGHT_CATEGORY=0.075
KNOWLEDGE_CHUNK_WORDS=160
KNOWLEDGE_CHUNK_OVERLAP_WORDS=30
KNOWLEDGE_MAX_CHUNKS_PER_DOC=24
KNOWLEDGE_MAX_QUERY_CHUNKS=64

# Dynamisches Top-K
KNOWLEDGE_TOP_K=6
KNOWLEDGE_AUDIT_TOP_K=10
KNOWLEDGE_CANDIDATE_MAX_GAP=0.20

KNOWLEDGE_TOP_K ist jetzt die maximale Anzahl von Kandidaten, die Ollama sehen darf, nicht die Anzahl, die blind immer übergeben wird. Nach dem Retrieval wird ein dynamischer Cutoff berechnet:

cutoff = max(KNOWLEDGE_RETRIEVAL_FLOOR, bester_score - KNOWLEDGE_CANDIDATE_MAX_GAP)

Beispiel: Bei Scores 0.82, 0.79, 0.76, 0.43 und KNOWLEDGE_CANDIDATE_MAX_GAP=0.20 gehen nur die ersten drei Treffer an Ollama, weil der Cutoff 0.62 beträgt. Bei einem unklareren Fall 0.66, 0.64, 0.63, 0.61, 0.59 dürfen dagegen bis zu fünf Kandidaten in den Modellkontext. Liegt bereits der beste Treffer unter KNOWLEDGE_RETRIEVAL_FLOOR, erhält Ollama keinen KB-Kandidaten.

KNOWLEDGE_AUDIT_TOP_K ist davon getrennt. Das Dashboard kann z. B. die besten zehn Treffer zur Diagnose zeigen, während höchstens sechs und meist deutlich weniger an Ollama gesendet werden. Jeder Audit-Kandidat wird mit an KI gesendet oder nur Audit gekennzeichnet.

Der angezeigte Retrieval-/Hybrid-Score ist keine Wahrscheinlichkeit. Er ist ein nachvollziehbarer Ranking-Score. Die Semantik verwendet die Ähnlichkeit des besten Body-Chunks; der Titel kombiniert Embedding- und lexikalischen Titelmatch; Keywords und Kategorie-/Lernsignale dienen als positive Evidenz. Fehlen solche Metadaten, werden sie nicht als Null-Strafe eingerechnet.

Für die spätere Auto-Reply-Freigabe gilt weiterhin die getrennte Evidenzlogik aus Retrieval, KI-Auswahl und Kategorieübereinstimmung. Der effektive finale Schwellwert ist max(KNOWLEDGE_MIN_SCORE, min_score des Artikels).

Operativer Kontext: Changes, Major Incidents, Uptime Kuma und Geräte

Der Agent kann vor der LLM-Entscheidung zusätzliche read-only Betriebsdaten einsammeln. Diese Daten werden normalisiert und als Fakten in den Prompt aufgenommen; das Modell erhält keine direkten Zugangsdaten und keine zusätzlichen Schreibwerkzeuge.

Change Calendar

CHANGE_CALENDAR_ENABLED=true
GLPI_CHANGE_PATH=/Assistance/Change
GLPI_CHANGE_FILTER=
GLPI_CHANGE_LIMIT=100
CHANGE_LOOKBACK=48h
CHANGE_LOOKAHEAD=24h

Der Agent lädt Changes im konfigurierten Zeitfenster, berechnet eine deterministische Relevanz zum Ticket (u. a. Tickettext, verknüpfte Geräte/Standorte) und übergibt höchstens die relevantesten Einträge an Ollama. GLPI_CHANGE_PATH wird beim Start gegen /api.php/doc.json geprüft. Bei einer Installation mit abweichender Route oder Filter-Syntax muss die Konfiguration an das OpenAPI-Schema der eigenen Instanz angepasst werden.

Aktive Major Incidents

MAJOR_INCIDENTS_ENABLED=false
GLPI_MAJOR_INCIDENT_FILTER=
GLPI_MAJOR_INCIDENT_LIMIT=20

Major Incidents werden bewusst nicht automatisch aus beliebigen Tickets erraten. Sie sind eine explizit vom Betreiber definierte Teilmenge der GLPI-Tickets. Erst wenn GLPI_MAJOR_INCIDENT_FILTER die in deiner Umgebung gültige Filterdefinition enthält, sollte MAJOR_INCIDENTS_ENABLED=true gesetzt werden. Ein zum aktuellen Ticket relevanter Major Incident blockiert standardmäßig einen normalen Auto-Reply; die Kategorieanalyse darf weiterlaufen.

Beispiel-Idee (die konkrete Syntax muss zu deinem GLPI-OpenAPI-Schema passen): ein Filter auf eine dedizierte Kategorie, Priorität/Impact oder ein eigenes Kennzeichen für Major Incidents.

Aktuelle Störungen mit Uptime Kuma

Für interne Uptime-Kuma-Instanzen ist der authentifizierte Prometheus-Endpunkt der empfohlene Modus:

UPTIME_KUMA_ENABLED=true
UPTIME_KUMA_URL=https://uptime.example.org
UPTIME_KUMA_MODE=metrics
UPTIME_KUMA_API_KEY=CHANGE_ME
UPTIME_KUMA_TIMEOUT=10s
UPTIME_KUMA_MAX_ISSUES=20

Der Client liest ausschließlich /metrics, verwendet den Uptime-Kuma-API-Key als HTTP-Basic-Auth-Passwort und gibt nur Monitore weiter, die nicht UP sind. Der API-Key wird nicht an Ollama übergeben.

Alternativ können bereits veröffentlichte Statusseiten gelesen werden:

UPTIME_KUMA_MODE=status_page
UPTIME_KUMA_STATUS_PAGES=it-services,network
UPTIME_KUMA_INCLUDE_MAINTENANCE=true

In diesem Modus liest der Agent /api/status-page/<slug> und /api/status-page/heartbeat/<slug> und berücksichtigt gepinnte Incidents, DOWN/PENDING-Monitore und optional Wartungen. Dieser Modus eignet sich nur für Informationen, die auf der betreffenden Statusseite ohnehin veröffentlicht werden dürfen.

Vordefinierte Antworten bei eindeutiger Störung oder Wartung

Optional kann zwischen Kategorie- und normaler KB-Antwortanalyse eine eigene Uptime-Kuma-Zuordnung aktiviert werden:

CONTEXT_STATUS_REPLY_ENABLED=true
CONTEXT_STATUS_REPLY_MIN_RELEVANCE=0.50
CONTEXT_STATUS_REPLY_MIN_AI_CONFIDENCE=0.80
CONTEXT_STATUS_REPLY_MIN_FINAL_SCORE=0.45
CONTEXT_INCIDENT_REPLY_TEXT=Zu Ihrer Meldung liegt derzeit wahrscheinlich eine zentrale Störung bei {{service_name}} vor. Die Einschränkung kann damit zusammenhängen. Wir beobachten den Status.
CONTEXT_MAINTENANCE_REPLY_TEXT=Für {{service_name}} läuft derzeit eine Wartung. Die von Ihnen beschriebene Einschränkung kann damit zusammenhängen. Bitte testen Sie den Dienst nach Abschluss der Wartung erneut.

Der Ablauf ist strikt getrennt:

  1. Die Kategorie wird bestimmt.
  2. Ollama darf ausschließlich bewerten, ob genau ein aktiver Uptime-Kuma-Eintrag zum Ticket passt. Die strukturierte Ausgabe enthält nur Treffer, Kandidaten-ID, Confidence und eine interne Begründung.
  3. Go prüft den deterministischen Relevanzscore, die KI-Confidence und Relevanz × KI-Confidence.
  4. Nur wenn alle drei Schwellwerte erreicht sind, wird der passende Betreibertext für Störung oder Wartung verwendet. Die normale KB-Antwortanalyse wird dann übersprungen.
  5. Bei Unsicherheit greift weiterhin der normale, fail-closed Reply-Pfad.

Die KI formuliert dabei keinen Benutzertext. Folgende Platzhalter werden ausschließlich mit den bereits gelesenen Uptime-Kuma-Daten ersetzt: {{service_name}}, {{status}}, {{status_page}}, {{message}}, {{incident_title}}, {{incident_content}} und {{last_heartbeat}}. In ENV-Werten kann \n für einen Zeilenumbruch verwendet werden.

AUTO_REPLY=true, ein Ticket ohne vorhandenes Followup und ein vollständiger Kontext sind weiterhin zwingend erforderlich.

Beziehungen zwischen Benutzer und Gerät

USER_DEVICE_CONTEXT_ENABLED=true
GLPI_USER_DEVICE_PATHS=/Assets/Computer
GLPI_USER_DEVICE_FILTER_TEMPLATE=user.id=={{user_id}}
GLPI_USER_DEVICE_LIMIT=20

Der Agent nutzt zunächst direkt am Ticket verknüpfte GLPI-Items. Zusätzlich werden soweit der Ticket-Response Requester-IDs enthält über die konfigurierten Asset-Routen dem Benutzer zugeordnete Geräte gelesen. Die Pfade werden beim Start gegen die OpenAPI-Dokumentation geprüft; die Filter-Syntax ist installationsabhängig und sollte im Shadow Mode verifiziert werden.

Die normalisierten Gerätedaten dienen u. a. dazu, Changes und Störungen besser zum Ticket zuzuordnen. Es werden keine Assets geändert.

Fail-closed Verhalten

CONTEXT_ENABLED=true
CONTEXT_TIMEOUT=12s
CONTEXT_RELEVANCE_MIN_SCORE=0.20
CONTEXT_BLOCK_AUTO_REPLY_ON_ERRORS=true
CONTEXT_BLOCK_AUTO_REPLY_ON_INCIDENT=true

Mit den sicheren Defaults gilt:

  • Fällt eine aktivierte Kontextquelle aus, wird der Lauf als unvollständig markiert und kein Auto-Reply gesendet.
  • Ein relevanter Major Incident oder eine relevante Uptime-Kuma-Störung blockiert einen normalen Standard-Auto-Reply.
  • Ist die optionale Statusantwort aktiviert und erreicht eine Uptime-Kuma-Zuordnung alle konfigurierten Schwellwerte, darf stattdessen ausschließlich der vordefinierte Störungs- oder Wartungstext gesendet werden.
  • Kategorieanalyse und Auditierung können trotzdem stattfinden.
  • Kontextquellen haben ausschließlich Leserechte.
  • Im Dashboard/Audit erscheinen pro Lauf die Anzahl der gefundenen Changes, Incidents, Uptime-Issues und Geräte sowie Warnungen bei unvollständigem Kontext.

Web- und Monitoring-Endpunkte

  • / Dashboard (Basic Auth)
  • /api/status Status JSON inklusive aktiver Sprache/Stil- und Quellenpolicy (Basic Auth)
  • /api/runs?limit=50 letzte Audit-Läufe (Basic Auth)
  • /api/diagnostics/analysis/{analysis_id} einzelner, eigenständiger KI-Analyselauf (Basic Auth)
  • /healthz Prozess lebt
  • /readyz GLPI und Ollama erreichbar
  • /metrics Prometheus Textformat
  • POST /webhook/glpi optionaler Webhook-Eingang, geschützt durch X-Webhook-Secret

Das Polling bleibt immer aktiv und dient als Fallback. Der Webhook-Parser akzeptiert übliche Ticket-ID-Felder sowie Ticket-URLs; prüfe die konkrete Payload deiner GLPI-Webhook-Konfiguration im Shadow Mode.

Erklärbare Entscheidungen im Dashboard

Für den Shadow-/Einführungsbetrieb speichert jeder Lauf die rohe KI-Empfehlung getrennt von der Policy-Entscheidung. Das Modell liefert bei Kategorien nur noch id und confidence; ein eigenes change=true/false gibt es nicht mehr. Ob tatsächlich geändert werden darf, entscheidet ausschließlich Go anhand der aktuellen Kategorie, der bekannten GLPI-Kategorien und CATEGORY_CONFIDENCE.

Das Dashboard zeigt deshalb unter anderem:

  • aktuelle Kategorie mit ID und Name,
  • von der KI empfohlene Kategorie mit ID und Name,
  • KI-Confidence und konfigurierten Schwellwert,
  • expliziten Entscheidungsgrund wie category_confidence_below_threshold, category_already_correct oder category_written,
  • KI-Empfehlung für Auto-Reply samt Confidence und Reply-Schwellwert,
  • besten Knowledge-Treffer mit Hybrid-Score, Einzelkomponenten, effektivem Schwellwert und bestem Artikelabschnitt,
  • den ersten Policy-Blocker für einen Reply, z. B. fehlendes Knowledge, vorhandenes Followup, unvollständigen Kontext oder einen relevanten Incident,
  • die fachliche KI-Begründung separat von den technischen Policy-Codes.

Damit ist auch ein Lauf ohne Schreibaktion nachvollziehbar. Beispiel: „KI empfiehlt Active Directory (#17) mit 82 %, Schwellwert 90 % → nicht geändert“. Die JSON-Details stehen zusätzlich unter /api/runs?limit=50 zur Verfügung.

Keine Doppelantworten

Der Schreibpfad ist bewusst streng:

  1. Ticket laden.
  2. Followups laden. Existiert eines: Stop.
  3. Knowledge sowie read-only Betriebskontext (Changes, Major Incidents, Uptime Kuma, Benutzer/Geräte) laden.
  4. KI empfiehlt Kategorie-ID + Confidence und optional einen Knowledge-basierten Reply; Kontextdaten sind nur Fakten, keine ausführbaren Anweisungen.
  5. Policy Engine entscheidet deterministisch über Kategorieänderung und Reply und protokolliert jeden akzeptierten oder blockierten Gate-Grund.
  6. Optional Kategorie ändern.
  7. Direkt vor Auto-Reply Ticket und Followups erneut laden. Existiert jetzt ein Followup oder hat sich die Entscheidungsgrundlage geändert: Stop.
  8. Freigegebenen KB-Antworttext als Followup schreiben.

GLPI_AGENT_USER_ID wird bei AUTO_REPLY=true absichtlich verlangt, damit die Betreiberkonfiguration eindeutig einem dedizierten GLPI-Konto zugeordnet ist. Der aktuelle Code blockiert bei jedem vorhandenen Followup einschließlich eines früheren Agent-Followups und ist damit konservativer als nur "fremde" Antworten zu prüfen.

Produktionshinweise

  • Dashboard hinter Reverse Proxy mit TLS betreiben; Compose bindet Port 8080 absichtlich nur an 127.0.0.1.
  • GLPI über HTTPS anbinden. Plain HTTP wird standardmäßig abgelehnt (GLPI_ALLOW_INSECURE_HTTP=false).
  • .env niemals committen; besser Docker/Kubernetes Secrets oder systemd EnvironmentFile mit restriktiven Dateirechten verwenden.
  • Servicekonto nach Least-Privilege-Prinzip konfigurieren.
  • Für mehrere parallele Agent-Replikate muss die lokale Queue/State-Sperre durch einen verteilten Store/Lock (z. B. PostgreSQL/Redis) ersetzt werden. Die mitgelieferte Version ist für eine aktive Agent-Instanz ausgelegt.
  • Vor Live-Auto-Reply Tests mit echten anonymisierten Ticketmustern durchführen.
  • Knowledge-Antworten fachlich freigeben und versionieren.

Build & Tests

Das Projekt verwendet nur die Go-Standardbibliothek; damit gibt es keine Laufzeit-Abhängigkeiten im Agent-Binary.

make fmt
make test
make vet
make build

Mehrere Ollama-Nodes

Der Agent unterstützt einen nativen Ollama-Pool mit Least-In-Flight-Routing, Healthchecks, Failover, Modelldigest-Prüfung und Node-Diagnose pro AnalysisRun. Ein einzelnes Modell wird dabei nicht über Rechner verteilt; jeder Node führt vollständige unabhängige Inferenzrequests aus.

OLLAMA_URLS=http://10.20.30.21:11434,http://10.20.30.22:11434,http://10.20.30.23:11434
OLLAMA_NODE_NAMES=lenovo-01,lenovo-02,lenovo-03
OLLAMA_ROUTING_MODE=least_inflight
OLLAMA_NODE_MAX_INFLIGHT=1
OLLAMA_FAILOVER_ENABLED=true
OLLAMA_FAILOVER_ATTEMPTS=0
OLLAMA_REQUIRE_SAME_MODEL_DIGEST=true
WORKERS=3

WORKERS begrenzt die Zahl gleichzeitig aktiver Ticketpipelines. Für drei gleichartige Nodes sind drei Worker ein sinnvoller Lasttest; die Ressourcen jedes einzelnen Rechners bleiben zusätzlich durch OLLAMA_NODE_MAX_INFLIGHT=1 geschützt.

Die vollständige Betriebsbeschreibung steht in OLLAMA-POOL.md. Der Agent wartet beim Start auf einen kompatiblen Pool, während Dashboard und Node-Diagnose bereits erreichbar bleiben. Bei externen Nodes kann mit docker compose up -d agent nur der Agent gestartet werden.

Docker troubleshooting: /app/data permission denied and slow Ollama

The Compose stack contains a one-shot agent-data-init service. It prepares the named agent-data volume for the non-root agent user before the agent starts. The agent also probes runs.jsonl at startup and exits immediately with a clear error if the volume is not writable.

For local LLMs, the default request budget is intentionally longer than a typical HTTP API call:

OLLAMA_TIMEOUT=10m
OLLAMA_NUM_PREDICT=256
OLLAMA_KEEP_ALIVE=10m
OLLAMA_THINK=false
OLLAMA_MAX_CONCURRENT=1

OLLAMA_NUM_PREDICT limits the maximum generated tokens for the small structured decision. OLLAMA_KEEP_ALIVE asks Ollama to keep the analysis model loaded between tickets. OLLAMA_THINK=false disables optional model thinking for this deterministic classification task. OLLAMA_NODE_MAX_INFLIGHT=1 serializes inference on each individual pool node. OLLAMA_MAX_CONCURRENT remains a backwards-compatible alias when the new per-node value is not set. On very slow CPU-only hosts, use a smaller local model and/or increase OLLAMA_TIMEOUT.

After upgrading an existing Compose deployment, recreate the stack so the init service runs:

docker compose down
docker compose build --no-cache agent agent-data-init
docker compose up -d

You do not need to delete agent-data; the init service fixes ownership on the existing named volume.

Human-in-the-loop-Lernen und Web-Knowledge-Base

Der Agent lernt nicht aus seinen eigenen Entscheidungen. Im Dashboard kann eine Kategorie eines verarbeiteten Tickets ausdrücklich bestätigt oder korrigiert werden. Diese menschlich bestätigten Beispiele werden in DATA_DIR/category-learning.json persistiert und bei ähnlichen Tickets als confirmed_examples an das Klassifikationsmodell übergeben. Zusätzlich werden Kategorie-Hinweise aus freigegebenen KB-Keywords und konservativen IT-Semantik-Hinweisen aufgebaut.

Konfiguration:

LEARNING_ENABLED=true
LEARNING_MAX_EXAMPLES=500
LEARNING_EXAMPLES_PER_CATEGORY=5

Das Dashboard zeigt außerdem den Status und die Anzahl der synchronisierten GLPI-KB-Artikel. Synchronisierte GLPI-Artikel sind read-only und als GLPI-Sync gekennzeichnet.

Das Dashboard enthält außerdem einen CRUD-Editor für interne Knowledge-Einträge. Er ist absichtlich nur bei authentifiziertem Dashboard aktiv:

WEB_ALLOW_ANONYMOUS=false
KNOWLEDGE_WEB_EDIT_ENABLED=true

Web-verwaltete Artikel landen nicht im statischen KNOWLEDGE_DIR, sondern unter DATA_DIR/knowledge-managed/. Dadurch kann knowledge/ weiterhin read-only aus Git/Image gemountet werden. Statische Artikel werden im Web angezeigt, können dort aber nicht überschrieben oder gelöscht werden. Neue bzw. im Web verwaltete Artikel werden nach dem Speichern sofort in den laufenden Such-/RAG-Store aufgenommen; ein Neustart ist nicht nötig.

Für stabilere Structured Outputs sind die empfohlenen Startwerte:

OLLAMA_NUM_PREDICT=768
OLLAMA_JSON_RETRIES=1

Bei unvollständigem/ungültigem JSON wird genau einmal erneut eine schema-konforme Antwort angefordert.

Deployment mit Gitea Container Registry unter Linux

Für ein bereits in Gitea gebautes Image ist docker-compose.registry.yml vorgesehen; lokal wird nichts gebaut.

export AGENT_IMAGE=gitea.example.de/organisation/glpi-ai-agent:latest
mkdir -p data knowledge
sudo chown 65532:65532 data
# knowledge bleibt absichtlich read-only; Web-KB landet unter data/knowledge-managed/
docker compose -f docker-compose.registry.yml pull
docker compose -f docker-compose.registry.yml up -d

Bei neuen Gitea-Builds genügt:

docker compose -f docker-compose.registry.yml up -d --pull always

Lange Tickets und KB-Artikel

Für die semantische Relevanz werden beide Seiten in überlappende Abschnitte zerlegt. Ticket-Abschnitte werden gegen KB-Abschnitte verglichen; der beste lokale Treffer bildet die semantische Komponente. Der Ticket-Betreff wird separat für den Titel-Score verwendet. Dadurch verwässern lange Ticketbeschreibungen einen klar passenden Lösungsabschnitt nicht mehr.

Der Ollama-Embedding-Aufruf verwendet truncate:false. Ein Text, der trotz Chunking das Kontextfenster des Embedding-Modells überschreitet, führt damit zu einem sichtbaren Fehler statt zu stiller Kürzung.

Dashboard / Control Center

Das integrierte Webinterface ist als Betriebs- und Diagnoseoberfläche ausgelegt. Neben Status und Metriken zeigt es die effektiven, nicht geheimen Konfigurationswerte, die einzelnen Policy-Entscheidungen und die Komponenten des Hybrid-RAG-Scores. Eine Verarbeitung kann geöffnet werden, um die Top-KB-Kandidaten, deren Score-Komponenten, die besten Ticket-/KB-Chunks sowie relevante Changes, Incidents, Uptime-Kuma-Störungen und Geräte zu sehen.

Die interne Knowledge Base kann bei KNOWLEDGE_WEB_EDIT_ENABLED=true direkt im authentifizierten Dashboard angelegt, bearbeitet und gelöscht werden. Der Editor lädt beim Bearbeiten immer den aktuellen Stand vom Server. Artikel-IDs sind nach der Anlage unveränderlich. Statische und aus GLPI synchronisierte Artikel bleiben read-only.

GLPI-KB Rich Text

Rich-Text-Formatierungen aus synchronisierten GLPI-KB-Artikeln bleiben in Ticketantworten erhalten; RAG und LLM sehen weiterhin nur bereinigten Plaintext.

Große Knowledge-Verzeichnisse und persistenter Index

Für große lokale Korpora wird der vollständige lokale Index nach dem ersten erfolgreichen Aufbau persistent unter DATA_DIR/knowledge-index/snapshot.gob gespeichert. Beim normalen Neustart mit KNOWLEDGE_INDEX_MODE=incremental wird dieser Snapshot zuerst geladen; die Ticketverarbeitung kann anschließend mit dem letzten konsistenten Index starten. Die Quelldateien werden danach im Hintergrund inkrementell geprüft.

Empfohlene Werte:

KNOWLEDGE_INDEX_MODE=incremental
KNOWLEDGE_EMBED_BATCH_SIZE=64
KNOWLEDGE_INDEX_SCAN_INTERVAL=5m

Beim Delta-Scan werden zunächst nur Dateiname, Größe und mtime geprüft. Unveränderte Dateien werden weder geöffnet noch geparst. Erst bei geänderten Metadaten wird der Dateiinhalt gelesen und gehasht; nur tatsächlich geänderte Retrieval-Inhalte werden erneut an Ollama /api/embed geschickt. Gelöschte Dateien werden aus dem Index entfernt.

Index-Modi:

  • incremental: vorhandenen Snapshot sofort laden und Änderungen im Hintergrund nachziehen. Empfohlen.
  • rebuild: Quelldateien beim Start vollständig neu einlesen; gültige Embeddings aus dem alten Cache können bei der Migration weiterhin wiederverwendet werden.
  • readonly: ausschließlich einen vorhandenen persistenten Snapshot verwenden; ohne kompatiblen Snapshot schlägt der Start fehl.

/api/status und das Dashboard zeigen unter anderem Snapshot-Zeitpunkt, letzten Delta-Scan, geänderte/gelöschte Dateien, wiederverwendete Vektoren, Embedding-Batchgröße und Scanintervall. Das komplette Vektorindex-Map wird bei einer Suche nicht mehr pro Ticket kopiert; Suchläufe lesen den warmen Index direkt unter einem Read-Lock.

Beim allerersten Aufbau ohne Snapshot startet das Dashboard weiterhin sofort und zeigt Scan-/Embedding-Fortschritt. Die Ticketverarbeitung wartet in diesem Fall, bis der erste konsistente Index fertig ist.

KI-Kennzeichnung automatischer Antworten

Mit AI_CONTENT_LABEL_ENABLED=true wird der konfigurierte TrustedNet-Kennzeichnungsblock unveraendert an den Anfang jeder automatisch vom Agenten ausgewaehlten Antwort gesetzt. Da der Badge HTML verwendet, werden auch Plaintext-KB-Antworten fuer den Versand sicher nach HTML escaped.

Entscheidungsdiagnose

Neben dem normalen Control Center steht unter /diagnostics ein separates Diagnose-Cockpit zur Verfügung. Es verwendet die vom Agenten selbst gespeicherten Policy-Regeln und zeigt pro Ticketlauf u. a.:

  • alle eigenständigen Analyseläufe (category, priority, status_match, reply_selection, escalation) als separat öffnbare Karten,

  • alle Kategorie-Gates mit Ist-/Sollwert und Blockierstatus,

  • alle Auto-Reply-Gates (Quelle, Sprache, Stil, Artikel-Freigabe, Retrieval-Floor, Evidenz, Kategoriebindung, Kontext),

  • Ausführungs-/Race-Protection (Followups, Dry-Run, Ticket-Recheck, GLPI-Write),

  • einen sichtbaren dreistufigen Ablauf mit Laufstatus und Dauer für Kategorie-, Uptime-Kuma- und Antwortanalyse,

  • eine getrennte Kandidatentabelle für KNOWLEDGE_CATEGORY_SOURCES, einschließlich der tatsächlich an die Kategorie-KI gesendeten Artikel,

  • eine zweite Kandidatentabelle für Antwort-KBs, die erst nach der Kategorieentscheidung neu gerankt und ausgewählt werden,

  • getrennte KI-Begründungen für Kategorie, Statuszuordnung und normale Antwortauswahl,

  • eine Uptime-Kuma-Kandidatentabelle mit Relevanz, KI-Confidence, Produktscore, Schwellwerten und dem deterministisch gerenderten Betreibertext,

  • die Audit-Auswahlgründe (sent_to_ai, below_retrieval_floor, outside_candidate_gap, max_candidates_reached),

  • einen KB-Inspector, der wahlweise aus Sicht der Kategorie- oder Antwortanalyse prüft.

Neue Ticketläufe verwenden mindestens die Kategorieanalyse und sofern nötig die normale Antwortanalyse. Ist CONTEXT_STATUS_REPLY_ENABLED=true und sind aktive Uptime-Kuma-Kandidaten vorhanden, liegt dazwischen ein eigener strukturierter Zuordnungslauf. Dieser Lauf erzeugt keinen Antworttext. Er darf nur einen bereitgestellten Kandidaten auswählen und eine Confidence liefern. Erreicht die Kombination aus deterministischer Relevanz, KI-Confidence und Produktscore alle Schwellwerte, wird das passende vordefinierte Störungs- oder Wartungstemplate verwendet und die normale Antwortanalyse übersprungen. Bei vorhandenen Followups, deaktiviertem Auto-Reply, unvollständigem Kontext oder fehlenden Kandidaten werden die jeweiligen Stufen mit einem expliziten Skip-Grund ausgelassen.

Der KB-Inspector rechnet einen Artikel auf Wunsch gegen den aktuellen Ticketstand neu. Hat sich das Ticket seit dem historischen Lauf verändert, kennzeichnet die UI diese Neu-Bewertung ausdrücklich als nicht historisch identisch. Für neue Läufe sind die gespeicherten Regelchecks die maßgebliche historische Erklärung.

Bei Knowledge-Dateien mit nicht gemappten externen Kategorien zeigt die Diagnose die betreffenden Kategorien explizit an. Im Modus unscoped bleibt der Artikel für Retrieval nutzbar; wenn die Kompatibilitätslogik die effektive Auto-Reply-Freigabe deaktiviert hat, wird dies als eigenes fehlgeschlagenes Gate dargestellt.

Kategorie-Mapping-Editor

Unter /category-mappings steht ein eigener Editor für KNOWLEDGE_CATEGORY_MAP_FILE zur Verfügung. Er verbindet externe/String-Kategorien aus lokalen Knowledge-JSONs mit den aktuell aus GLPI gelesenen ITIL-Kategorien. Der Editor unterstützt Mehrfachzuordnungen, Verwendungshäufigkeiten, Filter für nicht zugeordnete und verwaiste Einträge sowie unverbindliche Namensvorschläge. Schreibzugriff ist nur bei KNOWLEDGE_WEB_EDIT_ENABLED=true möglich.

Prioritätslauf blockiert die Queue nicht

Der optionale Prioritätslauf besitzt ein eigenes Zeitbudget:

PRIORITY_ANALYSIS_TIMEOUT=45s

Läuft das Modell in einen Timeout oder liefert es eine nicht verwertbare Antwort, wird ausschließlich der Prioritätslauf mit priority_ai_failed beendet. Kategorie- und Antwortverarbeitung laufen weiter. Semantische Inkonsistenzen zwischen expliziten Ticketbelegen und Reason Codes werden lokal normalisiert; dafür wird kein zusätzlicher Ollama-Aufruf gestartet.

Poll-Diagnose und manuelle Neuanalyse

Das Dashboard zeigt den letzten GLPI-Poll jetzt mit Anzahl der abgerufenen, bereits bekannten, neuen und eingereihten Ticketversionen. Unveränderte, bereits verarbeitete Tickets werden weiterhin nicht automatisch erneut analysiert. Für gezielte Tests steht in der Betriebsdiagnose eine manuelle Neuanalyse per Ticket-ID zur Verfügung; sie erzeugt einen separaten Lauf mit dem Trigger manual_recheck.

Description
No description provided
Readme MIT 72 MiB
Languages
Go 83.5%
HTML 16%
PowerShell 0.3%
Makefile 0.1%