Skip to content

feat(memory): knowledge-experience decoupling#642

Open
win4r wants to merge 1 commit intomasterfrom
feat/knowledge-experience-decoupling
Open

feat(memory): knowledge-experience decoupling#642
win4r wants to merge 1 commit intomasterfrom
feat/knowledge-experience-decoupling

Conversation

@win4r
Copy link
Copy Markdown
Collaborator

@win4r win4r commented Apr 17, 2026

Summary

Implements the knowledge / experience decoupling pattern from Graph-based Agent Memory: Taxonomy, Techniques, and Applications (arxiv:2602.05665 §III-C, §V-E).

  • Knowledge — static, objective reference data (profile / preferences / entities / patterns)
  • Experience — trajectory of interactions and outcomes (events / cases)

The plugin already did this partially on the write side (profile always-merges, events/cases append-only). This PR threads the same split through decay and retrieval without splitting storage.

What this changes

Layer Before After
Classification implicit in merge rules classifyMemoryType() in src/memory-categories.ts, stored as metadata.memory_type with lazy backfill on read
Decay β per tier only Weibull half-life now multiplied by knowledgeHalfLifeMultiplier (default 3.0) or experienceHalfLifeMultiplier (default 0.7) per memory
Intent category-only routing analyzeIntent now returns a memoryType hint; new "experience" rule catches 上次 / 之前 / last time / when we ...
Retrieval applyCategoryBoost same, then applyMemoryTypeBoost (1.15x) for type-matched hits in auto-recall
Recall tool query / scope / category adds type: "knowledge" | "experience" | "both" post-filter
Config decay + tier knobs two new decay.*Multiplier keys in openclaw.plugin.json schema

Decoupling sits at the semantic layer, not the storage layer — no new tables, same LanceDB memories, same scopes. Matches this plugin's existing pattern of extending via JSON metadata rather than schema changes.

Test plan

  • New test/knowledge-experience-decoupling.test.mjs — 18 subtests covering classifier mapping, parseSmartMetadata backfill, per-type half-life, intent routing (EN + 中文), applyMemoryTypeBoost
  • Registered under core-regression in scripts/ci-test-manifest.mjs + verify-ci-test-manifest.mjs
  • npm test locally: 197/197 pass
  • cli-smoke, core-regression, storage-and-schema, llm-clients-and-auth, packaging-and-workflow — all green

Backward compatibility

  • Existing memoriesmemory_type absent in metadata → parseSmartMetadata backfills on read from memory_category (or legacy category). No migration needed; no memory-pro upgrade required.
  • Default behavior — unconfigured callers get the paper's recommended multipliers (3.0 / 0.7). Tested to match legacy behavior when both are set to 1.0.
  • No API breaks — new type param on memory_recall is optional; schema is additive (additionalProperties: false still holds because multipliers are declared).

Incidental fix

test/hook-dedup-phase1.test.mjs was in CI_TEST_MANIFEST but missing from EXPECTED_BASELINE in verify-ci-test-manifest.mjs, so npm test on master was already failing its manifest-verify step. Bundled the one-line fix because this PR also touches the baseline.

Files touched

  • src/memory-categories.tsMemoryType + classifyMemoryType()
  • src/smart-metadata.tsmemory_type field + backfill + LifecycleMemory propagation
  • src/decay-engine.ts — per-type half-life multipliers
  • src/intent-analyzer.tsmemoryType on IntentSignal + new experience rule + applyMemoryTypeBoost()
  • src/tools.tstype param on memory_recall
  • index.ts — wired applyMemoryTypeBoost after applyCategoryBoost in auto-recall; PluginConfig.decay type
  • openclaw.plugin.json — schema + UI labels for the two multipliers
  • test/knowledge-experience-decoupling.test.mjs — new
  • scripts/ci-test-manifest.mjs + verify-ci-test-manifest.mjs — register new test + hook-dedup baseline fix

Implements the K/E decoupling pattern from arxiv:2602.05665 §III-C and §V-E:
static reference data (profile, preferences, entities, patterns) and
trajectory data (events, cases) now travel separately through decay and
retrieval without splitting storage.

Changes:
- classifyMemoryType() in src/memory-categories.ts — deterministic 6-cat
  (+ legacy) → knowledge/experience mapping
- memory_type threaded through SmartMemoryMetadata with lazy-backfill on
  legacy entries in parseSmartMetadata
- DecayEngine applies per-type half-life multipliers (knowledge 3.0x,
  experience 0.7x by default; set both to 1.0 to disable)
- analyzeIntent returns memoryType hint; new "experience" rule
  (last time / 上次 / 之前) routes trajectory queries
- applyMemoryTypeBoost() promotes matching-type results after the
  existing category boost in the auto-recall path
- memory_recall tool accepts type: "knowledge" | "experience" | "both"
- openclaw.plugin.json schema exposes the two multipliers

Backward-compat: old memories backfill on read; unconfigured callers
keep working (type defaults to knowledge, which decays slower — safer
than losing data).

Also registers the pre-existing test/hook-dedup-phase1.test.mjs in
verify-ci-test-manifest.mjs EXPECTED_BASELINE (was in the runtime
manifest but missing from the baseline check, so `npm test` was failing
the manifest-verify step on master).

Verified: 197/197 tests pass (npm test, full suite).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot requested a review from rwmjhb April 17, 2026 02:58
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 73e4b667f5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/tools.ts
Comment on lines +592 to +595
const typeFilter: MemoryType | undefined =
type === "knowledge" || type === "experience" ? type : undefined;
const results = typeFilter
? rawResults.filter(
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Apply type filter before enforcing recall limit

memory_recall now filters by type only after retrieval has already been capped by limit, so mixed-type top hits can crowd out matching items and produce too few (or zero) results even when relevant memories exist just below the cutoff. This is reproducible when callers pass type="knowledge" or type="experience" with small limits (default 3): the tool reports no matches although the store contains matches in later ranks. Fetching a larger candidate set when type is specified (then filtering and truncating) would preserve expected filter semantics.

Useful? React with 👍 / 👎.

@rwmjhb
Copy link
Copy Markdown
Collaborator

rwmjhb commented Apr 18, 2026

This PR currently has merge conflicts with the base branch (mergeable=CONFLICTING per GitHub). Deep review is paused until the branch rebases cleanly — reviewing now would:

  1. Give feedback against a branch you'll have to rewrite anyway
  2. Produce findings that may be invalidated once conflicts are resolved
  3. Potentially miss issues introduced by the conflict resolution itself

Please:

  1. Rebase this branch onto the latest base (or merge the base into this branch)
  2. Resolve all merge conflicts
  3. Push the rebased branch

Once that's done, the review pipeline will pick it up automatically on the next scan and do a full pass. Thanks for your patience.

Copy link
Copy Markdown
Collaborator

@rwmjhb rwmjhb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved based on orchestrator review results. Non-blocking follow-ups were noted separately; no merge-blocking issues from this review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants