Mem Skill
Implement a memory loop with hybrid retrieval.
Use SQLite as source of truth and Chroma as vector index.
Use Minimal Stack
Use this stack by default:
- SQLite file (
mem.db) for facts and history. - SQLite FTS5 for lexical retrieval.
- Chroma (embedded mode) for vector retrieval.
- LLM for extraction and arbitration.
Do not require PostgreSQL or Redis in the baseline implementation.
Define Data Model
Store atomic facts in SQLite.
Use this minimum schema:
{
"fact_id": "uuid",
"user_id": "string",
"key": "string",
"value_text": "string",
"value_json": "json|null",
"category": "identity|hard_preference|soft_preference|task_context|health|other",
"confidence": 0.0,
"event_time": "ISO-8601|null",
"valid_from": "ISO-8601",
"valid_to": "ISO-8601|null",
"status": "active|superseded|expired|pending_confirmation",
"source_turn_id": "string",
"source_excerpt": "string",
"embedding_id": "string|null",
"created_at": "ISO-8601",
"updated_at": "ISO-8601"
}
Create archive table for superseded or expired facts.
Build SQLite Retrieval Tables
Create these tables:
factsfor active and historical fact rows.fact_historyfor immutable audit records.facts_ftsas FTS5 mirror on searchable text.
Mirror facts_fts from facts with triggers or explicit sync jobs.
Index these columns in facts:
user_idkeystatusvalid_toupdated_at
Build Vector Collection
Use one Chroma collection for facts.
Store:
id = embedding_iddocument = value_textmetadata = {fact_id, user_id, key, category, status, valid_from, valid_to, confidence, updated_at}
Update vector records whenever fact text changes.
Delete or mark vectors for facts no longer retrievable.
Run Asynchronous Memory Observer
Queue each completed turn:
(user_input, assistant_response, turn_id, timestamp, user_id)
Run extraction, retrieval, arbitration, and persistence in background worker.
Keep reply loop independent from observer latency.
Stage 1: Hybrid Retrieval
Extract entities and intent from current input.
Run lexical retrieval from SQLite FTS5:
- Query by key entities and phrases.
- Filter by
user_id. - Filter
status in (active, pending_confirmation). - Return
top_k_lex(default20).
Run vector retrieval from Chroma:
- Embed current input.
- Query collection with user and status metadata filters.
- Return
top_k_vec(default20).
Fuse results into one candidate list.
Use default weighted score:
HybridScore = 0.45*NormLexical + 0.45*VecSim + 0.10*RecencyBoost
Use recency boost from updated_at or last_mentioned_at.
Fallback to lexical-only retrieval when vector service fails.
Stage 2: Evidence Arbitration
Run an internal auditor prompt over retrieved candidates.
Use this prompt template:
You are a memory auditor.
Old facts:
{OLD_FACTS}
New user input:
{USER_INPUT}
Tasks:
1) Detect confirmations and repetitions.
2) Detect updates for same key.
3) Detect contradictions.
4) Decide temporary versus durable change.
5) Return JSON actions only.
Require output:
{
"actions": [
{
"type": "insert|update|supersede|expire|noop|mark_pending_confirmation",
"key": "string",
"new_value_text": "string|null",
"new_value_json": "object|null",
"target_fact_id": "uuid|null",
"valid_from": "ISO-8601|null",
"valid_to": "ISO-8601|null",
"confidence_delta": 0.0,
"reason": "string"
}
]
}
Reject non-JSON output and re-prompt.
Stage 3: Atomic Upsert
Apply actions in one SQLite transaction.
Use these rules:
- Insert new fact when key does not exist.
- Update compatible refinements in place.
- Supersede contradictions by ending old validity and inserting new fact.
- Expire facts when explicit time invalidation is detected.
- Keep duplicates as noop and refresh mention timestamps and confidence.
- Mark low-confidence conflicts as pending confirmation.
Write every destructive change to fact_history.
Use idempotency key:
(turn_id, key, action_hash)
Stage 4: Sync Embeddings
After transaction commit, sync changed facts to Chroma.
Use this policy:
- Insert or upsert vectors for active and pending facts.
- Remove vectors for superseded and expired facts.
- Retry failed sync jobs with backoff.
Keep sync asynchronous so writes are not blocked by embedding calls.
Stage 5: Materialize User Profile
Build structured profile on schedule:
- End of session.
- Every
Nturns (default5).
Project only active and valid facts:
{
"identity": {},
"hard_preferences": {},
"soft_preferences": {},
"current_tasks": [],
"recent_changes": [],
"sensitive_topics": []
}
Keep confidence and timestamp fields in projection output.
Stage 6: Budget-Aware Prompt Injection
Inject only relevant profile slices for next prompt.
Use priority:
- Hard constraints and hard preferences.
- Current task context.
- Soft preferences.
Drop stale and low-confidence fields first under tight token budget.
Confidence Policy
Use default confidence rules:
- First mention:
0.40. - Repeated or explicit confirmation: increase toward
1.00. - Single contradiction against high-confidence fact: pending confirmation.
Ask clarification only for high-value conflicts.
Operational Requirements
Track these metrics:
- Lexical retrieval hit rate.
- Vector retrieval hit rate.
- Hybrid top-k recall.
- Wrong-overwrite rate.
- Pending-confirmation resolution rate.
- Observer latency p50 and p95.
- Profile injection token cost.
Run release ablations:
- Lexical-only.
- Vector-only.
- Hybrid.
Publish metric deltas for each mode.
Safety Requirements
Treat identity, health, and finance-like keys as high risk.
Use these guardrails:
- Do not auto-overwrite high-risk facts at low confidence.
- Require explicit confirmation when confidence is below
0.90. - Keep before and after audit records for high-risk changes.
- Support delete by
user_idandkey.
Completion Checklist
Complete implementation only when all items pass:
- SQLite schema and FTS5 retrieval are functional.
- Chroma vector index is functional.
- Hybrid retrieval and score fusion are implemented.
- Arbitration returns valid JSON actions.
- Upserts are atomic and idempotent.
- Embedding sync runs asynchronously and retries failures.
- Profile materialization and injection are functional.
- High-risk guardrails are enforced.
- Hybrid versus lexical-only metrics are reported.