Search
So you want to find something. Strata's got a few ways to help, depending on how elaborate you want to get. Out of the box - no extras, no fuss - it uses plain old filesystem grep for active and cooled files, plus SQLite FTS5 for the archive shadow index. Bring along the optional QMD package, and suddenly you've got BM25 full-text search and vector embeddings doing a hybrid dance. Fancy.
Search Flow
When you run strata search <query> or strata query <query>, the QueryEngine follows a cascade - a funnel that starts with the fanciest tool available and works its way down:
┌──────────────────┐
│ strata search │
│ "oauth2 koda" │
└────────┬─────────┘
│
┌─────────▼─────────┐
│ QMD available? │
│ (npm @tobilu/qmd)│
└─────┬──────┬──────┘
YES │ │ NO
┌──────────┘ └──────────┐
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ QMD hybrid │ │ Filesystem grep │
│ search │ │ stratum_1 + 2 │
│ (BM25 + vector) │ │ (simple term │
│ All collections │ │ matching) │
└────────┬─────────┘ └────────┬─────────┘
│ │
└──────────┬──────────────────┘
│
▼
┌──────────────────┐
│ Shadow Index │
│ FTS5 search │
│ (always runs) │
│ stratum_3 │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Rank & dedupe │
│ Return top_k │
│ results │
└──────────────────┘
Phase 1: QMD Hybrid Search (When Available)
If you've got QMD installed, Strata starts by running a hybrid search across both active and cooled collections. Here's the breakdown:
BM25 Full-Text Search (
strata searchviaqmd search): Good old keyword matching, weighted by term frequency. If your query words appear in the file, this finds them.Vector Semantic Search (
strata vsearchviaqmd vsearch): Embedding-based similarity. This is where it gets clever - it finds stuff that's conceptually related, not just keyword-matched.Reciprocal Rank Fusion (RRF): Combines both result sets using
RRFscoring (score += 1 / (k + position)wherek=60). The result: a ranked, deduplicated list that's better than either method alone.Optional LLM Reranking: Configured a reranker provider (say
openai://gpt-4o-mini)? QMD can re-rank results using an actual LLM. This costs API credits, so save it for when you really need that extra polish.
The neat thing about QMD hybrid search is that it gives you semantic understanding without needing an LLM at all. The BM25 + vector fusion catches both exact keyword matches and conceptually similar content. Best of both worlds.
Phase 1: Filesystem Search (Fallback)
No QMD? No problem. Strata falls back to a straightforward directory-walking grep:
- Walk
active/andcooled/recursively - Split the query into lowercase terms
- Score each file: 1.0 per term matched in the path, 0.5 per term in the content
- Return the top scorers from each stratum
It's zero-dependency and runs anywhere Python does. Semantic matching? Not so much - your results are only as good as your keyword game.
Phase 2: Shadow Index (Always Runs)
Whatever happened in phase 1, the engine always checks the 3rd Stratum Shadow Index. It's a safety net for archived content:
- Connect to the SQLite FTS5 database (
stratum_3_shadow.db) - Run the FTS5
MATCHquery againstkeywordsandsummary_previewcolumns - Score results by FTS5 rank (descending)
- If you passed tags, it filters by
keywords LIKE '%"tagname"%'
Archived files aren't gone - they're just in a different room. The shadow index makes sure you can still find them.
Phase 3: Fusion and Ranking
Results from all three phases get thrown into one pot, sorted by score (descending), and the top top_k (default: 10) come back to you:
[
{
"content": "# Koda Platform\nStack: React + Go...",
"tier": "stratum_1",
"source": "projects/koda/spec.md",
"score": 1.5,
"metadata": {"path": "projects/koda/spec.md", "size": 1234}
},
{
"content": "# Koda Database Schema...",
"tier": "stratum_3",
"source": "archive:stratum_3_abc123.json",
"score": 0.25,
"metadata": {
"id": "uuid-123",
"archive_path": "/path/to/archive/abc123.json",
"_needs_rehydration": true
}
}
]
Search Backends
Here's a closer look at each option.
Filesystem Grep (Fallback)
The built-in fallback. It walks directories and matches terms. No frills.
- Dependencies: Zero (stdlib only)
- Latency: Proportional to file count (walks everything)
- Quality: Keyword only, no semantic matching
- Best for: Small to moderate stores - under 1000 files, you're fine
Shadow Index (SQLite FTS5)
Always used for the archive stratum. A lightweight keyword index that punches above its weight.
- Dependencies: Zero (stdlib
sqlite3) - Latency: ~5ms per query, scale doesn't matter
- Quality: Keyword-based FTS5 with ranking
- Storage: ~200 bytes per entry (keywords + 200-char preview + path)
QMD (BM25 + Vector Fusion)
The optional hybrid engine. Runs locally, no LLM required for the base search.
- Dependencies: Node.js +
@tobilu/qmd - Latency: ~100-500ms per query (embeddings take a moment)
- Quality: Semantic matching via embeddings + BM25 keyword fusion
- Storage: Vector index size depends on collection size
Setup
npm install -g @tobilu/qmd
strata qmd-setup # Add active/ + cooled/ as QMD collections
strata qmd-embed # Generate vector embeddings
Collections are named strata_active and strata_cooled. The prefix is configurable via qmd_collection_prefix, if you're the type who likes custom labels.
Reranker Providers
QMD supports optional LLM rerankers for when you really need top-tier results:
| Provider | Example URL | Cost |
|---|---|---|
| OpenAI | openai://gpt-4o-mini |
API credits |
| Anthropic | anthropic://claude-3-haiku |
API credits |
| Ollama | ollama://llama3 |
Local compute |
| Local GGUF | local://path/to/model.gguf |
Local compute |
Configure via:
strata config set qmd_reranker "openai://gpt-4o-mini"
Performance Characteristics
| Search Backend | 100 files | 10,000 files | 1M archived |
|---|---|---|---|
| Filesystem grep | ~5ms | ~500ms | N/A |
| Shadow Index FTS5 | ~5ms | ~5ms | ~50ms |
| QMD hybrid | ~100ms | ~500ms | N/A (active/cooled only) |
Agent Usage
CLI (recommended for agents)
# Human-readable output
strata search "koda oauth2"
# JSON output for scripting
strata query "koda oauth2"
Python API
from strata import Strata
strata = Strata()
# Search across all tiers
results = strata.query("koda oauth2")
for r in results:
print(f"[{r['tier']}] {r['source']}: {r['content'][:80]}")
# With filters
results = strata.query(
"koda",
filters={"tags": ["project"]},
top_k=10,
)
Function Calling Tools
# Get tool schemas for agent registration
tools = strata.tools.all_schemas()
# The strata_query tool expects:
# - query: string (required)
# - tags: string[] (optional)
# - top_k: integer (optional, default 5)
result = strata.tools.execute("strata_query", {"query": "koda"})
Best Practices
- Be specific. "koda oauth2 stripe" beats "project info" every time. Precision is power.
- Start with
strata listorstrata read index.mdwhen you know where things live. Search is for discovery, not navigation. - Check the tier labels. Archived results score lower and might need rehydration before you can do much with them.
- Install QMD once you've got more than a handful of files. The filesystem fallback gets sluggish as your store grows; QMD stays snappy.
- Use
strata queryfor scripting. The JSON output is stable, parseable, and won't surprise you.
Cross-Reference
- CLI Reference --
strata searchandstrata querycommands - Architecture -- how the shadow index fits into the tiered system
- Installation -- QMD setup instructions
- Tracking -- cost comparison of search backends
- Configuration -- search_backend and qmd_reranker settings