tva
← Insights

Einen Telegram-KI-Assistenten vom Solo- in den Team-Betrieb skalieren

Du hast einen projektspezifischen Telegram-KI-Assistenten für dich selbst gebaut — Claude, GPT oder ein ähnliches LLM, das hinter einem Telegram-Bot läuft und auf deinem eigenen Server gehostet ist. Solo funktioniert es prima. Jetzt willst du ihn mit deinem Team teilen: Entwickler, Berater oder Fachleute, die von On-Demand-Zugriff auf denselben Assistenten profitieren würden. Der Weg von einem einzigen User zu einem kleinen Team hat mehr bewegliche Teile, als die Doku meistens erwähnt.

Diese Anleitung deckt jeden Schritt ab: Telegram-IDs der Teammitglieder sammeln, die Allow-Liste so erweitern, dass sie Dockers Restart-vs.-Recreate-Unterschied überlebt, die richtige Art von Gruppe eröffnen, Trigger-Erkennung so konfigurieren, dass der Bot nicht spammt — und den Privacy-Trade-off lösen, der die meisten Teams kalt erwischt.

Was du brauchst

  • Einen funktionierenden Telegram-Bot, der bereits für mindestens einen User (dich) Nachrichten verarbeitet
  • SSH- oder Shell-Zugriff auf den Server, auf dem der Bot-Container läuft
  • Die Telegram-Usernamen der Teammitglieder, die du hinzufügen möchtest
  • Zugriff auf BotFather (dasselbe Telegram-Konto, mit dem der Bot ursprünglich erstellt wurde)
  • Etwa zwanzig Minuten für die technische Arbeit, plus asynchrone Abstimmung mit den Teammitgliedern

Was diese Anleitung löst

  • Teammitglieder können deinen Bot nicht triggern — „Bei mir klappt's, bei denen nicht"
  • Allow-Listen-Änderungen scheinen anzuschlagen, aber der Bot ignoriert neue User nach einem Restart weiterhin
  • Bot spammt in Gruppen-Chats — antwortet auf jede Nachricht statt nur auf relevante
  • Verwirrende Trigger-Logik — wann soll der Bot antworten? Nur bei @Mention? Keyword? Reply?
  • Datenschutzbedenken wegen unerwarteter Memory-Lecks zwischen Usern

Schritt 1: Numerische Telegram-User-ID jedes Teammitglieds sammeln

Die Allow-Liste deines Telegram-KI-Assistenten arbeitet mit numerischen User-IDs, nicht mit Usernamen oder Anzeigenamen. Usernamen können sich ändern; die numerische ID ist für die Lebensdauer des Kontos stabil. Du brauchst diese ID für jedes Teammitglied, das du hinzufügen möchtest.

Der einfachste Weg: Bitte jedes Teammitglied, einen Chat mit @userinfobot (einem öffentlichen Utility-Bot) zu starten. Die erste Nachricht, die er zurückschickt, enthält die numerische ID — so etwas wie 100000001. Lass sie die ID kopieren und per DM an dich schicken.

Alternativen, wenn @userinfobot in eurer Region gesperrt oder nicht verfügbar ist:

  • Aus den Logs deines eigenen Bots auslesen. Füge vorübergehend eine „Alle Versuche loggen"-Zeile in die Allow-Listen-Middleware deines Bots ein, bitte das Teammitglied, dem Bot eine Nachricht zu schicken, und lies die User-ID aus deinen Container-Logs aus. Entferne die Log-Zeile, nachdem du die ID gesammelt hast.
  • Aus einer Gruppen-Nachricht auslesen. Wenn ein User bereits in einer Gruppe geschrieben hat, in der dein Bot Mitglied ist, enthält das Feld from.id dieser Nachricht seine numerische ID — auslesbar über den getUpdates-Endpoint des Bots.

Bewahre die gesammelten IDs sicher auf — in einer Notiz, einem Passwort-Manager oder direkt in deiner .env-Datei. Behandle sie wie E-Mail-Adressen: Sie identifizieren eine konkrete Person und können mit öffentlichen Telegram-Profilen verknüpft werden.

Schritt 2: Allow-Liste deines Bots erweitern

Die meisten Telegram-Bot-Frameworks — aiogram, python-telegram-bot, Telegraf, grammY — implementieren Allow-Listen-Prüfungen als Middleware. Jedes eingehende Update wird anhand der Sender-ID gefiltert, bevor ein Handler ausgeführt wird. Nicht zugelassene Sender werden stillschweigend verworfen: Dein Bot empfängt ihre Nachrichten intern, antwortet aber nie.

