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, schlägt eine Kategorie vor, sucht freigegebene Wissenseinträge und kann – nach mehreren harten Sicherheitsprüfungen – eine erste Antwort schreiben.
Sicherheitsmodell
DRY_RUN=trueist der Default.AUTO_REPLY=falseist 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). - Die Knowledge-Suche ist fail-closed: Nur Quellen aus
KNOWLEDGE_ALLOWED_SOURCESwerden überhaupt geladen/indexiert. - Auto-Replies benötigen zusätzlich eine Quelle aus
KNOWLEDGE_AUTO_REPLY_SOURCESsowie 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.
- Audit-Trail:
data/runs.jsonl.
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
apiScope. - 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 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:7080/
Vor dem ersten Live-Betrieb unbedingt mehrere Tage/Wochen im Shadow Mode lassen:
DRY_RUN=true
AUTO_CATEGORY=true
AUTO_REPLY=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
GLPI-Endpunkte
Default ist GLPI_API_VERSION=v2.3. Der Client verwendet:
- OAuth:
POST /api.php/token - Tickets:
/api.php/v2.3/Assistance/Ticket - Followups:
/api.php/v2.3/Assistance/Ticket/{id}/Timeline/Followup - 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:
# Nur diese Quellen werden geladen, eingebettet und durchsucht.
KNOWLEDGE_ALLOWED_SOURCES=internal-kb,glpi-kb,vendor-docs
# Nur diese Teilmenge 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. Mit KNOWLEDGE_AUTO_REPLY_SOURCES=none kann die Quellenfreigabe für Auto-Replies vollständig deaktiviert werden. Dokumente aus nicht erlaubten Quellen werden nicht in die Suchmenge aufgenommen und damit auch nicht an Ollama übergeben. Ein Knowledge-Dokument ohne source führt absichtlich zu einem Startfehler, damit die Herkunft nicht implizit geraten wird.
In dieser Version ist
knowledge/weiterhin der einzige physische Knowledge-Connector.sourceist ein verbindliches Herkunfts-/Vertrauenslabel für importierte Dokumente. Ein späterer GLPI-KB-, Wiki- oder Hersteller-Connector kann dieselbe Policy unverändert verwenden.
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
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 Cache landet in data/embeddings.json. Für Ticket und Knowledge wird dasselbe Embedding-Modell verwendet.
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.
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.
- 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)/healthz– Prozess lebt/readyz– GLPI und Ollama erreichbar/metrics– Prometheus TextformatPOST /webhook/glpi– optionaler Webhook-Eingang, geschützt durchX-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.
Keine Doppelantworten
Der Schreibpfad ist bewusst streng:
- Ticket laden.
- Followups laden. Existiert eines: Stop.
- Knowledge sowie read-only Betriebskontext (Changes, Major Incidents, Uptime Kuma, Benutzer/Geräte) laden.
- KI klassifizieren lassen; Kontextdaten sind nur Fakten, keine ausführbaren Anweisungen.
- Policy Engine validiert IDs, Confidence, RAG-Score, Quellenfreigabe, Sprache/Stil und die Kontext-Gates.
- Optional Kategorie ändern.
- Direkt vor Auto-Reply Ticket und Followups erneut laden. Existiert jetzt ein Followup oder hat sich die Entscheidungsgrundlage geändert: Stop.
- 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). .envniemals committen; besser Docker/Kubernetes Secrets oder systemdEnvironmentFilemit 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