Self-Correction Analysis Pipeline

Automated detection of user corrections/nudges in session logs and remediation (memory store, TOOLS.md rules, and proposed AGENTS/skill changes).

Multi-language support

Correction detection uses phrases (e.g. “that was wrong”, “try again”) from the same system as memory triggers:

  • English phrases are built in; other languages come from .language-keywords.json.
  • Run openclaw hybrid-mem build-languages once (or when you add new languages). It detects top languages from your memory and translates correction signals (and other keyword groups) into those languages. After that, self-correction-extract matches user messages in any of those languages.

So for full multi-language support: run build-languages, then use the self-correction commands or nightly job as below.


Emoji as signals

User messages that contain emoji are treated as implicit feedback and feed into both pipelines:

  • Negative emoji (e.g. 👎 😠 😤 💩 🙁 😞 😒) — Treated as correction signals. A message containing one of these (alone or with text) is picked up by self-correction-extract. If you add a follow-up message explaining what was wrong, the analyzer gets both: the emoji shows you were unhappy, and the next message shows what to fix. Useful when you react with a thumbs-down or angry face and then type “the command should use –dry-run first”.
  • Positive emoji (e.g. 👍 ❤️ 😊 😄 🔥 ⭐ ✨) — Treated as reinforcement (enforcer). A message containing one of these is picked up by extract-reinforcement and used to reinforce the preceding assistant turn (e.g. boost confidence on recalled facts or procedures). A lone “👍” or “❤️” after a good answer is enough to signal “I liked that” and strengthen the associated behavior in memory.

Emoji are language-agnostic and are always included in detection; no need to add them to .language-keywords.json. The same rate limits, confidence thresholds, and remediation caps apply.

For a short user-facing overview of how your replies and emoji feed into reinforcement and correction, see FAQ — How does the agent learn from my reactions?.


Learning your feedback wording (user-specific phrases)

Different users express praise and frustration differently. The plugin can learn your wording from session logs in a model-agnostic way (nano-tier and heavy-tier from your plugin config):

  1. Pre-filter: Messages that already match reinforcement/correction phrases are skipped. A nano-tier model labels the rest as positive/negative/neutral feedback.
  2. Phrase extraction: Only positive/negative messages are sent to a heavy-tier model to extract candidate phrases.
  3. Window: Omitting --days uses 30 days the first time (or when no .user-feedback-phrases.json exists), then 3 days on later runs—suitable for a weekly nightly.
# Auto window (30 days first run, 3 days after); models from config
openclaw hybrid-mem analyze-feedback-phrases

# Optional: override window or model
openclaw hybrid-mem analyze-feedback-phrases --days 30 --model <heavy-model>

# Merge discovered phrases into .user-feedback-phrases.json (used by detection from then on)
openclaw hybrid-mem analyze-feedback-phrases --learn

Discovered phrases are saved under ~/.openclaw/memory/.user-feedback-phrases.json and are merged with the built-in correction and reinforcement lists when building the detection regexes. So after you run with --learn, both self-correction extract and reinforcement extract will match your (and anyone else on the same install’s) typical phrases. Run it periodically (e.g. in a weekly nightly) to keep the list up to date.

Malformed JSONL: If a session file has a bad line (truncated write, partial copy), analyze-feedback-phrases logs a warning and continues with other files. The run aborts with error only when no session file could be read; otherwise sessionsScanned counts successfully read files and phrase extraction proceeds from valid lines.


Commands

1. Extract incidents (Phase 1)

Scans session JSONL from the last N days and finds user messages that look like corrections, using the merged correction signals (English + translated from .language-keywords.json).

# Default: last 3 days, print summary (and incidents to stdout if any)
openclaw hybrid-mem self-correction-extract

