Superseded by the one-shot reset spec under specs/memory-system-reset/requirements.md, design.md, and tasks.md. This document reflects the transitional multi-type migration path and is no longer the target architecture.
This document defines the migration from the current memory_records + metadata.facts architecture
to a layered memory system inspired by SimpleMem, but adapted for linX's requirements:
- PostgreSQL remains the source of truth.
- Milvus remains a vector index, not the business database.
- Session history becomes a first-class ledger.
- Long-term memory is built from atomic observations, not only from ad-hoc string facts.
- Agent memory focuses on reusable successful paths and execution experience, not generic SOP text.
The current system used to centralize too many concerns inside the legacy
MemorySystem runtime:
- fact extraction
- dedupe and merge
- write planning
- ranking and rerank
- Milvus synchronization
- retention and eviction
- compatibility shaping for UI
This causes recurring issues:
- semantically duplicated memories with different keys
- prompt-driven extraction leaking directly into storage shape
- different retrieval behavior between API and runtime paths
- heavy dependence on
metadataconventions - difficult evolution of agent memory beyond
interaction.sop.*
flowchart LR
A["Conversation Session"] --> B["Session Ledger"]
B --> C["Observation Builder"]
C --> D["Materializers"]
D --> E["Stable Projections"]
C --> F["Atomic Memory Entries"]
F --> G["Retrieval Gateway"]
E --> G
G --> H["Prompt Context Injection"]
F --> I["Consolidation Worker"]
I --> F
Capture what actually happened in a session:
- session metadata
- ordered user/assistant/tool/file events
- timestamps
- extraction provenance
This layer is append/replace oriented and should not decide long-term memory value by itself.
Extract reusable, session-independent observations from the ledger:
- user fact atoms
- agent successful paths
- decisions
- discoveries
- constraints
- recurring environment fixes
Each observation must have:
- a stable key
- confidence
- importance
- provenance back to the session and event indexes
Project observations into stable read models:
user_profileskill_proposal- later:
company_playbook,task_pattern,tool_failure_catalog
These are the views that should power personalization and experience reuse.
All runtime and API retrieval should converge on one path:
- scope filtering
- ACL enforcement
- semantic retrieval
- keyword retrieval
- structured retrieval
- rerank
- token-budgeted context bundle assembly
Periodic maintenance:
- decay stale entries
- merge near-duplicates
- mark superseded materializations
- keep projections compact and explainable
- session history and long-term memory are not the same thing
- atomic context-independent memory units
- provenance-aware observations
- separate write/build/retrieve/consolidate stages
- token-budgeted context injection
- over-reliance on multi-round LLM retrieval planning
- storage-engine-driven architecture decisions
- pretending prompt-only dedupe is real consolidation
- putting all retrieval intelligence behind opaque LLM loops
Agent memory should primarily store successful paths:
- what the agent was trying to achieve
- what path finally succeeded
- what failed or should be avoided
- where the path applies again
Typical examples:
- converting a difficult PDF and delivering it in a user-acceptable format
- recovering from repeated parser/tool failures and finding the workable route
- discovering the exact sequence of checks needed before a deployment succeeds
This is closer to:
- execution experience
- reusable workflow
- candidate skill
and less like:
- generic SOP prose
- a copy of the final answer
User memory is no longer limited to preference key/value pairs.
The builder should produce user fact atoms with enough structure to support:
- personalization
- factual recall
- relational recall
- temporal recall
- future task adaptation
Required coverage:
- preference: likes, dislikes, language, output style, habits
- relationship: who is related to whom
- experience: what the user has done before
- skill: what the user is strong at
- goal: long-term goals or plans
- constraint: budget, allergy, hard restrictions
- identity: role, background, stable descriptors
- event: important personal events or milestones when future tasks may reference them
Each user fact atom should aim to preserve:
fact_kindkeyvaluecanonical_statementpredicateobjectevent_timepersonsentitieslocationtopic- provenance and confidence
This is the main gap versus the previous implementation. The old system could capture
response_style=concise, but it was weak at remembering:
- "王敏是用户的配偶"
- "用户做过电商运营"
- "2024年8月用户搬到了杭州"
- "用户擅长 SQL"
SimpleMem's key strength here is the context-independent fact unit. We should keep that strength, but store it in the linX ledger / observation / entry pipeline instead of mirroring SimpleMem's storage layout.
memory_sessions and memory_session_events are operational provenance, not permanent product data.
Target policy:
- keep session ledger rows for a short operational window only
- preserve long-term memory products (
memory_entries,memory_materializations) - allow
source_session_idto becomeNULLafter session-ledger cleanup when appropriate
Recommended defaults:
session_ledger.retention_days: 14session_ledger.cleanup_interval_seconds: 21600session_ledger.run_on_startup: true- advisory-lock guarded cleanup
Important rule:
- deleting a session ledger row must not delete valid long-term entries/materializations that have already been consolidated into durable memory
This is already compatible with the schema because:
- session events and observations cascade from
memory_sessions - entries/materializations keep nullable
source_session_id
- a session-level LLM builder remains the primary way to compress raw dialogue into structured memory candidates
- agent successful-path extraction remains a first-class product requirement
- the builder output must shift from
user_preferences onlytouser_fact_atoms - the builder should produce context-independent statements, not only storage-friendly keys
- the post-builder stages should stop re-extracting the same semantics from the same session
- duplicate semantic extraction in the legacy compatibility write path
- old assumptions that all user memory can be represented as
user.preference.<key>=<value> - making session-end success entirely depend on one LLM response without deterministic fallback
The old config page is retrieval-centric. That no longer matches the architecture.
The new page should be organized by pipeline stage:
-
Session Extraction- provider / model / timeout / backoff
- max user facts
- max agent experience candidates
- fallback policy
-
Write Quality Gates- fail-closed by memory type
- low-value auto-generated filtering
- planner strictness
-
User Fact Atomization- canonical statement requirement
- relation / event extraction enablement
- event materialization policy
-
Dedup & Consolidation- exact/semantic dedupe thresholds
- conflict handling
- core-memory protection
-
Retrieval- top-k
- similarity threshold
- rerank controls
- keyword fallback
-
Maintenance- materialization maintenance schedule
- orphan cleanup schedule
- session-ledger retention schedule
- new user facts should first land in
memory_observationsandmemory_entries - only profile-like facts should project into
user_profile - event-like facts may remain as entries first, until a dedicated episodic user view is introduced
- legacy
memory_recordsremains compatibility-only and must not define the target memory model - every new user fact must preserve a searchable
canonical_statement
memory_sessionsmemory_session_eventsmemory_observationsmemory_materializations
memory_entriesmemory_linksmemory_consolidation_runs- optional
memory_projection_conflicts
Implemented in the current migration slice:
- dual-write of ended conversation sessions into
memory_sessions,memory_session_events,memory_observations, andmemory_materializations - initial
user_profilematerialization generation from extracted user preference signals - initial
skill_proposalprojection generation from successful-path candidates - runtime retrieval path now reads
user_profileand published skills through final reset-era services instead of the old genericAgentMemoryInterface - legacy session-end compatibility shaping has started moving into
dedicated builders instead of staying inside
agents.py SessionObservationBuildernow owns session-memory extraction, normalization, session-event projection, and observation/materialization constructionLegacyMemoryCompatibilityWriternow owns the legacymemory_recordscompatibility write path, so session-end router code only orchestrates- API now has dedicated read-only endpoints for
user_memoryandskill_proposalswithout reviving legacy memory-record CRUD semantics - runtime and API non-wildcard search now share the same semantic alignment + keyword-fallback retrieval gateway instead of maintaining duplicate logic
- materialization retrieval is now also exposed through the shared retrieval
gateway, and runtime scope retrieval (
agent/user_context) merges legacy records and materialized projections through the same gateway - wildcard list reads for
agent/user_contextnow also go through the shared retrieval gateway so list and search semantics are no longer split - reviewed skill proposals now sync publish/reject state into canonical
skill_proposalsrows and the skill registry MaterializationMaintenanceServicenow exists for:- legacy
memory_records -> memory_materializationsbackfill - status sync from review state /
is_active - duplicate agent-experience supersession
- legacy
- scheduled materialization maintenance is now wired into API startup/shutdown via a dedicated manager, using config + advisory locking
- atom-layer foundation now exists:
memory_entriesmemory_links
- session-ledger persistence now dual-writes normalized observations into
memory_entriesand records lineage links:observation -> entryentry -> materialization
memory_entriesnow participate directly in hot-pathagent/user_contextretrieval, with entry-first dedupe priority over materializations and legacy rows- API non-wildcard
agent/user_contextsearch now routes through the same scope-aware retrieval gateway used by runtime reads, instead of falling back to legacy-only search semantics - maintenance now covers
memory_entriesas well as materializations:- legacy
memory_records -> memory_entriesbackfill - entry status sync
- duplicate entry supersede for user facts and agent skill candidates
- legacy
- session extraction now prefers
user_factsoveruser_preferencesand preserves richer fields for:- relationship
- experience
- skill
- goal
- constraint
- event
- heuristic fallback now covers:
- relationship facts
- experience facts
- skill facts
- timed event facts
- session-ledger retention is now implemented:
- repository cleanup detaches durable rows before deleting old sessions
- scheduled manager is wired into API startup/shutdown
- config defaults now expose retention settings
- config API and config UI now expose the multi-stage memory pipeline:
- session extraction controls
- session-ledger retention controls
- materialization maintenance controls
- orphan cleanup controls
Not migrated yet:
agents.pystill owns the session callback and a few compatibility wrapper exportsmemory_entriesare on the hot path, but are not yet the sole retrieval source; legacymemory_recordsand materializations are still compatibility layers- consolidation is still coarse-grained; there is no standalone entry-centric worker with richer conflict classes or decay policies yet
- legacy
memory_recordsremains the primary compatibility surface - user fact extraction is stronger, but still not yet a full SimpleMem-style semantic density / episodic memory builder
- there is still no dedicated episodic user-memory projection; timed events remain entry-first instead of materializing into a separate read model
Deliverables:
- this plan
- new ledger/projection tables
- dual-write from current session flush into ledger + observation tables
- new skill-proposal observation type:
skill_proposal_candidate
Success criteria:
- no regression to current memory write path
- session flush continues to populate legacy memory
- new ledger tables receive session snapshots and projections
Deliverables:
- extract session-memory builder logic out of
agents.py - create reusable
SessionLedgerServiceandObservationBuilder - preserve old writes for compatibility
Success criteria:
agents.pyonly orchestrates- extraction and projection logic become independently testable
Deliverables:
user_profilematerialization as canonical preference/profile sourceskill_proposalprojection for successful paths- retrieval read path can query projections first
Success criteria:
- repeated user preference updates no longer depend on string-fact dedupe alone
- agent experience becomes queryable as a first-class reusable asset
Deliverables:
- shared retrieval service for API and runtime
- consistent DB alignment and fallback behavior
- consistent ACL/scope evaluation
Success criteria:
- API and runtime see the same memory semantics
- current duplicated retrieval glue is removed
Deliverables:
- materialization maintenance service
- admin/CLI execution path for backfill + consolidation
- scheduled worker hookup
- atom-layer dual-write foundation (
memory_entries/memory_links) - migration jobs from legacy fact-heavy
memory_records - gradual retirement of legacy-only extraction conventions
Success criteria:
- new projections become primary source for user/agent long-term memory
- legacy metadata-based heuristics are no longer on the hot path
Deliverables:
user_fact_atomsextraction prompt and normalization- observation types that preserve relation / experience / event facts
- canonical-statement-first entries for user memory
- session-ledger retention worker
- config page aligned with the multi-stage memory pipeline
Success criteria:
- user memory can answer relational and factual questions, not only style/preference questions
- session ledger no longer grows forever
- operators can understand and control each stage of the pipeline from the config page
Deliverables:
- entry-centric consolidation worker with richer conflict classes
- dedicated episodic user-memory projection for timed events
- retrieval path that can disable legacy
memory_recordsper scope - final retirement plan for metadata.fact-heavy hot-path semantics
Success criteria:
memory_entriesbecome the default durable unit for user and agent memory- legacy compatibility becomes opt-in instead of hot-path default
- timed event facts can be retrieved and optionally projected without polluting
user_profile
- Use additive schema changes first.
- Keep legacy writes running until retrieval migrates.
- Prefer dual-write before dual-read.
- Add new projections before removing old metadata contracts.
- Every migration slice must be testable in isolation.
- duplicate writes during the overlap period
- materialization drift from legacy memory
- prompt changes changing extraction distribution
- new tables filling without being read yet
- session snapshot writes are idempotent by
session_id - materializations are keyed by stable owner/type/key tuples
- old memory path remains authoritative during Phase 0 and Phase 1
- extraction failures in the new path must not block the old path
The first implementation slice in this branch introduces:
- session ledger tables
- observation and materialization tables
- dual-write from
_flush_session_memories - a clearer agent-memory extraction prompt centered on successful path experience
The next remaining slice should:
- shrink the remaining callback/compatibility wrappers in agents.py
- start reading
memory_entriesin retrieval/consolidation instead of using them only as lineage storage - retire more of the legacy
memory_records-specific compatibility path