chore(i18n): refresh de translations
This commit is contained in:
parent
9bb381e0f4
commit
e1734433e0
@ -1,36 +1,36 @@
|
||||
---
|
||||
read_when:
|
||||
- Sie möchten verstehen, wofür Active Memory gedacht ist
|
||||
- Sie möchten Active Memory für einen Konversationsagenten aktivieren
|
||||
- Sie möchten das Verhalten von Active Memory anpassen, ohne es überall zu aktivieren
|
||||
- Sie möchten verstehen, wofür Active Memory gedacht ist.
|
||||
- Sie möchten Active Memory für einen Konversationsagenten aktivieren.
|
||||
- Sie möchten das Verhalten von Active Memory anpassen, ohne es überall zu aktivieren.
|
||||
summary: Ein Plugin-eigener blockierender Speicher-Sub-Agent, der relevante Erinnerungen in interaktive Chat-Sitzungen einspeist
|
||||
title: Active Memory
|
||||
x-i18n:
|
||||
generated_at: "2026-04-14T02:08:44Z"
|
||||
generated_at: "2026-04-16T06:22:27Z"
|
||||
model: gpt-5.4
|
||||
provider: openai
|
||||
source_hash: b151e9eded7fc5c37e00da72d95b24c1dc94be22e855c8875f850538392b0637
|
||||
source_hash: ab36c5fea1578348cc2258ea3b344cc7bdc814f337d659cdb790512b3ea45473
|
||||
source_path: concepts/active-memory.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
# Active Memory
|
||||
|
||||
Active Memory ist ein optionaler Plugin-eigener blockierender Speicher-Sub-Agent, der
|
||||
Active Memory ist ein optionaler, Plugin-eigener blockierender Speicher-Sub-Agent, der
|
||||
vor der Hauptantwort für geeignete Konversationssitzungen ausgeführt wird.
|
||||
|
||||
Er existiert, weil die meisten Speichersysteme leistungsfähig, aber reaktiv sind. Sie verlassen sich darauf,
|
||||
dass der Haupt-Agent entscheidet, wann der Speicher durchsucht werden soll, oder darauf, dass der Benutzer Dinge sagt
|
||||
wie „merke dir das“ oder „durchsuche den Speicher“. Bis dahin ist der Moment, in dem der Speicher
|
||||
die Antwort natürlich hätte wirken lassen, bereits verstrichen.
|
||||
dass der Hauptagent entscheidet, wann der Speicher durchsucht werden soll, oder darauf, dass der Benutzer Dinge sagt
|
||||
wie „Merke dir das“ oder „Durchsuche den Speicher“. Zu diesem Zeitpunkt ist der Moment, in dem der Speicher
|
||||
die Antwort natürlich hätte wirken lassen, bereits vorbei.
|
||||
|
||||
Active Memory gibt dem System eine begrenzte Gelegenheit, relevanten Speicherinhalt anzuzeigen,
|
||||
bevor die Hauptantwort generiert wird.
|
||||
|
||||
## Fügen Sie dies in Ihren Agenten ein
|
||||
|
||||
Fügen Sie dies in Ihren Agenten ein, wenn Sie Active Memory mit einer
|
||||
eigenständigen, standardmäßig sicheren Konfiguration aktivieren möchten:
|
||||
Fügen Sie dies in Ihren Agenten ein, wenn Sie möchten, dass er Active Memory mit einer
|
||||
eigenständigen Konfiguration mit sicheren Standardwerten aktiviert:
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -56,9 +56,9 @@ eigenständigen, standardmäßig sicheren Konfiguration aktivieren möchten:
|
||||
}
|
||||
```
|
||||
|
||||
Dadurch wird das Plugin für den Agenten `main` aktiviert, standardmäßig auf Sitzungen im Stil von Direktnachrichten
|
||||
beschränkt, lässt es zuerst das aktuelle Sitzungsmodell erben und verwendet
|
||||
das konfigurierte Fallback-Modell nur dann, wenn kein explizites oder geerbtes Modell verfügbar ist.
|
||||
Dadurch wird das Plugin für den Agenten `main` aktiviert, standardmäßig auf Sitzungen
|
||||
im Stil von Direktnachrichten beschränkt, es kann zunächst das aktuelle Sitzungsmodell übernehmen und
|
||||
verwendet das konfigurierte Fallback-Modell nur, wenn kein explizites oder übernommenes Modell verfügbar ist.
|
||||
|
||||
Starten Sie danach das Gateway neu:
|
||||
|
||||
@ -66,7 +66,7 @@ Starten Sie danach das Gateway neu:
|
||||
openclaw gateway
|
||||
```
|
||||
|
||||
So prüfen Sie es live in einer Konversation:
|
||||
Um es live in einer Unterhaltung zu prüfen:
|
||||
|
||||
```text
|
||||
/verbose on
|
||||
@ -78,8 +78,8 @@ So prüfen Sie es live in einer Konversation:
|
||||
Die sicherste Einrichtung ist:
|
||||
|
||||
1. das Plugin aktivieren
|
||||
2. einen Konversationsagenten festlegen
|
||||
3. die Protokollierung nur während der Feinabstimmung aktiviert lassen
|
||||
2. einen Konversationsagenten als Ziel festlegen
|
||||
3. Logging nur während der Anpassung aktiviert lassen
|
||||
|
||||
Beginnen Sie mit Folgendem in `openclaw.json`:
|
||||
|
||||
@ -116,17 +116,102 @@ Das bedeutet:
|
||||
|
||||
- `plugins.entries.active-memory.enabled: true` aktiviert das Plugin
|
||||
- `config.agents: ["main"]` aktiviert Active Memory nur für den Agenten `main`
|
||||
- `config.allowedChatTypes: ["direct"]` sorgt standardmäßig dafür, dass Active Memory nur für Sitzungen im Stil von Direktnachrichten aktiv ist
|
||||
- wenn `config.model` nicht gesetzt ist, erbt Active Memory zuerst das aktuelle Sitzungsmodell
|
||||
- `config.modelFallback` stellt optional Ihren eigenen Fallback-Anbieter bzw. Ihr eigenes Fallback-Modell für die Erinnerung bereit
|
||||
- `config.allowedChatTypes: ["direct"]` hält Active Memory standardmäßig nur für Sitzungen im Stil von Direktnachrichten aktiv
|
||||
- wenn `config.model` nicht gesetzt ist, übernimmt Active Memory zunächst das aktuelle Sitzungsmodell
|
||||
- `config.modelFallback` stellt optional Ihr eigenes Fallback-Anbieter-/Modell für den Abruf bereit
|
||||
- `config.promptStyle: "balanced"` verwendet den allgemeinen Standard-Prompt-Stil für den Modus `recent`
|
||||
- Active Memory wird weiterhin nur für geeignete interaktive persistente Chat-Sitzungen ausgeführt
|
||||
- Active Memory wird weiterhin nur in geeigneten interaktiven persistenten Chat-Sitzungen ausgeführt
|
||||
|
||||
## So sehen Sie es
|
||||
## Empfehlungen zur Geschwindigkeit
|
||||
|
||||
Active Memory injiziert ein verborgenes, nicht vertrauenswürdiges Prompt-Präfix für das Modell. Es
|
||||
Die einfachste Einrichtung besteht darin, `config.model` nicht zu setzen und Active Memory
|
||||
dasselbe Modell verwenden zu lassen, das Sie bereits für normale Antworten verwenden. Das ist der sicherste Standard,
|
||||
weil dabei Ihre vorhandenen Anbieter-, Authentifizierungs- und Modellpräferenzen übernommen werden.
|
||||
|
||||
Wenn Sie möchten, dass sich Active Memory schneller anfühlt, verwenden Sie ein dediziertes Inferenzmodell,
|
||||
anstatt das Haupt-Chat-Modell zu übernehmen.
|
||||
|
||||
Beispiel für eine Einrichtung mit schnellem Anbieter:
|
||||
|
||||
```json5
|
||||
models: {
|
||||
providers: {
|
||||
cerebras: {
|
||||
baseUrl: "https://api.cerebras.ai/v1",
|
||||
apiKey: "${CEREBRAS_API_KEY}",
|
||||
api: "openai-completions",
|
||||
models: [{ id: "gpt-oss-120b", name: "GPT OSS 120B (Cerebras)" }],
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
entries: {
|
||||
"active-memory": {
|
||||
enabled: true,
|
||||
config: {
|
||||
model: "cerebras/gpt-oss-120b",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Schnelle Modelloptionen, die in Betracht gezogen werden sollten:
|
||||
|
||||
- `cerebras/gpt-oss-120b` für ein schnelles dediziertes Abrufmodell mit einer schmalen Tool-Oberfläche
|
||||
- Ihr normales Sitzungsmodell, indem Sie `config.model` nicht setzen
|
||||
- ein Fallback-Modell mit geringer Latenz wie `google/gemini-3-flash`, wenn Sie ein separates Abrufmodell möchten, ohne Ihr primäres Chat-Modell zu ändern
|
||||
|
||||
Warum Cerebras eine starke geschwindigkeitsorientierte Option für Active Memory ist:
|
||||
|
||||
- die Tool-Oberfläche von Active Memory ist schmal: Es ruft nur `memory_search` und `memory_get` auf
|
||||
- die Qualität des Abrufs ist wichtig, aber die Latenz ist wichtiger als beim Hauptantwortpfad
|
||||
- ein dedizierter schneller Anbieter vermeidet es, die Latenz des Speicherabrufs an Ihren primären Chat-Anbieter zu koppeln
|
||||
|
||||
Wenn Sie kein separates geschwindigkeitsoptimiertes Modell möchten, lassen Sie `config.model` ungesetzt
|
||||
und lassen Sie Active Memory das aktuelle Sitzungsmodell übernehmen.
|
||||
|
||||
### Cerebras-Einrichtung
|
||||
|
||||
Fügen Sie einen Anbietereintrag wie diesen hinzu:
|
||||
|
||||
```json5
|
||||
models: {
|
||||
providers: {
|
||||
cerebras: {
|
||||
baseUrl: "https://api.cerebras.ai/v1",
|
||||
apiKey: "${CEREBRAS_API_KEY}",
|
||||
api: "openai-completions",
|
||||
models: [{ id: "gpt-oss-120b", name: "GPT OSS 120B (Cerebras)" }],
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Richten Sie Active Memory dann darauf aus:
|
||||
|
||||
```json5
|
||||
plugins: {
|
||||
entries: {
|
||||
"active-memory": {
|
||||
enabled: true,
|
||||
config: {
|
||||
model: "cerebras/gpt-oss-120b",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Hinweis:
|
||||
|
||||
- stellen Sie sicher, dass der Cerebras-API-Schlüssel tatsächlich Modellzugriff für das von Ihnen gewählte Modell hat, da die Sichtbarkeit von `/v1/models` allein keinen Zugriff auf `chat/completions` garantiert
|
||||
|
||||
## Wie man es sieht
|
||||
|
||||
Active Memory fügt für das Modell ein verborgenes nicht vertrauenswürdiges Prompt-Präfix ein. Es
|
||||
zeigt keine rohen `<active_memory_plugin>...</active_memory_plugin>`-Tags in der
|
||||
normalen, für den Client sichtbaren Antwort an.
|
||||
normalen für den Client sichtbaren Antwort an.
|
||||
|
||||
## Sitzungsumschaltung
|
||||
|
||||
@ -139,8 +224,8 @@ aktuelle Chat-Sitzung pausieren oder fortsetzen möchten, ohne die Konfiguration
|
||||
/active-memory on
|
||||
```
|
||||
|
||||
Dies gilt nur für die Sitzung. Es ändert nicht
|
||||
`plugins.entries.active-memory.enabled`, die Agentenauswahl oder andere globale
|
||||
Dies gilt für die Sitzung. Es ändert nicht
|
||||
`plugins.entries.active-memory.enabled`, die Agentenzuordnung oder andere globale
|
||||
Konfigurationen.
|
||||
|
||||
Wenn der Befehl in die Konfiguration schreiben und Active Memory für
|
||||
@ -153,11 +238,11 @@ alle Sitzungen pausieren oder fortsetzen soll, verwenden Sie die explizite globa
|
||||
```
|
||||
|
||||
Die globale Form schreibt `plugins.entries.active-memory.config.enabled`. Dabei bleibt
|
||||
`plugins.entries.active-memory.enabled` aktiviert, damit der Befehl verfügbar bleibt, um
|
||||
Active Memory später wieder zu aktivieren.
|
||||
`plugins.entries.active-memory.enabled` aktiviert, sodass der Befehl verfügbar bleibt, um
|
||||
Active Memory später wieder einzuschalten.
|
||||
|
||||
Wenn Sie sehen möchten, was Active Memory in einer Live-Sitzung macht, aktivieren Sie die
|
||||
Sitzungsumschaltungen, die der gewünschten Ausgabe entsprechen:
|
||||
Sitzungsumschalter, die zu der gewünschten Ausgabe passen:
|
||||
|
||||
```text
|
||||
/verbose on
|
||||
@ -170,12 +255,12 @@ Wenn diese aktiviert sind, kann OpenClaw Folgendes anzeigen:
|
||||
- eine lesbare Debug-Zusammenfassung wie `Active Memory Debug: Lemon pepper wings with blue cheese.`, wenn `/trace on`
|
||||
|
||||
Diese Zeilen stammen aus demselben Active-Memory-Durchlauf, der das verborgene
|
||||
Prompt-Präfix speist, sind aber für Menschen formatiert, anstatt rohe Prompt-Markup-Strukturen offenzulegen.
|
||||
Sie werden als diagnostische Folgenachricht nach der normalen
|
||||
Assistentenantwort gesendet, sodass Kanal-Clients wie Telegram keine separate
|
||||
Diagnoseblase vor der Antwort anzeigen.
|
||||
Prompt-Präfix speist, sind aber für Menschen formatiert, anstatt rohe Prompt-
|
||||
Auszeichnung anzuzeigen. Sie werden als Diagnose-Folgenachricht nach der normalen
|
||||
Assistentenantwort gesendet, damit Kanal-Clients wie Telegram keine separate
|
||||
Diagnose-Sprechblase vor der Antwort aufblinken lassen.
|
||||
|
||||
Wenn Sie zusätzlich `/trace raw` aktivieren, zeigt der nachverfolgte Block `Model Input (User Role)`
|
||||
Wenn Sie zusätzlich `/trace raw` aktivieren, zeigt der verfolgte Block `Model Input (User Role)`
|
||||
das verborgene Active-Memory-Präfix wie folgt an:
|
||||
|
||||
```text
|
||||
@ -207,16 +292,16 @@ Erwartete sichtbare Antwortform:
|
||||
|
||||
## Wann es ausgeführt wird
|
||||
|
||||
Active Memory verwendet zwei Schranken:
|
||||
Active Memory verwendet zwei Gate-Prüfungen:
|
||||
|
||||
1. **Konfigurations-Opt-in**
|
||||
Das Plugin muss aktiviert sein, und die aktuelle Agenten-ID muss in
|
||||
`plugins.entries.active-memory.config.agents` enthalten sein.
|
||||
2. **Strenge Laufzeit-Eignung**
|
||||
Selbst wenn es aktiviert und als Ziel festgelegt ist, wird Active Memory nur für geeignete
|
||||
2. **Strikte Laufzeitberechtigung**
|
||||
Auch wenn es aktiviert ist und als Ziel festgelegt wurde, wird Active Memory nur für geeignete
|
||||
interaktive persistente Chat-Sitzungen ausgeführt.
|
||||
|
||||
Die tatsächliche Regel ist:
|
||||
Die tatsächliche Regel lautet:
|
||||
|
||||
```text
|
||||
plugin enabled
|
||||
@ -234,8 +319,8 @@ Wenn eine dieser Bedingungen fehlschlägt, wird Active Memory nicht ausgeführt.
|
||||
|
||||
## Sitzungstypen
|
||||
|
||||
`config.allowedChatTypes` steuert, welche Arten von Konversationen Active
|
||||
Memory überhaupt ausführen dürfen.
|
||||
`config.allowedChatTypes` steuert, in welchen Arten von Unterhaltungen Active
|
||||
Memory überhaupt ausgeführt werden darf.
|
||||
|
||||
Der Standardwert ist:
|
||||
|
||||
@ -243,8 +328,8 @@ Der Standardwert ist:
|
||||
allowedChatTypes: ["direct"]
|
||||
```
|
||||
|
||||
Das bedeutet, dass Active Memory standardmäßig in Sitzungen im Stil von Direktnachrichten ausgeführt wird,
|
||||
aber nicht in Gruppen- oder Kanalsitzungen, sofern Sie diese nicht ausdrücklich aktivieren.
|
||||
Das bedeutet, dass Active Memory standardmäßig in Sitzungen im Stil von Direktnachrichten ausgeführt wird, aber
|
||||
nicht in Gruppen- oder Kanalsitzungen, es sei denn, Sie aktivieren diese ausdrücklich.
|
||||
|
||||
Beispiele:
|
||||
|
||||
@ -262,23 +347,23 @@ allowedChatTypes: ["direct", "group", "channel"]
|
||||
|
||||
## Wo es ausgeführt wird
|
||||
|
||||
Active Memory ist eine Funktion zur Anreicherung von Konversationen, keine
|
||||
plattformweite Inferenzfunktion.
|
||||
Active Memory ist eine Funktion zur Anreicherung von Konversationen, keine plattformweite
|
||||
Inferenzfunktion.
|
||||
|
||||
| Oberfläche | Führt Active Memory aus? |
|
||||
| ------------------------------------------------------------------- | ------------------------------------------------------- |
|
||||
| Control UI / persistente Web-Chat-Sitzungen | Ja, wenn das Plugin aktiviert ist und der Agent ausgewählt ist |
|
||||
| Andere interaktive Kanal-Sitzungen auf demselben persistenten Chat-Pfad | Ja, wenn das Plugin aktiviert ist und der Agent ausgewählt ist |
|
||||
| Headless-Einmalläufe | Nein |
|
||||
| Heartbeat-/Hintergrundläufe | Nein |
|
||||
| Generische interne `agent-command`-Pfade | Nein |
|
||||
| Sub-Agent-/interne Hilfsausführung | Nein |
|
||||
| Surface | Active Memory wird ausgeführt? |
|
||||
| ------------------------------------------------------------------- | -------------------------------------------------------- |
|
||||
| Persistente Sitzungen in der Control UI / im Web-Chat | Ja, wenn das Plugin aktiviert ist und der Agent als Ziel festgelegt ist |
|
||||
| Andere interaktive Kanalsitzungen auf demselben persistenten Chat-Pfad | Ja, wenn das Plugin aktiviert ist und der Agent als Ziel festgelegt ist |
|
||||
| Headless-Einmalläufe | Nein |
|
||||
| Heartbeat-/Hintergrundläufe | Nein |
|
||||
| Generische interne `agent-command`-Pfade | Nein |
|
||||
| Ausführung von Sub-Agenten/internen Hilfen | Nein |
|
||||
|
||||
## Warum Sie es verwenden sollten
|
||||
## Warum es verwenden
|
||||
|
||||
Verwenden Sie Active Memory, wenn:
|
||||
|
||||
- die Sitzung persistent und benutzerseitig ist
|
||||
- die Sitzung persistent und benutzergerichtet ist
|
||||
- der Agent über sinnvollen Langzeitspeicher verfügt, der durchsucht werden kann
|
||||
- Kontinuität und Personalisierung wichtiger sind als reine Prompt-Deterministik
|
||||
|
||||
@ -286,16 +371,16 @@ Es funktioniert besonders gut für:
|
||||
|
||||
- stabile Präferenzen
|
||||
- wiederkehrende Gewohnheiten
|
||||
- langfristigen Benutzerkontext, der natürlich auftauchen sollte
|
||||
- langfristigen Benutzerkontext, der natürlich angezeigt werden soll
|
||||
|
||||
Es ist ungeeignet für:
|
||||
|
||||
- Automatisierung
|
||||
- interne Worker
|
||||
- einmalige API-Aufgaben
|
||||
- Orte, an denen verborgene Personalisierung überraschend wäre
|
||||
- Stellen, an denen verborgene Personalisierung überraschend wäre
|
||||
|
||||
## Funktionsweise
|
||||
## Wie es funktioniert
|
||||
|
||||
Die Laufzeitform ist:
|
||||
|
||||
@ -317,7 +402,7 @@ Wenn die Verbindung schwach ist, sollte er `NONE` zurückgeben.
|
||||
|
||||
## Abfragemodi
|
||||
|
||||
`config.queryMode` steuert, wie viel der Konversation der blockierende Speicher-Sub-Agent sieht.
|
||||
`config.queryMode` steuert, wie viel Unterhaltung der blockierende Speicher-Sub-Agent sieht.
|
||||
|
||||
## Prompt-Stile
|
||||
|
||||
@ -327,9 +412,9 @@ wenn er entscheidet, ob Speicherinhalt zurückgegeben werden soll.
|
||||
Verfügbare Stile:
|
||||
|
||||
- `balanced`: allgemeiner Standard für den Modus `recent`
|
||||
- `strict`: am wenigsten bereitwillig; am besten geeignet, wenn Sie sehr wenig Übertragung aus nahem Kontext möchten
|
||||
- `contextual`: am freundlichsten für Kontinuität; am besten geeignet, wenn der Konversationsverlauf wichtiger sein soll
|
||||
- `recall-heavy`: eher bereit, Speicher auch bei schwächeren, aber noch plausiblen Übereinstimmungen anzuzeigen
|
||||
- `strict`: am wenigsten bereitwillig; am besten, wenn Sie sehr wenig Übergreifen aus nahem Kontext möchten
|
||||
- `contextual`: am stärksten auf Kontinuität ausgerichtet; am besten, wenn der Unterhaltungsverlauf stärker ins Gewicht fallen soll
|
||||
- `recall-heavy`: eher bereit, Speicherinhalt auch bei schwächeren, aber dennoch plausiblen Übereinstimmungen anzuzeigen
|
||||
- `precision-heavy`: bevorzugt aggressiv `NONE`, sofern die Übereinstimmung nicht offensichtlich ist
|
||||
- `preference-only`: optimiert für Favoriten, Gewohnheiten, Routinen, Geschmack und wiederkehrende persönliche Fakten
|
||||
|
||||
@ -349,7 +434,7 @@ Beispiel:
|
||||
promptStyle: "preference-only"
|
||||
```
|
||||
|
||||
## Richtlinie für Modell-Fallbacks
|
||||
## Modell-Fallback-Richtlinie
|
||||
|
||||
Wenn `config.model` nicht gesetzt ist, versucht Active Memory, ein Modell in dieser Reihenfolge aufzulösen:
|
||||
|
||||
@ -368,52 +453,52 @@ Optionales benutzerdefiniertes Fallback:
|
||||
modelFallback: "google/gemini-3-flash"
|
||||
```
|
||||
|
||||
Wenn kein explizites, geerbtes oder konfiguriertes Fallback-Modell aufgelöst werden kann, überspringt Active Memory
|
||||
die Erinnerung für diesen Zug.
|
||||
Wenn kein explizites, übernommenes oder konfiguriertes Fallback-Modell aufgelöst werden kann, überspringt Active Memory
|
||||
den Abruf für diesen Zug.
|
||||
|
||||
`config.modelFallbackPolicy` wird nur noch als veraltetes Kompatibilitätsfeld
|
||||
für ältere Konfigurationen beibehalten. Es ändert das Laufzeitverhalten nicht mehr.
|
||||
für ältere Konfigurationen beibehalten. Es verändert das Laufzeitverhalten nicht mehr.
|
||||
|
||||
## Erweiterte Ausweichmöglichkeiten
|
||||
## Erweiterte Escape-Hatches
|
||||
|
||||
Diese Optionen sind absichtlich nicht Teil der empfohlenen Einrichtung.
|
||||
|
||||
`config.thinking` kann die Thinking-Stufe des blockierenden Speicher-Sub-Agenten überschreiben:
|
||||
`config.thinking` kann die Denkstufe des blockierenden Speicher-Sub-Agenten überschreiben:
|
||||
|
||||
```json5
|
||||
thinking: "medium"
|
||||
```
|
||||
|
||||
Standardwert:
|
||||
Standard:
|
||||
|
||||
```json5
|
||||
thinking: "off"
|
||||
```
|
||||
|
||||
Aktivieren Sie dies nicht standardmäßig. Active Memory läuft im Antwortpfad, daher erhöht zusätzliche
|
||||
Thinking-Zeit direkt die für Benutzer sichtbare Latenz.
|
||||
Denkzeit direkt die für Benutzer sichtbare Latenz.
|
||||
|
||||
`config.promptAppend` fügt zusätzliche Operator-Anweisungen nach dem standardmäßigen Active-
|
||||
Memory-Prompt und vor dem Konversationskontext hinzu:
|
||||
`config.promptAppend` fügt nach dem Standard-Prompt von Active Memory und vor dem Unterhaltungskontext
|
||||
zusätzliche Operator-Anweisungen hinzu:
|
||||
|
||||
```json5
|
||||
promptAppend: "Prefer stable long-term preferences over one-off events."
|
||||
promptAppend: "Bevorzuge stabile langfristige Präferenzen gegenüber einmaligen Ereignissen."
|
||||
```
|
||||
|
||||
`config.promptOverride` ersetzt den standardmäßigen Active-Memory-Prompt. OpenClaw
|
||||
hängt danach weiterhin den Konversationskontext an:
|
||||
`config.promptOverride` ersetzt den Standard-Prompt von Active Memory. OpenClaw
|
||||
hängt den Unterhaltungskontext danach weiterhin an:
|
||||
|
||||
```json5
|
||||
promptOverride: "You are a memory search agent. Return NONE or one compact user fact."
|
||||
```
|
||||
|
||||
Eine Prompt-Anpassung wird nicht empfohlen, es sei denn, Sie testen bewusst einen
|
||||
anderen Erinnerungsvertrag. Der Standard-Prompt ist darauf abgestimmt, entweder `NONE`
|
||||
oder kompakten Benutzerfakt-Kontext für das Hauptmodell zurückzugeben.
|
||||
Eine Anpassung des Prompts wird nicht empfohlen, es sei denn, Sie testen bewusst einen
|
||||
anderen Abrufvertrag. Der Standard-Prompt ist darauf abgestimmt, entweder `NONE`
|
||||
oder kompakten Benutzerfakten-Kontext für das Hauptmodell zurückzugeben.
|
||||
|
||||
### `message`
|
||||
|
||||
Es wird nur die neueste Benutzernachricht gesendet.
|
||||
Es wird nur die letzte Benutzernachricht gesendet.
|
||||
|
||||
```text
|
||||
Latest user message only
|
||||
@ -422,16 +507,16 @@ Latest user message only
|
||||
Verwenden Sie dies, wenn:
|
||||
|
||||
- Sie das schnellste Verhalten möchten
|
||||
- Sie die stärkste Ausrichtung auf die Erinnerung stabiler Präferenzen möchten
|
||||
- Folgezüge keinen Konversationskontext benötigen
|
||||
- Sie die stärkste Ausrichtung auf den Abruf stabiler Präferenzen möchten
|
||||
- Folgezüge keinen Unterhaltungskontext benötigen
|
||||
|
||||
Empfohlener Timeout:
|
||||
Empfohlenes Timeout:
|
||||
|
||||
- beginnen Sie bei etwa `3000` bis `5000` ms
|
||||
|
||||
### `recent`
|
||||
|
||||
Die neueste Benutzernachricht plus ein kleiner aktueller Konversationsverlauf werden gesendet.
|
||||
Die letzte Benutzernachricht plus ein kleiner aktueller Unterhaltungsausschnitt wird gesendet.
|
||||
|
||||
```text
|
||||
Recent conversation tail:
|
||||
@ -445,16 +530,16 @@ Latest user message:
|
||||
|
||||
Verwenden Sie dies, wenn:
|
||||
|
||||
- Sie ein besseres Gleichgewicht zwischen Geschwindigkeit und konversationeller Verankerung möchten
|
||||
- Rückfragen häufig von den letzten wenigen Zügen abhängen
|
||||
- Sie ein besseres Gleichgewicht zwischen Geschwindigkeit und Gesprächsverankerung möchten
|
||||
- Folgefragen oft von den letzten wenigen Zügen abhängen
|
||||
|
||||
Empfohlener Timeout:
|
||||
Empfohlenes Timeout:
|
||||
|
||||
- beginnen Sie bei etwa `15000` ms
|
||||
|
||||
### `full`
|
||||
|
||||
Die vollständige Konversation wird an den blockierenden Speicher-Sub-Agenten gesendet.
|
||||
Die vollständige Unterhaltung wird an den blockierenden Speicher-Sub-Agenten gesendet.
|
||||
|
||||
```text
|
||||
Full conversation context:
|
||||
@ -466,15 +551,15 @@ user: ...
|
||||
|
||||
Verwenden Sie dies, wenn:
|
||||
|
||||
- die bestmögliche Erinnerungsqualität wichtiger ist als Latenz
|
||||
- die Konversation wichtige Einrichtungsschritte weit oben im Verlauf enthält
|
||||
- die bestmögliche Abrufqualität wichtiger ist als die Latenz
|
||||
- die Unterhaltung wichtige Vorbereitung weit oben im Thread enthält
|
||||
|
||||
Empfohlener Timeout:
|
||||
Empfohlenes Timeout:
|
||||
|
||||
- erhöhen Sie ihn im Vergleich zu `message` oder `recent` deutlich
|
||||
- beginnen Sie bei etwa `15000` ms oder höher, je nach Größe des Threads
|
||||
- erhöhen Sie es deutlich im Vergleich zu `message` oder `recent`
|
||||
- beginnen Sie bei etwa `15000` ms oder höher, abhängig von der Thread-Größe
|
||||
|
||||
Im Allgemeinen sollte der Timeout mit der Kontextgröße steigen:
|
||||
Im Allgemeinen sollte das Timeout mit der Kontextgröße zunehmen:
|
||||
|
||||
```text
|
||||
message < recent < full
|
||||
@ -482,9 +567,8 @@ message < recent < full
|
||||
|
||||
## Transkriptpersistenz
|
||||
|
||||
Läufe des blockierenden Speicher-Sub-Agenten von Active Memory erzeugen während des
|
||||
Aufrufs des blockierenden Speicher-Sub-Agenten ein echtes `session.jsonl`-
|
||||
Transkript.
|
||||
Durchläufe des blockierenden Speicher-Sub-Agenten von Active Memory erzeugen während des Aufrufs des blockierenden Speicher-Sub-Agenten
|
||||
ein echtes `session.jsonl`-Transkript.
|
||||
|
||||
Standardmäßig ist dieses Transkript temporär:
|
||||
|
||||
@ -492,8 +576,8 @@ Standardmäßig ist dieses Transkript temporär:
|
||||
- es wird nur für den Lauf des blockierenden Speicher-Sub-Agenten verwendet
|
||||
- es wird sofort gelöscht, nachdem der Lauf abgeschlossen ist
|
||||
|
||||
Wenn Sie diese Transkripte des blockierenden Speicher-Sub-Agenten zur Fehlerdiagnose oder
|
||||
Inspektion auf dem Datenträger behalten möchten, aktivieren Sie die Persistenz ausdrücklich:
|
||||
Wenn Sie diese Transkripte des blockierenden Speicher-Sub-Agenten zur Fehlersuche oder
|
||||
Prüfung auf der Festplatte behalten möchten, aktivieren Sie die Persistenz ausdrücklich:
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -513,9 +597,9 @@ Inspektion auf dem Datenträger behalten möchten, aktivieren Sie die Persistenz
|
||||
```
|
||||
|
||||
Wenn aktiviert, speichert Active Memory Transkripte in einem separaten Verzeichnis unter dem
|
||||
Sitzungsordner des Ziel-Agenten, nicht im Haupttranskriptpfad der Benutzerkonversation.
|
||||
Sitzungsordner des Zielagenten, nicht im Haupttranskriptpfad der Benutzerunterhaltung.
|
||||
|
||||
Das Standardlayout sieht konzeptionell so aus:
|
||||
Das Standardlayout ist konzeptionell:
|
||||
|
||||
```text
|
||||
agents/<agent>/sessions/active-memory/<blocking-memory-sub-agent-session-id>.jsonl
|
||||
@ -523,10 +607,10 @@ agents/<agent>/sessions/active-memory/<blocking-memory-sub-agent-session-id>.jso
|
||||
|
||||
Sie können das relative Unterverzeichnis mit `config.transcriptDir` ändern.
|
||||
|
||||
Verwenden Sie dies mit Bedacht:
|
||||
Verwenden Sie dies mit Vorsicht:
|
||||
|
||||
- Transkripte des blockierenden Speicher-Sub-Agenten können sich in ausgelasteten Sitzungen schnell ansammeln
|
||||
- der Abfragemodus `full` kann viel Konversationskontext duplizieren
|
||||
- Transkripte des blockierenden Speicher-Sub-Agenten können sich bei ausgelasteten Sitzungen schnell ansammeln
|
||||
- der Abfragemodus `full` kann viel Unterhaltungskontext duplizieren
|
||||
- diese Transkripte enthalten verborgenen Prompt-Kontext und abgerufene Erinnerungen
|
||||
|
||||
## Konfiguration
|
||||
@ -539,32 +623,32 @@ plugins.entries.active-memory
|
||||
|
||||
Die wichtigsten Felder sind:
|
||||
|
||||
| Schlüssel | Typ | Bedeutung |
|
||||
| --------------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
|
||||
| `enabled` | `boolean` | Aktiviert das Plugin selbst |
|
||||
| `config.agents` | `string[]` | Agenten-IDs, die Active Memory verwenden dürfen |
|
||||
| `config.model` | `string` | Optionale Modellreferenz für den blockierenden Speicher-Sub-Agenten; wenn nicht gesetzt, verwendet Active Memory das aktuelle Sitzungsmodell |
|
||||
| `config.queryMode` | `"message" \| "recent" \| "full"` | Steuert, wie viel der Konversation der blockierende Speicher-Sub-Agent sieht |
|
||||
| `config.promptStyle` | `"balanced" \| "strict" \| "contextual" \| "recall-heavy" \| "precision-heavy" \| "preference-only"` | Steuert, wie bereitwillig oder streng der blockierende Speicher-Sub-Agent ist, wenn er entscheidet, ob Speicher zurückgegeben werden soll |
|
||||
| Key | Type | Bedeutung |
|
||||
| --------------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
|
||||
| `enabled` | `boolean` | Aktiviert das Plugin selbst |
|
||||
| `config.agents` | `string[]` | Agent-IDs, die Active Memory verwenden dürfen |
|
||||
| `config.model` | `string` | Optionaler Modellverweis für den blockierenden Speicher-Sub-Agenten; wenn nicht gesetzt, verwendet Active Memory das aktuelle Sitzungsmodell |
|
||||
| `config.queryMode` | `"message" \| "recent" \| "full"` | Steuert, wie viel Unterhaltung der blockierende Speicher-Sub-Agent sieht |
|
||||
| `config.promptStyle` | `"balanced" \| "strict" \| "contextual" \| "recall-heavy" \| "precision-heavy" \| "preference-only"` | Steuert, wie bereitwillig oder streng der blockierende Speicher-Sub-Agent bei der Entscheidung über die Rückgabe von Speicherinhalt ist |
|
||||
| `config.thinking` | `"off" \| "minimal" \| "low" \| "medium" \| "high" \| "xhigh" \| "adaptive"` | Erweiterte Thinking-Überschreibung für den blockierenden Speicher-Sub-Agenten; Standard `off` für Geschwindigkeit |
|
||||
| `config.promptOverride` | `string` | Erweiterter vollständiger Prompt-Ersatz; für die normale Nutzung nicht empfohlen |
|
||||
| `config.promptAppend` | `string` | Erweiterte zusätzliche Anweisungen, die an den Standard- oder überschriebenen Prompt angehängt werden |
|
||||
| `config.timeoutMs` | `number` | Harter Timeout für den blockierenden Speicher-Sub-Agenten |
|
||||
| `config.maxSummaryChars` | `number` | Maximal zulässige Gesamtanzahl von Zeichen in der Active-Memory-Zusammenfassung |
|
||||
| `config.logging` | `boolean` | Gibt während der Feinabstimmung Active-Memory-Protokolle aus |
|
||||
| `config.persistTranscripts` | `boolean` | Behält Transkripte des blockierenden Speicher-Sub-Agenten auf dem Datenträger, statt temporäre Dateien zu löschen |
|
||||
| `config.transcriptDir` | `string` | Relatives Transkriptverzeichnis des blockierenden Speicher-Sub-Agenten unter dem Sitzungsordner des Agenten |
|
||||
| `config.promptOverride` | `string` | Erweiterter vollständiger Prompt-Ersatz; für normale Verwendung nicht empfohlen |
|
||||
| `config.promptAppend` | `string` | Erweiterte zusätzliche Anweisungen, die an den Standard- oder überschriebenen Prompt angehängt werden |
|
||||
| `config.timeoutMs` | `number` | Hartes Timeout für den blockierenden Speicher-Sub-Agenten |
|
||||
| `config.maxSummaryChars` | `number` | Maximal zulässige Gesamtzahl von Zeichen in der Active-Memory-Zusammenfassung |
|
||||
| `config.logging` | `boolean` | Gibt Active-Memory-Logs während der Anpassung aus |
|
||||
| `config.persistTranscripts` | `boolean` | Behält Transkripte des blockierenden Speicher-Sub-Agenten auf der Festplatte, anstatt temporäre Dateien zu löschen |
|
||||
| `config.transcriptDir` | `string` | Relatives Transkriptverzeichnis des blockierenden Speicher-Sub-Agenten unter dem Sitzungsordner des Agenten |
|
||||
|
||||
Nützliche Felder zur Feinabstimmung:
|
||||
Nützliche Felder zur Anpassung:
|
||||
|
||||
| Schlüssel | Typ | Bedeutung |
|
||||
| ----------------------------- | -------- | ------------------------------------------------------------------- |
|
||||
| `config.maxSummaryChars` | `number` | Maximal zulässige Gesamtanzahl von Zeichen in der Active-Memory-Zusammenfassung |
|
||||
| `config.recentUserTurns` | `number` | Frühere Benutzerzüge, die einbezogen werden, wenn `queryMode` `recent` ist |
|
||||
| `config.recentAssistantTurns` | `number` | Frühere Assistentenzüge, die einbezogen werden, wenn `queryMode` `recent` ist |
|
||||
| `config.recentUserChars` | `number` | Maximale Zeichen pro aktuellem Benutzerzug |
|
||||
| `config.recentAssistantChars` | `number` | Maximale Zeichen pro aktuellem Assistentenzug |
|
||||
| `config.cacheTtlMs` | `number` | Cache-Wiederverwendung für wiederholte identische Abfragen |
|
||||
| Key | Type | Bedeutung |
|
||||
| ----------------------------- | -------- | ------------------------------------------------------------ |
|
||||
| `config.maxSummaryChars` | `number` | Maximal zulässige Gesamtzahl von Zeichen in der Active-Memory-Zusammenfassung |
|
||||
| `config.recentUserTurns` | `number` | Vorherige Benutzerzüge, die einbezogen werden, wenn `queryMode` `recent` ist |
|
||||
| `config.recentAssistantTurns` | `number` | Vorherige Assistentenzüge, die einbezogen werden, wenn `queryMode` `recent` ist |
|
||||
| `config.recentUserChars` | `number` | Maximale Zeichenanzahl pro aktuellem Benutzerzug |
|
||||
| `config.recentAssistantChars` | `number` | Maximale Zeichenanzahl pro aktuellem Assistentenzug |
|
||||
| `config.cacheTtlMs` | `number` | Cache-Wiederverwendung für wiederholte identische Abfragen |
|
||||
|
||||
## Empfohlene Einrichtung
|
||||
|
||||
@ -590,8 +674,8 @@ Beginnen Sie mit `recent`.
|
||||
}
|
||||
```
|
||||
|
||||
Wenn Sie das Live-Verhalten während der Feinabstimmung prüfen möchten, verwenden Sie `/verbose on` für die
|
||||
normale Statuszeile und `/trace on` für die Active-Memory-Debug-Zusammenfassung statt
|
||||
Wenn Sie das Live-Verhalten während der Anpassung prüfen möchten, verwenden Sie `/verbose on` für die
|
||||
normale Statuszeile und `/trace on` für die Active-Memory-Debug-Zusammenfassung, anstatt
|
||||
nach einem separaten Active-Memory-Debug-Befehl zu suchen. In Chat-Kanälen werden diese
|
||||
Diagnosezeilen nach der Hauptantwort des Assistenten gesendet und nicht davor.
|
||||
|
||||
@ -607,76 +691,74 @@ Wenn Active Memory nicht dort angezeigt wird, wo Sie es erwarten:
|
||||
1. Bestätigen Sie, dass das Plugin unter `plugins.entries.active-memory.enabled` aktiviert ist.
|
||||
2. Bestätigen Sie, dass die aktuelle Agenten-ID in `config.agents` aufgeführt ist.
|
||||
3. Bestätigen Sie, dass Sie über eine interaktive persistente Chat-Sitzung testen.
|
||||
4. Aktivieren Sie `config.logging: true` und beobachten Sie die Gateway-Protokolle.
|
||||
5. Verifizieren Sie mit `openclaw memory status --deep`, dass die Speichersuche selbst funktioniert.
|
||||
4. Aktivieren Sie `config.logging: true` und beobachten Sie die Gateway-Logs.
|
||||
5. Überprüfen Sie mit `openclaw memory status --deep`, dass die Speichersuche selbst funktioniert.
|
||||
|
||||
Wenn Speicher-Treffer zu verrauscht sind, verschärfen Sie:
|
||||
Wenn Speicher-Treffer zu ungenau sind, verschärfen Sie:
|
||||
|
||||
- `maxSummaryChars`
|
||||
|
||||
Wenn Active Memory zu langsam ist:
|
||||
|
||||
- `queryMode` reduzieren
|
||||
- `timeoutMs` reduzieren
|
||||
- die Anzahl aktueller Züge reduzieren
|
||||
- die Zeichenobergrenzen pro Zug reduzieren
|
||||
- verringern Sie `queryMode`
|
||||
- verringern Sie `timeoutMs`
|
||||
- reduzieren Sie die Anzahl aktueller Züge
|
||||
- reduzieren Sie die Zeichenobergrenzen pro Zug
|
||||
|
||||
## Häufige Probleme
|
||||
|
||||
### Embedding-Anbieter wurde unerwartet geändert
|
||||
### Embedding-Anbieter hat sich unerwartet geändert
|
||||
|
||||
Active Memory verwendet die normale `memory_search`-Pipeline unter
|
||||
`agents.defaults.memorySearch`. Das bedeutet, dass die Einrichtung des Embedding-Anbieters nur dann
|
||||
erforderlich ist, wenn Ihre `memorySearch`-Einrichtung Embeddings für das gewünschte Verhalten
|
||||
benötigt.
|
||||
erforderlich ist, wenn Ihre `memorySearch`-Einrichtung Embeddings für das gewünschte Verhalten benötigt.
|
||||
|
||||
In der Praxis gilt:
|
||||
|
||||
- eine explizite Anbieter-Einrichtung ist **erforderlich**, wenn Sie einen Anbieter möchten, der nicht
|
||||
automatisch erkannt wird, etwa `ollama`
|
||||
- eine explizite Anbieter-Einrichtung ist **erforderlich**, wenn die automatische Erkennung
|
||||
keinen verwendbaren Embedding-Anbieter für Ihre Umgebung auflösen kann
|
||||
- eine explizite Anbieter-Einrichtung ist **dringend empfohlen**, wenn Sie eine deterministische
|
||||
Anbieterauswahl statt „der erste verfügbare gewinnt“ möchten
|
||||
- eine explizite Anbieter-Einrichtung ist in der Regel **nicht erforderlich**, wenn die automatische Erkennung bereits
|
||||
den gewünschten Anbieter auflöst und dieser in Ihrer Bereitstellung stabil ist
|
||||
- eine explizite Anbietereinrichtung ist **erforderlich**, wenn Sie einen Anbieter möchten, der nicht
|
||||
automatisch erkannt wird, z. B. `ollama`
|
||||
- eine explizite Anbietereinrichtung ist **erforderlich**, wenn die automatische Erkennung
|
||||
keinen verwendbaren Embedding-Anbieter für Ihre Umgebung auflöst
|
||||
- eine explizite Anbietereinrichtung ist **dringend empfohlen**, wenn Sie eine deterministische
|
||||
Anbieterauswahl statt „erstes verfügbares gewinnt“ möchten
|
||||
- eine explizite Anbietereinrichtung ist normalerweise **nicht erforderlich**, wenn die automatische Erkennung bereits
|
||||
den gewünschten Anbieter auflöst und dieser Anbieter in Ihrer Bereitstellung stabil ist
|
||||
|
||||
Wenn `memorySearch.provider` nicht gesetzt ist, erkennt OpenClaw automatisch den ersten verfügbaren
|
||||
Embedding-Anbieter.
|
||||
|
||||
Das kann in realen Bereitstellungen verwirrend sein:
|
||||
|
||||
- ein neu verfügbarer API-Schlüssel kann ändern, welcher Anbieter für die Speichersuche verwendet wird
|
||||
- ein neu verfügbarer API-Schlüssel kann ändern, welchen Anbieter die Speichersuche verwendet
|
||||
- ein Befehl oder eine Diagnoseoberfläche kann den ausgewählten Anbieter anders erscheinen lassen
|
||||
als den Pfad, den Sie tatsächlich bei der Live-Speichersynchronisierung oder beim
|
||||
Bootstrap der Suche verwenden
|
||||
- gehostete Anbieter können mit Kontingent- oder Rate-Limit-Fehlern scheitern, die erst sichtbar werden,
|
||||
wenn Active Memory vor jeder Antwort Erinnerungsabfragen ausführt
|
||||
Such-Bootstrap verwenden
|
||||
- gehostete Anbieter können mit Kontingent- oder Ratenbegrenzungsfehlern scheitern, die erst sichtbar werden,
|
||||
sobald Active Memory vor jeder Antwort Abrufsuchen ausführt
|
||||
|
||||
Active Memory kann auch ohne Embeddings ausgeführt werden, wenn `memory_search` im
|
||||
degradierten rein lexikalischen Modus arbeiten kann, was typischerweise geschieht, wenn kein Embedding-
|
||||
Anbieter aufgelöst werden kann.
|
||||
Active Memory kann auch ohne Embeddings laufen, wenn `memory_search` im degradierten rein lexikalischen Modus
|
||||
arbeiten kann, was typischerweise geschieht, wenn kein Embedding-Anbieter aufgelöst werden kann.
|
||||
|
||||
Gehen Sie nicht davon aus, dass derselbe Fallback bei Laufzeitfehlern des Anbieters gilt, etwa bei ausgeschöpftem Kontingent,
|
||||
Rate-Limits, Netzwerk-/Anbieterfehlern oder fehlenden lokalen/Remote-Modellen, nachdem bereits
|
||||
ein Anbieter ausgewählt wurde.
|
||||
Gehen Sie nicht davon aus, dass derselbe Fallback auch bei Laufzeitfehlern des Anbieters funktioniert, etwa bei
|
||||
Kontingenterschöpfung, Ratenbegrenzungen, Netzwerk-/Anbieterfehlern oder fehlenden lokalen/entfernten
|
||||
Modellen, nachdem bereits ein Anbieter ausgewählt wurde.
|
||||
|
||||
In der Praxis gilt:
|
||||
In der Praxis:
|
||||
|
||||
- wenn kein Embedding-Anbieter aufgelöst werden kann, kann `memory_search` auf
|
||||
rein lexikalen Abruf degradieren
|
||||
- wenn kein Embedding-Anbieter aufgelöst werden kann, kann `memory_search` zu
|
||||
rein lexikalischem Abruf degradieren
|
||||
- wenn ein Embedding-Anbieter aufgelöst wird und dann zur Laufzeit fehlschlägt, garantiert OpenClaw
|
||||
derzeit keinen lexikalen Fallback für diese Anfrage
|
||||
- wenn Sie eine deterministische Anbieterauswahl benötigen, setzen Sie
|
||||
`agents.defaults.memorySearch.provider` fest
|
||||
derzeit keinen lexikalischen Fallback für diese Anfrage
|
||||
- wenn Sie eine deterministische Anbieterauswahl benötigen, pinnen Sie
|
||||
`agents.defaults.memorySearch.provider`
|
||||
- wenn Sie Anbieter-Failover bei Laufzeitfehlern benötigen, konfigurieren Sie
|
||||
`agents.defaults.memorySearch.fallback` explizit
|
||||
|
||||
Wenn Sie auf embedding-gestützten Abruf, multimodale Indizierung oder einen bestimmten
|
||||
lokalen/Remote-Anbieter angewiesen sind, setzen Sie den Anbieter explizit fest, statt sich auf
|
||||
die automatische Erkennung zu verlassen.
|
||||
Wenn Sie auf embeddinggestützten Abruf, multimodale Indizierung oder einen bestimmten
|
||||
lokalen/entfernten Anbieter angewiesen sind, pinnen Sie den Anbieter explizit, anstatt sich auf
|
||||
automatische Erkennung zu verlassen.
|
||||
|
||||
Häufige Beispiele für das Festlegen:
|
||||
Häufige Beispiele für das Pinning:
|
||||
|
||||
OpenAI:
|
||||
|
||||
@ -723,8 +805,8 @@ Ollama:
|
||||
}
|
||||
```
|
||||
|
||||
Wenn Sie Anbieter-Failover bei Laufzeitfehlern wie ausgeschöpftem Kontingent erwarten,
|
||||
reicht das Festlegen eines Anbieters allein nicht aus. Konfigurieren Sie zusätzlich einen expliziten Fallback:
|
||||
Wenn Sie Anbieter-Failover bei Laufzeitfehlern wie Kontingenterschöpfung erwarten,
|
||||
ist das Pinning eines Anbieters allein nicht ausreichend. Konfigurieren Sie zusätzlich einen expliziten Fallback:
|
||||
|
||||
```json5
|
||||
{
|
||||
@ -741,30 +823,30 @@ reicht das Festlegen eines Anbieters allein nicht aus. Konfigurieren Sie zusätz
|
||||
|
||||
### Fehlerbehebung bei Anbieterproblemen
|
||||
|
||||
Wenn Active Memory langsam ist, leer bleibt oder Anbieter unerwartet zu wechseln scheint:
|
||||
Wenn Active Memory langsam ist, leer bleibt oder scheinbar unerwartet den Anbieter wechselt:
|
||||
|
||||
- beobachten Sie die Gateway-Protokolle, während Sie das Problem reproduzieren; suchen Sie nach Zeilen wie
|
||||
- beobachten Sie die Gateway-Logs, während Sie das Problem reproduzieren; achten Sie auf Zeilen wie
|
||||
`active-memory: ... start|done`, `memory sync failed (search-bootstrap)` oder
|
||||
anbieterbezogenen Embedding-Fehlern
|
||||
anbieterspezifische Embedding-Fehler
|
||||
- aktivieren Sie `/trace on`, um die Plugin-eigene Active-Memory-Debug-Zusammenfassung in
|
||||
der Sitzung anzuzeigen
|
||||
- aktivieren Sie `/verbose on`, wenn Sie zusätzlich die normale `🧩 Active Memory: ...`
|
||||
Statuszeile nach jeder Antwort möchten
|
||||
- führen Sie `openclaw memory status --deep` aus, um das aktuelle Backend der Speichersuche
|
||||
und den Zustand des Index zu prüfen
|
||||
- prüfen Sie `agents.defaults.memorySearch.provider` und die zugehörige Authentifizierung/Konfiguration, um sicherzustellen,
|
||||
dass der Anbieter, den Sie erwarten, tatsächlich zur Laufzeit aufgelöst werden kann
|
||||
- wenn Sie `ollama` verwenden, verifizieren Sie, dass das konfigurierte Embedding-Modell installiert ist, zum
|
||||
- aktivieren Sie `/verbose on`, wenn Sie zusätzlich die normale Statuszeile
|
||||
`🧩 Active Memory: ...` nach jeder Antwort sehen möchten
|
||||
- führen Sie `openclaw memory status --deep` aus, um das aktuelle Memory-Search-
|
||||
Backend und den Zustand des Index zu prüfen
|
||||
- prüfen Sie `agents.defaults.memorySearch.provider` und die zugehörige Authentifizierung/Konfiguration, um
|
||||
sicherzustellen, dass der Anbieter, den Sie erwarten, tatsächlich derjenige ist, der zur Laufzeit aufgelöst werden kann
|
||||
- wenn Sie `ollama` verwenden, prüfen Sie, ob das konfigurierte Embedding-Modell installiert ist, zum
|
||||
Beispiel mit `ollama list`
|
||||
|
||||
Beispiel für eine Debugging-Schleife:
|
||||
Beispiel für einen Debugging-Ablauf:
|
||||
|
||||
```text
|
||||
1. Starten Sie das Gateway und beobachten Sie seine Protokolle
|
||||
2. Führen Sie in der Chat-Sitzung /trace on aus
|
||||
3. Senden Sie eine Nachricht, die Active Memory auslösen sollte
|
||||
4. Vergleichen Sie die im Chat sichtbare Debug-Zeile mit den Gateway-Protokollzeilen
|
||||
5. Wenn die Anbieterwahl unklar ist, setzen Sie agents.defaults.memorySearch.provider explizit fest
|
||||
1. Gateway starten und seine Logs beobachten
|
||||
2. In der Chat-Sitzung /trace on ausführen
|
||||
3. Eine Nachricht senden, die Active Memory auslösen sollte
|
||||
4. Die im Chat sichtbare Debug-Zeile mit den Gateway-Logzeilen vergleichen
|
||||
5. Wenn die Anbieterauswahl uneindeutig ist, agents.defaults.memorySearch.provider explizit pinnen
|
||||
```
|
||||
|
||||
Beispiel:
|
||||
@ -796,8 +878,8 @@ Oder, wenn Sie Gemini-Embeddings möchten:
|
||||
}
|
||||
```
|
||||
|
||||
Nach dem Ändern des Anbieters starten Sie das Gateway neu und führen einen frischen Test mit
|
||||
`/trace on` aus, damit die Active-Memory-Debug-Zeile den neuen Embedding-Pfad widerspiegelt.
|
||||
Nachdem Sie den Anbieter geändert haben, starten Sie das Gateway neu und führen Sie mit
|
||||
`/trace on` einen neuen Test aus, damit die Active-Memory-Debug-Zeile den neuen Embedding-Pfad widerspiegelt.
|
||||
|
||||
## Verwandte Seiten
|
||||
|
||||
|
||||
@ -1,32 +1,32 @@
|
||||
---
|
||||
read_when:
|
||||
- Implementieren oder Aktualisieren von Gateway-WS-Clients
|
||||
- Debuggen von Protokollabweichungen oder Verbindungsfehlern
|
||||
- Implementierung oder Aktualisierung von Gateway-WS-Clients
|
||||
- Fehlersuche bei Protokollabweichungen oder Verbindungsfehlern
|
||||
- Erneutes Generieren von Protokollschema/-modellen
|
||||
summary: 'Gateway-WebSocket-Protokoll: Handshake, Frames, Versionierung'
|
||||
title: Gateway-Protokoll
|
||||
x-i18n:
|
||||
generated_at: "2026-04-11T02:44:51Z"
|
||||
generated_at: "2026-04-16T06:22:25Z"
|
||||
model: gpt-5.4
|
||||
provider: openai
|
||||
source_hash: 83c820c46d4803d571c770468fd6782619eaa1dca253e156e8087dec735c127f
|
||||
source_hash: 683e61ebe993a2d739bc34860060b0e3eda36b5c57267a2bcc03d177ec612fb3
|
||||
source_path: gateway/protocol.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
# Gateway-Protokoll (WebSocket)
|
||||
|
||||
Das Gateway-WS-Protokoll ist die **einzige Steuerungsebene + der Knotentransport** für
|
||||
OpenClaw. Alle Clients (CLI, Web-UI, macOS-App, iOS-/Android-Knoten, headless
|
||||
Knoten) verbinden sich über WebSocket und deklarieren ihre **Rolle** + ihren **Scope** zum
|
||||
Zeitpunkt des Handshakes.
|
||||
Das Gateway-WS-Protokoll ist die **einzige Control Plane + der einzige Node-Transport** für
|
||||
OpenClaw. Alle Clients (CLI, Web-UI, macOS-App, iOS-/Android-Nodes, Headless-
|
||||
Nodes) verbinden sich über WebSocket und deklarieren ihre **Rolle** + ihren **Scope** beim
|
||||
Handshake.
|
||||
|
||||
## Transport
|
||||
|
||||
- WebSocket, Text-Frames mit JSON-Payloads.
|
||||
- Der erste Frame **muss** eine `connect`-Anfrage sein.
|
||||
|
||||
## Handshake (connect)
|
||||
## Handshake (`connect`)
|
||||
|
||||
Gateway → Client (Pre-Connect-Challenge):
|
||||
|
||||
@ -80,11 +80,25 @@ Gateway → Client:
|
||||
"type": "res",
|
||||
"id": "…",
|
||||
"ok": true,
|
||||
"payload": { "type": "hello-ok", "protocol": 3, "policy": { "tickIntervalMs": 15000 } }
|
||||
"payload": {
|
||||
"type": "hello-ok",
|
||||
"protocol": 3,
|
||||
"server": { "version": "…", "connId": "…" },
|
||||
"features": { "methods": ["…"], "events": ["…"] },
|
||||
"snapshot": { "…": "…" },
|
||||
"policy": {
|
||||
"maxPayload": 26214400,
|
||||
"maxBufferedBytes": 52428800,
|
||||
"tickIntervalMs": 15000
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Wenn ein Gerätetoken ausgestellt wird, enthält `hello-ok` außerdem:
|
||||
`server`, `features`, `snapshot` und `policy` sind laut Schema alle erforderlich
|
||||
(`src/gateway/protocol/schema/frames.ts`). `auth` und `canvasHostUrl` sind optional.
|
||||
|
||||
Wenn ein Device-Token ausgestellt wird, enthält `hello-ok` außerdem:
|
||||
|
||||
```json
|
||||
{
|
||||
@ -116,14 +130,14 @@ gebundene Rolleneinträge in `deviceTokens` enthalten:
|
||||
}
|
||||
```
|
||||
|
||||
Für den integrierten Bootstrap-Flow für Knoten/Operatoren bleibt das primäre Knotentoken bei
|
||||
`scopes: []`, und jedes übergebene Operatortoken bleibt auf die Bootstrap-
|
||||
Allowlist für Operatoren beschränkt (`operator.approvals`, `operator.read`,
|
||||
`operator.talk.secrets`, `operator.write`). Prüfungen des Bootstrap-Scopes bleiben
|
||||
rollenpräfixbasiert: Operatoreinträge erfüllen nur Operatoranfragen, und Nicht-Operator-
|
||||
Für den integrierten Bootstrap-Ablauf für Node/Operator bleibt das primäre Node-Token bei
|
||||
`scopes: []`, und jedes übergebene Operator-Token bleibt auf die Bootstrap-
|
||||
Operator-Allowlist beschränkt (`operator.approvals`, `operator.read`,
|
||||
`operator.talk.secrets`, `operator.write`). Bootstrap-Scope-Prüfungen bleiben
|
||||
rollenpräfixiert: Operator-Einträge erfüllen nur Operator-Anfragen, und Nicht-Operator-
|
||||
Rollen benötigen weiterhin Scopes unter ihrem eigenen Rollenpräfix.
|
||||
|
||||
### Knotenbeispiel
|
||||
### Node-Beispiel
|
||||
|
||||
```json
|
||||
{
|
||||
@ -160,20 +174,20 @@ Rollen benötigen weiterhin Scopes unter ihrem eigenen Rollenpräfix.
|
||||
|
||||
## Framing
|
||||
|
||||
- **Anfrage**: `{type:"req", id, method, params}`
|
||||
- **Antwort**: `{type:"res", id, ok, payload|error}`
|
||||
- **Ereignis**: `{type:"event", event, payload, seq?, stateVersion?}`
|
||||
- **Request**: `{type:"req", id, method, params}`
|
||||
- **Response**: `{type:"res", id, ok, payload|error}`
|
||||
- **Event**: `{type:"event", event, payload, seq?, stateVersion?}`
|
||||
|
||||
Methoden mit Nebeneffekten erfordern **Idempotenzschlüssel** (siehe Schema).
|
||||
Methoden mit Nebenwirkungen erfordern **Idempotenzschlüssel** (siehe Schema).
|
||||
|
||||
## Rollen + Scopes
|
||||
|
||||
### Rollen
|
||||
|
||||
- `operator` = Client der Steuerungsebene (CLI/UI/Automatisierung).
|
||||
- `node` = Fähigkeits-Host (camera/screen/canvas/system.run).
|
||||
- `operator` = Control-Plane-Client (CLI/UI/Automatisierung).
|
||||
- `node` = Capability-Host (camera/screen/canvas/system.run).
|
||||
|
||||
### Scopes (operator)
|
||||
### Scopes (`operator`)
|
||||
|
||||
Häufige Scopes:
|
||||
|
||||
@ -187,283 +201,286 @@ Häufige Scopes:
|
||||
`talk.config` mit `includeSecrets: true` erfordert `operator.talk.secrets`
|
||||
(oder `operator.admin`).
|
||||
|
||||
Von Plugins registrierte Gateway-RPC-Methoden können ihren eigenen Operator-Scope anfordern, aber
|
||||
Plugin-registrierte Gateway-RPC-Methoden können ihren eigenen Operator-Scope anfordern, aber
|
||||
reservierte Core-Admin-Präfixe (`config.*`, `exec.approvals.*`, `wizard.*`,
|
||||
`update.*`) werden immer zu `operator.admin` aufgelöst.
|
||||
|
||||
Der Methodenscope ist nur die erste Hürde. Einige Slash-Befehle, die über
|
||||
`chat.send` erreicht werden, wenden zusätzlich strengere Prüfungen auf Befehlsebene an. Zum Beispiel erfordern persistente
|
||||
Schreibvorgänge mit `/config set` und `/config unset` `operator.admin`.
|
||||
`chat.send` erreicht werden, wenden zusätzlich strengere Prüfungen auf
|
||||
Befehlsebene an. Zum Beispiel erfordern persistente Schreibvorgänge mit
|
||||
`/config set` und `/config unset` `operator.admin`.
|
||||
|
||||
`node.pair.approve` hat zusätzlich zur
|
||||
grundlegenden Methodenscope-Prüfung eine weitere Scope-Prüfung zum Zeitpunkt der Genehmigung:
|
||||
`node.pair.approve` hat zusätzlich zur Basis-Methodenscope außerdem eine weitere
|
||||
Scope-Prüfung zum Zeitpunkt der Freigabe:
|
||||
|
||||
- anfragelose Requests: `operator.pairing`
|
||||
- Requests mit Nicht-Exec-Knotenbefehlen: `operator.pairing` + `operator.write`
|
||||
- Requests, die `system.run`, `system.run.prepare` oder `system.which` enthalten:
|
||||
- Anfragen ohne Befehle: `operator.pairing`
|
||||
- Anfragen mit Nicht-Exec-Node-Befehlen: `operator.pairing` + `operator.write`
|
||||
- Anfragen, die `system.run`, `system.run.prepare` oder `system.which` enthalten:
|
||||
`operator.pairing` + `operator.admin`
|
||||
|
||||
### Caps/commands/permissions (node)
|
||||
### Caps/commands/permissions (`node`)
|
||||
|
||||
Knoten deklarieren ihre Fähigkeitsansprüche zum Zeitpunkt von `connect`:
|
||||
Nodes deklarieren ihre Capability-Claims beim Verbinden:
|
||||
|
||||
- `caps`: übergeordnete Fähigkeitskategorien.
|
||||
- `commands`: Allowlist von Befehlen für `invoke`.
|
||||
- `permissions`: granulare Umschalter (z. B. `screen.record`, `camera.capture`).
|
||||
- `caps`: allgemeine Capability-Kategorien.
|
||||
- `commands`: Befehls-Allowlist für `invoke`.
|
||||
- `permissions`: granulare Schalter (z. B. `screen.record`, `camera.capture`).
|
||||
|
||||
Das Gateway behandelt diese als **Ansprüche** und erzwingt serverseitige Allowlists.
|
||||
Das Gateway behandelt diese als **Claims** und erzwingt serverseitige Allowlists.
|
||||
|
||||
## Präsenz
|
||||
## Presence
|
||||
|
||||
- `system-presence` gibt Einträge zurück, die nach Geräteidentität gruppiert sind.
|
||||
- Präsenzeinträge enthalten `deviceId`, `roles` und `scopes`, damit UIs eine einzelne Zeile pro Gerät anzeigen können,
|
||||
- `system-presence` gibt Einträge zurück, die nach Geräteidentität indiziert sind.
|
||||
- Presence-Einträge enthalten `deviceId`, `roles` und `scopes`, damit UIs eine einzelne Zeile pro Gerät anzeigen können,
|
||||
selbst wenn es sowohl als **operator** als auch als **node** verbunden ist.
|
||||
|
||||
## Häufige RPC-Methodenfamilien
|
||||
|
||||
Diese Seite ist kein vollständig generierter Dump, aber die öffentliche WS-Oberfläche ist breiter
|
||||
als die oben gezeigten Handshake-/Auth-Beispiele. Dies sind die wichtigsten Methodenfamilien, die das
|
||||
Gateway heute bereitstellt.
|
||||
Diese Seite ist kein generierter vollständiger Dump, aber die öffentliche WS-Oberfläche ist umfassender
|
||||
als die Handshake-/Auth-Beispiele oben. Dies sind die wichtigsten Methodenfamilien, die das
|
||||
Gateway derzeit bereitstellt.
|
||||
|
||||
`hello-ok.features.methods` ist eine konservative Discovery-Liste, die aus
|
||||
`src/gateway/server-methods-list.ts` plus geladenen Plugin-/Kanal-Methodenexporten erstellt wird.
|
||||
Behandeln Sie sie als Feature-Erkennung, nicht als generierten Dump jeder aufrufbaren Hilfsfunktion,
|
||||
die in `src/gateway/server-methods/*.ts` implementiert ist.
|
||||
`src/gateway/server-methods-list.ts` sowie geladenen Plugin-/Channel-Methodenexports aufgebaut wird.
|
||||
Behandle sie als Feature Discovery, nicht als generierten Dump aller aufrufbaren Helfer,
|
||||
die in `src/gateway/server-methods/*.ts` implementiert sind.
|
||||
|
||||
### System und Identität
|
||||
|
||||
- `health` gibt den gecachten oder frisch geprüften Gateway-Health-Snapshot zurück.
|
||||
- `status` gibt die Gateway-Zusammenfassung im Stil von `/status` zurück; sensible Felder sind
|
||||
nur für Operator-Clients mit Admin-Scope enthalten.
|
||||
- `status` gibt die Gateway-Zusammenfassung im Stil von `/status` zurück; sensible Felder werden
|
||||
nur für Operator-Clients mit Admin-Scope einbezogen.
|
||||
- `gateway.identity.get` gibt die Gateway-Geräteidentität zurück, die von Relay- und
|
||||
Pairing-Flows verwendet wird.
|
||||
- `system-presence` gibt den aktuellen Präsenz-Snapshot für verbundene
|
||||
Operator-/Knotengeräte zurück.
|
||||
- `system-event` hängt ein Systemereignis an und kann den Präsenzkontext
|
||||
aktualisieren/übertragen.
|
||||
Pairing-Abläufen verwendet wird.
|
||||
- `system-presence` gibt den aktuellen Presence-Snapshot für verbundene
|
||||
Operator-/Node-Geräte zurück.
|
||||
- `system-event` hängt ein Systemereignis an und kann den Presence-
|
||||
Kontext aktualisieren/übertragen.
|
||||
- `last-heartbeat` gibt das zuletzt persistierte Heartbeat-Ereignis zurück.
|
||||
- `set-heartbeats` schaltet die Heartbeat-Verarbeitung auf dem Gateway um.
|
||||
|
||||
### Modelle und Nutzung
|
||||
|
||||
- `models.list` gibt den zur Laufzeit erlaubten Modellkatalog zurück.
|
||||
- `usage.status` gibt Zusammenfassungen von Provider-Nutzungsfenstern/verbleibender Quote zurück.
|
||||
- `usage.cost` gibt aggregierte Kosten-Nutzungszusammenfassungen für einen Datumsbereich zurück.
|
||||
- `doctor.memory.status` gibt die Bereitschaft von Vektorspeicher/Embeddings für den
|
||||
- `usage.status` gibt Provider-Nutzungsfenster/Zusammenfassungen des verbleibenden Kontingents zurück.
|
||||
- `usage.cost` gibt aggregierte Zusammenfassungen der Kostennutzung für einen Datumsbereich zurück.
|
||||
- `doctor.memory.status` gibt den Bereitschaftsstatus für Vector Memory / Embeddings für den
|
||||
aktiven Standard-Agent-Workspace zurück.
|
||||
- `sessions.usage` gibt Nutzungszusammenfassungen pro Sitzung zurück.
|
||||
- `sessions.usage.timeseries` gibt Zeitreihen-Nutzungsdaten für eine Sitzung zurück.
|
||||
- `sessions.usage.logs` gibt Nutzungseinträge für eine Sitzung zurück.
|
||||
- `sessions.usage.timeseries` gibt Zeitreihennutzung für eine Sitzung zurück.
|
||||
- `sessions.usage.logs` gibt Nutzungslogeinträge für eine Sitzung zurück.
|
||||
|
||||
### Kanäle und Login-Helfer
|
||||
### Channels und Login-Helfer
|
||||
|
||||
- `channels.status` gibt Statuszusammenfassungen für integrierte und gebündelte Kanäle/Plugins zurück.
|
||||
- `channels.logout` meldet einen bestimmten Kanal/ein bestimmtes Konto ab, wenn der Kanal
|
||||
- `channels.status` gibt Statuszusammenfassungen für integrierte und gebündelte Channels/Plugins zurück.
|
||||
- `channels.logout` meldet einen bestimmten Channel/Account ab, sofern der Channel
|
||||
Logout unterstützt.
|
||||
- `web.login.start` startet einen QR-/Web-Login-Flow für den aktuellen QR-fähigen Web-
|
||||
Kanal-Provider.
|
||||
- `web.login.wait` wartet auf den Abschluss dieses QR-/Web-Login-Flows und startet bei Erfolg den
|
||||
Kanal.
|
||||
- `push.test` sendet einen Test-APNs-Push an einen registrierten iOS-Knoten.
|
||||
- `web.login.start` startet einen QR-/Web-Login-Ablauf für den aktuell QR-fähigen Web-
|
||||
Channel-Provider.
|
||||
- `web.login.wait` wartet darauf, dass dieser QR-/Web-Login-Ablauf abgeschlossen wird, und startet bei Erfolg den
|
||||
Channel.
|
||||
- `push.test` sendet einen Test-APNs-Push an einen registrierten iOS-Node.
|
||||
- `voicewake.get` gibt die gespeicherten Wake-Word-Trigger zurück.
|
||||
- `voicewake.set` aktualisiert Wake-Word-Trigger und überträgt die Änderung.
|
||||
|
||||
### Messaging und Logs
|
||||
|
||||
- `send` ist die direkte RPC für ausgehende Zustellung für auf Kanal/Konto/Thread ausgerichtete
|
||||
Sendungen außerhalb des Chat-Runners.
|
||||
- `logs.tail` gibt den konfigurierten Gateway-Dateilog-Tail mit Cursor-/Limit- und
|
||||
- `send` ist die direkte RPC für ausgehende Zustellung an Channel/Account/Thread-Ziele
|
||||
außerhalb des Chat-Runners.
|
||||
- `logs.tail` gibt den konfigurierten Gateway-Dateilog-Tail mit Cursor/Limit und
|
||||
Max-Byte-Steuerung zurück.
|
||||
|
||||
### Talk und TTS
|
||||
|
||||
- `talk.config` gibt die effektive Talk-Konfigurations-Payload zurück; `includeSecrets`
|
||||
erfordert `operator.talk.secrets` (oder `operator.admin`).
|
||||
- `talk.mode` setzt/überträgt den aktuellen Zustand des Talk-Modus für WebChat-/Control-UI-
|
||||
- `talk.mode` setzt/überträgt den aktuellen Talk-Modusstatus für WebChat-/Control-UI-
|
||||
Clients.
|
||||
- `talk.speak` synthetisiert Sprache über den aktiven Talk-Sprachprovider.
|
||||
- `talk.speak` synthetisiert Sprache über den aktiven Talk-Speech-Provider.
|
||||
- `tts.status` gibt den aktivierten TTS-Status, den aktiven Provider, Fallback-Provider
|
||||
und den Zustand der Provider-Konfiguration zurück.
|
||||
- `tts.providers` gibt das sichtbare Inventar der TTS-Provider zurück.
|
||||
- `tts.enable` und `tts.disable` schalten den TTS-Präferenzstatus um.
|
||||
und den Provider-Konfigurationsstatus zurück.
|
||||
- `tts.providers` gibt das sichtbare TTS-Provider-Inventar zurück.
|
||||
- `tts.enable` und `tts.disable` schalten den TTS-Status der Voreinstellungen um.
|
||||
- `tts.setProvider` aktualisiert den bevorzugten TTS-Provider.
|
||||
- `tts.convert` führt eine einmalige Text-zu-Sprache-Konvertierung aus.
|
||||
|
||||
### Secrets, Konfiguration, Update und Wizard
|
||||
|
||||
- `secrets.reload` löst aktive SecretRefs erneut auf und tauscht den Laufzeit-Secret-Status
|
||||
- `secrets.reload` löst aktive SecretRefs erneut auf und tauscht den Laufzeitstatus für Secrets
|
||||
nur bei vollständigem Erfolg aus.
|
||||
- `secrets.resolve` löst zielgerichtete Secret-Zuweisungen für einen bestimmten
|
||||
Befehl/Zielsatz auf.
|
||||
- `secrets.resolve` löst Secret-Zuweisungen für ein bestimmtes
|
||||
Befehls-/Ziel-Set auf.
|
||||
- `config.get` gibt den aktuellen Konfigurations-Snapshot und Hash zurück.
|
||||
- `config.set` schreibt eine validierte Konfigurations-Payload.
|
||||
- `config.patch` führt eine teilweise Konfigurationsaktualisierung zusammen.
|
||||
- `config.patch` führt eine partielle Konfigurationsaktualisierung zusammen.
|
||||
- `config.apply` validiert und ersetzt die vollständige Konfigurations-Payload.
|
||||
- `config.schema` gibt die Live-Konfigurationsschema-Payload zurück, die von Control UI und
|
||||
CLI-Tooling verwendet wird: Schema, `uiHints`, Version und Generierungsmetadaten, einschließlich
|
||||
Plugin- und Kanalschema-Metadaten, wenn die Laufzeit sie laden kann. Das Schema
|
||||
enthält die Feldmetadaten `title` / `description`, die aus denselben Labels
|
||||
und Hilfetexten abgeleitet sind, die von der UI verwendet werden, einschließlich verschachtelter Objekt-,
|
||||
Wildcard-, Array-Item- und `anyOf` / `oneOf` / `allOf`-Kompositionszweige, wenn passende Felddokumentation vorhanden ist.
|
||||
Plugin- und Channel-Schema-Metadaten, wenn die Laufzeit sie laden kann. Das Schema
|
||||
enthält Feldmetadaten `title` / `description`, die aus denselben Labels
|
||||
und Hilfetexten abgeleitet werden, die von der UI verwendet werden, einschließlich verschachtelter Objekte,
|
||||
Wildcards, Array-Items und `anyOf`- / `oneOf`- / `allOf`-Kompositionszweigen, wenn passende
|
||||
Felddokumentation existiert.
|
||||
- `config.schema.lookup` gibt eine pfadbezogene Lookup-Payload für einen Konfigurationspfad zurück:
|
||||
normalisierter Pfad, ein flacher Schemaknoten, passender Hint + `hintPath` sowie
|
||||
Zusammenfassungen der unmittelbaren Kinder für UI-/CLI-Drill-down.
|
||||
- Lookup-Schemaknoten behalten die benutzerseitige Dokumentation und allgemeine Validierungsfelder:
|
||||
normalisierter Pfad, ein flacher Schemaknoten, passender Hint + `hintPath` und
|
||||
Zusammenfassungen der direkten Kindknoten für UI-/CLI-Drill-down.
|
||||
- Lookup-Schemaknoten behalten die nutzerseitige Dokumentation und häufige Validierungsfelder:
|
||||
`title`, `description`, `type`, `enum`, `const`, `format`, `pattern`,
|
||||
numerische/String-/Array-/Objekt-Grenzen sowie boolesche Flags wie
|
||||
numerische/String-/Array-/Objekt-Grenzen und boolesche Flags wie
|
||||
`additionalProperties`, `deprecated`, `readOnly`, `writeOnly`.
|
||||
- Kindzusammenfassungen stellen `key`, normalisierten `path`, `type`, `required`,
|
||||
- Zusammenfassungen der Kindknoten stellen `key`, normalisierten `path`, `type`, `required`,
|
||||
`hasChildren` sowie den passenden `hint` / `hintPath` bereit.
|
||||
- `update.run` führt den Gateway-Update-Flow aus und plant einen Neustart nur dann,
|
||||
- `update.run` führt den Gateway-Update-Ablauf aus und plant einen Neustart nur dann,
|
||||
wenn das Update selbst erfolgreich war.
|
||||
- `wizard.start`, `wizard.next`, `wizard.status` und `wizard.cancel` stellen den
|
||||
Onboarding-Wizard über WS RPC bereit.
|
||||
|
||||
### Bestehende große Familien
|
||||
### Vorhandene große Familien
|
||||
|
||||
#### Agenten- und Workspace-Helfer
|
||||
#### Agent- und Workspace-Helfer
|
||||
|
||||
- `agents.list` gibt konfigurierte Agenteneinträge zurück.
|
||||
- `agents.create`, `agents.update` und `agents.delete` verwalten Agentendatensätze und
|
||||
- `agents.list` gibt konfigurierte Agent-Einträge zurück.
|
||||
- `agents.create`, `agents.update` und `agents.delete` verwalten Agent-Datensätze und
|
||||
Workspace-Verdrahtung.
|
||||
- `agents.files.list`, `agents.files.get` und `agents.files.set` verwalten die
|
||||
exponierten Bootstrap-Workspace-Dateien eines Agenten.
|
||||
- `agent.identity.get` gibt die effektive Assistentenidentität für einen Agenten oder
|
||||
eine Sitzung zurück.
|
||||
- `agent.wait` wartet darauf, dass ein Lauf abgeschlossen wird, und gibt den terminalen Snapshot zurück,
|
||||
wenn verfügbar.
|
||||
- `agent.wait` wartet darauf, dass ein Lauf abgeschlossen wird, und gibt den terminalen Snapshot zurück, wenn
|
||||
verfügbar.
|
||||
|
||||
#### Sitzungssteuerung
|
||||
|
||||
- `sessions.list` gibt den aktuellen Sitzungsindex zurück.
|
||||
- `sessions.subscribe` und `sessions.unsubscribe` schalten Abonnements für Sitzungsänderungsereignisse
|
||||
für den aktuellen WS-Client ein bzw. aus.
|
||||
für den aktuellen WS-Client um.
|
||||
- `sessions.messages.subscribe` und `sessions.messages.unsubscribe` schalten
|
||||
Protokoll-/Nachrichtenereignis-Abonnements für eine Sitzung ein bzw. aus.
|
||||
- `sessions.preview` gibt begrenzte Protokollvorschauen für bestimmte Sitzungsschlüssel
|
||||
zurück.
|
||||
Abonnements für Transkript-/Nachrichtenereignisse für eine Sitzung um.
|
||||
- `sessions.preview` gibt begrenzte Transkriptvorschauen für bestimmte Sitzungs-
|
||||
Schlüssel zurück.
|
||||
- `sessions.resolve` löst ein Sitzungsziel auf oder kanonisiert es.
|
||||
- `sessions.create` erstellt einen neuen Sitzungseintrag.
|
||||
- `sessions.send` sendet eine Nachricht in eine bestehende Sitzung.
|
||||
- `sessions.steer` ist die Interrupt-und-Steer-Variante für eine aktive Sitzung.
|
||||
- `sessions.steer` ist die Variante zum Unterbrechen und Neu-Steuern für eine aktive Sitzung.
|
||||
- `sessions.abort` bricht aktive Arbeit für eine Sitzung ab.
|
||||
- `sessions.patch` aktualisiert Sitzungsmetadaten/-Überschreibungen.
|
||||
- `sessions.patch` aktualisiert Sitzungsmetadaten/-Overrides.
|
||||
- `sessions.reset`, `sessions.delete` und `sessions.compact` führen Sitzungs-
|
||||
Wartungsaufgaben aus.
|
||||
Wartung durch.
|
||||
- `sessions.get` gibt die vollständige gespeicherte Sitzungszeile zurück.
|
||||
- Die Chat-Ausführung verwendet weiterhin `chat.history`, `chat.send`, `chat.abort` und
|
||||
`chat.inject`.
|
||||
- `chat.history` ist für UI-Clients anzeige-normalisiert: Inline-Direktiv-Tags werden
|
||||
- `chat.history` ist für UI-Clients anzeige-normalisiert: Inline-Direktiventags werden
|
||||
aus sichtbarem Text entfernt, XML-Payloads von Tool-Aufrufen im Klartext (einschließlich
|
||||
`<tool_call>...</tool_call>`, `<function_call>...</function_call>`,
|
||||
`<tool_calls>...</tool_calls>`, `<function_calls>...</function_calls>` und
|
||||
abgeschnittener Tool-Call-Blöcke) sowie durchgesickerte ASCII-/Full-Width-
|
||||
Modellsteuerungstoken werden entfernt, reine stille-Token-Assistentenzeilen wie exakt `NO_REPLY` /
|
||||
`no_reply` werden ausgelassen, und übergroße Zeilen können durch Platzhalter ersetzt werden.
|
||||
Modellsteuerungstoken werden entfernt, reine Assistant-Zeilen mit stillen Tokens wie exaktem `NO_REPLY` /
|
||||
`no_reply` werden weggelassen, und übergroße Zeilen können durch Platzhalter ersetzt werden.
|
||||
|
||||
#### Geräte-Pairing und Gerätetoken
|
||||
#### Geräte-Pairing und Device-Tokens
|
||||
|
||||
- `device.pair.list` gibt ausstehende und genehmigte gepaarte Geräte zurück.
|
||||
- `device.pair.list` gibt ausstehende und genehmigte gekoppelte Geräte zurück.
|
||||
- `device.pair.approve`, `device.pair.reject` und `device.pair.remove` verwalten
|
||||
Geräte-Pairing-Einträge.
|
||||
- `device.token.rotate` rotiert ein gepaartes Gerätetoken innerhalb seiner genehmigten Rollen-
|
||||
- `device.token.rotate` rotiert ein Token für ein gekoppeltes Gerät innerhalb seiner genehmigten Rollen-
|
||||
und Scope-Grenzen.
|
||||
- `device.token.revoke` widerruft ein gepaartes Gerätetoken.
|
||||
- `device.token.revoke` widerruft ein Token für ein gekoppeltes Gerät.
|
||||
|
||||
#### Knoten-Pairing, Invoke und ausstehende Arbeit
|
||||
#### Node-Pairing, Invoke und ausstehende Arbeit
|
||||
|
||||
- `node.pair.request`, `node.pair.list`, `node.pair.approve`,
|
||||
`node.pair.reject` und `node.pair.verify` decken Knoten-Pairing und Bootstrap-
|
||||
`node.pair.reject` und `node.pair.verify` decken Node-Pairing und Bootstrap-
|
||||
Verifizierung ab.
|
||||
- `node.list` und `node.describe` geben den bekannten/verbundenen Knotenzustand zurück.
|
||||
- `node.rename` aktualisiert eine Bezeichnung eines gepaarten Knotens.
|
||||
- `node.invoke` leitet einen Befehl an einen verbundenen Knoten weiter.
|
||||
- `node.list` und `node.describe` geben bekannte/verbundene Node-Zustände zurück.
|
||||
- `node.rename` aktualisiert ein Label für einen gekoppelten Node.
|
||||
- `node.invoke` leitet einen Befehl an einen verbundenen Node weiter.
|
||||
- `node.invoke.result` gibt das Ergebnis für eine Invoke-Anfrage zurück.
|
||||
- `node.event` überträgt von Knoten stammende Ereignisse zurück in das Gateway.
|
||||
- `node.canvas.capability.refresh` aktualisiert bereichsbezogene Canvas-Fähigkeitstoken.
|
||||
- `node.pending.pull` und `node.pending.ack` sind die Queue-APIs für verbundene Knoten.
|
||||
- `node.event` überträgt von Nodes stammende Ereignisse zurück ins Gateway.
|
||||
- `node.canvas.capability.refresh` aktualisiert bereichsbezogene Canvas-Capability-Tokens.
|
||||
- `node.pending.pull` und `node.pending.ack` sind die Queue-APIs für verbundene Nodes.
|
||||
- `node.pending.enqueue` und `node.pending.drain` verwalten dauerhafte ausstehende Arbeit
|
||||
für offline/getrennte Knoten.
|
||||
für offline/getrennte Nodes.
|
||||
|
||||
#### Genehmigungsfamilien
|
||||
|
||||
- `exec.approval.request`, `exec.approval.get`, `exec.approval.list` und
|
||||
`exec.approval.resolve` decken einmalige Exec-Genehmigungsanfragen plus Nachschlagen/Wiederholen
|
||||
`exec.approval.resolve` decken einmalige Exec-Genehmigungsanfragen sowie Lookup/Replay
|
||||
ausstehender Genehmigungen ab.
|
||||
- `exec.approval.waitDecision` wartet auf eine ausstehende Exec-Genehmigung und gibt
|
||||
die endgültige Entscheidung zurück (oder `null` bei Zeitüberschreitung).
|
||||
- `exec.approvals.get` und `exec.approvals.set` verwalten Gateway-Exec-Genehmigungs-
|
||||
Richtlinien-Snapshots.
|
||||
- `exec.approvals.node.get` und `exec.approvals.node.set` verwalten knotenspezifische Exec-
|
||||
Genehmigungsrichtlinien über Knoten-Relay-Befehle.
|
||||
die finale Entscheidung zurück (oder `null` bei Timeout).
|
||||
- `exec.approvals.get` und `exec.approvals.set` verwalten Snapshots der Gateway-Exec-
|
||||
Genehmigungsrichtlinie.
|
||||
- `exec.approvals.node.get` und `exec.approvals.node.set` verwalten Node-lokale Exec-
|
||||
Genehmigungsrichtlinien über Node-Relay-Befehle.
|
||||
- `plugin.approval.request`, `plugin.approval.list`,
|
||||
`plugin.approval.waitDecision` und `plugin.approval.resolve` decken
|
||||
plugin-definierte Genehmigungs-Flows ab.
|
||||
Plugin-definierte Genehmigungsabläufe ab.
|
||||
|
||||
#### Andere große Familien
|
||||
#### Weitere große Familien
|
||||
|
||||
- Automatisierung:
|
||||
- `wake` plant eine sofortige oder bei einem nächsten Heartbeat erfolgende Wake-Text-Injektion
|
||||
- `wake` plant eine sofortige oder beim nächsten Heartbeat erfolgende Wake-Textinjektion
|
||||
- `cron.list`, `cron.status`, `cron.add`, `cron.update`, `cron.remove`,
|
||||
`cron.run`, `cron.runs`
|
||||
- Skills/Tools: `commands.list`, `skills.*`, `tools.catalog`, `tools.effective`
|
||||
|
||||
### Häufige Ereignisfamilien
|
||||
|
||||
- `chat`: UI-Chat-Updates wie `chat.inject` und andere reine Protokoll-Chat-
|
||||
Ereignisse.
|
||||
- `session.message` und `session.tool`: Protokoll-/Ereignisstream-Updates für eine
|
||||
- `chat`: UI-Chat-Updates wie `chat.inject` und andere Chat-
|
||||
Ereignisse nur für das Transkript.
|
||||
- `session.message` und `session.tool`: Transkript-/Ereignisstream-Updates für eine
|
||||
abonnierte Sitzung.
|
||||
- `sessions.changed`: Sitzungsindex oder Metadaten haben sich geändert.
|
||||
- `presence`: Snapshot-Aktualisierungen der Systempräsenz.
|
||||
- `sessions.changed`: Sitzungsindex oder -metadaten wurden geändert.
|
||||
- `presence`: Updates des System-Presence-Snapshots.
|
||||
- `tick`: periodisches Keepalive-/Liveness-Ereignis.
|
||||
- `health`: Gateway-Health-Snapshot-Aktualisierung.
|
||||
- `heartbeat`: Aktualisierung des Heartbeat-Ereignisstreams.
|
||||
- `cron`: Änderungsereignis für Cron-Lauf/-Job.
|
||||
- `shutdown`: Gateway-Abschaltbenachrichtigung.
|
||||
- `node.pair.requested` / `node.pair.resolved`: Lebenszyklus des Knoten-Pairings.
|
||||
- `node.invoke.request`: Broadcast einer Knoten-Invoke-Anfrage.
|
||||
- `device.pair.requested` / `device.pair.resolved`: Lebenszyklus gepaarter Geräte.
|
||||
- `voicewake.changed`: Konfiguration von Wake-Word-Triggern wurde geändert.
|
||||
- `health`: Update des Gateway-Health-Snapshots.
|
||||
- `heartbeat`: Update des Heartbeat-Ereignisstreams.
|
||||
- `cron`: Ereignis für Änderung von Cron-Lauf/Job.
|
||||
- `shutdown`: Benachrichtigung über das Herunterfahren des Gateways.
|
||||
- `node.pair.requested` / `node.pair.resolved`: Lebenszyklus des Node-Pairings.
|
||||
- `node.invoke.request`: Broadcast einer Node-Invoke-Anfrage.
|
||||
- `device.pair.requested` / `device.pair.resolved`: Lebenszyklus gekoppelter Geräte.
|
||||
- `voicewake.changed`: Konfiguration des Wake-Word-Triggers wurde geändert.
|
||||
- `exec.approval.requested` / `exec.approval.resolved`: Lebenszyklus der Exec-
|
||||
Genehmigung.
|
||||
- `plugin.approval.requested` / `plugin.approval.resolved`: Lebenszyklus der Plugin-Genehmigung.
|
||||
- `plugin.approval.requested` / `plugin.approval.resolved`: Lebenszyklus der Plugin-
|
||||
Genehmigung.
|
||||
|
||||
### Hilfsmethoden für Knoten
|
||||
### Node-Helfermethoden
|
||||
|
||||
- Knoten können `skills.bins` aufrufen, um die aktuelle Liste ausführbarer Skills
|
||||
- Nodes können `skills.bins` aufrufen, um die aktuelle Liste ausführbarer Skill-Dateien
|
||||
für Auto-Allow-Prüfungen abzurufen.
|
||||
|
||||
### Hilfsmethoden für Operatoren
|
||||
### Operator-Helfermethoden
|
||||
|
||||
- Operatoren können `commands.list` (`operator.read`) aufrufen, um das Laufzeit-
|
||||
Befehlsinventar für einen Agenten abzurufen.
|
||||
- `agentId` ist optional; lassen Sie es weg, um den Standard-Agent-Workspace zu lesen.
|
||||
- `scope` steuert, welche Oberfläche vom primären `name` angesprochen wird:
|
||||
- `agentId` ist optional; lasse es weg, um den Standard-Agent-Workspace zu lesen.
|
||||
- `scope` steuert, auf welche Oberfläche sich `name` primär bezieht:
|
||||
- `text` gibt das primäre Textbefehlstoken ohne führendes `/` zurück
|
||||
- `native` und der Standardpfad `both` geben providerbewusste native Namen
|
||||
zurück, wenn verfügbar
|
||||
- `textAliases` enthält exakte Slash-Aliasse wie `/model` und `/m`.
|
||||
- `nativeName` enthält den providerbewussten nativen Befehlsnamen, wenn einer existiert.
|
||||
- `provider` ist optional und beeinflusst nur native Benennung sowie die Verfügbarkeit
|
||||
nativer Plugin-Befehle.
|
||||
- `nativeName` enthält den providerbewussten nativen Befehlsnamen, wenn ein solcher existiert.
|
||||
- `provider` ist optional und beeinflusst nur die native Benennung sowie die Verfügbarkeit nativer Plugin-
|
||||
Befehle.
|
||||
- `includeArgs=false` lässt serialisierte Argumentmetadaten in der Antwort weg.
|
||||
- Operatoren können `tools.catalog` (`operator.read`) aufrufen, um den Laufzeit-Toolkatalog für einen
|
||||
Agenten abzurufen. Die Antwort enthält gruppierte Tools und Herkunftsmetadaten:
|
||||
- `source`: `core` oder `plugin`
|
||||
- `pluginId`: Plugin-Eigentümer, wenn `source="plugin"`
|
||||
- `optional`: ob ein Plugin-Tool optional ist
|
||||
- Operatoren können `tools.effective` (`operator.read`) aufrufen, um das zur Laufzeit wirksame Tool-
|
||||
- Operatoren können `tools.effective` (`operator.read`) aufrufen, um das zur Laufzeit effektive Tool-
|
||||
Inventar für eine Sitzung abzurufen.
|
||||
- `sessionKey` ist erforderlich.
|
||||
- Das Gateway leitet vertrauenswürdigen Laufzeitkontext serverseitig aus der Sitzung ab, statt
|
||||
vom Aufrufer gelieferten Auth- oder Zustellungskontext zu akzeptieren.
|
||||
- Die Antwort ist sitzungsbezogen und spiegelt wider, was die aktive Unterhaltung derzeit nutzen kann,
|
||||
einschließlich Core-, Plugin- und Kanal-Tools.
|
||||
- Das Gateway leitet den vertrauenswürdigen Laufzeitkontext serverseitig aus der Sitzung ab, statt
|
||||
vom Aufrufer bereitgestellten Auth- oder Zustellungskontext zu akzeptieren.
|
||||
- Die Antwort ist sitzungsbezogen und spiegelt wider, was die aktive Unterhaltung derzeit verwenden kann,
|
||||
einschließlich Core-, Plugin- und Channel-Tools.
|
||||
- Operatoren können `skills.status` (`operator.read`) aufrufen, um das sichtbare
|
||||
Skill-Inventar für einen Agenten abzurufen.
|
||||
- `agentId` ist optional; lassen Sie es weg, um den Standard-Agent-Workspace zu lesen.
|
||||
- Die Antwort enthält Eignung, fehlende Anforderungen, Konfigurationsprüfungen und
|
||||
Skills-Inventar für einen Agenten abzurufen.
|
||||
- `agentId` ist optional; lasse es weg, um den Standard-Agent-Workspace zu lesen.
|
||||
- Die Antwort enthält Eignung, fehlende Voraussetzungen, Konfigurationsprüfungen und
|
||||
bereinigte Installationsoptionen, ohne rohe Secret-Werte offenzulegen.
|
||||
- Operatoren können `skills.search` und `skills.detail` (`operator.read`) für
|
||||
ClawHub-Discovery-Metadaten aufrufen.
|
||||
@ -473,134 +490,166 @@ die in `src/gateway/server-methods/*.ts` implementiert ist.
|
||||
- Gateway-Installer-Modus: `{ name, installId, dangerouslyForceUnsafeInstall?, timeoutMs? }`
|
||||
führt eine deklarierte `metadata.openclaw.install`-Aktion auf dem Gateway-Host aus.
|
||||
- Operatoren können `skills.update` (`operator.admin`) in zwei Modi aufrufen:
|
||||
- Der ClawHub-Modus aktualisiert einen verfolgten Slug oder alle verfolgten ClawHub-Installationen im
|
||||
Standard-Agent-Workspace.
|
||||
- Der Konfigurationsmodus patcht `skills.entries.<skillKey>`-Werte wie `enabled`,
|
||||
`apiKey` und `env`.
|
||||
- Im ClawHub-Modus wird ein verfolgter Slug oder werden alle verfolgten ClawHub-Installationen im
|
||||
Standard-Agent-Workspace aktualisiert.
|
||||
- Im Konfigurationsmodus werden Werte in `skills.entries.<skillKey>` wie `enabled`,
|
||||
`apiKey` und `env` gepatcht.
|
||||
|
||||
## Exec-Genehmigungen
|
||||
|
||||
- Wenn eine Exec-Anfrage eine Genehmigung benötigt, überträgt das Gateway `exec.approval.requested`.
|
||||
- Operator-Clients lösen dies durch Aufruf von `exec.approval.resolve` auf (erfordert Scope `operator.approvals`).
|
||||
- Für `host=node` muss `exec.approval.request` `systemRunPlan` enthalten (kanonische `argv`/`cwd`/`rawCommand`/Sitzungsmetadaten). Anfragen ohne `systemRunPlan` werden abgelehnt.
|
||||
- Nach der Genehmigung verwenden weitergeleitete `node.invoke system.run`-Aufrufe dieses kanonische
|
||||
`systemRunPlan` als maßgeblichen Befehls-/cwd-/Sitzungskontext.
|
||||
- Wenn eine Exec-Anfrage eine Genehmigung benötigt, sendet das Gateway `exec.approval.requested`.
|
||||
- Operator-Clients lösen dies durch Aufruf von `exec.approval.resolve` auf (erfordert den Scope `operator.approvals`).
|
||||
- Für `host=node` muss `exec.approval.request` `systemRunPlan` enthalten (kanonische `argv`-/`cwd`-/`rawCommand`-/Sitzungsmetadaten). Anfragen ohne `systemRunPlan` werden abgelehnt.
|
||||
- Nach der Genehmigung verwenden weitergeleitete `node.invoke system.run`-Aufrufe denselben kanonischen
|
||||
`systemRunPlan` als maßgeblichen Befehls-/`cwd`-/Sitzungskontext.
|
||||
- Wenn ein Aufrufer `command`, `rawCommand`, `cwd`, `agentId` oder
|
||||
`sessionKey` zwischen der Vorbereitung und der endgültigen genehmigten Weiterleitung von `system.run` verändert, lehnt das
|
||||
Gateway den Lauf ab, statt der veränderten Payload zu vertrauen.
|
||||
`sessionKey` zwischen `prepare` und dem finalen weitergeleiteten genehmigten `system.run` ändert, lehnt das
|
||||
Gateway den Lauf ab, statt der geänderten Payload zu vertrauen.
|
||||
|
||||
## Agenten-Zustellungs-Fallback
|
||||
## Agent-Zustellungs-Fallback
|
||||
|
||||
- `agent`-Anfragen können `deliver=true` enthalten, um ausgehende Zustellung anzufordern.
|
||||
- `bestEffortDeliver=false` behält striktes Verhalten bei: nicht auflösbare oder nur intern verfügbare Zustellungsziele geben `INVALID_REQUEST` zurück.
|
||||
- `bestEffortDeliver=true` erlaubt Fallback auf reine Sitzungsausführung, wenn keine extern zustellbare Route aufgelöst werden kann (zum Beispiel interne/WebChat-Sitzungen oder mehrdeutige Multi-Kanal-Konfigurationen).
|
||||
- `bestEffortDeliver=false` behält striktes Verhalten bei: Nicht auflösbare oder nur intern verfügbare Zustellungsziele geben `INVALID_REQUEST` zurück.
|
||||
- `bestEffortDeliver=true` erlaubt Fallback auf reine Sitzungsausführung, wenn keine extern zustellbare Route aufgelöst werden kann (zum Beispiel bei internen/WebChat-Sitzungen oder mehrdeutigen Multi-Channel-Konfigurationen).
|
||||
|
||||
## Versionierung
|
||||
|
||||
- `PROTOCOL_VERSION` befindet sich in `src/gateway/protocol/schema.ts`.
|
||||
- `PROTOCOL_VERSION` befindet sich in `src/gateway/protocol/schema/protocol-schemas.ts`.
|
||||
- Clients senden `minProtocol` + `maxProtocol`; der Server lehnt Abweichungen ab.
|
||||
- Schemas + Modelle werden aus TypeBox-Definitionen generiert:
|
||||
- `pnpm protocol:gen`
|
||||
- `pnpm protocol:gen:swift`
|
||||
- `pnpm protocol:check`
|
||||
|
||||
### Client-Konstanten
|
||||
|
||||
Der Referenzclient in `src/gateway/client.ts` verwendet diese Standardwerte. Die Werte sind
|
||||
über Protokoll v3 hinweg stabil und bilden die erwartete Basis für Drittanbieter-Clients.
|
||||
|
||||
| Konstante | Standardwert | Quelle |
|
||||
| ----------------------------------------- | ----------------------------------------------------- | ---------------------------------------------------------- |
|
||||
| `PROTOCOL_VERSION` | `3` | `src/gateway/protocol/schema/protocol-schemas.ts` |
|
||||
| Request-Timeout (pro RPC) | `30_000` ms | `src/gateway/client.ts` (`requestTimeoutMs`) |
|
||||
| Preauth- / Connect-Challenge-Timeout | `10_000` ms | `src/gateway/handshake-timeouts.ts` (Clamp `250`–`10_000`) |
|
||||
| Initialer Reconnect-Backoff | `1_000` ms | `src/gateway/client.ts` (`backoffMs`) |
|
||||
| Maximaler Reconnect-Backoff | `30_000` ms | `src/gateway/client.ts` (`scheduleReconnect`) |
|
||||
| Fast-Retry-Clamp nach Device-Token-Close | `250` ms | `src/gateway/client.ts` |
|
||||
| Force-Stop-Schonfrist vor `terminate()` | `250` ms | `FORCE_STOP_TERMINATE_GRACE_MS` |
|
||||
| Standard-Timeout von `stopAndWait()` | `1_000` ms | `STOP_AND_WAIT_TIMEOUT_MS` |
|
||||
| Standard-Tick-Intervall (vor `hello-ok`) | `30_000` ms | `src/gateway/client.ts` |
|
||||
| Tick-Timeout-Close | Code `4000`, wenn Stille `tickIntervalMs * 2` überschreitet | `src/gateway/client.ts` |
|
||||
| `MAX_PAYLOAD_BYTES` | `25 * 1024 * 1024` (25 MB) | `src/gateway/server-constants.ts` |
|
||||
|
||||
Der Server kündigt das effektive `policy.tickIntervalMs`, `policy.maxPayload`
|
||||
und `policy.maxBufferedBytes` in `hello-ok` an; Clients sollten diese Werte
|
||||
statt der Standardwerte vor dem Handshake beachten.
|
||||
|
||||
## Auth
|
||||
|
||||
- Gateway-Auth mit gemeinsamem Secret verwendet `connect.params.auth.token` oder
|
||||
- Authentifizierung des Gateways per Shared Secret verwendet `connect.params.auth.token` oder
|
||||
`connect.params.auth.password`, abhängig vom konfigurierten Auth-Modus.
|
||||
- Identitätstragende Modi wie Tailscale Serve
|
||||
(`gateway.auth.allowTailscale: true`) oder Nicht-Loopback-
|
||||
(`gateway.auth.allowTailscale: true`) oder nicht über Loopback laufendes
|
||||
`gateway.auth.mode: "trusted-proxy"` erfüllen die Connect-Auth-Prüfung über
|
||||
Request-Header statt über `connect.params.auth.*`.
|
||||
- `gateway.auth.mode: "none"` für private Ingress überspringt die Connect-Auth mit gemeinsamem Secret
|
||||
vollständig; setzen Sie diesen Modus nicht an öffentlichem/nicht vertrauenswürdigem Ingress ein.
|
||||
- Nach dem Pairing stellt das Gateway ein **Gerätetoken** aus, das auf die Verbindungs-
|
||||
Rolle + Scopes begrenzt ist. Es wird in `hello-ok.auth.deviceToken` zurückgegeben und sollte
|
||||
- `gateway.auth.mode: "none"` für privaten Ingress überspringt die Connect-Auth per Shared Secret
|
||||
vollständig; diesen Modus nicht auf öffentlichem/nicht vertrauenswürdigem Ingress bereitstellen.
|
||||
- Nach dem Pairing stellt das Gateway ein **Device-Token** aus, das auf die Rollen + Scopes
|
||||
der Verbindung begrenzt ist. Es wird in `hello-ok.auth.deviceToken` zurückgegeben und sollte
|
||||
vom Client für zukünftige Verbindungen persistiert werden.
|
||||
- Clients sollten das primäre `hello-ok.auth.deviceToken` nach jeder
|
||||
erfolgreichen Verbindung persistieren.
|
||||
- Eine erneute Verbindung mit diesem **gespeicherten** Gerätetoken sollte außerdem den gespeicherten
|
||||
genehmigten Scope-Satz für dieses Token wiederverwenden. Dadurch bleibt bereits gewährter
|
||||
Lese-/Probe-/Status-Zugriff erhalten und es wird vermieden, dass Reconnects stillschweigend auf einen
|
||||
engeren impliziten Admin-Only-Scope zurückfallen.
|
||||
- Die normale Priorität bei Connect-Auth ist zuerst explizites gemeinsames Token/Passwort, dann
|
||||
explizites `deviceToken`, dann gespeichertes gerätespezifisches Token, dann Bootstrap-Token.
|
||||
- Zusätzliche Einträge in `hello-ok.auth.deviceTokens` sind Bootstrap-Übergabetoken.
|
||||
Persistieren Sie diese nur, wenn die Verbindung Bootstrap-Auth auf einem vertrauenswürdigen Transport
|
||||
wie `wss://` oder Loopback/lokal verwendet hat.
|
||||
- Beim erneuten Verbinden mit diesem **gespeicherten** Device-Token sollte auch der gespeicherte
|
||||
genehmigte Scope-Satz für dieses Token wiederverwendet werden. Dadurch bleibt bereits gewährter
|
||||
Zugriff für Lesen/Probe/Status erhalten und es wird vermieden, dass Reconnects stillschweigend auf einen
|
||||
engeren impliziten Admin-only-Scope zusammenfallen.
|
||||
- Clientseitiger Aufbau der Connect-Auth (`selectConnectAuth` in
|
||||
`src/gateway/client.ts`):
|
||||
- `auth.password` ist orthogonal und wird immer weitergeleitet, wenn gesetzt.
|
||||
- `auth.token` wird in dieser Prioritätsreihenfolge befüllt: zuerst explizites Shared Token,
|
||||
dann ein explizites `deviceToken`, dann ein gespeichertes gerätespezifisches Token (indiziert nach
|
||||
`deviceId` + `role`).
|
||||
- `auth.bootstrapToken` wird nur gesendet, wenn keines der oben genannten ein
|
||||
`auth.token` aufgelöst hat. Ein Shared Token oder ein aufgelöstes Device-Token unterdrückt es.
|
||||
- Automatische Promotion eines gespeicherten Device-Tokens beim einmaligen
|
||||
Retry bei `AUTH_TOKEN_MISMATCH` ist **nur für vertrauenswürdige Endpunkte** aktiviert —
|
||||
Loopback oder `wss://` mit angeheftetem `tlsFingerprint`. Öffentliches `wss://`
|
||||
ohne Pinning qualifiziert sich nicht.
|
||||
- Zusätzliche Einträge in `hello-ok.auth.deviceTokens` sind Bootstrap-Handoff-Tokens.
|
||||
Persistiere sie nur, wenn die Verbindung Bootstrap-Auth über einen vertrauenswürdigen Transport
|
||||
wie `wss://` oder Loopback/lokales Pairing verwendet hat.
|
||||
- Wenn ein Client ein **explizites** `deviceToken` oder explizite `scopes` angibt, bleibt dieser
|
||||
vom Aufrufer angeforderte Scope-Satz maßgeblich; gecachte Scopes werden nur
|
||||
wiederverwendet, wenn der Client das gespeicherte gerätespezifische Token wiederverwendet.
|
||||
- Gerätetoken können über `device.token.rotate` und
|
||||
`device.token.revoke` rotiert/widerrufen werden (erfordert Scope `operator.pairing`).
|
||||
- Die Ausgabe/Rotation von Token bleibt auf den genehmigten Rollensatz begrenzt, der im
|
||||
Pairing-Eintrag dieses Geräts gespeichert ist; durch das Rotieren eines Tokens kann das Gerät nicht auf eine
|
||||
Rolle erweitert werden, die durch die Pairing-Genehmigung nie erteilt wurde.
|
||||
- Für Sitzungen mit gepaarten Gerätetokens ist die Geräteverwaltung auf das eigene Gerät beschränkt, sofern der
|
||||
vom Aufrufer angeforderte Scope-Satz maßgeblich; gecachte Scopes werden nur wiederverwendet, wenn
|
||||
der Client das gespeicherte gerätespezifische Token wiederverwendet.
|
||||
- Device-Tokens können über `device.token.rotate` und
|
||||
`device.token.revoke` rotiert/widerrufen werden (erfordert den Scope `operator.pairing`).
|
||||
- Ausstellung/Rotation von Tokens bleibt auf den genehmigten Rollensatz beschränkt, der im
|
||||
Pairing-Eintrag dieses Geräts aufgezeichnet ist; durch die Rotation eines Tokens kann ein Gerät nicht
|
||||
auf eine Rolle erweitert werden, die durch die Pairing-Genehmigung nie gewährt wurde.
|
||||
- Für Sitzungen mit Tokens gekoppelter Geräte ist die Geräteverwaltung selbstbegrenzt, sofern der
|
||||
Aufrufer nicht zusätzlich `operator.admin` hat: Nicht-Admin-Aufrufer können nur ihren **eigenen**
|
||||
Geräteeintrag entfernen/widerrufen/rotieren.
|
||||
- `device.token.rotate` prüft außerdem den angeforderten Operator-Scope-Satz gegen die
|
||||
aktuellen Sitzungsscopes des Aufrufers. Nicht-Admin-Aufrufer können ein Token nicht in einen
|
||||
breiteren Operator-Scope-Satz rotieren, als sie bereits besitzen.
|
||||
- Auth-Fehler enthalten `error.details.code` plus Wiederherstellungshinweise:
|
||||
- `error.details.canRetryWithDeviceToken` (boolean)
|
||||
umfassenderen Operator-Scope-Satz rotieren, als sie bereits besitzen.
|
||||
- Auth-Fehler enthalten `error.details.code` sowie Wiederherstellungshinweise:
|
||||
- `error.details.canRetryWithDeviceToken` (boolesch)
|
||||
- `error.details.recommendedNextStep` (`retry_with_device_token`, `update_auth_configuration`, `update_auth_credentials`, `wait_then_retry`, `review_auth_configuration`)
|
||||
- Client-Verhalten für `AUTH_TOKEN_MISMATCH`:
|
||||
- Vertrauenswürdige Clients können einen begrenzten Wiederholungsversuch mit einem gecachten gerätespezifischen Token unternehmen.
|
||||
- Wenn dieser Wiederholungsversuch fehlschlägt, sollten Clients automatische Reconnect-Schleifen beenden und Hinweise für erforderliche Operatoraktionen anzeigen.
|
||||
- Client-Verhalten bei `AUTH_TOKEN_MISMATCH`:
|
||||
- Vertrauenswürdige Clients können einen begrenzten Retry mit einem gecachten gerätespezifischen Token versuchen.
|
||||
- Wenn dieser Retry fehlschlägt, sollten Clients automatische Reconnect-Schleifen stoppen und Hinweise für Operator-Aktionen anzeigen.
|
||||
|
||||
## Geräteidentität + Pairing
|
||||
|
||||
- Knoten sollten eine stabile Geräteidentität (`device.id`) angeben, die von einem
|
||||
- Nodes sollten eine stabile Geräteidentität (`device.id`) angeben, die aus einem
|
||||
Schlüsselpaar-Fingerprint abgeleitet ist.
|
||||
- Gateways stellen Token pro Gerät + Rolle aus.
|
||||
- Pairing-Genehmigungen sind für neue Geräte-IDs erforderlich, sofern keine lokale automatische Genehmigung
|
||||
aktiviert ist.
|
||||
- Die automatische Pairing-Genehmigung konzentriert sich auf direkte lokale local loopback-Verbindungen.
|
||||
- OpenClaw hat außerdem einen engen backend-/containerlokalen Self-Connect-Pfad für
|
||||
vertrauenswürdige Helper-Flows mit gemeinsamem Secret.
|
||||
- Same-Host-Tailnet- oder LAN-Verbindungen werden für das Pairing weiterhin als remote behandelt und
|
||||
- Gateways stellen Tokens pro Gerät + Rolle aus.
|
||||
- Pairing-Genehmigungen sind für neue Geräte-IDs erforderlich, sofern lokale automatische Genehmigung
|
||||
nicht aktiviert ist.
|
||||
- Die automatische Pairing-Genehmigung ist auf direkte lokale Loopback-Verbindungen ausgerichtet.
|
||||
- OpenClaw hat außerdem einen engen lokalen Self-Connect-Pfad für Backend-/Container-
|
||||
Kontexte für vertrauenswürdige Shared-Secret-Helferabläufe.
|
||||
- Verbindungen über dasselbe Host-Tailnet oder LAN werden für Pairing weiterhin als remote behandelt und
|
||||
erfordern eine Genehmigung.
|
||||
- Alle WS-Clients müssen während `connect` eine `device`-Identität angeben (operator + node).
|
||||
- Alle WS-Clients müssen während `connect` die Geräteidentität `device` angeben (`operator` + `node`).
|
||||
Control UI kann sie nur in diesen Modi weglassen:
|
||||
- `gateway.controlUi.allowInsecureAuth=true` für localhost-only inkompatibilitätsbedingte unsichere HTTP-Unterstützung.
|
||||
- erfolgreiche `gateway.auth.mode: "trusted-proxy"`-Authentifizierung für operator Control UI.
|
||||
- `gateway.controlUi.dangerouslyDisableDeviceAuth=true` (Break-Glass, schwerwiegende Sicherheitsverschlechterung).
|
||||
- Alle Verbindungen müssen den vom Server bereitgestellten `connect.challenge`-Nonce signieren.
|
||||
- `gateway.controlUi.allowInsecureAuth=true` für localhost-only-Kompatibilität mit unsicherem HTTP.
|
||||
- erfolgreiche `gateway.auth.mode: "trusted-proxy"`-Authentifizierung der Operator-Control-UI.
|
||||
- `gateway.controlUi.dangerouslyDisableDeviceAuth=true` (Break-Glass, erhebliche Sicherheitsabsenkung).
|
||||
- Alle Verbindungen müssen die vom Server bereitgestellte Nonce `connect.challenge` signieren.
|
||||
|
||||
### Diagnostik für die Migration der Geräteauthentifizierung
|
||||
### Migrationsdiagnostik für Geräte-Auth
|
||||
|
||||
Für Legacy-Clients, die noch das Signaturverhalten vor der Challenge verwenden, gibt `connect` jetzt
|
||||
`DEVICE_AUTH_*`-Detailcodes unter `error.details.code` mit einem stabilen `error.details.reason` zurück.
|
||||
Detailcodes `DEVICE_AUTH_*` unter `error.details.code` mit einem stabilen `error.details.reason` zurück.
|
||||
|
||||
Häufige Migrationsfehler:
|
||||
|
||||
| Nachricht | details.code | details.reason | Bedeutung |
|
||||
| ---------------------------- | -------------------------------- | ------------------------ | --------------------------------------------------- |
|
||||
| `device nonce required` | `DEVICE_AUTH_NONCE_REQUIRED` | `device-nonce-missing` | Client hat `device.nonce` weggelassen (oder leer gesendet). |
|
||||
| `device nonce mismatch` | `DEVICE_AUTH_NONCE_MISMATCH` | `device-nonce-mismatch` | Client hat mit einem veralteten/falschen Nonce signiert. |
|
||||
| `device signature invalid` | `DEVICE_AUTH_SIGNATURE_INVALID` | `device-signature` | Die Signatur-Payload stimmt nicht mit der v2-Payload überein. |
|
||||
| `device signature expired` | `DEVICE_AUTH_SIGNATURE_EXPIRED` | `device-signature-stale` | Der signierte Zeitstempel liegt außerhalb der zulässigen Abweichung. |
|
||||
| `device identity mismatch` | `DEVICE_AUTH_DEVICE_ID_MISMATCH` | `device-id-mismatch` | `device.id` stimmt nicht mit dem Public-Key-Fingerprint überein. |
|
||||
| `device public key invalid` | `DEVICE_AUTH_PUBLIC_KEY_INVALID` | `device-public-key` | Format/Kanonisierung des Public Key ist fehlgeschlagen. |
|
||||
| Meldung | details.code | details.reason | Bedeutung |
|
||||
| --------------------------- | -------------------------------- | ------------------------ | ----------------------------------------------------------- |
|
||||
| `device nonce required` | `DEVICE_AUTH_NONCE_REQUIRED` | `device-nonce-missing` | Client hat `device.nonce` weggelassen (oder leer gesendet). |
|
||||
| `device nonce mismatch` | `DEVICE_AUTH_NONCE_MISMATCH` | `device-nonce-mismatch` | Client hat mit einer veralteten/falschen Nonce signiert. |
|
||||
| `device signature invalid` | `DEVICE_AUTH_SIGNATURE_INVALID` | `device-signature` | Signatur-Payload entspricht nicht der v2-Payload. |
|
||||
| `device signature expired` | `DEVICE_AUTH_SIGNATURE_EXPIRED` | `device-signature-stale` | Signierter Zeitstempel liegt außerhalb des erlaubten Skews. |
|
||||
| `device identity mismatch` | `DEVICE_AUTH_DEVICE_ID_MISMATCH` | `device-id-mismatch` | `device.id` entspricht nicht dem Public-Key-Fingerprint. |
|
||||
| `device public key invalid` | `DEVICE_AUTH_PUBLIC_KEY_INVALID` | `device-public-key` | Public-Key-Format/Kanonisierung fehlgeschlagen. |
|
||||
|
||||
Migrationsziel:
|
||||
|
||||
- Warten Sie immer auf `connect.challenge`.
|
||||
- Signieren Sie die v2-Payload, die den Server-Nonce enthält.
|
||||
- Senden Sie denselben Nonce in `connect.params.device.nonce`.
|
||||
- Immer auf `connect.challenge` warten.
|
||||
- Die v2-Payload signieren, die die Server-Nonce enthält.
|
||||
- Dieselbe Nonce in `connect.params.device.nonce` senden.
|
||||
- Die bevorzugte Signatur-Payload ist `v3`, die zusätzlich zu den Feldern für device/client/role/scopes/token/nonce auch `platform` und `deviceFamily` bindet.
|
||||
- Legacy-`v2`-Signaturen bleiben aus Kompatibilitätsgründen akzeptiert, aber das Pinning der gepaarten Geräte-
|
||||
Metadaten steuert weiterhin die Befehlsrichtlinie beim Wiederverbinden.
|
||||
- Legacy-Signaturen vom Typ `v2` bleiben aus Kompatibilitätsgründen akzeptiert, aber das Anheften von Metadaten gekoppelter Geräte steuert weiterhin die Befehlsrichtlinie beim erneuten Verbinden.
|
||||
|
||||
## TLS + Pinning
|
||||
|
||||
- TLS wird für WS-Verbindungen unterstützt.
|
||||
- Clients können optional den Gateway-Zertifikat-Fingerprint pinnen (siehe `gateway.tls`-
|
||||
Konfiguration plus `gateway.remote.tlsFingerprint` oder CLI `--tls-fingerprint`).
|
||||
- Clients können optional den Fingerprint des Gateway-Zertifikats anheften (siehe Konfiguration `gateway.tls`
|
||||
sowie `gateway.remote.tlsFingerprint` oder CLI `--tls-fingerprint`).
|
||||
|
||||
## Scope
|
||||
## Umfang
|
||||
|
||||
Dieses Protokoll stellt die **vollständige Gateway-API** bereit (status, channels, models, chat,
|
||||
agent, sessions, nodes, approvals usw.). Die genaue Oberfläche ist durch die
|
||||
Dieses Protokoll stellt die **vollständige Gateway-API** bereit (Status, Channels, Modelle, Chat,
|
||||
Agent, Sitzungen, Nodes, Genehmigungen usw.). Die genaue Oberfläche wird durch die
|
||||
TypeBox-Schemas in `src/gateway/protocol/schema.ts` definiert.
|
||||
|
||||
@ -0,0 +1,132 @@
|
||||
---
|
||||
x-i18n:
|
||||
generated_at: "2026-04-16T06:22:19Z"
|
||||
model: gpt-5.4
|
||||
provider: openai
|
||||
source_hash: 95e56c5411204363676f002059c942201503e2359515d1a4b409882cc2e04920
|
||||
source_path: refactor/async-exec-duplicate-completion-investigation.md
|
||||
workflow: 15
|
||||
---
|
||||
|
||||
# Untersuchung zu doppeltem Abschluss bei Async Exec
|
||||
|
||||
## Umfang
|
||||
|
||||
- Sitzung: `agent:main:telegram:group:-1003774691294:topic:1`
|
||||
- Symptom: Derselbe Async-Exec-Abschluss für Sitzung/Run `keen-nexus` wurde in LCM zweimal als Nutzer-Turn erfasst.
|
||||
- Ziel: Ermitteln, ob dies höchstwahrscheinlich eine doppelte Sitzungsinjektion oder ein einfacher Retry bei der ausgehenden Zustellung ist.
|
||||
|
||||
## Schlussfolgerung
|
||||
|
||||
Am wahrscheinlichsten ist dies eine **doppelte Sitzungsinjektion**, nicht ein reiner Retry bei der ausgehenden Zustellung.
|
||||
|
||||
Die stärkste Lücke auf Gateway-Seite liegt im **Pfad für Node-Exec-Abschlüsse**:
|
||||
|
||||
1. Ein Exec-Abschluss auf Node-Seite sendet `exec.finished` mit der vollständigen `runId`.
|
||||
2. Gateway `server-node-events` wandelt das in ein System-Event um und fordert einen Heartbeat an.
|
||||
3. Der Heartbeat-Run injiziert den geleerten System-Event-Block in den Agent-Prompt.
|
||||
4. Der eingebettete Runner persistiert diesen Prompt als neuen Nutzer-Turn im Sitzungs-Transkript.
|
||||
|
||||
Wenn dasselbe `exec.finished` aus irgendeinem Grund (Replay, erneute Verbindung mit Duplikat, erneutes Senden Upstream, duplizierter Producer) zweimal mit derselben `runId` beim Gateway ankommt, hat OpenClaw auf diesem Pfad derzeit **keine Idempotenzprüfung, die nach `runId`/`contextKey` schlüsselt**. Die zweite Kopie wird zu einer zweiten Nutzernachricht mit identischem Inhalt.
|
||||
|
||||
## Exakter Code-Pfad
|
||||
|
||||
### 1. Producer: Event für Node-Exec-Abschluss
|
||||
|
||||
- `src/node-host/invoke.ts:340-360`
|
||||
- `sendExecFinishedEvent(...)` sendet `node.event` mit dem Event `exec.finished`.
|
||||
- Die Payload enthält `sessionKey` und die vollständige `runId`.
|
||||
|
||||
### 2. Gateway-Event-Ingestion
|
||||
|
||||
- `src/gateway/server-node-events.ts:574-640`
|
||||
- Verarbeitet `exec.finished`.
|
||||
- Baut Text auf:
|
||||
- `Exec finished (node=..., id=<runId>, code ...)`
|
||||
- Stellt ihn in die Queue über:
|
||||
- `enqueueSystemEvent(text, { sessionKey, contextKey: runId ? \`exec:${runId}\` : "exec", trusted: false })`
|
||||
- Fordert sofort ein Wake an:
|
||||
- `requestHeartbeatNow(scopedHeartbeatWakeOptions(sessionKey, { reason: "exec-event" }))`
|
||||
|
||||
### 3. Schwäche bei der Deduplizierung von System-Events
|
||||
|
||||
- `src/infra/system-events.ts:90-115`
|
||||
- `enqueueSystemEvent(...)` unterdrückt nur **aufeinanderfolgende doppelte Texte**:
|
||||
- `if (entry.lastText === cleaned) return false`
|
||||
- Es speichert `contextKey`, verwendet `contextKey` jedoch **nicht** für Idempotenz.
|
||||
- Nach dem Drain wird die Duplikatunterdrückung zurückgesetzt.
|
||||
|
||||
Das bedeutet: Ein erneut gesendetes `exec.finished` mit derselben `runId` kann später erneut akzeptiert werden, obwohl der Code bereits einen stabilen Kandidaten für Idempotenz hatte (`exec:<runId>`).
|
||||
|
||||
### 4. Wake-Handling ist nicht der primäre Verursacher von Duplikaten
|
||||
|
||||
- `src/infra/heartbeat-wake.ts:79-117`
|
||||
- Wakes werden nach `(agentId, sessionKey)` zusammengefasst.
|
||||
- Doppelte Wake-Anfragen für dasselbe Ziel werden zu einem ausstehenden Wake-Eintrag zusammengeführt.
|
||||
|
||||
Dadurch ist **doppeltes Wake-Handling allein** eine schwächere Erklärung als doppelte Event-Ingestion.
|
||||
|
||||
### 5. Heartbeat verarbeitet das Event und macht daraus Prompt-Eingabe
|
||||
|
||||
- `src/infra/heartbeat-runner.ts:535-574`
|
||||
- Preflight prüft ausstehende System-Events vorab und klassifiziert Runs mit Exec-Events.
|
||||
- `src/auto-reply/reply/session-system-events.ts:86-90`
|
||||
- `drainFormattedSystemEvents(...)` leert die Queue für die Sitzung.
|
||||
- `src/auto-reply/reply/get-reply-run.ts:400-427`
|
||||
- Der geleerte System-Event-Block wird dem Agent-Prompt-Body vorangestellt.
|
||||
|
||||
### 6. Injektionspunkt ins Transkript
|
||||
|
||||
- `src/agents/pi-embedded-runner/run/attempt.ts:2000-2017`
|
||||
- `activeSession.prompt(effectivePrompt)` übergibt den vollständigen Prompt an die eingebettete PI-Sitzung.
|
||||
- Das ist der Punkt, an dem der vom Abschluss abgeleitete Prompt als persistierter Nutzer-Turn gespeichert wird.
|
||||
|
||||
Sobald also dasselbe System-Event zweimal in den Prompt eingebaut wird, sind doppelte LCM-Nutzernachrichten zu erwarten.
|
||||
|
||||
## Warum ein einfacher Retry bei der ausgehenden Zustellung weniger wahrscheinlich ist
|
||||
|
||||
Es gibt einen realen Fehlerpfad für ausgehende Zustellung im Heartbeat-Runner:
|
||||
|
||||
- `src/infra/heartbeat-runner.ts:1194-1242`
|
||||
- Die Antwort wird zuerst generiert.
|
||||
- Die ausgehende Zustellung erfolgt später über `deliverOutboundPayloads(...)`.
|
||||
- Ein Fehler dort liefert `{ status: "failed" }` zurück.
|
||||
|
||||
Für denselben System-Event-Queue-Eintrag reicht das allein jedoch **nicht aus**, um die doppelten Nutzer-Turns zu erklären:
|
||||
|
||||
- `src/auto-reply/reply/session-system-events.ts:86-90`
|
||||
- Die System-Event-Queue ist bereits geleert, bevor die ausgehende Zustellung erfolgt.
|
||||
|
||||
Ein Retry beim Kanalversand allein würde das exakt gleiche Queue-Event also nicht erneut erzeugen. Das könnte fehlende/fehlgeschlagene externe Zustellung erklären, aber für sich genommen nicht eine zweite identische Nutzer-Nachricht in der Sitzung.
|
||||
|
||||
## Sekundäre Möglichkeit mit geringerer Sicherheit
|
||||
|
||||
Es gibt eine Retry-Schleife für vollständige Runs im Agent-Runner:
|
||||
|
||||
- `src/auto-reply/reply/agent-runner-execution.ts:741-1473`
|
||||
- Bestimmte transiente Fehler können den gesamten Run erneut versuchen und denselben `commandBody` erneut absenden.
|
||||
|
||||
Das kann einen persistierten Nutzer-Prompt **innerhalb derselben Antwortausführung** duplizieren, wenn der Prompt bereits angehängt wurde, bevor die Retry-Bedingung ausgelöst hat.
|
||||
|
||||
Ich stufe das niedriger ein als doppelte `exec.finished`-Ingestion, weil:
|
||||
|
||||
- der beobachtete Abstand bei etwa 51 Sekunden lag, was eher wie ein zweiter Wake/Turn aussieht als wie ein In-Process-Retry;
|
||||
- im Bericht bereits wiederholte Fehler beim Nachrichtensenden erwähnt werden, was eher auf einen separaten späteren Turn hindeutet als auf einen unmittelbaren Modell-/Runtime-Retry.
|
||||
|
||||
## Hypothese zur Grundursache
|
||||
|
||||
Hypothese mit der höchsten Sicherheit:
|
||||
|
||||
- Der Abschluss von `keen-nexus` kam über den **Node-Exec-Event-Pfad**.
|
||||
- Dasselbe `exec.finished` wurde zweimal an `server-node-events` zugestellt.
|
||||
- Das Gateway akzeptierte beide, weil `enqueueSystemEvent(...)` nicht nach `contextKey` / `runId` dedupliziert.
|
||||
- Jedes akzeptierte Event löste einen Heartbeat aus und wurde als Nutzer-Turn in das PI-Transkript injiziert.
|
||||
|
||||
## Vorgeschlagener kleiner, gezielter Fix
|
||||
|
||||
Falls ein Fix gewünscht ist, ist die kleinste Änderung mit hohem Nutzen:
|
||||
|
||||
- dafür zu sorgen, dass die Idempotenz von Exec-/System-Events `contextKey` für einen kurzen Zeitraum berücksichtigt, zumindest für exakte Wiederholungen von `(sessionKey, contextKey, text)`;
|
||||
- oder eine dedizierte Deduplizierung in `server-node-events` für `exec.finished` hinzuzufügen, geschlüsselt nach `(sessionKey, runId, event kind)`.
|
||||
|
||||
Das würde erneut gesendete `exec.finished`-Duplikate direkt blockieren, bevor sie zu Sitzungsturns werden.
|
||||
Loading…
Reference in New Issue
Block a user