Skip to main content

Vectorless RAG with MCP — BM25 + temporal KG + link walking. Zero dependencies.

Project description

Locus

Vectorless RAG  ·  MCP-Native  ·  Zero Dependencies

Python 3.11+ License: MIT Version Tests Zero Dependencies MCP Compatible 26 MCP Tools CI

Retrieval-Augmented Generation without vectors, without GPUs, without black boxes.

Every result tells you exactly why it was returned.

Quick Start  ·  Architecture  ·  MCP Tools  ·  CLI Reference  ·  Cluster  ·  OMPA Bridge


Why Vectorless?

Standard RAG pipelines require embedding models, vector databases, and GPU compute. The retrieval signal — cosine similarity — is a single opaque float that tells you nothing about why a document was returned. Locus takes a different path:

Vector RAG Locus
Dependencies sentence-transformers, faiss / pinecone / chroma Zero
GPU required Yes (or paid embedding API) No
Why was this returned? "similarity = 0.82" "BM25: [jwt, auth]; KG: Alice links to doc"
Temporal queries Not supported Yes — validity windows on every KG fact
Knowledge graph Not supported Auto-extracted from prose
Contradiction detection Not supported Built in
Multi-node Requires external infrastructure Built-in cluster
Deterministic results No (model-dependent) Yes

Six Retrieval Signals

Locus fuses six independent signals via Reciprocal Rank Fusion. Query intent is auto-classified and adjusts signal weights per query.

                        ┌───────────────────────────────────────────┐
  Query ──────────────► │  1. BM25          keyword ranking (BM25+) │
                        │  2. KG            entity expansion + hops  │
  Intent classifier     │  3. Link Walk     wikilink graph traversal  │
  ┌────────────────┐    │  4. Structural    date / tag / type match   │
  │ kg_first       │    │  5. Recency       freshness decay prior     │
  │ bm25_first     │    │  6. Link Popularity hub docs (inbound links)│
  │ balanced       │    └──────────────────────┬────────────────────┘
  └────────────────┘                           │
                                   Weighted RRF Fusion
                                               │
                              ┌────────────────▼──────────────────┐
                              │  Ranked chunks + provenance tags  │
                              │  bm25 · kg · link:hop1 · structural│
                              │  recency · link_pop               │
                              └───────────────────────────────────┘
Signal Weight What it captures
BM25 intent-weighted Keyword frequency and term importance
KG Entity intent-weighted Entities from query linked to documents
Link Walk intent-weighted Documents reachable via wikilinks from top hits
Structural 1.0 Frontmatter date ranges, tags, doc types
Recency 0.3 Freshness prior (half-life: 30 days)
Link Popularity 0.2 Hub documents cited by many others

Quick Start

pip install locus-rag

Index and retrieve in three commands:

# Index a directory of markdown files
locus index ./my-docs

# Retrieve — every result includes a provenance tag
locus retrieve "how does authentication work?"
# [1] auth/jwt.md  score=0.0421  via=bm25
#     JWT tokens are validated on every request. The token is...
# [2] auth/oauth.md  score=0.0318  via=kg
#     OAuth 2.0 flow for third-party integrations...

# Explain why a specific result was returned
locus explain <chunk_id> --query "authentication"
# {
#   "narrative": "Chunk from 'auth/jwt.md' (section: 'JWT Validation').
#                 Retrieved because: BM25: terms [jwt, authentication]
#                 appear in this chunk; KG: entity Alice links to this
#                 document via 3 KG triples.",
#   "bm25_matched_terms": ["jwt", "authentication"],
#   "kg_matched_entities": ["Alice"],
#   ...
# }

As a Python library:

from locus import LocusEngine

engine = LocusEngine(store_path=".locus")
engine.index("./my-docs")

chunks = engine.retrieve("JWT authentication flow", limit=5)
for chunk in chunks:
    print(f"{chunk.doc_path}  via={chunk.provenance}")
    print(chunk.content[:200])

# Explain a result
print(engine.explain(chunks[0].chunk_id, query="JWT")["narrative"])

As an MCP server (Claude Desktop / Claude Code / Cursor / Windsurf):

claude mcp add locus -- py -3 -m locus.mcp.server --store /path/to/.locus

Then ask Claude: "What does the codebase say about the auth system?" — Locus retrieves context, Claude answers.


Architecture