Die Allow-Liste selbst liegt meistens in einer Umgebungsvariable, die über env_file: in deiner docker-compose.yml in den Container geladen wird:

BOT_ALLOWED_USERS=100000001,100000002,100000003
BOT_OPERATOR_ID=100000001

Zwei Formate sind üblich:

  • CSV-Liste: BOT_ALLOWED_USERS=111,222,333. Wird im Settings-Code des Bots als list[int] geparst.
  • JSON-Array: BOT_ALLOWED_USERS=[111,222,333]. Kommt zum Einsatz, wenn der Settings-Parser für komplexe Typen JSON erwartet.

Wenn dein Bot Python und pydantic-settings für die Konfiguration nutzt, ist die JSON-Array-Form die sicherere Wahl — selbst bei einem einzigen User ist BOT_ALLOWED_USERS=[100000001] BOT_ALLOWED_USERS=100000001 vorzuziehen. Der Grund wird weiter unten im pydantic-Abschnitt erklärt, aber kurz gesagt: Die JSON-Form umgeht eine Parser-Mehrdeutigkeit, die deinen Container beim Start zum Absturz bringt.

Schritt 3: Container richtig neu starten

Hier gehen die meisten Allow-Listen-Updates lautlos schief. Du bearbeitest die .env, führst docker compose restart your-bot aus, siehst den Container wieder hochkommen — und die neuen User können den Bot immer noch nicht triggern. Die Änderung „hat nicht gezogen".

Der Grund: docker compose restart stoppt und startet nur den bestehenden Container. Er wird nicht neu erstellt. Umgebungsvariablen — einschließlich allem aus env_file: — werden beim Erstellen eines Containers injiziert, nicht beim Starten. Ein Restart bewahrt den ursprünglichen Env-Var-Snapshot. Deine bearbeitete .env-Datei ist für einen neu gestarteten Container irrelevant.

Der korrekte Befehl:

docker compose up -d --force-recreate --no-deps your-bot-service-name

Was jedes Flag bewirkt:

  • --force-recreate stoppt den alten Container, entfernt ihn und erstellt einen neuen mit der aktuellen Compose-Spec — einschließlich der frisch bearbeiteten env_file:-Inhalte.
  • --no-deps verhindert, dass Compose auch alle Services neu erstellt, von denen dein Bot abhängt (Datenbanken, Message-Queues). Wenn dein Bot kein depends_on hat, ist dieses Flag wirkungslos, schadet aber auch nicht.
  • -d lässt den neu erstellten Container im Hintergrund laufen, sodass dein Terminal sofort zurückkehrt.

Prüfe, ob das Recreate erfolgreich war, indem du die Laufzeit des Containers überprüfst:

docker ps --filter name=your-bot --format "{{.Status}}"
# Erwartetes Ergebnis: "Up 10 seconds" (nicht "Up 4 hours")

Wenn der Status dieselbe lange Laufzeit wie zuvor zeigt, hat das Recreate nicht stattgefunden — prüfe auf Tippfehler im Service-Namen oder ob du den Befehl im richtigen Verzeichnis ausgeführt hast.

Dieses Pattern gilt für jeden Docker Compose-Service, dessen Konfiguration in einem env-file liegt: API-Gateways, Worker, Scraper, Monitoring-Agents. Dieselbe Falle schnappt jedes Mal zu. Wir haben breitere Patterns für die Verwaltung vieler solcher Container in unserem Routine-Health-Check-Guide für dockerisierte Infrastruktur dokumentiert.

pydantic-settings: Ein Parser-Fallstrick, den du kennen solltest

Wenn die Python-Settings-Schicht deines Bots pydantic-settings verwendet — die Standardbibliothek für Pydantic-v2-Settings — und du deine Allow-Liste als list[int] deklarierst, wirst du auf ein Parser-Problem stoßen, das du kennen solltest, bevor es dich erwischt.

pydantic-settings behandelt komplexe Typen (list, dict, tuple) standardmäßig als JSON-kodiert. Wenn es BOT_ALLOWED_USERS=111,222 aus deinem env-file liest, versucht es zunächst json.loads("111,222"). Das schlägt mit JSONDecodeError: Extra data fehl, weil reines CSV kein gültiges JSON ist. Dein Container stürzt beim Start mit einem SettingsError: error parsing value for field ab.

