Skip to content

Repository files navigation

ContextMesh banner

ContextMesh

FrontendBackendDockerizedHybrid RetrievalTests

ContextMesh is a private knowledge intelligence platform for enterprise documents and operational knowledge. It ingests mixed-format content, builds multiple retrieval paths over the same corpus, routes each question through an explicit planner, and returns grounded answers with transparent evidence instead of decorative citations.

The default stack runs fully local. You can boot it with no model keys, log in with seeded accounts, inspect the retrieval traces, browse the graph, run evaluation benchmarks, and upload new documents into a tenant-scoped corpus. External model providers are optional and swappable through the provider abstraction.

Important

This repository is built like an internal platform, not a chatbot demo. The answer path is structured, inspectable, and access-aware. Dense search, sparse search, graph retrieval, reranking, evals, and trace persistence are all first-class parts of the system.

Why It Matters

Most RAG examples stop at vector search plus a prompt wrapper. That breaks down quickly in real internal knowledge systems:

  • Relationship-heavy questions need graph structure, not just semantic proximity.
  • Policy and operational queries need access filtering and metadata-aware retrieval.
  • Internal teams need to debug why evidence was selected, not trust a black box.
  • Retrieval quality regresses silently without benchmarks, traces, and experiment comparisons.

ContextMesh treats retrieval as an operational system. It keeps the planner explicit, the evidence visible, the traces durable, and the tenant boundaries enforced throughout the lifecycle.

Feature Grid

AreaWhat is implemented
Multi-source ingestionPDF, DOCX, PPTX, Markdown, HTML, TXT, CSV; parser-specific metadata; image/diagram summaries surfaced as searchable text hints
Retrieval stackDense LSA-style embeddings in Qdrant, BM25 sparse retrieval, hybrid reciprocal-rank fusion, metadata filtering, semantic-style rerank heuristics
Graph-aware RAGEntity extraction, relation extraction, Neo4j population, graph lookups for relationship-heavy questions, graph + vector grounding
OrchestrationQuery classification, retrieval planning, dense/sparse/graph execution, rerank, evidence validation, answer synthesis, trace persistence
Model interoperabilityHeuristic local provider by default, OpenAI-compatible adapter, Anthropic-compatible adapter, local endpoint hook
Grounded UXEvidence cards, source metadata, retrieval path chips, graph hit indicators, confidence hints, trace views
Evaluation loopSeeded evaluation dataset, experiment records, retrieval recall, grounding score, latency summary, failure pattern surfacing
ObservabilityIngestion jobs, embedding status, retrieval runs, rerank results, answer records, token usage payloads, health endpoints
Tenant boundariesOrganizations, memberships, roles, access tags, scoped retrieval filtering, admin-only surfaces

Architecture

flowchart LR
U["User / Analyst"] --> F["Next.js control surface"]
F --> A["FastAPI API layer"]
A --> O["Query workflow engine"]
A --> I["Ingestion service"]
O --> P["Planner / classifier"]
O --> R["Retrieval service"]
O --> M["Provider router"]
R --> Q["Qdrant dense index"]
R --> S["BM25 sparse scorer"]
R --> G["Neo4j knowledge graph"]
I --> X["Parser + chunker"]
I --> Q
I --> G
I --> PG["PostgreSQL domain store"]
I --> MIN["MinIO object storage"]
O --> PG
O --> RED["Redis cache / queue hook"]
A --> PG
A --> RED
Loading

Query Lifecycle

sequenceDiagram
participant User
participant UI as Ask workspace
participant API as FastAPI
participant Planner as Query workflow
participant Retriever as Retrieval service
participant Graph as Neo4j
participant Provider as Answer provider
participant DB as Postgres trace store
User->>UI: Submit question
UI->>API: POST /queries/ask
API->>Planner: Create workflow state
Planner->>Planner: Classify query
Planner->>Planner: Choose retrieval strategy
Planner->>Retriever: Dense + sparse retrieval
alt relationship-heavy
Planner->>Graph: Graph lookup
end
Retriever-->>Planner: Candidate evidence
Planner->>Planner: Rerank + confidence
Planner->>Provider: Grounded synthesis request
Provider-->>Planner: Answer + follow-ups
Planner->>DB: Persist trace, rerank, answer
Planner-->>API: Grounded response
API-->>UI: Answer, evidence, retrieval path
Loading

Ingestion Lifecycle

flowchart LR
F["Uploaded file"] --> P["Parser by media type"]
P --> C["Cleanup + metadata extraction"]
C --> H["Section-aware chunking"]
H --> D["Dense embedding rebuild"]
H --> B["Sparse term indexing"]
H --> E["Entity extraction"]
E --> R["Relation extraction"]
D --> Q["Qdrant collection"]
B --> PG["Postgres chunk store"]
R --> N["Neo4j graph"]
C --> O["MinIO object storage"]
H --> T["Ingestion job metrics + traceability"]
Loading

Retrieval Strategy

ContextMesh does not route every question through the same search path.

