Integrationsunterlagen · v0.1.0 · M1 abgeschlossen Integration material · v0.1.0 · M1 complete
ACP ist eine mandantenfähige Plattform für agentische Kundenkommunikation: Sprache und Chat, Deutsch und Englisch, Datenhaltung in der EU. Diese Seite beschreibt, was heute tatsächlich im Repository steht. Jede Zahl unten ist im Code nachprüfbar, und was fehlt, steht am Ende der Seite. ACP is a multi-tenant platform for agentic customer conversations: voice and chat, German and English, EU data residency. This page describes what is actually in the repository today. Every figure below is checkable against the code, and what is missing is listed at the end of the page.
Im ÜberblickAt a glance
Nachgezählt am Repository, nicht geschätzt: pytest -m "not live" meldet 840 bestandene Tests; contracts.py deklariert zwölf Protocols; build_v1_router() registriert dreizehn HTTP-Routen und einen WebSocket; das Language-Enum hat sieben Werte (DE, EN, IT, CS, SK, PL, FR) und das Compliance-Gate hält für jeden ein vollständiges Eröffnungsskript bereit. Die Konsolen und das Kunden-Widget selbst sind zweisprachig (DE/EN). Counted from the repository, not estimated: pytest -m "not live" reports 840 passing tests; contracts.py declares twelve Protocols; build_v1_router() registers thirteen HTTP routes and one WebSocket; the Language enum has seven members (DE, EN, IT, CS, SK, PL, FR) and the compliance gate carries a complete opening script for each. The consoles and the customer widget themselves ship DE + EN.
Die eine IdeeThe one idea
Jede Fähigkeit, die man kaufen kann — Spracherkennung, Sprachsynthese, das Sprachmodell, der Medientransport — liegt hinter einem Protocol in src/acp/contracts.py. Die Schichten, die dem Produkt gehören (Orchestrator, Compliance, Voice-Pipeline, ERP-Repository, Konsolen), importieren kein einziges Anbieter-SDK. Every capability that can be bought — speech recognition, speech synthesis, the language model, the media transport — sits behind a Protocol in src/acp/contracts.py. The layers the product owns (orchestrator, compliance, voice pipeline, ERP repository, consoles) import zero vendor SDKs.
Anbieterbibliotheken werden lazy importiert, innerhalb der Methode, die sie benutzt. Deshalb importiert, startet und testet die gesamte Plattform ohne Zugangsdaten und ohne installierte optionale Pakete — und deshalb ist ein Anbieterwechsel eine Konfigurationsänderung, kein Code-Eingriff. Vendor libraries are imported lazily, inside the method that uses them. That is why the whole platform imports, runs and tests with no credentials and no optional packages installed — and why swapping a provider is a configuration change, not a code change.
Konfiguration wird aus einem expliziten Mapping aufgelöst (Settings.from_env(mapping)), nicht aus der Prozessumgebung. Ein TenantContext trägt die eingefrorenen Settings eines Mandanten durch Graph, Router, Gate, Odoo-Client und alle Adapter-Fabriken. Folge: zwei Mandanten können in einem Prozess unterschiedliche Anbieter fahren — ohne os.environ zu mutieren. Configuration is resolved from an explicit mapping (Settings.from_env(mapping)), not read from the process environment. A TenantContext carries one tenant's frozen settings through the graph, router, gate, Odoo client and every adapter factory. The consequence: two tenants can run different providers inside one process — with no os.environ mutation.
| FähigkeitCapability | Protocol | Adapter im RepoAdapters in the repo | Umschalten überSwapped via |
|---|---|---|---|
| SprachmodellLanguage model | LLMClient · LLMRouter | claude · openai · gemini · grok · oss · mock | RouterPolicy · ACP_CONFIDENTIAL_FRONTIER |
| SpracherkennungSpeech to text | STTEngine | speechmatics · parakeet · mock | ACP_STT |
| SprachsyntheseText to speech | TTSEngine | azure · elevenlabs · cartesia · fish · piper · mock | ACP_TTS |
| MedientransportMedia transport | TransportSession | livekit · mock | ACP_TRANSPORT |
| Sprache-zu-SpracheSpeech to speech | RealtimeVoiceSession | openai_realtime · gemini_live · mock | ACP_VOICE_MODE · ACP_REALTIME |
| ERPERP | OdooRepository | Odoo JSON-RPC · In-Memory | ODOO_URL · ODOO_DB · ODOO_USER · ODOO_API_KEY |
| WissensindexKnowledge index | KnowledgeIndex | deterministischer Offline-Embedder (Standard) deterministic offline embedder (default) | Protocol-Implementierung Protocol implementation |
Jede Fähigkeit hat einen Mock, und der Mock ist überall der Standard. Ein falsch gesetzter Engine-Name für STT oder TTS fällt mit einer Warnung auf den Mock zurück, statt die Plattform zu stoppen; ein unbekannter Name wird abgelehnt. Every capability has a mock, and the mock is the default everywhere. A selected STT or TTS engine whose credential is missing falls back to the mock with a warning rather than crashing the platform; an unknown name is rejected outright.
Kanäle · Sprachen · DatenraumChannels · languages · residency
Der Sprachpfad läuft wahlweise kaskadiert (STT → LLM → TTS) oder als Realtime-Sprache-zu-Sprache-Sitzung; beides steht hinter demselben Kontrakt und wird pro Anruf gewählt. Chat läuft über dasselbe Gehirn, nicht über ein zweites System. Die Region ist voreingestellt auf eu-central-1 (Frankfurt). The voice path runs either cascaded (STT → LLM → TTS) or as a realtime speech-to-speech session; both sit behind the same contract and are chosen per call. Chat runs on the same brain, not on a second system. Region defaults to eu-central-1 (Frankfurt).
Die Sensitivität eines Gesprächs ist ein hartes Tor, keine Präferenz. Geschwindigkeit (schnell für Live-Sprachzüge, stark für asynchrones Nachdenken) optimiert erst innerhalb dessen, was die Sensitivität erlaubt. Einen Rückfall über die Grenze hinweg gibt es nicht: eine sensible Bahn, die on-prem nicht bedient werden kann, scheitert — sie leckt nicht zu einer Frontier-API. A conversation's sensitivity is a hard gate, not a preference. Speed (fast for live voice turns, strong for asynchronous reasoning) optimises only within what sensitivity allows. There is no cross-boundary fallback: a sensitive lane that cannot be served on-prem fails rather than leaking to a frontier API.
| SensitivitätSensitivity | Was das heißtWhat it means | Wohin der Zug gehtWhere the turn goes |
|---|---|---|
| PUBLIC | keine Kundendatenno customer data | Frontier-Anbieter, von der Geschwindigkeit gewähltfrontier provider, chosen by speed |
| INTERNAL | Geschäftsdaten, keine personenbezogenen Datenbusiness data, no personal data | Frontier-Anbieter, von der Geschwindigkeit gewähltfrontier provider, chosen by speed |
| CONFIDENTIAL | benannter Kunde mit Kontaktdaten oder kommerziellen Konditionennamed customer with contact data or commercial terms | on-prem, außer ACP_CONFIDENTIAL_FRONTIER ist gesetzt — das erklärt ausdrücklich eine EU-Region mit Zero-Retention-Haltung on-prem, unless ACP_CONFIDENTIAL_FRONTIER is set — which explicitly declares an EU-region, zero-retention posture |
| RESTRICTED | besondere Kategorien oder vertragliches Exportverbotspecial-category data or a contractual no-export clause | immer on-prem; die Stimme wird zwangsweise kaskadiert gefahren (Parakeet-STT, Piper-TTS), weil Cloud-Sprache-zu-Sprache diese Bahn nicht bedienen kann always on-prem; the voice stack is forced to cascaded (Parakeet STT, Piper TTS), because cloud speech-to-speech cannot serve this lane |
MandantentrennungMulti-tenancy
| DimensionDimension | MechanismusMechanism |
|---|---|
| KonfigurationConfiguration | pro Mandant aufgelöste, eingefrorene Settingsper-tenant, frozen Settings |
| Routing und RegionRouting and region | eigene RouterPolicy und eigene Region je Mandanta per-tenant RouterPolicy and region |
| GeheimnisseSecrets | aus einem expliziten Mapping aufgelöst, nie aus os.environresolved from an explicit mapping, never from os.environ |
| MotorenEngines | jede Adapter-Fabrik nimmt settings= entgegenevery adapter factory takes settings= |
| WissenKnowledge | eigenes Git-Verzeichnis unter dem Mandanten-Rootits own Git directory under the tenant root |
| TranskripteTranscripts | TranscriptStore wird für einen Mandanten gebaut — es gibt kein Mandanten-Argument zum VergessenTranscriptStore is constructed for a tenant — there is no per-call tenant argument to forget |
| Live-ZustandLive state | Hub-Registry je Mandant; der prozessglobale, mandantenblinde Hub war der Befund einer feindlichen Prüfung und ist geschlossena per-tenant hub registry; the process-global, tenant-blind hub was a hostile-review finding and is closed |
| MetrikenMetrics | Label tenant; Gesprächs-IDs werden nie zu Labelsa tenant label; conversation ids never become labels |
| WebhooksWebhooks | eigene Endpunkte und eigene Signaturgeheimnisse je Mandantper-tenant endpoints and per-tenant signing secrets |
Mensch in der SchleifeHuman in the loop
WissenKnowledge
Veröffentlichen ist ein Commit. Der semantische Index und der kompilierte Prompt sind Ableitungen, die jederzeit neu gebaut werden können; geht der Index verloren, geht nichts verloren. Ein Mensch kann die Wahrheit lesen, diffen und reviewen, ohne ein Werkzeug zu öffnen. Publishing is a commit. The semantic index and the compiled prompt are derived artefacts that can be rebuilt at any time; losing the index loses nothing. A human can read, diff and review the source of truth without opening a tool.
Das Tor, das zähltThe gate that matters
Zwei Etiketten entscheiden alles, und sie zu verwechseln ist der Fehler, der wehtut. Der Typ sagt, wie ein Datensatz wirkt (Fakt wird abgerufen, Verhalten wird in den Prompt kompiliert, Anleitung wird zum Handbuch, Quelle ist nur Herkunft). Autorität × Publikum sagt, wie er verwendet werden darf. may_be_told_to_customer() ist das eine, testbare Tor: eine Wettbewerbsanalyse ist Kontext, ein Transkript ist ein Signal — und keines von beiden darf einem Kunden als Tatsache gesagt werden, so relevant der Abruf sie auch findet. Vertrauen kommt aus der Quelle, nie aus der Prosa: der Klassifizierer darf das Publikum verengen, aber niemals über das hinaus erweitern, was die Quellenart erlaubt. Das Tor läuft zur Abfragezeit, nicht nur beim Bauen. Two labels decide everything, and confusing them is the failure that hurts. Type says how a record acts (a fact is retrieved, a behaviour is compiled into the prompt, a guide renders to a manual, a source is provenance only). Authority × audience says how it may be used. may_be_told_to_customer() is the single, testable gate: a competitor analysis is context and a transcript is a signal — and neither may be stated to a customer as fact, however relevant retrieval finds them. Trust comes from the source, never from the prose: the classifier may narrow the audience but can never widen it past what the source kind allows. The gate runs at query time, not only at build time.
Regierte SchreibvorgängeGoverned writes
RegulatorikCompliance
BeobachtbarkeitObservability
Die Pipeline misst jeden Zug end-to-end und je Stufe, und das Ergebnis wird emittiert, nicht berechnet und weggeworfen — das war ein Prüfbefund. Die Metriken liegen im Prometheus-Textformat unter /metrics, hinter einem Token, das fehlend nicht etwa durchlässt, sondern den Endpunkt schließt. The pipeline measures every turn end-to-end and per stage, and the result is emitted rather than computed and discarded — that was an audit finding. Metrics are exposed in Prometheus text format at /metrics, behind a token whose absence closes the endpoint rather than opening it.
Protokolle sind strukturiert (in Produktion als JSON, eine Zeile ein Objekt), und der Mandant ist an den Kontext gebunden — er steht auf jeder Zeile, statt an jeder Aufrufstelle mitgereicht werden zu müssen. Logs are structured (JSON in production, one object per line), and the tenant is bound into the context — it appears on every line rather than having to be passed at every call site.
| MetrikMetric | Was sie beantwortetWhat it answers |
|---|---|
| acp_turn_latency_ms · acp_stage_latency_ms | Wie lange dauert ein Zug — insgesamt und je Stufe?How long does a turn take — in total and per stage? |
| acp_turns_total · acp_turn_budget_breach_total | Wie viele Züge, und wie viele davon rissen das Budget?How many turns, and how many of them broke the budget? |
| acp_tool_failure_total · acp_transcript_write_failure_total | Welche Werkzeuge scheiterten, und welche Transkripte konnten nicht geschrieben werden?Which tools raised, and which transcripts could not be persisted? |
| acp_webhook_delivered_total · acp_webhook_failed_total · acp_webhook_blocked_total | Kommen Ereignisse an, und was hat die Egress-Richtlinie abgewiesen?Are events arriving, and what did the egress policy refuse? |
| acp_golden_gate_pass_total · acp_golden_gate_fail_total | Wie viele Wissens-Builds hat das Golden-Gate durchgelassen oder zurückgerollt?How many knowledge builds did the Golden gate pass or roll back? |
| acp_widget_session_total · acp_widget_refused_total | Wie viele Widget-Sitzungen wurden ausgegeben, wie viele wegen Schlüssel oder Origin abgelehnt?How many widget sessions were issued, and how many refused for a bad key or origin? |
EhrlichkeitHonesty
Alles Obenstehende ist heute grün — auf Mocks. Das Folgende ist es nicht, und es steht hier, weil eine Integrationsunterlage, die Lücken verschweigt, beim ersten echten Anruf auffliegt. Everything above is green today — on mocks. The following is not, and it is listed here because integration material that hides its gaps is found out on the first real call.
| LückeGap | Was das für eine Integration bedeutetWhat it means for an integration | FälligDue |
|---|---|---|
| H2 — LiveKit-Token wird unauthentifiziert ausgestelltH2 — LiveKit token minting is unauthenticated | Offener Prüfbefund. Er sitzt im Referenz-Deployment-Klebstoff, nicht im besessenen Kern, muss aber authentifiziert und ratenbegrenzt sein, bevor Telefonie live geht.An open audit finding. It lives in the reference deployment glue, not in the owned core, but it must be authenticated and rate-limited before telephony goes live. | M6 |
| H3 — der Live-Claude-Pfad bricht nach einem WerkzeugaufrufH3 — the live Claude path breaks after a tool call | Offener Prüfbefund, in CI unsichtbar, weil dort nur Mocks laufen. Es fehlt ein Loopback-Vertragstest, der den Rollenwechsel prüft. Bis dahin: keine Live-Werkzeugnutzung annehmen.An open audit finding, invisible in CI because only mocks run there. It needs a loopback contract test that validates role alternation. Until then, do not assume live tool use works. | M6 |
| Keine Ratenbegrenzung, nirgendsNo rate limiting, anywhere | Auch nicht auf dem Konsolen-Login — die rund 25 ms von argon2id sind die einzige Bremse — und auch nicht bei der Webhook-Zustellung.Not on the console login either — argon2id's roughly 25 ms is the only brake — and not on webhook delivery. | M7 |
| Kein Widerruf von SitzungenNo session revocation | Cookies sind zustandslos mit zwölf Stunden Laufzeit. Einen Benutzer zu löschen oder eine Rolle herabzustufen entwertet eine bereits ausgestellte Sitzung nicht; das Abmelden löscht nur das Cookie im Browser.Cookies are stateless with a twelve-hour life. Deleting a user or demoting a role does not invalidate an outstanding session; logging out only clears the client-side cookie. | M7 |
| Kein Endpunkt für Mandanten-Anlage und SchlüsselausgabeNo endpoint for tenant provisioning or key issuance | Die Mandanten-Registry ist im Speicher und wird aus der Umgebung befüllt. Die Admin-Konsole zeigt den serverseitigen Aufruf an, statt so zu tun, als führte sie ihn aus. Die Schnittstelle steht fest, damit der Austausch später nichts anderes berührt.The tenant registry is in memory and seeded from the environment. The admin console shows the server-side call rather than pretending to perform it. The interface is fixed, so the later swap touches nothing else. | M7 |
| Kein Bildmodell für Avatare verdrahtetNo image model wired for avatars | Avatar-Vorschläge erscheinen als Prompt-Briefings, und die Oberfläche sagt genau das. Es wird kein Erfolg vorgetäuscht.Avatar proposals render as prompt briefs, and the interface says exactly that. No success is faked. | — |
| Echte Motoren und SIP-TelefonieReal engines and SIP telephony | Die Adapter sind geschrieben, aber grün ist heute alles auf Mocks. Der Live-Nachweis — ein echter Anruf, der durchläuft — steht noch aus und hängt an Zugangsdaten, einem SIP-Trunk und einer Rufnummer.The adapters are written, but everything green today is green on mocks. The live proof — a real call that completes — is still outstanding and depends on credentials, a SIP trunk and a phone number. | M6 |
| Das Ingest-Token gilt der Plattform, nicht dem MandantenThe ingest token is a platform credential, not a tenant one | Voice-Worker sind Plattform-Infrastruktur, deshalb reist der Mandant im (geprüften) Rumpf mit. Mandantenspezifische Ingest-Zugangsdaten kommen später.Voice workers are platform infrastructure, so the tenant travels in the (validated) body. Per-tenant ingest credentials come later. | M7 |
| Wissens-Intake: Datei, URL und Ernte sind noch lokalKnowledge intake: file, URL and harvest are still local | Getippter Text legt einen echten Entwurf an. Die übrigen Wege stellen sich lokal in eine Warteschlange und sind sichtbar als „wartet auf Backend“ gekennzeichnet.Typed text files a real draft. The other paths queue locally and are visibly labelled as awaiting a backend. | — |
| /openapi.json und /docs sind öffentlich/openapi.json and /docs are public | Sie legen die Routenoberfläche offen. Als informativ eingestuft, nicht als Blocker — aber wer das nicht will, blendet sie im Reverse-Proxy aus.They disclose the route surface. Classified as informational, not a blocker — but an operator who does not want that hides them at the reverse proxy. | — |