Locus is three layers — memory, retrieval, and interface — with no layer depending upward:

┌─────────────────────────────────────────────────────────────────┐
│  Interface Layer                                                  │
│  ┌────────────────┐  ┌──────────────────┐  ┌─────────────────┐  │
│  │  CLI (locus)   │  │  MCP stdio server│  │  HTTP JSON-RPC  │  │
│  │  26 commands   │  │  26 tools        │  │  POST /rpc      │  │
│  │                │  │  4 prompts       │  │  Bearer token   │  │
│  │                │  │  resources       │  │  CORS headers   │  │
│  └────────────────┘  └──────────────────┘  └─────────────────┘  │
├─────────────────────────────────────────────────────────────────┤
│  Retrieval Layer                                                  │
│  BM25Retriever  ·  KGRetriever  ·  LinkWalker                    │
│  StructuralRetriever  ·  RecencyRetriever  ·  LinkPopularity     │
│  ──────────────────────────────────────────────────────────────  │
│  RRF Fusion  ·  QueryClassifier  ·  ContextBulletin              │
│  TokenBudget  ·  LocusCluster (multi-node)                       │
├─────────────────────────────────────────────────────────────────┤
│  Memory Layer                                                     │
│  Corpus         BM25 inverted index, checksum dedup, SQLite      │
│  TemporalKG     Triples + validity windows, traversal, match     │
│  EntityResolver Alias table → canonical entity names            │
│  Chunker        Section-aware (heading boundaries) + word-count  │
└─────────────────────────────────────────────────────────────────┘

All storage is SQLite — no external databases, no servers, no configuration.


Features

Retrieval

  • Six-signal pipeline fused via weighted Reciprocal Rank Fusion
  • Section-aware chunking — splits at # heading boundaries; long sections fall back to word-count
  • Temporal retrievalas_of="2024-01-01" returns only facts valid on that date
  • Query intent routing — auto-classifies kg_first / bm25_first / balanced and shifts weights
  • Checksum dedup — unchanged files never re-indexed; safe to run index on every startup

Knowledge Graph

  • Prose triple extraction — 14 relation patterns (leads, works_at, is_a, part_of, replaced, depends_on...) auto-run during indexing — no annotation required
  • Entity resolution — alias table maps name variants to canonical entities transparently
  • Contradiction detection — surfaces same-predicate triples with different objects and overlapping validity windows
  • BFS traversalkg.traverse("Alice", max_depth=2) returns the full entity subgraph
  • Pattern matchingkg.match("*", "works_at", "Acme") finds everyone at Acme; * is a wildcard
  • KG export — GraphML (Gephi/Cytoscape), JSONL (scripting), DOT (Graphviz)

Explainability

engine.explain(chunk_id, query="authentication")
# {
#   "bm25_matched_terms": ["jwt", "authentication"],
#   "kg_matched_entities": ["Alice"],
#   "structural_matches": ["date 2025-02-10 in range Q1 2025"],
#   "kg_context": [{"entity": "Alice", "fact_count": 3, "sample_fact": "Alice --leads--> Auth team"}],
#   "narrative": "Chunk from 'auth.md'. Retrieved because: BM25: terms [jwt,
#                 authentication]; KG: Alice links to document..."
# }

This is structurally impossible with vector search. No cosine score has a "why."

MCP Integration

  • 26 tools spanning retrieval, KG operations, bulletin management, cluster, evaluation, export
  • MCP Resources — all indexed documents browsable as locus://doc/{path}
  • 4 MCP Prompts — live-context templates pre-filled with retrieved knowledge before sending to the model
  • HTTP transportlocus serve --port 7391 --token <secret> for remote clients

Memory Management

  • Tiered bulletin board (Tier 0 Pinned / Tier 1 Hot / Tier 2 Archive) with hit-boost + age-decay scoring
  • Persistent hot tierbulletin.sqlite3 survives process restarts
  • Soft token budget — WARNING / TREND / CRITICAL alerts; never blocks retrieval
  • File watcherlocus watch ./docs auto-reindexes changes; deletions detected

Operations

  • Health diagnosticslocus doctor checks corpus, KG, bulletin, store size, version
  • Multi-node cluster — cross-corpus RRF fusion with node-prefixed provenance (jarv:bm25)
  • OMPA bridge — direct import from OMPA vaults (identical SQLite KG schema)
  • Evaluationlocus benchmark qa.json measures Recall@K and MRR

