Decision Logic & Memory Pruning

How Sieve evaluates candidate agent memories, scores importance and actionability, supersedes outdated facts, and curates compact, high-signal active prompt context in sub-millisecond local sweeps.

Overview #

Vector databases retrieve memories based strictly on lexical or semantic embedding similarity. However, semantic similarity cannot determine temporal order, factual contradiction, or actionability.

When an agent records "User is in Tokyo for the week" on Monday, and then "User relocated to Berlin permanently" on Thursday, vector search retrieves both memories simultaneously. This causes conflicting context, ballooning token usage, and inevitable model hallucinations.

Sieve acts as the deterministic garbage collection layer between your agent storage and your model prompts. It continuously identifies superseding facts, marks superseded items as archived with direct pointer attribution, and produces clean prompt context without requiring recurring per-turn LLM evaluator calls.

The Memory Decision Lifecycle #

Every memory ingested into Sieve passes through a disciplined four-stage pipeline: Ingestion, Calibration, Pairing, and Policy Execution.

Deterministic Decision Pipeline
STAGE 01
Ingest & Tokenize
Content written to SQLite WAL with namespace isolation and indexed in FTS5 full-text table.
STAGE 02
Calibrated Scoring
Scorer evaluates importance (0–1), actionable readiness (0–1), and structural category.
STAGE 03
BM25 Candidate Pairing
FTS5 pairs incoming fact against historical memories in the same namespace to check contradiction.
STAGE 04
Policy Resolution
PolicyEngine supersedes stale item, preserves pinned invariants, and updates active prompt views.
Sub-Millisecond Execution Guarantee
Memory curation occurs locally via C-level SQLite extensions and lightweight scoring. Sweeps execute in ~0.19ms in local mock benchmarks, keeping agent turn latency virtually instantaneous.

Quickstart in 5 Minutes #

Integrate Sieve with any Python agent in three lines of code. The SieveMemory class manages local database storage, automatic scoring, and prompt context rendering.

quickstart.py
from sieve import SieveMemory

# 1. Initialize local persistent engine (SQLite WAL mode)
memory = SieveMemory(db_path="agent_memory.db")

# 2. Ingest observations across turns
memory.remember("Target database is PostgreSQL 15 on port 5432", namespace="tenant_1")
memory.remember("Actually, database was migrated to PostgreSQL 16 on port 5433", namespace="tenant_1")

# 3. Execute automatic garbage collection sweep
results = memory.sweep(namespace="tenant_1")
print(f"Archived superseded facts: {results['items_archived']}")

# 4. Inject clean, deduplicated context into your agent prompt
prompt_context = memory.get_context(namespace="tenant_1")
print(prompt_context)

Installation Options #

Sieve is designed to have zero mandatory heavy external dependencies for its core engine. Install via pip, uv, or clone from source:

# Install core memory engine and CLI
pip install sieve-memory

# Optional: Install with full server and API dashboard support
pip install "sieve-memory[serve]"

# Optional: Install with real Laya neural calibration backend
pip install "sieve-memory[laya]"

Memory Scoring Engine #

Every memory item is scored across two continuous axes and one discrete category classifier:

Metric Type Range Description
importance float 0.0 – 1.0 Significance of the memory to user profile, permanent business rules, or core identity. High importance memories survive decay longer.
actionable float 0.0 – 1.0 Operational readiness. High actionable scores indicate pending tasks, explicit commands, architectural constraints, or current objectives.
category string enum Classification tag: decision (conclusions/commitments), reference (facts/specs), task (open action items), smalltalk (transient conversation).

Scoring Backends: Mock vs Real Laya

Sieve provides two pluggable scorer implementations:

  • MockScorer (Default): Ultra-fast, deterministic rule-based heuristic scoring (~0.19ms). Features built-in interrogative and clarification guards so inquiries ("What port is PostgreSQL on?") are never mistaken for superseding decisions. Delivers 100% Precision (0 false alarms) and 88.9% F1 on adversarial evaluation datasets without requiring a GPU or API keys.
  • LayaScorer: Neural calibrated ModernBERT cross-encoder backend trained on contradiction resolution and memory salience (convaiinnovations/laya). Evaluates semantic supersession probabilities on GPU (~33ms) or CPU (~390ms), achieving 100% Precision and 75.0% F1 against hard negatives. Enabled via SIEVE_SCORER=laya or SieveMemory(use_real_laya=True).

Staleness & Superseding Logic #

The core decision rule for resolving factual contradiction is Pairwise Staleness Resolution.