Wenn du einen benutzerdefinierten BeforeValidator am Feld hast, der CSV parsen kann, könntest du annehmen, dass er zuerst läuft und den Raw-String abfängt, bevor der JSON-Decode-Versuch startet. Tut er aber nicht. pydantic-settings wendet den JSON-Decode-Schritt vor allen feldspezifischen Validatoren für komplexe Typen an.

Du hast zwei Workarounds:

Schnelle Lösung — JSON-Array-Syntax im env-file:

BOT_ALLOWED_USERS=[100000001,100000002,100000003]

Das ist gültiges JSON. pydantic-settings dekodiert es direkt in eine Liste von Ints. Kein Validator nötig. Der einzige Trade-off ist rein kosmetisch: Klammern um eine Liste.

Saubere Lösung — das Feld mit NoDecode annotieren:

from pydantic_settings import NoDecode
from pydantic import BeforeValidator
from typing import Annotated

def parse_csv(v):
    if isinstance(v, str):
        return [int(x.strip()) for x in v.split(",")]
    return v

class Settings(BaseSettings):
    bot_allowed_users: Annotated[list[int], NoDecode, BeforeValidator(parse_csv)]

NoDecode unterdrückt den JSON-Decode-Schritt vollständig. Dein BeforeValidator empfängt den Raw-String und parst ihn als CSV. Das ist der sauberere Fix, wenn du den Settings-Code selbst kontrollierst.

Das zugrunde liegende Problem wird in pydantic-settings Issue #157 verfolgt, mit weiterer Diskussion in #184 und #570. Das Verhalten ist in allen aktuell veröffentlichten Versionen von pydantic-settings (v2.x) konsistent. Wenn du den Settings-Code nicht kontrollierst — weil du ein Third-Party-Bot-Framework verwendest — nutze den JSON-Array-Syntax-Workaround.

Schritt 4: Eine Telegram-Gruppe mit deinem Bot und dem Team eröffnen

Telegram bietet zwei Arten von Gruppen für diesen Anwendungsfall:

  • Reguläre Gruppe: bis zu 200 Mitglieder, einfaches Admin-Modell, keine erweiterten Features. Gut für kleine Teams.
  • Supergruppe: bis zu 200.000 Mitglieder, fein abgestufte Admin-Rechte, Thread-Diskussionen, persistente Nachrichtenhistorie. Später in eine umwandeln, wenn das Team wächst.

Für Team-Workflows mit bis zu einem Dutzend Mitgliedern reicht eine reguläre Gruppe. Vorgehen:

  • Tippe in deiner Telegram-App auf „Neue Gruppe" und wähle deine Teammitglieder aus deinen Kontakten
  • Gib der Gruppe einen aussagekräftigen Namen — „Projekt X — KI-Assistent", Engineering Bot Workspace" o.ä.
  • Öffne nach der Erstellung die Gruppeneinstellungen, tippe auf „Mitglied hinzufügen", suche nach dem Username deines Bots (@your_bot_name) und füge ihn hinzu
  • Mache den Bot nur dann zum Admin, wenn er Admin-Aktionen braucht (Nachrichten löschen, anpinnen). Für reinen Frage-und-Antwort-Betrieb reicht der normale Mitgliedsstatus

Wenn du mehrere projektspezifische Bots über mehrere Teams hinweg betreibst (wir verwalten eine Handvoll davon auf gemeinsamer Infrastruktur), sind die Multi-Tenant-Patterns, die wir verwenden, in unserem Multi-Tenant-Docker-Entwicklungsstack-Guide dokumentiert.

Schritt 5: Trigger-Erkennung konfigurieren

Standardmäßig empfängt ein Telegram-Bot in einer Gruppe nur Nachrichten, die ihn explizit erwähnen (@your_bot_name), auf seine Nachrichten antworten oder einen Slash-Command verwenden. Telegram nennt das „Privacy Mode" — standardmäßig aktiviert und eine sinnvolle Grundeinstellung, die versehentlichen Bot-Spam verhindert.