MCP Tools

All 26 tools
Tool Description
locus_index Index a file or directory; skips unchanged files
locus_retrieve Six-signal retrieval — returns chunks with provenance tags
locus_explain Explain why a chunk was retrieved for a query
locus_add_fact Add a temporal KG triple (subject–predicate–object)
locus_query_entity Query all KG facts about an entity; supports as_of
locus_kg_stats Triple count, entity count, source count
locus_kg_traverse BFS graph walk from a starting entity
locus_kg_match Wildcard pattern match (* matches any)
locus_contradictions Find conflicting triples (same predicate, different objects)
locus_add_alias Register entity alias → canonical mapping
locus_suggest_aliases Suggest entity name pairs that may be duplicates
locus_hot_context Get the hot-tier bulletin board for context injection
locus_promote Pin a chunk to Tier 0 (never auto-archived)
locus_session_start Warm open: corpus + KG + bulletin stats + hot context
locus_wrap_up Session close: tick bulletin decay, return summary
locus_status Full store status: corpus, KG, bulletin, budget, resolver
locus_forget Remove a document from the corpus
locus_sync Full reindex: wipe and rebuild from directory
locus_doctor Health diagnostics — PASS / WARN / FAIL per check
locus_export_kg Export KG to GraphML / JSONL / DOT
locus_ingest_ompa Import an OMPA vault (markdown + KG triples)
locus_benchmark Recall@K + MRR evaluation against a QA JSON file
locus_cluster_retrieve Cross-node retrieval fused via RRF
locus_add_node Register a named node in the cluster
locus_remove_node Unregister a node
locus_list_nodes List nodes with corpus and KG stats

MCP Prompts

Four templates rendered with live context before delivery to the model:

Prompt What it pre-fills
locus_research Retrieved chunks for a topic
locus_entity_summary All KG facts + document context for an entity
locus_timeline Temporal fact diff for an entity between two dates
locus_contradiction_analysis Contradictions framed for human resolution

CLI Reference

