Agenten-Strecke

Technische Architektur der Agenten-Strecke für eingehende Mail — Schrittmodule, der Walker, Sicherheitsprüfung, Threading und menschliche Prüfung.

Architektur der Agenten-Strecke

Die Agenten-Strecke ist das technische Herzstück von Owlats Entwicklung von einer Plattform für ausgehende E-Mail hin zu einem System für Kommunikationsintelligenz. Jede eingehende E-Mail durchläuft eine mehrstufige Verarbeitungskette, die prüft, Kontext abruft, klassifiziert, entwirft und entweder an den automatischen Versand oder an eine Warteschlange zur menschlichen Prüfung routet.

Status: Kern ausgeliefert, Ränder werden enger

Die Kern-Strecke ist gebaut und in Betrieb: die Schrittmodule und der Walker (apps/api/convex/agent/), der Empfänger für eingehende Nachrichten (apps/api/convex/inbox/messages.ts), das vollständige Schema (apps/api/convex/schema/inbox.ts) und die Postfach-Oberfläche (apps/web/app/pages/dashboard/inbox/*) sind allesamt ausgeliefert.

Das meiste, was dieses Dokument einst als Zukunftsmusik markiert hat, ist inzwischen verdrahtet: der LLM-Klassifikator für Prompt-Injections (Muster + Best-Effort-LLM der Guard-Stufe), das Zusammenfassen von Nachrichten (Coalescing, per Opt-in), das Routing mit abgestufter Autonomie samt lebendiger Feedback-Schleife und drei Circuit Breakern sowie der Wissensgraph und die relevanten Quelldateien, die das Kontext-Briefing des Agenten speisen. Zukunft bleibt die Verzweigung bei mehreren Absichten / parallele Worker — der Walker führt weiterhin eine einzelne lineare Kette aus. Jede Stelle ist im Text entsprechend markiert. Für die Wissensseite siehe Wissensgraph, für den Leitfaden aus Betreibersicht KI-Agent & Autonomie.

Architektur der Schrittmodule

Die Strecke ist nicht eine große Funktion. Gemäß ADR-0014 ist jede Stufe ein Agenten-Schrittmodul — ein kleines Objekt mit zwei reinen Funktionen — und ein einzelner Walker führt sie der Reihe nach aus. Die Schrittmodule liegen unter apps/api/convex/agent/steps/<kind>/index.ts; der Walker ist apps/api/convex/agent/walker.ts.

Ein Schrittmodul (apps/api/convex/agent/steps/types.ts) sieht so aus:

interface AgentStepModule<K, In, Out> {
  readonly kind: K
  readonly llm?: { tier: 'fast' | 'capable' }   // present iff the step calls an LLM
  execute(ctx, input): Promise<AgentStepResult<Out>>  // does the work
  route(output, input, runCtx): AgentRoute            // decides what happens next
}

route() gibt eine von drei Formen zurück:

RouteBedeutung
in_stateIm aktuellen processingStatus bleiben, den nächsten Schritt einplanen.
transitionprocessingStatus in einen neuen Zustand überführen (optional einen Folgeschritt einplanen).
doneEnde — die Strecke ist für diese Nachricht abgeschlossen.

Der Walker (runStep) legt eine agentActions-Zeile an, ruft module.execute() auf, wendet module.route() an und plant den nächsten Schritt selbst über ctx.scheduler.runAfter(0, …) ein. Jede Ausnahme innerhalb eines Schritts überführt die Nachricht nach failed, wobei die fehlgeschlagene Aktion festgehalten wird.

Eine lineare Kette, keine Verzweigung

Der Walker führt eine einzige lineare Schrittkette pro Nachricht aus. Die Klassifizierung erzeugt ein einziges Classification-Objekt, und das Routing ist in_state / transition / done — es gibt keine parallele Verzweigung „Worker A / Worker B“. Die Verzweigung bei mehreren Absichten (weiter unten) bleibt ein Zukunftsentwurf.

Die fünf Schritte

Die Schrittarten sind exakt: security_scan, context_retrieval, classify, draft, route (AgentStepKind in agent/steps/types.ts). Ein plan-Schritt war entworfen, wurde aber mit ADR-0014 vor der Produktion verworfen — es gibt keine Stufe zur Aktionsplanung.

Schritt 1: Sicherheitsprüfung

apps/api/convex/agent/steps/security_scan/index.ts — der Einstiegsschritt, ausgeführt, während processingStatus auf received/security_check steht. Er prüft die eingehende Nachricht auf Prompt-Injection, eingeschmuggelte Anweisungen und Spam. Die deterministische Schicht nutzt detectInjection, detectSmuggling und calculateSpamScore aus patterns.ts; darüber hinaus fängt ein LLM-Injection-Klassifikator der Guard-Stufe (getLLMProvider('guard') + runLlmObject) neuartige oder verschleierte Formulierungen ab, welche die regulären Ausdrücke verfehlen. Die beiden Urteile werden ODER-verknüpft — eine Markierung mit hoher Konfidenz auf einer der beiden Seiten gilt als Injection — und der LLM-Aufruf fällt offen aus (jeder Modellfehler fällt auf das reine Musterurteil zurück, sodass ein wackliges Modell die Strecke nie blockiert).

Routing:

  • Injection erkannt (Muster oder Guard-LLM), Konfidenz ≥ 0,7quarantined (bis zur menschlichen Freigabe zurückgehalten).
  • Spam-Score ≥ 80archived (Grund spam).
  • Sauber, Agent aktiviert → Übergang nach classifying und context_retrieval einplanen.
  • Sauber, Agent deaktiviert → die Prüfung festhalten und anhalten (done).
LLM-Injection-Klassifikator — ausgeliefert (Defense in Depth)

Die Modellstufe guard (apps/api/convex/lib/llmProvider.ts) ist inzwischen verdrahtet: Neben der Regex-Prüfung führt security_scan/index.ts einen runLlmObject-Klassifikator aus, der { isInjection, confidence, reason } zurückgibt. Ein LLM-Urteil mit hoher Konfidenz (≥ 0,8) markiert eine Injection auch dann, wenn kein Muster gegriffen hat (injectionType: llm_prompt_injection). Die Erkennung ist damit nicht mehr vollständig deterministisch — sie besteht aus Mustern plus einem Best-Effort-LLM, das bei jedem Modellfehler offen ausfällt.

Schritt 2: Kontextabruf

apps/api/convex/agent/steps/context_retrieval/index.ts — läuft in_state, während processingStatus auf classifying steht. Er stellt eine Briefing-Zeichenkette zusammen, welche die LLM-Schritte konsumieren, und zwar aus:

  • Kontaktprofil — Name, Sprache, Zeitzone aus contacts
  • Jüngste Aktivität — die letzten paar contactActivities
  • Konversationsverlauf — frühere Nachrichten im selben Thread (inboundMessages nach Thread)
  • Wissensgraph — semantisch relevante typisierte Einträge über internal.knowledge.retrieval.semanticSearch (der Abschnitt [KNOWLEDGE], begrenzt durch knowledgeEntryLimit)
  • Relevante Quelldateien — semantisch relevante hochgeladene Dokumente über internal.semanticFileProcessing.semanticSearch (der Abschnitt [RELEVANT FILES], begrenzt durch fileLimit)
  • Die aktuelle Nachricht — Von / An / Betreff / Text

Das Briefing ist tokenbudgetiert (≈ 4 Zeichen/Token, Budget von 4000 Token) und kennt drei Verdichtungsstufen, festgehalten in inboundMessages.contextTier:

StufeAuslöserVorgehen
normalPasst ins BudgetGesamten Kontext wortgetreu weitergeben.
compactedBis zum 3-Fachen des BudgetsDas Ende des zusammengestellten Kontexts behalten (das jüngste Material).
emergencyÜber dem 3-Fachen des BudgetsNur die Kontaktzeile und die aktuelle Nachricht behalten.
Wissensgraph-Kontext — verdrahtet

Der Kontextabruf fragt nun den Wissensgraphen und den Dateiindex ab. Ist der Abfragetext lang genug, führt er eine semantische Vektorsuche über knowledgeEntries (internal.knowledge.retrieval.semanticSearch) und über hochgeladene Dateien (internal.semanticFileProcessing.semanticSearch) aus und faltet die Treffer in die Briefing-Abschnitte [KNOWLEDGE] und [RELEVANT FILES] hinein — hinter demselben Tokenbudget. Sowohl knowledgeEntryLimit als auch fileLimit sind jetzt aktiv. Zur Abrufseite siehe Wissensgraph.

Schritt 3: Klassifizierung

apps/api/convex/agent/steps/classify/index.ts — nutzt die schnelle Modellstufe über getLLMProvider('classify') und runLlmObject für typsichere strukturierte Ausgabe (generateObject unter der Haube). Er erzeugt genau eine Klassifizierung:

{
  category: 'support' | 'sales' | 'billing' | 'feature_request'
          | 'complaint' | 'spam' | 'internal' | 'other',
  priority: 'urgent' | 'normal' | 'low',
  sentiment: 'positive' | 'neutral' | 'negative',
  intent: 'question' | 'complaint' | 'request' | 'information'
        | 'escalation' | 'acknowledgment',
  confidence: number,  // 0–1
}

Routing: spamarchived; complaint oder urgent → direkt nach draft_ready (den Entwurfsschritt überspringen, menschliche Prüfung erzwingen); alles Übrige → Übergang nach drafting und Einplanen des draft-Schritts. Die Klassifizierung wird in inboundMessages.classification abgelegt.

Schritt 4: Entwurfserzeugung

apps/api/convex/agent/steps/draft/index.ts — nutzt die leistungsfähige Modellstufe über getLLMProvider('draft') und runLlmText. Der Entwurf ist in agentConfig.toneDescription, agentConfig.signatureTemplate und dem zusammengestellten Kontext verankert. Als Defense in Depth prüft der Entwurfsschritt den zusammengestellten Kontext erneut auf Injection-Muster (Thread-Verlauf, der stromaufwärts nicht einzeln geprüft wurde); ein Treffer mit hoher Konfidenz lässt den Schritt fehlschlagen. Die Ausgabe wird zusammen mit einem confidenceScore in inboundMessages.draftResponse / draftSubject abgelegt.

Schritt 5: Routing

apps/api/convex/agent/steps/route/index.ts — der abschließende Schritt. Er entscheidet über automatische Freigabe versus menschliche Prüfung in drei Stufen, die sicherste zuerst:

  1. Sicherheitsschranke der Circuit Breaker. Jeder offene Breaker (internal.agentHealth.getCircuitBreakersInternal) erzwingt human_review — eine beeinträchtigte Strecke versendet nie automatisch, unabhängig von der Konfiguration.
  2. Abgestufte Autonomie (wenn das Flag ai.autonomy aktiv ist). internal.autonomy.checkPermissionInternal wendet die kategoriebezogenen autonomyRules an (Schwellwert + Tageslimit + Breaker-Prüfung). Eine Kategorie ohne Regel wird nie automatisch freigegeben; eine erlaubte belastet den kategoriebezogenen Tageszähler.
  3. Klassischer globaler Rückfall (Autonomie-Flag aus). Das einzelne agentConfig-Singleton — isAutoReplyEnabled, confidenceThreshold und die Felder für das Tageslimit.

Bei auto_approve erfolgt der Übergang nach approved; der Effekt schedule_send_approved des Lebenszyklus versendet die Antwort dann über internal.agent.agentPipeline.sendApprovedReply. Andernfalls erfolgt der Übergang nach draft_ready (wartet auf menschliche Prüfung).

Routing mit abgestufter Autonomie — verdrahtet

Der Routing-Schritt zieht nicht mehr nur agentConfig heran. Er liest inzwischen die Circuit Breaker und — hinter dem Flag ai.autonomy — die kategoriebezogenen autonomyRules über internal.autonomy.checkPermissionInternal; der klassische globale Schwellwert ist nur noch die Rückfallstufe. Siehe Abgestufte Autonomie weiter unten.

Integration eingehender E-Mail

Ablauf MTA → Convex

Eingehende Mail für das gemeinsame Agenten-Postfach kommt über den üblichen MTA-Webhook an, nicht über eine eigene Eingangsroute:

Inbound SMTP → owlat-mta
  → POST /webhooks/mta  (apps/api/convex/http.ts → providerFeedbackWebhook('mta'),
                         apps/api/convex/webhooks/providerFeedbackHttp.ts)
    → webhook pipeline verifies HMAC, parses the `inbound.received` event
      (apps/api/convex/webhooks/adapters/mta.ts)
    → dispatcher routes inbound.received → internal.inbox.messages.receiveMessage
Verwechseln Sie die beiden MTA-Webhooks nicht

POST /webhooks/mta transportiert Zustellereignisse und das Ereignis inbound.received für das Agenten-Postfach. Die separate Route POST /webhooks/mta-mailbox gehört zu Postbox (persönliche Mail pro Benutzerin und Benutzer) und läuft über mail/webhook.ts, nicht über die Agenten-Strecke.

receiveMessage (apps/api/convex/inbox/messages.ts, eine internalMutation) tut anschließend Folgendes:

  1. Legt die Absenderin oder den Absender als contacts-Zeile an bzw. aktualisiert sie (Kanal email, Quelle inbound).
  2. Ermittelt oder erzeugt über die Threading-Kaskade eine conversationThreads-Zeile.
  3. Fügt die inboundMessages-Zeile mit processingStatus: 'received' ein.
  4. Hält eine Kontaktaktivität inbound_received fest.
  5. Plant internal.agent.walker.start ein, was die Strecke bei security_scan anstößt.

Threading

Das Threading (verantwortet vom Thread-Modul, das aus receiveMessage aufgerufen wird) ist eine Kaskade aus drei Strategien:

  1. In-Reply-To-Header → den Thread der referenzierten Nachricht finden.
  2. References-Header → den Thread irgendeiner referenzierten Nachricht finden.
  3. Rückfall: normalisierter Betreff (Re:/Fwd: entfernt, kleingeschrieben) + contactIdentifier.

Ein passender Thread wird wieder geöffnet, falls er geschlossen war; messageCount / lastMessageAt des Threads werden atomar gepflegt.

Schema-Erweiterungen

Die Postfach-/Agenten-Tabellen sind in apps/api/convex/schema/inbox.ts definiert.

inboundMessages

Speichert jede eingehende E-Mail samt ihrem Verarbeitungszustand.

FeldTypBeschreibung
messageIdstringSMTP-Message-ID
from, to, subjectstringEnvelope-Daten
textBody, htmlBodystring?Nachrichteninhalt
inReplyTo, referencesstring?Threading-Header
headers, attachmentMetastring?Rohe Header / Anhang-Metadaten (JSON)
threadIdid → conversationThreadsKonversations-Thread
contactIdid → contactsVerknüpfter Kontakt
processingStatusenumreceived, security_check, quarantined, classifying, drafting, draft_ready, approved, sent, rejected, archived, failed
securityFlagsobject?Ergebnisse der Musterprüfung (Injection, Spam-Score, Konfidenz)
classificationobject?Ergebnis des Klassifikators (Kategorie, Priorität, Stimmung, Absicht, Konfidenz)
draftResponse, draftSubjectstring?Vom Agenten erzeugter Entwurf
confidenceScorenumber?Gesamtkonfidenz für die Routing-Entscheidung
contextTierenum?normal / compacted / emergency
assignedTostring?Menschliche Prüfinstanz (BetterAuth-Benutzer-ID)
errorMessagestring?Fehlerdetails der Strecke
receivedAtnumberZeitstempel des Nachrichteneingangs
processedAtnumber?Zeitstempel, zu dem die Strecke fertig war (gesetzt, sobald die Verarbeitung abgeschlossen ist)

conversationThreads

Fasst zusammengehörige Nachrichten zu Konversationen zusammen.

FeldTypBeschreibung
subject, normalizedSubjectstringAnzeigebetreff / Abgleichsschlüssel
contactIdid → contactsVerknüpfter Kontakt
contactIdentifierstringKanalneutraler Anzeigebezeichner — E-Mail für E-Mail/generisch, Telefonnummer/Handle für SMS/WhatsApp/Chat (in ADR-0032 aus contactEmail umbenannt)
statusenumopen, waiting, resolved, closed
assignedTostring?Zugewiesenes Teammitglied
latestDraftStatusenum?pending / approved / rejected / sent — für schnelles Filtern der Warteschlange
messageCount, firstMessageAt, lastMessageAt, createdAtnumberThread-Metadaten

agentActions

Eine Zeile pro Schrittausführung der Strecke (nicht pro geplanter Aktion).

FeldTypBeschreibung
inboundMessageIdid → inboundMessagesQuellnachricht
actionTypeenumsecurity_scan, context_retrieval, classify, draft, route (die Schrittarten)
statusenumpending, running, completed, failed, skipped
input, outputstring?Eingabe / Ausgabe des Schritts (JSON)
errorMessage, retryCountstring? / numberFehlernachverfolgung
startedAt, completedAt, durationMsnumber?Zeitmessung
modelUsed, tokenUsagestring? / object?LLM-Nutzung (für classify / draft)

agentConfig

Das Singleton für die betriebliche Feinjustierung. Der Hauptschalter ist das Feature-Flag ai.agent (isAgentEnabled in agent/agentPipeline.ts ruft isFeatureEnabled(ctx, 'ai.agent') auf), nicht eine Spalte dieser Tabelle.

FeldTypBeschreibung
isAutoReplyEnabledbooleanAutomatischen Versand ohne menschliche Prüfung erlauben
confidenceThresholdnumber (0–1)Mindestkonfidenz für die automatische Freigabe
toneDescriptionstring?Kommunikationsstil der Organisation
signatureTemplatestring?E-Mail-Signatur für Agentenentwürfe
maxDailyAutoRepliesnumber?Tageslimit für automatische Antworten
dailyAutoReplyCount, dailyAutoReplyResetAtnumber?Fortlaufende Zähler für das Tageslimit
coalesceWindowMsnumber?Debounce-Fenster für das Zusammenfassen. Opt-in: Ein positiver Wert aktiviert das Zusammenfassen; nicht gesetzt bzw. 0 (der Standard) verarbeitet jede Nachricht sofort

Modell-Routing

Verschiedene Schritte der Strecke haben verschiedene Anforderungen. Die Klassifizierung braucht Tempo und strukturierte Ausgabe; die Entwurfserzeugung braucht Qualität und Nuance. Der Anbieter-Resolver (apps/api/convex/lib/llmProvider.ts) routet jede Aufgabe an eine von zwei Modellstufen.

Modellauswahl je Aufgabe

getLLMProvider(task) löst einen OpenAI-kompatiblen Client und die Modell-ID für die Stufe der jeweiligen Aufgabe auf:

// apps/api/convex/lib/llmProvider.ts
export function getLLMProvider(task: LLMTask = 'draft') {
  return getClient()(modelIdForTier(taskTier(task)))
}

Die Auflösung der Stufe steckt in taskTier() (apps/api/convex/lib/llmProvider.ts):

AufgabeStufeGenutzt von
classifyfastKlassifizierungsschritt
extractfastWissensextraktion
guardfastLLM-Injection-Klassifikator der Sicherheitsprüfung
summarizefastZusammenfassung
draftcapableSchritt der Entwurfserzeugung
plancapable(reserviert — der plan-Schritt wurde verworfen)

Konfiguration

Siehe ADR-007 für die Anbieter-Abstraktion und ADR-009 für die Routing-Entscheidung. Die Standardwerte sind gpt-4o-mini (fast) und gpt-4o (capable), gemäß apps/api/convex/lib/llmProvider.ts.

VariableBeschreibungStandard
LLM_MODELRückfallmodell für alle Aufgaben
LLM_MODEL_CAPABLEModell für die Stufe draft (capable)Fällt zurück auf LLM_MODEL, dann gpt-4o
LLM_MODEL_FASTModell für Aufgaben der schnellen Stufe (classify, extract, guard, summarize)Fällt zurück auf LLM_MODEL, dann gpt-4o-mini

Wer selbst hostet und ein einzelnes Ollama-Modell betreibt, kann nur LLM_MODEL setzen — beide Stufen fallen darauf zurück. Organisationen mit GPU-Budget können aufteilen: ein kleines Modell für die Klassifizierung und ein größeres für das Entwerfen.

Quarantäne

Von der Sicherheitsprüfung markierte Nachrichten werden nicht stillschweigend verworfen. Sie werden mit processingStatus: 'quarantined' und einem securityFlags-Objekt gespeichert und erscheinen in einer eigenen Admin-Ansicht (apps/web/app/pages/dashboard/inbox/quarantine.vue), in der ein Mensch sie prüfen und freigeben kann. Die Freigabe einer Nachricht aus der Quarantäne überführt sie zurück nach received (die Quelle release_quarantine des Lebenszyklus), wodurch die Strecke ab security_scan erneut läuft.

Prüf-Warteschlange

Die Prüf-Warteschlange ist die Schnittstelle für den Menschen in der Schleife — eine Sicht auf inboundMessages, gefiltert nach processingStatus = 'draft_ready', kein separates System.

Oberfläche

  • /dashboard/inbox — Thread-Liste mit Filtern.
  • /dashboard/inbox/[threadId] — vollständige Konversation mit dem Agentenentwurf und den Bedienelementen Freigeben / Bearbeiten / Ablehnen.
  • /dashboard/inbox/review (review.vue) — fokussierte Prüf-Warteschlange der Vorgänge, die Aufmerksamkeit brauchen.
  • /dashboard/inbox/quarantine (quarantine.vue) — sicherheitsmarkierte Nachrichten.

Aktionen

  • Freigeben — versendet den Entwurf unverändert; Status → approved, dann sent nach bestätigter Übergabe an den Anbieter.
  • Bearbeiten und freigeben — den Entwurf ändern, dann senden.
  • Ablehnen — Status → rejected.
  • Neu zuweisen — an ein anderes Teammitglied routen.

Den vollständigen Arbeitsablauf finden Sie in den Leitfäden aus Betreibersicht Team-Postfach und KI-Agent & Autonomie.

Abgestufte Autonomie

Die Autonomie- und Betriebsschicht (Tabellen in schema/inbox.ts) ist inzwischen in die aktive Routing-Entscheidung eingebunden, hinter dem Feature-Flag ai.autonomy.

TabelleZweckStatus
autonomyRulesKategoriebezogener autoApproveThreshold + Tageslimit maxDailyAutoActions + Schalter isEnabledAktiv — der route-Schritt liest sie über internal.autonomy.checkPermissionInternal (Stufe 2); Crons setzen die Zähler zurück und adjustThresholds justiert sie wöchentlich
autonomyFeedbackMenschliche Signale aus Freigabe/Ablehnung/Bearbeitung zur SchwellwertjustierungAktiv — Freigeben/Ablehnen/Bearbeiten in inbox/mutations.ts rufen internal.autonomy.recordFeedback auf und speisen damit sowohl den wöchentlichen adjustThresholds-Cron als auch den Breaker rejection_spike
agentMetricsMetriken über ein gleitendes Fensterqueue_depth, processing_latency, error_rate, auto_approve_ratio, rejection_rate und llm_cost werden sämtlich von agentHealth.ts:rollupMetrics befüllt. classification_accuracy wird ebenfalls von rollupMetrics befüllt, als Näherung über die mittlere selbstberichtete Konfidenz — einen Strom menschlich gelabelter Referenzwerte gibt es weiterhin nicht
agentCircuitBreakersAutomatisch auslösende Sicherung bei llm_failure / confidence_degradation / rejection_spikeAlle drei werden bei jedem Rollup mit Hysterese ausgewertet (open → half_open → closed); der route-Schritt verweigert die automatische Freigabe, solange irgendein Breaker offen ist
knowledgeBackfillJobsEinmalige Extraktion historischen WissensAktiv — wird angestoßen, wenn das Flag ai.agent von false auf true kippt (agent/knowledgeBackfill.ts)
Die Autonomie ist jetzt eine geschlossene Feedback-Schleife

Der Routing-Schritt entscheidet in drei Stufen — Sicherheitsschranke der Circuit Breaker, kategoriebezogene autonomyRules (hinter dem Flag ai.autonomy), dann der klassische globale Schwellwert aus agentConfig. Menschliche Entscheidungen zu Freigabe / Ablehnung / Bearbeitung fließen als autonomyFeedback zurück; der wöchentliche adjustThresholds-Cron nutzt sie, um kategoriebezogene Schwellwerte zu straffen oder zu lockern, und das Health-Rollup nutzt sie für den Breaker rejection_spike. Ist ai.autonomy aus, greift allein die klassische globale Steuerung über agentConfig (Schalter für automatische Antworten, Konfidenzschwelle, Tageslimit).

Zusammenfassen von Nachrichten

E-Mail-Threads treffen oft in Schüben ein — eine CC-Kette mit drei Antworten in 30 Sekunden. Das Zusammenfassen (Coalescing) faltet einen solchen Schub in einen einzigen Streckenlauf auf der jüngsten Nachricht zusammen, statt die Strecke je Nachricht auszuführen.

Zusammenfassen — ausgeliefert (Opt-in)

apps/api/convex/agent/coalescing.ts ist implementiert. shouldCoalesce ist ein Debounce: Es storniert einen processCoalescedBatch-Job pro Thread und plant ihn coalesceWindowMs in die Zukunft neu ein (eine coalesceBatches-Zeile pro Thread), sodass der Stapel erst dann feuert, wenn der Thread ein volles Fenster lang ruhig bleibt. processCoalescedBatch wählt anschließend die neueste noch mit received markierte Nachricht als Anführerin, verdrängt die älteren (archiviert mit Grund coalesced, deren Inhalt den Agenten weiterhin über den Thread-Verlauf erreicht) und führt die Strecke einmal für die Anführerin aus.

Es ist Opt-in: Das Zusammenfassen ist nur aktiv, wenn agentConfig.coalesceWindowMs einen positiven Wert hat. Nicht gesetzt (der Standard) bedeutet, dass jede Nachricht sofort verarbeitet wird, ohne zusätzliche Latenz.

Verzweigung bei mehreren Absichten (Zukunft)

Ein künftiger Entwurf lässt eine Nachricht mit mehreren Absichten (etwa „prüfen Sie meinen Rechnungsstatus, und wir hätten gern einen Dark Mode“) in parallele Worker-Zweige verzweigen, von denen jeder seinen eigenen Entwurf erzeugt, wobei die Prüf-Warteschlange beide verknüpft mit derselben eingehenden Nachricht anzeigt.

Nicht im Code

Der Walker führt eine einzelne lineare Schrittkette aus, und classify erzeugt ein einziges Classification-Objekt — heute gibt es keinen Mechanismus für Verzweigung / Worker A / Worker B. Zur Richtung der Prozessarchitektur siehe ADR-008.

Verwandt