123 lines
7.2 KiB
Markdown
123 lines
7.2 KiB
Markdown
<!-- status: canonical -->
|
||
<!-- lastReviewed: 2026-04-24 -->
|
||
<!-- verifiedAgainst: service-teams-browser-bot (documentation review 2026-02-18); gateway/modules/features/teamsbot/{service,routeFeatureTeamsbot,interfaceFeatureTeamsbot,datamodelTeamsbot}.py + gateway/tests/unit/teamsbot/test_directorPrompts.py (Director Prompts review 2026-04-24) -->
|
||
|
||
# Teams Meeting Bot -- Architektur
|
||
|
||
## Überblick
|
||
|
||
AI-gesteuerter Meeting-Bot für Microsoft Teams. Tritt Meetings als regulärer Teilnehmer bei (Browser-Automation via Playwright/Chromium), erfasst Live-Transkripte, reagiert per Sprache (TTS) und/oder Chat. Kein Teams-Graph-SDK nötig -- funktioniert mandantenübergreifend ohne Admin-Approval.
|
||
|
||
## System-Architektur
|
||
|
||
```
|
||
┌────────────┐ SSE ┌──────────────┐ WebSocket ┌─────────────┐
|
||
│ Frontend │◄──────────────│ Gateway │◄───────────────│ Bot Service │
|
||
│ (Nyla UI) │ │ (AI, TTS, │ HTTP (join/ │ (Playwright │
|
||
│ │ │ Sessions) │ leave) │ Chromium) │
|
||
└────────────┘ └──────────────┘ └─────────────┘
|
||
```
|
||
|
||
| Verbindung | Protokoll | Zweck |
|
||
|------------|----------|-------|
|
||
| Gateway ↔ Bot | WebSocket | Echtzeit: Transkripte, Chat, Audio, Status |
|
||
| Gateway → Bot | HTTP | Session-Steuerung (join, leave, status) |
|
||
| Frontend ← Gateway | SSE | Live-Transkript-Stream für UI |
|
||
|
||
## Kernfähigkeiten
|
||
|
||
- **Live Transcription:** Erfasst Untertitel mit Sprecher-Zuordnung, streamt via SSE
|
||
- **AI-Analyse:** Transkript-Segmente werden durch AI-Modell (GPT-4o-mini / Claude) analysiert
|
||
- **Voice Response:** TTS-Audio wird über den Mikrofon-Kanal ins Meeting gespielt
|
||
- **Chat Response:** Bot kann Chat-Nachrichten ins Meeting schreiben
|
||
- **Multi-Session:** Mehrere Bot-Instanzen parallel in verschiedenen Meetings
|
||
|
||
## Use Cases
|
||
|
||
| UC | Beschreibung |
|
||
|----|-------------|
|
||
| AI Meeting Assistant | Bot nimmt teil, hört zu, antwortet auf Ansprache ("Hey Nyla, ...") |
|
||
| Live Transcription | Echtzeit-Transkript-Stream für Teilnehmer ausserhalb des Meetings |
|
||
| Meeting Summary | AI-generierte Zusammenfassung nach Meeting-Ende |
|
||
| Multi-Bot | Mehrere parallele Sessions in verschiedenen Meetings |
|
||
| Director Prompts | Operator gibt dem laufenden Bot **private** Regieanweisungen (One-Shot oder Persistent), Antwort wird ins Meeting eingespielt |
|
||
| Hybrid Agent Escalation | SPEECH_TEAMS-Pfad kann komplexe Anfragen via `needsAgent=true` an den vollen Agent (`agentService.runAgent`) eskalieren |
|
||
|
||
## Integration mit Gateway
|
||
|
||
Der Gateway (Feature `teamsbot`) verwaltet Sessions und stellt die AI-Pipeline bereit:
|
||
- Session-Lifecycle: erstellen, starten, stoppen
|
||
- WebSocket-Verbindung pro Session
|
||
- AI-Analyse der Transkript-Segmente via `serviceAi`
|
||
- TTS-Generierung für Voice-Responses
|
||
|
||
## Schlüssel-Dateien
|
||
|
||
| Datei / Bereich | Rolle |
|
||
|-----------------|-------|
|
||
| `gateway/modules/features/teamsbot/` | Gateway-seitiges Feature-Modul |
|
||
| `service-teams-browser-bot/` | Eigenständiger Bot-Service (separates Repository) |
|
||
|
||
## Regeln / Invarianten
|
||
|
||
- Bot tritt als **regulärer Web-Teilnehmer** bei (Browser-Automation), nicht via Graph Communications SDK
|
||
- Jede Session läuft in einer **eigenen Browser-Instanz** (Isolation)
|
||
- Authentifizierter Join (mit Microsoft-Account) oder Anonymous Guest -- je nach Konfiguration
|
||
- Gateway ist die **einzige** Schnittstelle für AI-Aufrufe und TTS -- der Bot-Service selbst hat keine AI-Logik
|
||
|
||
## Hybrid-Routing: SPEECH_TEAMS + Agent
|
||
|
||
Der Teamsbot läuft auf zwei kooperierenden Pfaden:
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
Audio["Meeting Audio"] --> STT["STT"]
|
||
STT --> ST["SPEECH_TEAMS<br/>(fast, low-latency)"]
|
||
ST -->|shouldRespond=true| TTS["TTS + Chat"]
|
||
ST -->|needsAgent=true| Agent["agentService.runAgent<br/>(toolSet=core, web=on,<br/>maxRounds=5, maxCostCHF=0.10)"]
|
||
Agent -->|FINAL text| TTS
|
||
UI["Operator UI<br/>(Regie-Panel + UDB)"] -->|POST directorPrompt| Route["routeFeatureTeamsbot"]
|
||
Route -->|submitDirectorPrompt| Svc["TeamsbotService<br/>(_activeServices)"]
|
||
Svc -->|asyncio.create_task| Agent
|
||
TTS --> Meeting
|
||
Svc -->|SSE 'directorPrompt'| UI
|
||
```
|
||
|
||
- **`SPEECH_TEAMS`** bleibt der Default-Pfad mit niedrigster Latenz. Der dazugehörige System-Prompt erlaubt dem Modell explizit, `needsAgent=true` + `agentReason` zu setzen, wenn die Anfrage Web-Recherche, Mail oder Multi-Step-Tools erfordert.
|
||
- **Director Prompts** umgehen `SPEECH_TEAMS` komplett und feuern direkt einen `runAgent`-Lauf, dessen `FINAL`-Event-Text wieder über die bestehenden TTS-/Chat-Kanäle ins Meeting geliefert wird.
|
||
|
||
## Director Prompts (private Operator-Anweisungen)
|
||
|
||
Operator-Prompts sind **privat** (nur per SSE an den Session-Owner sichtbar) und werden in PostgreSQL gespeichert (`poweron_teamsbot.TeamsbotDirectorPrompt`).
|
||
|
||
| Modus | Verhalten |
|
||
|---|---|
|
||
| `oneShot` | Einmaliger Agent-Lauf, danach Status `consumed` |
|
||
| `persistent` | Agent-Lauf wird ausgeführt **und** der Text wird als `OPERATOR_DIRECTIVES`-Block in jeden folgenden `SPEECH_TEAMS`-Trigger eingemischt, bis der Operator den Prompt löscht |
|
||
|
||
| Lifecycle-Status | Bedeutung |
|
||
|---|---|
|
||
| `queued` | Eingereicht, Agent noch nicht gestartet |
|
||
| `running` | Agent läuft |
|
||
| `succeeded` | Agent fertig, persistent bleibt aktiv |
|
||
| `consumed` | One-Shot abgeschlossen oder persistent gelöscht |
|
||
| `failed` | Agent-Lauf fehlgeschlagen, persistent wird automatisch aus aktiven Direktiven entfernt |
|
||
|
||
**Persistenz beim Reconnect:** Bei jedem WebSocket-Reconnect ruft der Service `interface.getActivePersistentPrompts(sessionId)` auf und füllt `_activePersistentPrompts` neu, damit Direktiven Network-Drops überleben.
|
||
|
||
**Limits:** `DIRECTOR_PROMPT_TEXT_LIMIT = 8000` Zeichen, `DIRECTOR_PROMPT_FILE_LIMIT = 10` UDB-Dateien (Pflicht-Prüfung in der Route, validiert auch von `TeamsbotDirectorPromptCreateRequest`).
|
||
|
||
**RBAC:** Routes prüfen `_validateInstanceAccess` + `_validateSessionOwnership`. Ein Nicht-Owner sieht 404, niemals 403, um die Existenz der Session nicht preiszugeben.
|
||
|
||
**Rate-Limit:** `30/minute` pro Operator (slowapi).
|
||
|
||
## Schicht-Trennung (Plan #5 abgeschlossen 2026-04-24)
|
||
|
||
| Verantwortung | Datei |
|
||
|---|---|
|
||
| Persistenz + Lifecycle | `interfaceFeatureTeamsbot.py` (`createDirectorPrompt`, `getActivePersistentPrompts`, `updateDirectorPrompt`, `deleteDirectorPrompt`) |
|
||
| Orchestrierung + Agent-Lauf + SSE | `service.py` (`submitDirectorPrompt`, `_processDirectorPrompt`, `_runAgentForMeeting`, `_buildPersistentDirectorContext`, `removePersistentPrompt`) |
|
||
| HTTP + RBAC + Limits | `routeFeatureTeamsbot.py` (POST/GET/DELETE `/sessions/{id}/directorPrompts`) |
|
||
| Frontend Regie-Panel + UDB-Sidebar + SSE-Listener | `frontend_nyla/src/pages/views/teamsbot/TeamsbotSessionView.tsx` + `Teamsbot.module.css` |
|
||
| Frontend API-Wrapper | `frontend_nyla/src/api/teamsbotApi.ts` (`submitDirectorPrompt`, `listDirectorPrompts`, `deleteDirectorPrompt`) |
|
||
| Tests | `gateway/tests/unit/teamsbot/test_directorPrompts.py` (26 Tests, AC 5 + 6 abgedeckt; AC 1–4 manuell live) |
|