Mattermost-Bot mit lokaler KI
Was dieser Artikel behandelt
- Wie Du einen Bot auf Deinem eigenen Mattermost-Server mit Ollama baust.
- Bot-Account anlegen, REST-API und WebSocket-Events.
- Slash-Commands und interaktive Posts.
- Erweiterungen: RAG auf Firmenwissen, Moderation, Alerting.
- Warum Mattermost + Ollama die komplett souveräne Kombination ist.
Einleitung
Mattermost ist der selbst-hostbare Slack-Klon: Open Source (Teams Edition kostenlos), läuft auf eigenem Server, alle Daten bleiben bei Dir. Kombiniert mit Ollama bekommst Du die komplett souveräne Lösung: Chat-Plattform UND KI lokal, nichts geht raus.
Die Bot-Integration ist offiziell und gut dokumentiert: Bot-Accounts mit eigenem Token, REST-API für alles, WebSocket für Echtzeit-Events, Slash-Commands, interaktive Message-Buttons.
Typische Anwendungsszenarien
- Interner Wissensbot: Firmenwissen per RAG, komplett on-premise, DSGVO-freundlich.
- DevSecOps-Assistent: Log-Analyse, Runbook-Antworten, Incident-Support.
- Behörden/Regulierte: KI-Chat dort, wo Cloud-Dienste nicht zulässig sind.
- Alarm-Drehscheibe: Monitoring → Mattermost-Channel → KI fasst zusammen.
- Onboarding: Neue Mitarbeiter fragen den Bot, ohne Kollegen zu stören.
Voraussetzungen
- Laufender Mattermost-Server (Docker:
mattermost/mattermost-team-edition) - Admin-Zugang zum Anlegen des Bot-Accounts
- Ollama mit Modell auf demselben Host/Netzwerk
- Python 3.10+
Schritt 1: Mattermost selbst hosten (Kurzversion)
# docker-compose.yml — Mattermost-Grundgerüst
services:
db:
image: postgres:16
environment:
POSTGRES_DB: mattermost
POSTGRES_USER: mmuser
POSTGRES_PASSWORD: geheim-aendern
volumes: [db_data:/var/lib/postgresql/data]
restart: always
mattermost:
image: mattermost/mattermost-team-edition:latest
depends_on: [db]
ports: ["8065:8065"]
environment:
MM_SQLSETTINGS_DRIVERNAME: postgres
MM_SQLSETTINGS_DATASOURCE: "postgres://mmuser:geheim-aendern@db:5432/mattermost?sslmode=disable"
MM_SERVICESETTINGS_SITEURL: "https://chat.deine-domain.de"
volumes: [mm_data:/mattermost/data]
restart: always
volumes:
db_data:
mm_data:
Im Admin-Panel (System Console → Integrations) Bot Accounts aktivieren.
Schritt 2: Bot-Account anlegen
Im UI: System Console → Integrations → Bot Accounts → „Add Bot Account” → Username ki-assistent. Oder per API/CLI:
# per mmctl im Container
docker exec mattermost mmctl --local bot create ki-assistent --display-name "KI-Assistent" --description "Lokaler KI-Bot"
Du bekommst ein Bot-Token (xxx...), wie ein API-Key, in .env halten:
MM_URL=http://mattermost:8065
MM_TOKEN=dein-bot-token
MM_TEAM=dein-team
OLLAMA_URL=http://ollama:11434
OLLAMA_MODEL=llama3.1:8b
Schritt 3: Bot mit WebSocket (Echtzeit)
Mattermost hat eine einfache WebSocket-Event-API: Nachrichten kommen live rein:
import asyncio
import json
import os
import aiohttp
import websockets
import ollama
from dotenv import load_dotenv
load_dotenv()
MM_URL = os.environ["MM_URL"]
MM_TOKEN = os.environ["MM_TOKEN"]
MODEL = os.environ.get("OLLAMA_MODEL", "llama3.1:8b")
ollama_client = ollama.Client(host=os.environ["OLLAMA_URL"])
HEADERS = {"Authorization": f"Bearer {MM_TOKEN}"}
SYSTEM = "Du bist der interne KI-Assistent. Antworte kurz auf Deutsch."
async def api(method, path, payload=None):
async with aiohttp.ClientSession() as s:
async with s.request(method, f"{MM_URL}/api/v4{path}",
headers=HEADERS, json=payload) as r:
return await r.json()
async def post_message(channel_id, text, root_id=None):
await api("POST", "/posts", {
"channel_id": channel_id,
"message": text[:16000],
"root_id": root_id,
})
def ask_ollama(text, history=None):
msgs = [{"role": "system", "content": SYSTEM}]
if history:
msgs += history
msgs.append({"role": "user", "content": text})
return ollama_client.chat(model=MODEL, messages=msgs)["message"]["content"]
async def main():
me = await api("GET", "/users/me")
bot_id = me["id"]
ws_url = MM_URL.replace("http", "ws") + "/api/v4/websocket"
async with websockets.connect(ws_url) as ws:
await ws.send(json.dumps({
"seq": 1, "action": "authentication_challenge",
"data": {"token": MM_TOKEN}
}))
async for raw in ws:
evt = json.loads(raw)
if evt.get("event") != "posted":
continue
post = json.loads(evt["data"]["post"])
if post["user_id"] == bot_id: # eigene Posts ignorieren
continue
text = post["message"]
mention = f"@{me['username']}"
channel_type = evt["data"].get("channel_type")
# Nur auf DM oder @mention reagieren
if channel_type != "D" and mention not in text:
continue
text = text.replace(mention, "").strip()
if not text:
continue
# Thread-Kontext aus Root-Posts laden
history = []
if post.get("root_id"):
thread = await api("GET", f"/posts/{post['root_id']}")
for p in thread["posts"].values():
role = "assistant" if p["user_id"] == bot_id else "user"
history.append({"role": role, "content": p["message"]})
answer = ask_ollama(text, history)
await post_message(post["channel_id"], answer,
root_id=post.get("root_id") or post["id"])
asyncio.run(main())
Slash-Commands
In der Admin-Console einen Slash-Command /ki auf Deinen HTTP-Endpoint registrieren (Mattermost pusht dann an Dich, dafür braucht der Bot einen HTTP-Port):
from aiohttp import web
async def slash_ki(request):
data = await request.post()
prompt = data.get("text", "").strip()
if not prompt:
return web.json_response({"text": "Nutzung: `/ki <Frage>`",
"response_type": "ephemeral"})
answer = ask_ollama(prompt)
return web.json_response({
"text": answer,
"response_type": "ephemeral", # nur der Fragende sieht es
})
app = web.Application()
app.router.add_post("/slash/ki", slash_ki)
web.run_app(app, port=8080)
Erweiterungen
RAG auf Firmenwissen
Identisch zum Slack-Bot: Embeddings der Frage → Qdrant-Suche in firmenwissen-Collection → Kontext in den System-Prompt. Bei Mattermost einfacher zu rechtfertigen, alle Daten ohnehin on-premise.
Interaktive Posts (Buttons)
await api("POST", "/posts", {
"channel_id": channel_id,
"message": "Antwort hilfreich?",
"props": {"attachments": [{
"actions": [
{"id": "good", "name": "👍 Hilfreich", "type": "button",
"integration": {"url": f"{BOT_URL}/action", "context": {"vote": "up"}}},
{"id": "bad", "name": "👎 Falsch", "type": "button",
"integration": {"url": f"{BOT_URL}/action", "context": {"vote": "down"}}},
]
}]},
})
Proaktive Alerts
Der Bot schreibt selbst in Channels: Monitoring-Integration:
# Cron/Systemd-Timer oder Event-Handler ruft:
async def alert(channel_id, service, status):
summary = ask_ollama(
f"Fasse diesen Monitoring-Alert kurz zusammen und schlage den "
f"wahrscheinlichsten Fix vor:\nService: {service}\nStatus: {status}"
)
await post_message(channel_id, f"🚨 **{service}**\n{summary}")
Moderation
Neue Posts klassifizieren lassen (OK/SPAM/TOXIC), bei Verstoß Post löschen (DELETE /posts/{id}) und User warnen, gleiches Muster wie im Telegram-Artikel.
Deployment: Alles in einem Stack
services:
mm-bot:
build: ./bot
restart: always
env_file: .env
depends_on: [mattermost, ollama]
ports: ["127.0.0.1:8080:8080"] # für Slash-Commands/Actions
ollama:
image: ollama/ollama:latest
volumes: [ollama_data:/root/.ollama]
restart: always
Bot, Mattermost und Ollama im selben Docker-Netz → keine externen Abhängigkeiten, keine Daten gehen raus.
Sicherheit
- Bot-Token: Volle Bot-Rechte, wie ein Admin-Token behandeln. In
.env/Docker Secrets. - Channel-Isolation: Der Bot sieht nur Channels, in denen er Mitglied ist, nicht automatisch alles.
- Rate-Limiting: Bei öffentlichen Teams Zugriffs-Limit pro User einbauen (Token-Bucket).
- Logging: Bot-Aufrufe protokollieren (Audit Logging).
- TLS: Für Slash-Commands/Actions über HTTPS, intern reicht HTTP im Docker-Netz.
Typische Stolpersteine
- Bot sieht Channel nicht: Bot muss Channel-Mitglied sein, per UI oder
POST /channels/{id}/membershinzufügen. - WebSocket trennt sich: Reconnect-Logik einbauen: Netzwerk-Blips sind normal.
- Eigene Posts als Loop:
post.user_id == bot_idprüfen, sonst antwortet sich der Bot selbst. - SiteURL falsch: Interaktive Buttons und Attachments brauchen korrekte
SiteURL. - Message-Limit: Posts auf ~16.383 Zeichen limitiert, lange Antworten splitten.
Weiterführende Links
- IRC-Coding.de: Vertiefende Programmier-Tutorials: WebSocket, REST-APIs, Async-Python.
- Slack-Bot: Fast identische API, aber gehostet.
- Rocket.Chat-Bot: Zweite Self-Hosted-Alternative.
- Matrix-Bots: Dezentrale Alternative.
- Docker: Deployment-Grundlagen.
- Lokales RAG: Firmenwissen anbinden.
Key Takeaways:
- Mattermost + Ollama = komplett souveräne KI-Chat-Plattform, 100% on-premise.
- Bot-Account + REST/WebSocket reichen, kein App-Approval-Prozess wie bei Slack.
- WebSocket-Events für Echtzeit, REST für Posts, Slash-Commands per HTTP-Endpoint.
- Ideal für Firmen, Behörden und alle, die keine Chat-Daten rausgeben wollen.
- Team-Edition ist kostenlos und unbegrenzt nutzbar.
FAQ
Welche Mattermost-Edition brauche ich?
Mattermost oder Slack?
Bot-Account oder normaler User?
Wie viele Ressourcen braucht Mattermost?
Sieht der Bot alle Channels?
Bots oder Mattermost-Plugins?
Was muss ich sichern?
Quellen und weiterführende Literatur
- Mattermost: Self-Hosted Chat.
- Mattermost API: REST-Referenz.
- Mattermost Bot Accounts: Bot-Doku.
- IRC-Coding.de: Programmier-Tutorials.