When a sweep is triggered, Sieve runs BM25 candidate generation across memory pairs in the target namespace. If candidate pair (A, B) satisfies:

  1. B.created_at > A.created_at (B was recorded after A, supporting backdated historical timestamps)
  2. score_staleness(A, B) >= staleness_threshold (lexical overlap or neural cross-encoder contradiction score)
  3. not is_question(B) (questions, inquiries, and clarification requests cannot supersede decisions)
  4. A.pinned == 0 (A is not an immutable system directive)
  5. A.user_restored == 0 (A was not manually restored by the user with sticky immunity)
  6. not has_swept_pair(A, B) (cached evaluation table ensures repeat sweeps execute in O(1))

Memory A is automatically updated: status = 'archived' and superseded_by = B.id. It is evicted from active prompt context, but remains queryable for audit trails or historical replay.

staleness_example.py
# Memory A (Turn 1)
id_a = memory.remember("Primary cluster is us-east-1", namespace="infra")

# Memory B (Turn 12)
id_b = memory.remember("Cluster failed over to us-west-2 permanently", namespace="infra")

# Execute staleness pruning sweep
report = memory.sweep(namespace="infra")

# Inspect archived item
archived_item = memory.store.get(id_a)
assert archived_item.status == "archived"
assert archived_item.superseded_by == id_b

Pinned Invariants #

Certain agent guidelines must never be pruned regardless of time or superficial topic overlap. Examples include:

  • System constraints: "Never disclose internal customer IDs in output".
  • User accessibility settings: "Always respond in concise markdown".
  • Compliance policies: "HIPAA data redaction rule 4.2 active".

Marking a memory as pinned ensures it is permanently immune to staleness eviction, redundancy sweeps, or retention expiration, while remaining included at the highest priority in all prompt context generation.

pinned_memory.py
# Save and immediately pin critical guideline
item_id = memory.remember(
    "Never delete production tables without explicit multi-sig approval",
    namespace="security_policy"
)
memory.pin(item_id)

# Verify pinned item persists through aggressive sweeps
memory.sweep(namespace="security_policy")
assert memory.store.get(item_id).pinned == 1

LangChain Memory History #

Drop Sieve directly into your existing LangChain agent or runnable chain using SieveChatMessageHistory:

langchain_integration.py
from sieve.integrations.langchain import SieveChatMessageHistory

# Wrap Sieve as a native LangChain chat message history
history = SieveChatMessageHistory(
    session_id="session_user_42",
    db_path="sieve.db",
    auto_sweep_interval=4   # Automatically run curation sweep every 4 turns
)

# Append user and AI messages
history.add_user_message("My shipping address is 100 Main St")
history.add_ai_message("Noted shipping address.")
history.add_user_message("Change shipping address to 450 Broadway St")

# Returns only valid, active messages (the old address is pruned!)
messages = history.messages

ChromaDB Vector Store Sync #

If your agent already stores dense embeddings in ChromaDB, use sync_from_chroma to synchronize memories, score them, prune stale contradictions, and purge archived vectors:

chroma_sync.py
import chromadb
from sieve import SieveMemory
from sieve.integrations.chroma import sync_from_chroma

client = chromadb.Client()
collection = client.create_collection("agent_knowledge")
memory = SieveMemory(db_path="sieve.db")

# Synchronize, score, run staleness sweeps, and delete pruned vectors from Chroma
summary = sync_from_chroma(
    collection=collection,
    memory=memory,
    namespace="rag_knowledge",
    prune_vectors=True
)
print(f"Synced: {summary['synced']}, Archived: {summary['archived']}")

REST API Endpoints #

Sieve includes a high-performance REST API built on FastAPI. Start the server using the CLI: sieve serve --port 8000 (or sieve ui).

Method Endpoint Description
POST /items Ingest and immediately score a new memory item. Accepts text, namespace, metadata, and optional created_at (float UNIX timestamp) to backdate ingestion.
POST /items/batch Batch ingest multiple memories in an atomic transaction with per-item created_at support.
GET /active Retrieve all active and pinned memories for an agent namespace. Supports pagination.
GET /context Generate formatted plain-text or JSON context ready for direct LLM system prompt injection.
POST /sweep/staleness Execute pairwise staleness sweep with BM25 filtering and cache deduplication. Flags and archives superseded facts.
POST /sweep/redundancy Execute redundancy sweep. Detects identical or highly repetitive memories.
POST /item/{id}/restore Restore an archived item back to active status, clearing superseded_by and granting sticky immunity against re-archiving.
POST /item/{id}/pin Pin a memory invariant so it remains active and immune to staleness pruning.
POST /item/{id}/unpin Unpin a memory, restoring standard policy pruning thresholds.
PATCH /item/{id}/status Update item status: active, archived, or pinned.
GET /stats Retrieve memory counts (active, pinned, archived) and sweep telemetry metrics.