Query shapeRetrieval strategy
Relationship-heavy questionsgraph-assisted with dense + sparse + graph corroboration
Policy and controls lookupsection-aware with heading boosts and access tag enforcement
Tabular or vendor lookuphybrid with sparse-heavy weighting
Short semantic lookupvector path with rerank and evidence packaging
flowchart TD
Q["Question"] --> C["Classifier"]
C --> V["Vector search"]
C --> H["Hybrid search"]
C --> GA["Graph-assisted search"]
V --> F["Reciprocal-rank fusion / rerank"]
H --> F
GA --> G["Graph neighbors"]
G --> F
F --> E["Evidence package"]
E --> A["Grounded answer"]
Loading

Graph Layer

The graph is a real retrieval primitive, not a label over vector search.

  • Entities are extracted from chunk text and titles.
  • Relations are extracted from operational verbs such as depends on, integrates with, reports to, governs, and supports.
  • Entities and relations are persisted in both PostgreSQL and Neo4j.
  • Relationship-heavy questions trigger graph lookups and graph-based rerank boosts.
  • The UI surfaces graph nodes, edges, linked documents, and graph hits from query runs.
flowchart LR
C["Chunk text"] --> EE["Entity extraction"]
C --> RE["Relation extraction"]
EE --> PG["graph_entities table"]
RE --> PR["graph_relationships table"]
EE --> N["Neo4j nodes"]
RE --> E["Neo4j edges"]
Q["Relationship question"] --> M["Entity match"]
M --> N
E --> H["Graph hits"]
H --> R["Hybrid reranker"]
Loading

Evaluation And Observability

flowchart LR
D["Seeded eval dataset"] --> W["Workflow run"]
W --> RR["Retrieval recall"]
W --> FG["Grounding / faithfulness rate"]
W --> LT["Latency summary"]
W --> FC["Failure case capture"]
RR --> ER["evaluation_runs"]
FG --> ER
LT --> ER
FC --> ER
ER --> UI["Eval + dashboard surfaces"]
Loading

The system persists enough detail to debug both ingestion and answer quality:

  • ingestion_jobs track chunk count, parser stage, and asset metadata.
  • embedding_records track which chunks are indexed and how.
  • query_traces keep classification, retrieval strategy, latency, and provider usage.
  • retrieval_runs and rerank_results show what the planner executed and why top evidence survived.
  • answer_records keep the grounded response and retrieval path.

Deployment Topology

flowchart TB
subgraph Local Stack
FE["Frontend :3000"]
BE["Backend :8080"]
PG["Postgres :5432"]
RD["Redis :6379"]
QD["Qdrant :6333"]
NG["Neo4j :7474 / :7687"]
MI["MinIO :9000 / :9001"]
end
FE --> BE
BE --> PG
BE --> RD
BE --> QD
BE --> NG
BE --> MI
Loading

Data Model

