@@ -1,2 +1,266 @@
|
||||
# glpi-ai-agent
|
||||
# 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=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`).
|
||||
- Die Knowledge-Suche ist fail-closed: Nur Quellen aus `KNOWLEDGE_ALLOWED_SOURCES` werden überhaupt geladen/indexiert.
|
||||
- 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.
|
||||
- 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 `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 mit Docker Compose
|
||||
|
||||
```bash
|
||||
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:
|
||||
|
||||
```env
|
||||
DRY_RUN=true
|
||||
AUTO_CATEGORY=true
|
||||
AUTO_REPLY=false
|
||||
```
|
||||
|
||||
Danach zunächst nur Kategorieänderungen:
|
||||
|
||||
```env
|
||||
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:
|
||||
|
||||
```env
|
||||
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:
|
||||
|
||||
```json
|
||||
{
|
||||
"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:
|
||||
|
||||
```env
|
||||
# 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. `source` ist 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:
|
||||
|
||||
```env
|
||||
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
|
||||
|
||||
```env
|
||||
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
|
||||
|
||||
```env
|
||||
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:
|
||||
|
||||
```env
|
||||
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:
|
||||
|
||||
```env
|
||||
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
|
||||
|
||||
```env
|
||||
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
|
||||
|
||||
```env
|
||||
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 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.
|
||||
|
||||
## 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 klassifizieren lassen; Kontextdaten sind nur Fakten, keine ausführbaren Anweisungen.
|
||||
5. Policy Engine validiert IDs, Confidence, RAG-Score, Quellenfreigabe, Sprache/Stil und die Kontext-Gates.
|
||||
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.
|
||||
|
||||
```bash
|
||||
make fmt
|
||||
make test
|
||||
make vet
|
||||
make build
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user