Skip to content

Architecture

Processing Pipeline

Every message goes through this pipeline:

User Message
 AgentRouter ──────── agents/*.yaml  (TF-IDF routing, hot-reload)
 ContextPlanner
      │  intent : coding | personal | task | knowledge | conversation
      │  budget : { working:5, episodic:2, semantic:4, skills:3 }
 ContextEngine
      ├── Retriever   → collect candidates from storage
      ├── Resolver    → TTL check, mark expired nodes
      ├── Filter      → active nodes only
      ├── Scorer      → rank by relevance + importance + recency + path
      └── Selector    → enforce budget + max_nodes + max_chars
 PromptBuilder  (deterministic, bullet-format per tier)
    LLM  ←→  Gemini / OpenAI / Claude / Ollama / any
 MemoryProcessor
      ├── store messages  → working tier
      ├── extract facts   → semantic tier (rule-based, no LLM)
      ├── dedup           → Jaccard similarity ≥ 0.65
      ├── conflict resolve → confidence-based supersedes
      └── update importance scores

Components

AgentRouter

Routes each message to the most appropriate agent using TF-IDF keyword matching. Agents are defined in YAML files and hot-reloaded without restart.

result = sc.router.route(user_id, message)
# result.agent_id → "coding"
# result.personality_level → "expert"
# result.system_prompt → full system prompt string

ContextPlanner

Analyzes the message to determine:

  • Intent — what type of request is this?
  • Budget — how many nodes from each tier?
  • Focus — any specific tags or paths to prioritize?
plan = sc.planner.plan(message, user_id, profile)
# plan.intent → "coding"
# plan.budget → {"working": 5, "episodic": 2, "semantic": 4}

ContextEngine

Orchestrates the full retrieval pipeline: retrieve → resolve → filter → score → select.

nodes = sc.engine.retrieve(plan)
# Returns ranked list of ContextNode objects within budget

ContextRetriever

Collects candidate nodes from storage:

  • Working tier nodes (recent messages, task state)
  • Episodic tier nodes (session summaries)
  • Semantic tier nodes + keyword search (facts, knowledge)
  • Skills namespace (agent-specific skills)
  • Focus paths (preferred paths from plan)

ContextScorer

Ranks nodes using a weighted formula:

score = relevance × 0.55
      + importance × 0.25
      + recency × 0.10
      + path_priority × 0.10

Relevance is computed from token overlap (text, tags, path) with fuzzy matching fallback.

Recency uses exponential decay with tier-specific half-lives:

Tier Half-life
Working 1 hour
Episodic 72 hours
Semantic 720 hours

PromptBuilder

Builds a deterministic, structured system prompt from selected nodes:

[System prompt from agent]

## Working Memory
- [recent message 1]
- [recent message 2]

## Knowledge
- user name: Alice
- user uses: Python, FastAPI

MemoryProcessor

Processes each turn after the LLM responds:

  1. Stores messages to working tier
  2. Extracts facts using rule-based patterns (no LLM required)
  3. Deduplicates using Jaccard similarity ≥ 0.65
  4. Resolves conflicts using confidence scores
  5. Updates importance scores for used nodes

Storage Layer

SimpleContext supports multiple storage backends:

Backend Install Best For
SQLite Zero (built-in) Development, small deployments
Memory Zero (built-in) Testing, ephemeral use
Redis pip install redis Production, multi-instance
PostgreSQL pip install psycopg2-binary Production, advanced queries

Plugin System

Plugins attach to the pipeline via hooks:

on_message_saved      ← after each message stored
on_context_build      ← before messages sent to LLM
on_before_llm         ← final chance to modify messages
on_after_llm          ← after LLM responds
on_agent_routed       ← when routing decision is made
on_prompt_build       ← after system prompt is built

See Plugin Development for details.


File Structure

simplecontext/
├── core.py              ← SimpleContext + ChatContext (main entry point)
├── memory.py            ← Memory (v3) + TieredMemory (v4)
├── skills.py            ← Skills: groups, conditions, inheritance
├── enums.py             ← Tier, NodeKind, NodeStatus, Intent
├── context/
│   ├── node.py          ← ContextNode dataclass
│   ├── planner.py       ← ContextPlanner + RetrievalPlan
│   ├── engine.py        ← ContextEngine + LRU cache
│   ├── retriever.py     ← candidate collection
│   ├── resolver.py      ← TTL → mark expired
│   ├── scorer.py        ← scoring formula
│   ├── selector.py      ← budget enforcement
│   ├── builder.py       ← PromptBuilder
│   ├── processor.py     ← MemoryProcessor + decay
│   ├── fuzzy.py         ← fuzzy matching
│   ├── graph.py         ← relationship graph
│   ├── patterns.py      ← interaction pattern detection
│   ├── adaptive.py      ← adaptive scoring from feedback
│   └── cache.py         ← LRU cache (30s TTL)
├── storage/
│   ├── sqlite.py
│   ├── redis.py
│   └── postgres.py
├── agent/
│   ├── schema.py        ← parse YAML agent definitions
│   ├── registry.py      ← hot-reload
│   └── router.py        ← TF-IDF routing + chaining
└── plugins/
    ├── base.py          ← BasePlugin + AppCommandContext
    ├── loader.py        ← dynamic loader + dependency resolver
    └── state.py         ← persistent plugin state