erDiagram
ORGANIZATIONS ||--o{ MEMBERSHIPS : has
USERS ||--o{ MEMBERSHIPS : joins
ORGANIZATIONS ||--o{ DOCUMENTS : owns
DOCUMENTS ||--o{ DOCUMENT_VERSIONS : versions
DOCUMENTS ||--o{ INGESTION_JOBS : tracks
DOCUMENT_VERSIONS ||--o{ CHUNKS : yields
CHUNKS ||--|| EMBEDDING_RECORDS : indexes
ORGANIZATIONS ||--o{ GRAPH_ENTITIES : contains
GRAPH_ENTITIES ||--o{ GRAPH_RELATIONSHIPS : links
USERS ||--o{ QUERY_SESSIONS : opens
QUERY_SESSIONS ||--o{ QUERY_TRACES : records
QUERY_TRACES ||--o{ RETRIEVAL_RUNS : executes
QUERY_TRACES ||--o{ RERANK_RESULTS : ranks
QUERY_TRACES ||--|| ANSWER_RECORDS : produces
ORGANIZATIONS ||--o{ EVALUATION_DATASETS : benchmarks
EVALUATION_DATASETS ||--o{ EVALUATION_CASES : contains
ORGANIZATIONS ||--o{ EXPERIMENTS : compares
EXPERIMENTS ||--o{ EVALUATION_RUNS : produces
ORGANIZATIONS ||--o{ PROMPT_CONFIG_VERSIONS : versions
ORGANIZATIONS ||--o{ AUDIT_EVENTS : logs
Loading

API Overview

RoutePurpose
POST /api/v1/auth/loginSeeded login for admin / analyst / reviewer roles
GET /api/v1/auth/meCurrent user and memberships
GET /healthHealth probe across database, Redis, Qdrant, and storage mode
GET /api/v1/dashboard/summaryDashboard metrics and high-signal activity
GET /api/v1/documentsDocument library with tenant filtering
POST /api/v1/documents/uploadUpload and index a document
GET /api/v1/documents/{id}Document detail with chunks, entities, relationships, ingestion history
POST /api/v1/documents/{id}/reindexRebuild retrieval state for a document
POST /api/v1/queries/askRun the planner and return grounded evidence
GET /api/v1/queries/tracesRecent query trace summaries
GET /api/v1/graph/overviewGraph nodes, edges, and linked evidence
GET /api/v1/evals/datasetsEvaluation dataset catalog
GET /api/v1/evals/experimentsExperiment definitions
POST /api/v1/evals/runsLaunch an evaluation benchmark
GET /api/v1/admin/overviewProvider, prompt, tenant, and health settings

Example grounded query

curl -X POST http://localhost:8080/api/v1/queries/ask \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{ "organization_id": "<org-id>", "question": "Which component governs retention exceptions and who can review finance-confidential evidence?" }'

Local Setup

1. Prepare the environment

make setup

This copies .env.example to .env if it is not already present.

2. Boot the full stack

docker compose up --build

3. Open the product surfaces

SurfaceURL
Frontendhttp://localhost:3000
Backend docshttp://localhost:8080/docs
Neo4j Browserhttp://localhost:7474
MinIO Consolehttp://localhost:9001
Qdranthttp://localhost:6333/dashboard

Seeded users

RoleEmailPassword
Adminadmin@northstar.localcontextmesh-admin
Analystanalyst@northstar.localcontextmesh-analyst
Reviewerreviewer@northstar.localcontextmesh-reviewer

Working Surfaces

Dashboard

  • ingestion activity
  • corpus size
  • recent query traces
  • provider usage
  • evaluation summaries
  • failure pattern counters

Ask workspace

  • query composer
  • retrieval path indicator
  • answer panel
  • evidence cards
  • source preview
  • graph hits
  • follow-up suggestions

Library

  • upload flow
  • drag and drop
  • access scope visibility
  • tag visibility
  • document drill-down
  • reindex action

Graph explorer

  • node and edge rendering
  • entity detail panel
  • linked evidence list

Eval / Experiments

  • dataset inventory
  • experiment catalog
  • benchmark launch
  • retrieval recall and grounding summaries

Admin

  • provider settings
  • prompt versions
  • retrieval controls
  • tenant settings
  • health overview

Folder Structure

ContextMesh/
├── backend/
│ ├── app/
│ │ ├── api/routes/
│ │ ├── auth/
│ │ ├── core/
│ │ ├── evals/
│ │ ├── ingestion/
│ │ ├── models/
│ │ ├── orchestration/
│ │ ├── retrieval/
│ │ ├── schemas/
│ │ ├── services/
│ │ └── utils/
│ ├── data/
│ │ ├── evals/
│ │ └── seed_corpus/
│ ├── scripts/
│ └── tests/
├── docs/assets/
├── frontend/src/
│ ├── app/
│ ├── components/
│ └── lib/
├── docker-compose.yml
└── README.md

Sample Use Cases

  • “What depends on Search Federation Service across the support workflow?”
  • “Which component governs retention exceptions and who can review finance-confidential evidence?”
  • “Show the relationship path between Policy Ledger and Evidence Vault.”
  • “Which runbooks are visible to reviewers but not analysts?”
  • “Compare vendor dependencies mentioned in the risk register against the incident playbook.”

Design Decisions

Default dense retrieval is local-first

The default embedder uses a TF-IDF + SVD latent semantic representation and stores vectors in Qdrant. That keeps the system runnable with zero external model credentials while still supporting a multi-stage dense path. The provider layer is there for stronger hosted embeddings and generation when desired.

Dense index rebuild favors simplicity over streaming complexity

On ingestion, ContextMesh rebuilds the tenant’s dense index. For a local enterprise knowledge demo and seeded corpus, that is a reasonable tradeoff: simple, deterministic, and easy to inspect. The interfaces are structured so background incremental indexing can be added later without changing the product surface.

Graph extraction is explicit and debuggable

The entity and relation extraction logic is deterministic and verb-based. That gives up some recall versus a heavier model-based IE pipeline, but it keeps the graph leg explainable and stable for local development.

The orchestration layer is controlled

The planner is explicit. It is not an autonomous agent loop. Each query moves through classification, strategy choice, retrieval, rerank, validation, and synthesis with durable records at each step.

Backend Verification

cd backend
python3.11 -m venv .venv
. .venv/bin/activate
pip install -e '.[dev]'
pytest
ruff check app tests scripts

Frontend Verification

cd frontend
npm install
npm run build

Roadmap

  • incremental background indexing with Redis-backed workers
  • stronger relation extraction via pluggable IE providers
  • richer graph traversal operators in the planner
  • document diffing across versions
  • export bundles for audit and review workflows
  • per-experiment prompt overlays and retrieval parameter tuning

ContextMesh is opinionated about the hard parts of private RAG: grounded evidence, access scope, inspectable planning, graph structure, and regression discipline.

About

Private knowledge intelligence platform for enterprise document ingestion, hybrid retrieval, graph-aware RAG, grounded answers, and traceable evidence.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages