receptionist — Plattform-Capability
Der digitale Empfangstresen fuer KMU-Kunden. Eine Brain-Lambda nimmt alle WhatsApp-Inbounds von allen Customers entgegen, ordnet die per phone_number_id einem Customer-Config-Eintrag in DDB zu, lädt das Vertical-spezifische Tool-Set (heute nur hairdresser → Cal.com; spaeter restaurant → Resy etc.) und antwortet autonom.
B2B-Pitch: „Wir bauen den digitalen Empfangstresen fuer KMU. Termine buchen, verschieben, stornieren ueber WhatsApp — ohne Mitarbeiter zu unterbrechen, 24/7 erreichbar.”
Architektur
WhatsApp-Inbound (Kunde sendet WA)
→ Meta-Webhook → mcp-whatsapp-hosted.agenticventures.de/webhook
→ mcp-whatsapp-inbox (DynamoDB)
→ DDB-Stream (NEW_IMAGE)
→ receptionist-brain Lambda
├── lookup customer (receptionist-customers per phone_number_id)
├── lookup end-customer (receptionist-end-customers fuer Wiedererkennung)
├── load conversation (receptionist-conversations per from_phone, TTL 24h)
├── render system-prompt (vertical-template + customer-vars)
├── Bedrock Claude Haiku-4.5 mit Tool-Use-Loop (max 5 Iterations)
│ Tools: send_text, send_interactive_buttons,
│ list_services, list_slots, book_appointment,
│ cancel_appointment, reschedule_appointment,
│ lookup_existing_bookings, save_end_customer_info,
│ update_conversation_context
│ Tool-Backends: mcp-whatsapp-hosted + mcp-calcom-hosted
│ (via HTTP MCP, Auth ueber Cloudflare Access Service-Token)
├── persist conversation-state
├── upsert end-customer
├── emit KPI-Events (receptionist-events)
└── mark inbox-item processed
DDB-Tables
| Tabelle | PK | SK | TTL | Zweck |
|---|---|---|---|---|
receptionist-customers | phone_number_id (S) | — | — | B2B-Plattform-Kunden (Salons, Restaurants). Customer-Config inkl. Vertical, Persona, Mitarbeiter, Oeffnungszeiten, Booking-Backend-Endpoint. PITR an. |
receptionist-conversations | from_phone (S) | — | expires_at (24h) | Conversation-State per End-Customer-Phone. Speichert letzte ~10 Turns + Intent + Context. Auto-Expire nach 24h (passt zum WhatsApp-Service-Window). |
receptionist-end-customers | id (<slug>#<phone>) (S) | — | — | B2C-Kunden zur Wiedererkennung. Speichert Name, bisherige Bookings, bevorzugter Mitarbeiter. PITR an. |
receptionist-events | customer_slug (S) | ts_uuid (S) | expires_at (365d) | Event-Stream fuer KPIs. Event-Types: message_received, bot_response_sent, booking_created, booking_cancelled, booking_rescheduled, bedrock_tokens_used, bot_confused, tool_error, brain_error. |
Plus die bestehende mcp-whatsapp-inbox-Tabelle (im McpWhatsappHosted-Stack), aus der der Brain ueber DDB-Stream getriggert wird.
Customer-Config-Schema
phone_number_id: "1170948102758111" # PK
customer_slug: "friseur-im-sueden"
vertical: "hairdresser" # spaeter: "restaurant", "doctor", ...
persona_name: "Anja" # Bot redet als Anja
booking_backend: "calcom" # spaeter: "resy", "opentable"
calcom:
team_slug: "friseur-im-sueden" # Cal.com Team-Slug
event_type_default_service: 5684152 # Fallback fuer "Termin morgen?"
staff:
- name: "Anja"
calcom_user_id: 1234
aliases: ["Anna", "Anjala"] # fuzzy match auf Kundeneingabe
- name: "Lars"
calcom_user_id: 1235
aliases: ["Lasse"]
business:
name: "Friseur im Sueden"
address: "Hamm, ..."
timezone: "Europe/Berlin"
business_hours:
mon: "09:00-17:00"
...
sat: "geschlossen"
state:
active: true
paused_until: null # ISO-ts bei Wartungs-Modus
model_id: "eu.anthropic.claude-haiku-4-5-..." # pro Customer toggelbar (Sonnet upgrade moeglich)Vertical-Adapter-Pattern
BookingBackend-Interface mit Methoden list_services, list_slots, book, cancel, reschedule, list_bookings_for_phone. Heute eine Implementierung: CalcomBackend (gegen mcp-calcom-hosted).
Spaeter:
ResyBackend(gegen Resy-API direkt oder eigenes mcp-resy-hosted)OpenTableBackendCustomBookingBackend(gegen Kunden-eigenes Restaurant-System)
Pro Vertical eigenes Markdown-Prompt-Template in lambdas/receptionist-brain/prompts/<vertical>.md.
KPIs (Pilot)
Event-Sourcing in receptionist-events. Separate KPI-Daily-Summary-Lambda (noch nicht gebaut) liest Events der letzten 24h pro Customer und pushed Telegram-Stats morgens 8:00.
Geplante Stats pro Customer:
- Anzahl
message_received - Anzahl
bot_response_sent - Anzahl
booking_created - Anzahl
booking_cancelled/booking_rescheduled - Bedrock-Token-Cost (Summe
bedrock_tokens_used.input_tokens+output_tokensx Pricing) - Anzahl
bot_confused/tool_error/brain_error - Average Response-Latency
Code-Layout
| Pfad | Inhalt |
|---|---|
~/source/agents-platform/lambdas/receptionist-brain/main.py | Lambda-Handler + Tool-Loop + Tool-Dispatcher |
~/source/agents-platform/lambdas/receptionist-brain/mcp_client.py | MCP-HTTP-Client (CF-Access-aware) |
~/source/agents-platform/lambdas/receptionist-brain/adapters.py | BookingBackend-Interface + CalcomBackend |
~/source/agents-platform/lambdas/receptionist-brain/state.py | DDB-helpers fuer alle 4 Tables + Inbox |
~/source/agents-platform/lambdas/receptionist-brain/prompts.py | Prompt-Template-Loader + Variablen-Substitution |
~/source/agents-platform/lambdas/receptionist-brain/prompts/hairdresser.md | System-Prompt-Template Friseur-Vertical |
~/source/agents-platform/infra/lib/receptionist-brain-stack.ts | CDK-Stack ReceptionistBrainStack |
Live-Status 2026-05-19
| Komponente | Status |
|---|---|
CDK-Stack ReceptionistBrainStack | ✅ deployed (arn:aws:cloudformation:eu-central-1:425924867359:stack/ReceptionistBrainStack/...) |
| 4 DDB-Tables | ✅ live, PITR an wo sinnvoll |
Brain-Lambda receptionist-brain | ✅ live, Stream-getriggert, am 2026-05-18 16:35 erfolgreich auf Inbound „Test” reagiert |
DDB-Stream auf mcp-whatsapp-inbox | ✅ aktiv (NEW_IMAGE) |
| Customer 1 — Friseur im Sueden | ✅ in DDB geseedet (Placeholder-Daten, echte Daten nach Arne-Setup-Call) |
| CF-Access-Service-Token-Setup fuer Brain → mcp-whatsapp | ✅ fertig seit 2026-05-15 — Token cd8b35f6-... in Policy receptionist-brain service token auf App mcp-whatsapp (mcp-whatsapp.agenticventures.de/mcp*). AWS-Secret receptionist-brain/cf-access-mcp (client_id+client_secret JSON), via main.py:_cf_access_creds() Cold-Start-Cache geladen, Header an mcp_client durchgereicht. CloudWatch-Log am 18.05. zeigt „CF Access service token loaded”. mcp-calcom-hosted hat KEIN CF Access davor — Brain ruft direkt. |
| End-to-End-Smoke | ⚠️ blockt heute auf abgelaufenem WHATSAPP_ACCESS_TOKEN — graph.facebook.com/v23.0/.../messages gibt 401 Unauthorized (letzter Datenpunkt 2026-05-18 16:35). Permanent-Token im Meta Business Settings → System Users regenerieren, Secret mcp-whatsapp-hosted/whatsapp-config ACCESS_TOKEN-Key updaten, aws ecs update-service --force-new-deployment |
| KPI-Daily-Summary-Lambda | ⏳ pending (eigene Session) |
Roadmap — Multi-Channel
Entschieden im Brainstorm 2026-05-18 (2026-05-18-multi-channel-receptionist-requirements.md). Pausiert bis 360dialog Phase 1 (mcp-whatsapp Multi-Provider-Refactor) durch ist — damit ChannelTransport-Verallgemeinerung sauber auf vorhandener Transport-Abstraktion aufsetzt.
Strategischer Entscheid: receptionist wird Multi-Channel-Plattform. Das Brain bleibt zentral, alle Channels docken via ChannelTransport-Interface an. Erste zweite Channel-Implementierung: Voice parallel zu WhatsApp.
Non-Goal: keine externe Plattform (Botpress / Voiceflow / n8n / AiSensy) einfuehren. „Plattform-Frage” beantwortet sich durch eigenes ChannelTransport-Interface, nicht durch Vendor-Lock auf Agent-Logik-Ebene. Vertical-Adapter (Tools + Prompt) bleibt das Pattern.
Sequencing:
- Erst 360dialog Phase 1 abschliessen (siehe _index) — bringt
WhatsappTransport-Abstraktion in mcp-whatsapp - Dann
ChannelTransportausWhatsappTransportextrahieren (Sub-Klasse wird zur Default-Implementation) VoiceTransportals zweite Implementation, Pilot auf Icking-Dachhandwerk-Greenfield (nicht Friseur — der bleibt WhatsApp-only)- Friseur bekommt Voice spaeter als Add-on, kein WhatsApp-Decommissioning
Offene Decisions (vor Planning klaeren):
- Voice-Provider / Voice-Stack: Amazon Connect + Lex V2 + Bedrock + Polly (= existierender _index-POC), ElevenLabs Conversational AI (EU, MCP schon im Stack), Vapi, Retell, oder Eigenbau (Twilio + ElevenLabs-TTS + Deepgram-STT)
- Konvergenz mit Telefon-Assistent-POC: einschmelzen in receptionist (Amazon Connect = VoiceTransport), bewusst zwei Stacks parallel, oder Telefon-Assistent decommissionieren? Re-Eval Telefon-Assistent ist 2026-07-01.
- Pilot-Case: Icking ist Pause-bis-2026-06-30 — Alternative Aylem (Telefon-Assistent-Demo-Case laeuft schon) oder Friseur Voice-Add-on
Vertical-Adapter bleiben channel-agnostisch: Tool-Set ist Channel-egal (Cal.com / Stock-Check / Quote-Request), nur Prompt-Modifikator unterscheidet WhatsApp-Text-mit-Buttons vs Voice-Speech-mit-DTMF.
Offene Setup-Punkte
- Cloudflare Access Service-Token fuer Brain anlegen. Cloudflare Zero Trust → Access → Service Auth → Create Service Token. Name
receptionist-brain, Duration „Forever” (oder 1 Jahr mit Rotation-Reminder). client_id + client_secret kopieren. - Service-Token-Policy auf beide Access Apps adden (
mcp-whatsapp.agenticventures.de/mcp+mcp-calcom.agenticventures.de/mcp). - AWS-Secret anlegen:
aws --profile av-production secretsmanager create-secret \ --name receptionist-brain/cf-access-mcp \ --region eu-central-1 \ --secret-string '{"client_id":"<ID>","client_secret":"<SECRET>"}' - Setup-Call mit Arne durchziehen → echte Salon-Daten in
receptionist-customers:- Adresse
- Oeffnungszeiten
- 5 Mitarbeiter-Namen + Cal.com-User-IDs + Aliasse
- Service-Liste mit Preisen
- Persona-Name (z.B. „Anja” oder anders)
- Echtes Cal.com-Konto fuer Friseur im Sueden aufsetzen mit Team + 5 Mitgliedern + Event-Types pro Service.
- Smoke retry nach Punkt 1-3 mit fake Inbox-Item, dann mit echter WhatsApp.
- KPI-Daily-Summary-Lambda bauen (eigene Session, ce:plan + ce:work).
- Conversation-Audit-Trail (Compliance — Arne will sehen was sein Bot gesagt hat). Separate Lambda oder Stream-Aggregator nach S3.
Related
- _index — Customer 1, war urspruenglich „Friseur-Bot”, ist jetzt Customer der receptionist-Plattform.
- whatsapp — Inbox-Backend
- calcom — Cal.com-Adapter
- mcp-hosting-fargate-tunnel — Hosting-Pattern fuer MCPs hinter Cloudflare Access
- agents-platform — Lambda-Plattform-Konvention (receptionist-brain nutzt aber NICHT den AgenticVenturesAgent-Construct, weil DDB-Stream-getriggert statt EventBridge-Cron)