Für einen Telegram-KI-Assistenten, der auf natürliche Fragen reagieren soll („Hey Bot, wie ist der Deployment-Status?" ohne explizite Erwähnung), ist der Privacy Mode aber zu restriktiv. Du hast zwei Optionen:

Option A: Privacy Mode lassen, Team auf @Mention trainieren. Einfach, keine Konfigurationsänderung nötig. Der Bot sieht nur, worauf er reagieren soll. Nachteil: Reibung. Teammitglieder vergessen das @, und der Bot bleibt stumm.

Option B: Privacy Mode deaktivieren und eigene Trigger-Logik implementieren. Öffne BotFather, sende /setprivacy, wähle deinen Bot, setze auf Disable. Der Bot empfängt jetzt jede Gruppen-Nachricht. Du implementierst die „Soll ich antworten?"-Prüfung selbst.

Ein praktischer Trigger-Set, den wir in Produktion nutzen:

  • Direkte Nachricht: immer antworten — du redest direkt mit dem Bot
  • @Mention in der Gruppe: immer antworten — expliziter Aufruf
  • Reply auf eine der Bot-Nachrichten: immer antworten — Fortsetzung eines vom Bot gestarteten Threads
  • Gruppen-Nachricht mit dem Trigger-Wort des Bots: antworten. Das Trigger-Wort ist typischerweise der Spitzname des Bots oder der Projektname, mit Word-Boundary-Regex abgeglichen, damit „advisor" nicht versehentlich auf „advisory" triggert
  • Alles andere: stillschweigend in eine Konversations-Datei loggen, keine Antwort

Das stille Loggen ist wichtig. Auch wenn der Bot nicht antwortet, sieht er trotzdem den Gesprächsfluss der Gruppe. Jede Nachricht in eine Chat-spezifische Datei zu loggen gibt dem Bot zukünftigen Kontext — wenn jemand ihn schließlich mit einer Frage wie „Was haben wir beschlossen?" @mentioned, hat der Bot die jüngste Konversation als Kontext zur Verfügung.

Die Implementierung hängt vom Framework ab. In aiogram läuft ein einziger Message-Handler alle fünf Prüfungen durch, bevor er entscheidet, ob er dein LLM aufruft und antwortet. In Telegraf oder grammY ist das Pattern identisch — ein bot.on('message')-Handler, der explizit filtert, bevor er reagiert.

Schritt 6: Den Privacy-Trade-off klären

Hier ist die Frage, über die die meisten Teams erst nachdenken, wenn sie zum Problem wird: Pflegt dein Bot getrennten Memory pro User, pro Gruppe oder global über alle Konversationen hinweg?

Drei Patterns sind verbreitet:

  • Per-Chat-Memory: Der Bot startet für jeden Chat eine frische Session. DM mit User A ist unabhängig von DM mit User B, und beide sind unabhängig von Gruppe X. Maximale Privacy. Nachteil: Der Bot erinnert sich nicht an Kontext über Sessions hinweg, was seinen Nutzwert als „Assistent, der unser Projekt kennt" einschränkt
  • Per-User-Memory: Der Bot pflegt separate Memory-Threads pro User, teilt sie aber über DMs und Gruppen-Mentions desselben Users. Vernünftiger Mittelweg
  • Globaler Memory: Der Bot hat eine einzige Session, zu der alle Konversationen beitragen. Maximales Kontext-Sharing — DMs und Gruppen-Gespräche bauen gemeinsam dasselbe Langzeit-Memory auf. Nachteil: Privacy-Lecks. Eine vertrauliche Information, die ein Teammitglied dem Bot per DM mitteilt, kann in einer Gruppen-Antwort auf die Frage eines anderen Teammitglieds auftauchen

Jedes Pattern ist vertretbar. Jedes hat Trade-offs, über die sich dein Team einigen muss, bevor ihr auf Multi-User geht.

Wenn du dich für globalen Memory entscheidest — wir tun das, bei eingespielten Teams, bei denen Kontext-Sharing ein Teil des Mehrwerts ist — sei gegenüber deinem Team transparent, bevor sie den Bot nutzen: „Alles, was du diesem Bot sagst, kann in Antworten auftauchen, die für die ganze Gruppe sichtbar sind. Behandle ihn als geteilten Workspace, nicht als vertraulichen Gesprächspartner."

Wenn du Per-Chat-Memory wählst, verzichtest du auf kontextübergreifendes Reasoning („Was haben wir letzte Woche zu X entschieden?"), vermeidest aber das Leck-Risiko vollständig.

Das ist eine Design-Entscheidung mit echten sozialen Konsequenzen, kein technischer Schalter, den du später ohne team-weite Abstimmung umstellen kannst. Wir diskutieren ähnliche Trade-offs in unserem umfassenderen Artikel über KI-Agenten-Skills für domänenspezifische Workflows, wo Shared-Context-Konfigurationen bei jedem Kundenprojekt auftauchen, das wir betreuen.

Deinen Telegram-KI-Assistenten über ein kleines Team hinaus skalieren

Das env-file-Allow-Listen-Pattern funktioniert sauber für Teams mit bis zu etwa zwanzig bis dreißig Usern. Darüber hinaus werden fest eingetragene Einträge mühsam — jedes Onboarding erfordert einen Git-Commit (wenn dein env via SOPS oder einem ähnlichen Secrets-Management-Layer eingecheckt ist), ein Deploy und ein Container-Recreate.

Patterns, die weiter skalieren:

  • Datenbank-gestützte Allow-Liste: User leben in einer SQL-Tabelle, der Bot liest und cached die Liste beim Start und aktualisiert sie periodisch (oder per Webhook bei User-Änderungen). Einen User onzuboarden wird zu einem INSERT-Statement — kein Deploy nötig
  • Gruppen-Mitgliedschaft als Zugangskontrolle: Statt einzelne User zuzulassen, alle User erlauben, die Mitglied einer bestimmten Telegram-Gruppe (oder eines kleinen Sets von Gruppen) sind. Die Gruppen-Mitgliedschaft wird zur Zugriffsgrenze. Telegramms getChatMember-API bestätigt die Mitgliedschaft vor jedem Aufruf
  • Channel-basiert: Für Read-Only-Assistenten (tägliche Zusammenfassungen, Alerts, Monitoring-Digests) einen Telegram-Channel statt einer Gruppe nutzen. Channels haben ein anderes Berechtigungsmodell — nur Admins posten, andere lesen. Nützlich, wenn es um einen One-to-Many-Fan-out geht statt um bidirektionale Konversation

Für kleine Team-Workflows — Entwickler, Berater, gelegentliche Spezialisten — reicht das env-file-Allow-Listen-Pattern. Wir nutzen es quer durch unsere interne Infrastruktur und behandeln die datenbankgestützte Variante als Refactor, den wir machen, sobald ein Projekt die einfachere Form erkennbar überwächst.

Abschluss-Checkliste

Bevor dein Team den Bot zu nutzen beginnt, überprüfe:

  • Numerische Telegram-IDs aller Teammitglieder gesammelt und in die Allow-Liste eingetragen
  • Allow-Listen-Format stimmt mit dem überein, was dein Settings-Parser erwartet — JSON-Array, wenn pydantic-settings im Stack ist
  • Container neu erstellt (nicht nur neu gestartet), damit die neuen Env-Vars in einen frischen Container geladen werden
  • Telegram-Gruppe erstellt mit Bot als Mitglied (und zum Admin befördert, wenn er Admin-Aktionen braucht)
  • BotFather Privacy Mode passend zu deiner Trigger-Strategie konfiguriert — nur deaktivieren, wenn du eigene Filter-Logik implementiert hast
  • Trigger-Logik im Bot-Code mit dem Privacy Mode abgestimmt (Privacy Mode nicht deaktivieren ohne Filterung, sonst spammt der Bot jede Gruppen-Nachricht)
  • Memory-Modell (Per-Chat / Per-User / Global) gewählt und dem Team kommuniziert
  • Privacy-Erwartungen explizit mit Teammitgliedern geklärt, bevor sie den Bot nutzen

Wenn du einen projektspezifischen Assistenten von Grund auf baust und verstehen möchtest, wie die zugrundeliegende Bot-Infrastruktur zusammenhängt, deckt unser begleitender Artikel über den Aufbau eines projektspezifischen KI-Assistenten via Telegram das Fundament ab. Für E-Mail-Infrastruktur, die diese Setups oft begleitet — Benachrichtigungen, Eskalationspfade, Audit-Trails — sieh dir unseren Artikel über das Einrichten projektspezifischer Postfächer mit DKIM und DMARC an.

Wenn du diese Art von Multi-User-Telegram-KI-Assistenten-Infrastruktur für Kunden oder dein eigenes Team betreibst und beim Rollout Unterstützung möchtest, meld dich gerne. Wir bauen und betreiben diese Art von Setup im Rahmen unserer Projektarbeit.


Verwandte Insights

Weitere Artikel