# Last 7 days, write incidents to a file for review or Phase 2
openclaw hybrid-mem self-correction-extract --days 7 --output /path/to/incidents.json
  • Sessions are read from ~/.openclaw/agents/*/sessions/*.jsonl (same as session distillation).
  • Skip filters: heartbeat prompts, cron job text, compaction messages, sub-agent announcements, very short messages.
  • Output: { incidents: [...], sessionsScanned }. Each incident has userMessage, precedingAssistant, followingAssistant, timestamp, sessionFile, and optionally precedingUserMessage, toolCallSequence, and recalledMemoryIds (20-turn lookback via shared session-signal context).

2. Analyze + remediate + report (Phases 2–4)

Takes incidents (from a file or by running extract in memory), sends them to the LLM for categorization and remediation type, then:

  • MEMORY_STORE: Stores the suggested fact. Dedup is exact text plus semantic (embedding similarity) when selfCorrection.semanticDedup is true (default). Threshold configurable via selfCorrection.semanticDedupThreshold (default 0.92).
  • TOOLS_RULE: By default, suggested rules are applied (inserted under the configured section, e.g. “Self-correction rules”). To opt out of applying: set selfCorrection.applyToolsByDefault: false in config, or pass --no-apply-tools for that run. When opt-out is set, use --approve to apply for a run. Auto-rewrite (opt-in): set selfCorrection.autoRewriteTools: true to have the LLM rewrite the whole TOOLS.md instead of section insert.
  • AGENTS_RULE / SKILL_UPDATE: Always added to the report as proposals (no auto-apply).

Cap: 5 auto-remediations per run. Report is written to memory/reports/self-correction-YYYY-MM-DD.md.

# Use incidents from file
openclaw hybrid-mem self-correction-run --extract /path/to/incidents.json

# Run extract in memory then analyze (no file)
openclaw hybrid-mem self-correction-run

# Preview only (no store, no TOOLS changes)
openclaw hybrid-mem self-correction-run --dry-run

# Skip applying TOOLS rules this run (only suggest in report)
openclaw hybrid-mem self-correction-run --no-apply-tools

# Force apply when config has applyToolsByDefault: false
openclaw hybrid-mem self-correction-run --approve

# Custom workspace and model
openclaw hybrid-mem self-correction-run --workspace /path/to/project --model gemini-2.0-flash
  • Workspace (for TOOLS.md and memory/reports/): --workspace, or OPENCLAW_WORKSPACE, or ~/.openclaw/workspace.
  • Model: --model or heavy-tier resolution (llm.heavy primary, then built-in heavy default).
  • Fallbacks: when llm.heavy has one primary model, fallback candidates are merged from llm.fallbackModel and distill.fallbackModels (de-duplicated) and tried in order.
  • --model override fallback behavior: keeps configured fallback candidates by prepending the heavy-tier primary model to the same fallback chain.
  • --no-apply-tools: Do not insert TOOLS rules this run (only suggest in report). Opt-out from default apply.
  • --approve: Force apply TOOLS rules this run when config has applyToolsByDefault: false.

M3 / MiniMax output envelopes and parsing (#1876)

Some models (notably MiniMax M3) return structured JSON in message.tool_calls[].function.arguments with an empty message.content string, or wrap payloads in envelopes such as { "items": [...] } or { "tool_calls": [...] }.

The pipeline handles this in two layers:

  1. extractAssistantMessageText (shared util, wired into chatComplete and direct OpenAI bypass sites) — extracts parseable text from string content, array text blocks, native tool_calls, or reasoning fallbacks (in that order).
  2. parseStructuredItems / parseStructuredItemsAcceptingEmpty (shared util in utils/llm-json-array.ts) — parses JSON arrays, M3-style envelopes, NDJSON lines, or single valid objects using a per-item validator.

Self-correction analysis uses both when parsing remediation items from the LLM.

Empty arrays ([]) vs parse failure

By default, parseStructuredItems returns null when the model emits a valid but empty array ([]), because it cannot distinguish “no items” from “keep scanning for another array span.” That is correct for callers that only care about non-empty results.

For pipelines where zero items is a successful LLM answer (no remediations, no proposals, etc.), use parseStructuredItemsAcceptingEmpty instead (or pass { acceptEmptyArray: true } to parseStructuredItems). Then:

Return value Meaning
null Unparseable or empty model output — treat as parse/model failure (or retry/repair).
[] Parsed successfully; model explicitly returned no items.
[...] Parsed successfully with one or more valid items.

Call sites today: parseSelfCorrectionLLMResponse (self-correction-run), extract-reinforcement analysis, generate-proposals. All branch on parsed === null for failure; an empty list is handled by downstream logic (e.g. semantic_empty, partial_no_matches, or simply no stores applied).

Do not combine parseStructuredItemsAcceptingEmpty with a failure check that treats empty arrays as errors (for example !parsed || parsed.length === 0) — that turns [] back into a false parse failure. Use parsed === null only for failure; use parsed.length === 0 when empty output should trigger a separate semantic/policy error.

Batch resume state

Long runs persist progress under the workspace temp dir (not memory/reports/):

<workspace>/tmp/self-correction/m3-batches-<fingerprint>.json

The fingerprint includes incidents, model, batch size, dry-run, and apply-tools flags. Changing --model or batch options starts a fresh run instead of reusing stale analysed items.

  • State is not written during --dry-run.
  • State is removed on successful completion (including suspect zero-parsed exits that finish the run).
  • Stale state files in the same directory are pruned when a new run starts.

Configure batch size with selfCorrection.analysisBatchSize (default 5 for MiniMax/M3 models, 25 otherwise). When a batch returns fewer items than incidents (or hits output truncation), the pipeline auto-splits the batch in half recursively. Optional selfCorrection.batchDelayMs (default 250) paces sequential batches for MiniMax rate limits.

Run status values

Status Meaning
success_analyzed Incidents were analysed and remediations parsed/applied normally.
success_no_incidents Scan/extract found zero correction incidents.
skipped_cooldown Run skipped due to scan cooldown (not a zero-incident success).
failed_parse LLM response could not be parsed into remediation items.
failed_partial One or more batches completed; a later batch failed. Partial remediations may have been applied; resume state is retained under tmp/self-correction/. Cron treats this as a semantic failure.
failed_suspect_zero_parsed Incidents were present but zero remediation items were parsed (suspect model/parser failure). Cron validation treats this as a semantic failure.

Verbose diagnostics

When --verbose is set (or diagnostics are logged at info level), the run prints counters such as:

  • Batches started
  • Parsed item lines
  • Retry / fallback lines
  • Parse failures / unparseable failures
  • parse_success=true|false summary line

These align with verified M3 batch logs (expected=N parsed=M warnings per batch when counts diverge).


Nightly cron job (optional)

To run the full pipeline nightly (e.g. 02:30 Europe/Stockholm):

  1. Extract from the last 3 days (uses multi-language correction signals if build-languages has been run).
  2. Analyze with the configured LLM (e.g. Gemini for cost/context).
  3. Auto-remediate (memory store + TOOLS.md append; cap 5).
  4. Report to memory/reports/self-correction-YYYY-MM-DD.md.

Example job definition (schedule format depends on your OpenClaw/jobs setup):

{
  "name": "self-correction-analysis",
  "schedule": "30 2 * * *",
  "tz": "Europe/Stockholm",
  "message": "Run the nightly self-correction analysis: openclaw hybrid-mem self-correction-run. Uses last 3 days of sessions, multi-language correction detection from .language-keywords.json (run build-languages first for non-English). Report is written to workspace memory/reports/self-correction-YYYY-MM-DD.md.",
  "sessionTarget": "isolated",
  "model": "sonnet"
}

If your runner executes shell commands, you can instead run:

openclaw hybrid-mem self-correction-run

Ensure OPENCLAW_WORKSPACE (or your workspace root) is set so the report and TOOLS.md paths are correct.


Configuration (optional)

Under plugins.entries["openclaw-hybrid-memory"].config.selfCorrection:

Option Default Description
semanticDedup true Skip storing facts that are semantically similar to existing ones (embedding similarity).
semanticDedupThreshold 0.92 Similarity threshold 0–1; higher = stricter (fewer near-duplicates stored).
toolsSection "Self-correction rules" TOOLS.md section heading under which to insert rules.
applyToolsByDefault true When true, apply (insert) suggested TOOLS rules by default. Set false to only suggest (then use --approve to apply). Use CLI --no-apply-tools to skip applying for one run.
autoRewriteTools false When true, LLM rewrites TOOLS.md to integrate new rules (no duplicates/contradictions). When false, use section insert.
analyzeViaSpawn false When true and incident count > spawnThreshold, run Phase 2 (analyze) via openclaw sessions spawn --model <spawnModel> for large context (e.g. Gemini).
spawnThreshold 15 Use spawn for Phase 2 when incidents exceed this count.
spawnModel "gemini" Model for spawn when analyzeViaSpawn is true.
analysisBatchSize 5 (MiniMax/M3) / 25 (others) Incidents per LLM analysis batch. Auto-split-on-mismatch recovers partial M3 batches.
batchDelayMs 250 Delay between sequential analysis batches (MiniMax RPM/TPM pacing).

Example (in openclaw.json or plugin config):

"selfCorrection": {
  "semanticDedup": true,
  "semanticDedupThreshold": 0.92,
  "toolsSection": "Self-correction rules",
  "autoRewriteTools": false,
  "analyzeViaSpawn": true,
  "spawnThreshold": 15,
  "spawnModel": "gemini"
}

Implicit-feedback bridge (optional)

extract-implicit can optionally invoke self-correction-run on capped negative signals after storing implicit-feedback facts:

Option Default Description
implicitFeedback.triggerSelfCorrectionRun false Run self-correction analysis on bridge incidents in the same extract-implicit pass.
implicitFeedback.selfCorrectionBridgeMaxIncidents 5 Max incidents forwarded per run.
implicitFeedback.selfCorrectionBridgeMinConfidence 0.7 Minimum negative-signal confidence for bridge incidents.

This is separate from the nightly self-correction-run cron job; use it when you want immediate analysis after implicit extraction.

Phase 2 via spawn (large batch prompts)

For very large per-batch prompts, Phase 2 (LLM analysis) can be run via openclaw sessions spawn so the analysis uses a separate process and a model with a large context (e.g. Gemini).

  • Set selfCorrection.analyzeViaSpawn: true. Spawn is used per batch when estimated input tokens exceed ~100k (not only when total incident count exceeds spawnThreshold).
  • Requires the OpenClaw CLI and a working sessions spawn command. If spawn fails, the run returns an error.

Historical testing (e.g. Feb 13–18)

To test with a fixed date range or existing extract:

  1. Extract incidents from the last N days and save to a file:
    openclaw hybrid-mem self-correction-extract --days 6 --output /path/to/incidents.json
    
  2. Run the pipeline on that file (optionally with --dry-run first):
    openclaw hybrid-mem self-correction-run --extract /path/to/incidents.json
    # Or with approval for TOOLS rules:
    openclaw hybrid-mem self-correction-run --extract /path/to/incidents.json --approve
    

Adjust --days and paths as needed. The report is still written to memory/reports/self-correction-YYYY-MM-DD.md (today’s date).


Protocol summary (for the cron agent)

  1. Run openclaw hybrid-mem self-correction-extract --days 3 (or rely on self-correction-run to do the extract in memory).
  2. Run openclaw hybrid-mem self-correction-run (optionally with --extract <path> if you saved incidents to a file).
  3. Report path: <workspace>/memory/reports/self-correction-YYYY-MM-DD.md. Review proposals (AGENTS_RULE / SKILL_UPDATE) before applying.

  • GitHub issue #34: Nightly Self-Correction Analysis
  • build-languages: CLI reference — run first for non-English correction detection.
  • Reinforcement (positive signals): openclaw hybrid-mem extract-reinforcement — uses praise phrases and positive emoji (👍 ❤️ etc.) to reinforce facts and procedures. LLM analysis is batched with resume under <workspace>/tmp/reinforcement-analysis/reinforcement-batches-<fingerprint>.json. Config keys reinforcementLLMAnalysis, positiveRulesSection, reinforcementToProposals, analysisBatchSize, and maxIncidentsPerRun live under reinforcement (deprecated aliases under selfCorrection still work). See CLI-REFERENCE.md.
  • Session distillation: SESSION-DISTILLATION.md — separate pipeline (fact extraction from sessions).

Back to top

OpenClaw Hybrid Memory — durable agent memory

This site uses Just the Docs, a documentation theme for Jekyll.