💡 Note on Interactive Dashboard (/ui): The web dashboard is served dynamically by the FastAPI server when running locally (sieve serve or sieve ui) or deployed as an API backend container. On static hosting providers (such as static Vercel deployments), the documentation and landing pages are served statically, while the interactive /ui dashboard requires the running FastAPI backend server.

HTTP REST Example (cURL)
# Ingest memory via HTTP
curl -X POST http://localhost:8000/items \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_SECRET_KEY" \
  -d '{"text": "User preferred language is Python 3.12", "namespace": "user_42"}'

# Trigger staleness sweep
curl -X POST "http://localhost:8000/sweep/staleness?namespace=user_42" \
  -H "X-API-Key: YOUR_SECRET_KEY"

# Fetch compact prompt context
curl "http://localhost:8000/context?namespace=user_42&format=text"

Python SDK Interface #

The Sieve Python library provides complete object models and lifecycle wrappers. Primary classes include:

  • SieveMemory(db_path, use_real_laya=False, policy_config=None): High-level developer wrapper with turnkey methods.
  • Store(db_path): Thread-safe, WAL-enabled SQLite persistence layer with BM25 FTS5 indexing.
  • PolicyEngine(store, config): Core rule evaluation engine containing staleness and redundancy algorithms.
  • SievePipeline(store, scorer, policy): Fully orchestrated end-to-end pipeline.

PolicyEngine Configuration #

Fine-tune pruning aggression, similarity thresholds, and memory limits via PolicyConfig:

Attribute Type Default Description
staleness_threshold float 0.70 Minimum lexical overlap required to flag older memory as superseded by newer assertion.
redundancy_threshold float 0.75 Token similarity threshold for detecting duplicate or repeated memories.
min_importance_to_keep float 0.20 Memories below this threshold are prioritized for auto-archiving during memory pressure.
retention_days int 30 Age threshold after which unpinned memories are considered for archival.
max_active_items int 100 Hard upper limit of active context items in prompt rendering.

Authentication & Security #

Secure your Sieve HTTP service by configuring the SIEVE_API_KEY environment variable:

auth_setup.sh
# Export secret key before launching API
export SIEVE_API_KEY="sv_prod_89f3a920c812d"
sieve serve --port 8000

# Requests must pass Authorization: Bearer  or X-API-Key: 

When SIEVE_API_KEY is active, the interactive Dashboard (/ui) prompts for your key and caches it in this browser's localStorage to authenticate all background polling calls.

SQLite WAL Mode & Thread-Safe Concurrency #

Sieve initializes SQLite with Write-Ahead Logging (WAL) mode and a 30-second busy timeout, coupled with a reentrant threading lock (threading.RLock) protecting all database operations. This architecture guarantees:

  • Concurrent Readers & Writers: Readers never block writers, and writers never block readers. Multiple parallel agent threads can fetch prompt context while background worker processes ingest and score new turns.
  • Thread-Safe Store: The Store instance serializes SQLite cursor operations across concurrent worker threads, preventing race conditions and eliminating sqlite3.OperationalError: database is locked.
  • Index Trigger Consistency: SQLite trigger trg_items_delete automatically synchronizes FTS5 full-text indices whenever items are purged, preventing orphaned index entries.

Zero-Downtime Backups & Migrations #

Never risk database corruption with raw file copies. Sieve utilizes the SQLite Online Backup API for hot, atomic live snapshots:

hot_backup.py
from sieve import Store

store = Store("production.db")

# Execute online backup without locking concurrent writers
store.backup("/backups/sieve_backup_2026_10_11.db")
print("Atomic snapshot completed successfully.")

Background Daemon Worker #

Run Sieve's autonomous background worker in Python to score pending memories and run curation sweeps continuously across agent namespaces:

background_worker.py
from sieve import SieveMemory

memory = SieveMemory("production.db")

# Start autonomous background worker checking and sweeping every 60 seconds
memory.start_daemon(interval_seconds=60.0, namespaces=["user_prod", "agent_session"])

# Stop worker when shutting down
# memory.stop_daemon()

CLI Command Reference #

Command Arguments Description
sieve serve / sieve ui --host, --port, --db, --api-key Launch FastAPI REST API server and interactive UI dashboard.
sieve remember "<text>", --namespace, --db Ingest and score a single text memory from the shell.
sieve context --namespace, --max-items, --db Print active memories formatted for LLM system prompt injection.
sieve sweep --namespace, --db Trigger immediate staleness and redundancy sweeps.
sieve pin / sieve unpin <item_id>, --db Protect critical guidelines from pruning or remove pinned status.
sieve stats --namespace, --db Display summary counts of active, pinned, and archived memories.
sieve backup <destination>, --db Create an atomic online backup of SQLite database in WAL mode.