locus [--store PATH] COMMAND [args]
Command Description
index <path> Index files; --pattern **/*.md
retrieve <query> Retrieve; --limit 5 --as-of 2024-01-01 --raw
explain <chunk_id> Explain retrieval; --query "..."
status Corpus + KG + bulletin + budget stats
session-start Warm context open
wrap-up Session close + bulletin tick
add-fact S P O Add KG triple; --valid-from --valid-to --source
query-entity <name> KG entity facts; --as-of
add-alias A B Register alias A → canonical B
contradictions [entity] Find conflicting KG triples
kg-traverse <start> BFS traversal; --depth 2 --direction out
kg-match Pattern match; --subject --predicate --obj --as-of
forget <doc_path> Remove document from corpus
sync <path> Full reindex
watch <path> Auto-reindex on changes; --interval 5
mcp Start MCP stdio server
serve Start HTTP JSON-RPC server; --port 7391 --token
ingest-ompa <vault> Import OMPA vault
benchmark <qa.json> Recall@K + MRR; --k 1,3,5
doctor Health check report
export-kg <path> Export KG; --format graphml|jsonl|dot

Multi-node Cluster

Locus has first-class support for running multiple named nodes — each indexing a different knowledge domain — with cross-node retrieval fused via RRF.

from locus import LocusCluster

cluster = LocusCluster("cluster.json")
cluster.add_node("docs",    "/vaults/docs/.locus")
cluster.add_node("code",    "/vaults/code/.locus")
cluster.add_node("logs",    "/vaults/logs/.locus")

# Query all three simultaneously
chunks = cluster.retrieve("auth system deployment", limit=5)
for c in chunks:
    print(f"{c.provenance}  {c.doc_path}")
    # docs:bm25     auth/jwt.md
    # code:kg       src/auth/middleware.py.md
    # logs:recency  incidents/2025-02-auth-outage.md

Each result is tagged node_name:signal so you always know which knowledge base contributed which chunk. The registry persists across restarts — add a node once, always available.


OMPA Bridge

If you already use OMPA as your agent memory layer, Locus can import your vault directly. The KG schemas are identical — no transformation required.

locus ingest-ompa ~/path/to/ompa-vault --store .locus
from locus.bridge.ompa import OMPABridge
bridge = OMPABridge(engine, vault_path="~/ompa-vault")
result = bridge.ingest()
# {"chunks_indexed": 847, "triples_imported": 3_241, ...}

Markdown files are indexed via the normal pipeline. KG triples from .palace/knowledge_graph.sqlite3 are copied row-by-row. Safe to re-run — checksum dedup prevents duplicate indexing.


Health Diagnostics

locus doctor
Locus Doctor
============================================
[PASS]  corpus               47 docs | 203 chunks | avg 4.3 chunks/doc
[PASS]  knowledge_graph      1847 triples | 312 entities | 2 contradictions
[WARN]  bulletin             Tier 0 (pinned) is full — oldest entries will be demoted
[PASS]  entity_resolver      14 aliases registered
[PASS]  store_size           3.21 MB at .locus
[PASS]  version              Locus v0.6.0

Result: 5 pass  1 warn  0 fail

Benchmarking

Create a QA JSON file and run evaluation against your indexed corpus:

[
  {"query": "how does JWT authentication work?",    "expected_docs": ["auth/jwt.md"]},
  {"query": "incident response runbook",            "expected_docs": ["ops/ir.md"]},
  {"query": "Q1 2025 architecture decisions",       "expected_docs": ["adrs/q1-2025.md"]}
]
locus benchmark qa.json --k 1,3,5
Locus Benchmark
========================================
Queries:  20

Recall@1: 0.650  (13/20)
Recall@3: 0.800  (16/20)
Recall@5: 0.850  (17/20)
MRR:      0.723

Misses (top 5):
  [MISS] "incident response process" expected=['ir.md'] got=['deploy.md', ...]

KG Export & Visualization

locus export-kg knowledge.graphml   # Gephi / Cytoscape / yEd
locus export-kg knowledge.dot       # Graphviz → PNG/PDF
locus export-kg knowledge.jsonl     # scripting / grep
# Render a quick PNG with Graphviz
dot -Tpng knowledge.dot -o knowledge.png

Installation

pip install locus-rag

Zero runtime dependencies. Locus uses only Python stdlib: sqlite3, re, pathlib, threading, http.server, hashlib, math, json.

Optional extras:

pip install "locus-rag[ompa]"   # OMPA vault bridge
pip install "locus-rag[dev]"    # pytest, coverage

Requirements: Python 3.11+


Contributing

See CONTRIBUTING.md. All contributions welcome — bug reports, feature requests, PRs.

git clone https://github.com/jmiaie/locus
cd locus
pip install -e ".[dev]"
pytest tests/ -v

License

MIT — see LICENSE.


Acknowledgements

Locus is architecturally descended from OMPA (Obsidian-MemPalace-Agnostic), which pioneered the three-layer vault → palace → KG design for agent memory. The tiered bulletin board, temporal KG schema, and session lifecycle patterns are direct adaptations. The OMPA bridge enables seamless import of existing OMPA vaults.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

locus_rag-1.0.0.tar.gz (102.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

locus_rag-1.0.0-py3-none-any.whl (95.8 kB view details)

Uploaded Python 3

File details

Details for the file locus_rag-1.0.0.tar.gz.

File metadata

  • Download URL: locus_rag-1.0.0.tar.gz
  • Upload date:
  • Size: 102.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for locus_rag-1.0.0.tar.gz
Algorithm Hash digest
SHA256 9f6556c71712480396737d2172a352274bb07cb8cd06c072f267aad1e8293211
MD5 ee91746b13a2547142f174f6a897c0bd
BLAKE2b-256 6de79e121c1978592904ef415809887dc7af44239bd708687a4eade548c55a59

See more details on using hashes here.

Provenance

The following attestation bundles were made for locus_rag-1.0.0.tar.gz:

Publisher: publish.yml on jmiaie/locus

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file locus_rag-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: locus_rag-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 95.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for locus_rag-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 87976d4db3fd94b9234beac67f17309d46281afb02cca14d6d571b630a4e7dd4
MD5 bcf42e97f8ed9bff3cd082ced9372ae4
BLAKE2b-256 690fdcf4e13759f0ddcbce080d0a26d954f5a1655b4872d1c871d796ad166d3c

See more details on using hashes here.

Provenance

The following attestation bundles were made for locus_rag-1.0.0-py3-none-any.whl:

Publisher: publish.yml on jmiaie/locus

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page