picoqmd
Health Uyari
- License — License: MIT
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 6 GitHub stars
Code Gecti
- Code scan — Scanned 1 files during light audit, no dangerous patterns found
Permissions Gecti
- Permissions — No dangerous permissions requested
Bu listing icin henuz AI raporu yok.
Lightweight QMD alternative: fully local hybrid search engine (BM25 + vector + rerank) and MCP server in a single ~15MB Go binary. No Node.js. Runs on Raspberry Pi.
PicoQMD: a lightweight QMD alternative for low-resource computers
A fully local search engine and MCP server in a single ~16MB Go binary (~11MB stripped). PicoQMD is a from-scratch Go reimplementation of tobi's QMD built for machines where QMD's Node.js/Bun stack is too heavy: Raspberry Pi (including Pi Zero), old laptops, small VPSes, air-gapped boxes, and dev machines that just don't want another Node runtime.
It runs the same search pipeline as QMD (SQLite FTS5 BM25, semantic vector search, hybrid query expansion + Reciprocal Rank Fusion + cross-encoder reranking, the same GGUF models) without Node.js, Bun, Python, or native-module ABI headaches. One static binary and a SQLite file.
Point Claude Code, OpenClaw, PicoClaw, MiniClaw, or any other MCP agent at it for instant local search over code, docs, configs, and notes. No cloud, no telemetry, works offline.
PicoQMD vs QMD
If you're looking for "QMD but for a lower-spec computer", this is the trade-off table:
| QMD | PicoQMD | |
|---|---|---|
| Install | Node.js/Bun + npm package (native modules: better-sqlite3, sqlite-vec, node-llama-cpp) | one static Go binary (~16MB, ~11MB stripped) |
| Runtime | Node/Bun VM | none (measured ~29MB peak RSS for a BM25 search over a 10,800-doc index) |
| Index size on disk | ~11.9GB measured on a 10,800-doc corpus | 0.78GB for the same corpus (~15× smaller) with 256-dim Matryoshka vectors (default since v0.6.0) |
| BM25 keyword search | SQLite FTS5 | SQLite FTS5 (pure-Go driver, contentless index; documents are not duplicated into the DB) |
| Semantic vector search | sqlite-vec + node-llama-cpp | pure-Go brute-force cosine + llama.cpp via FFI (same EmbeddingGemma model) |
| Hybrid pipeline | query expansion → fan-out → RRF → rerank | same design, same GGUF models |
| MCP server | stdio + HTTP | stdio + HTTP |
| Minimum hardware for keyword search | needs Node-capable box | Raspberry Pi Zero / ARM32 / RISC-V |
| Vector/hybrid search | yes | yes, on arm64/amd64 (Linux, macOS) |
Line-numbered get with :from:count ranges |
yes | yes (v0.4.0) |
| Embedding fingerprints (stale-vector detection) | yes | yes; v0.5.0 hashes the model file's bytes, so a swapped model re-embeds instead of silently serving mismatched vectors |
| Search-quality benchmarking | qmd bench |
picoqmd bench (v0.5.0) |
| Index diagnostics | qmd doctor |
picoqmd doctor + cleanup (v0.5.0) |
| AST/tree-sitter code chunking | yes | not yet (roadmap) |
| CJK trigram search | yes | no (left out on purpose; it doubles the index) |
PicoQMD is not a fork. It's an independent Go implementation that tracks QMD's retrieval design and ports the fixes that apply (v0.4.0 covered QMD v2.1 to v2.6.3; v0.5.0 covers the v2.8.x round). If you have a fast dev machine and live in the Node ecosystem, use QMD. If you want the same local search quality in a fraction of the footprint, or on hardware QMD can't run at all, use PicoQMD.
Why PicoQMD?
Most search tools assume fast hardware. PicoQMD is built for everything else:
- ~16MB binary (~11MB with
-ldflags="-s -w"), smaller than most npm installs - ~15× smaller index than QMD. The 10,800-doc corpus that filled an 11.9GB QMD index fits in 0.78GB, measured on the same machine and models
- 3× smaller, 3× faster vectors. Embeddings are Matryoshka-truncated to 256 dims by default (EmbeddingGemma is MRL-trained; ~97.6% of full quality).
PICOQMD_EMBED_DIMoverrides - Minimal RAM. BM25 mode runs in tens of MB and fits alongside an agent on $10 hardware
- Zero dependencies. No runtime, no interpreters, no containers, no C toolchain (pure-Go SQLite)
- MCP native. stdio and HTTP transports, works with any MCP-compatible agent
- Cross-compiles anywhere Go does: ARM32, ARM64, RISC-V, x86 in one command
- Scales up. Add semantic vector search and hybrid re-ranking when your hardware allows
- Degrades gracefully. Without models, vector/hybrid tools are hidden from the agent; BM25, get, and observations still work
- Safe under launchd/cron/systemd. Progress output auto-quiets when stdout is not a TTY, so captured logs stay bounded
What's New in v0.6.x
v0.6.1: reranker fixed. The v0.5.0 bench exposed the hybrid pipeline scoring worse than plain vector search; five compounding defects in the rerank stage (KV-cache contamination between candidates, an out-of-distribution prompt instead of Qwen3-Reranker's documented template, title-only candidate text, an RRF/rerank score-scale mismatch, and a too-small decode batch) are fixed. Measured: hybrid hit@10 60% → 100%, MRR 0.50 → 1.00 on the reference fixture, now the strongest pipeline.
v0.6.0: Matryoshka-256 vectors (full details in CHANGELOG.md):
- Embeddings are truncated to 256 dims (from EmbeddingGemma's 768) and L2-renormalized, at both document and query time. 3× smaller vector storage, 3× faster brute-force scans, ~97.6% of full-dimension quality. Measured on a 10,800-doc corpus: index 853MB → 743MB, vector search 0.5s → 0.3s, bench quality within noise of 768-dim (hit@10 unchanged at 100%, MRR 1.0 → 0.9 on a 5-query fixture).
picoqmd migrate-vectorsconverts an existing index in place in seconds. MRL training means truncate+renormalize produces exactly what embedding at 256 dims would; no re-embed needed. Includes a VACUUM to reclaim the space.PICOQMD_EMBED_DIMoverrides the target (0 = full model dimension). The dimension is part of the embedding fingerprint, so mixed-dimension search is impossible;doctorflags mismatches.
Ports of the applicable QMD v2.8.x improvements plus hardening from a real-world failure:
- Model-hash embedding fingerprints. The fingerprint now includes a sha256 of the model file's bytes, not just its name. A re-downloaded model with the same filename used to invalidate every stored vector silently; now it just triggers a re-embed.
picoqmd doctorreports model identity, per-fingerprint vector distribution, and stale or orphaned vectors, and exits non-zero on problems so cron jobs can gate on it.picoqmd cleanup [--dry-run]deletes stale and orphaned vectors so the nextsyncregenerates them.picoqmd bench <fixture.json>measures search quality (hit@k, precision, recall, MRR) per pipeline against a fixture of known-good queries. Seeexample-bench.json.- Multi-collection scope.
collectionaccepts a comma-separated list everywhere; each collection is searched separately and the results merged, so a big collection can't crowd a small one out of the top-k. - Intent-aware expansion. A dominant keyword match no longer skips LLM query expansion when the caller supplied an
intenthint. - New
-c/--collectionflag onsearch,vsearch, andquery.
- Robust FTS5 queries. Version strings (
v3.9.7), hyphenated terms (real-time), and operator words (AND/OR/NOT) can no longer produce FTS5 syntax errors; every term is emitted as a quoted phrase with prefix matching. - Real document retrieval.
get/multi_getreturn content from disk with line-numbered output,qmd://+#docidheaders, and line-range refs:get notes.md:120:40reads 40 lines from line 120.--full-pathswaps in the on-disk path for piping into editors and file tools. - BM25 snippets with line citations. Snippets are extracted from the source file (
>>>term<<<highlighting,path:L<n>), which also feeds the reranker real text instead of bare titles. - Embedding fingerprints. Vectors are stamped with the model + chunker identity; changing either marks documents pending for re-embed instead of silently searching stale vectors.
- Honest embed tracking. A document only counts as embedded when every chunk has a current vector; interrupted embed runs resume instead of being forgotten.
- Concurrency-safe SQLite. 120s busy timeout (override:
PICOQMD_SQLITE_BUSY_TIMEOUT, ms) so a scheduled sync racing the MCP daemon queues instead of throwingdatabase is locked. - Scoped embedding.
picoqmd embed -c <collection>embeds one collection without re-indexing, so huge collections are opt-in. --no-rerankskips the cross-encoder for faster hybrid results on constrained hardware.- First test suite.
go test ./...covers query sanitization, retrieval, and embed tracking against a real store.
Quick Start
# Install (or grab a prebuilt binary from Releases)
go install github.com/altSeneca/picoqmd@latest
# Index markdown docs (default)
picoqmd add ~/docs --no-embed
# Index a codebase: Go, Python, TypeScript, and markdown
picoqmd add ~/myproject --glob "**/*.{go,py,ts,md}" --no-embed
# Search, with prefix matching built in
picoqmd search "kubernetes deployment"
picoqmd search "deploy" # matches "deployment", "deployed", "deploying"
# Retrieve with line ranges
picoqmd get notes.md:120:40 # 40 lines starting at line 120
MCP Server
PicoQMD is an MCP server first. Point your agent at it and you get search, get, multi_get, and status, plus vector_search, deep_search, and research when models are available.
Claude Code
Add to ~/.claude/settings.json under mcpServers:
{
"picoqmd": {
"command": "picoqmd",
"args": ["mcp"]
}
}
OpenClaw / PicoClaw / MiniClaw / Any MCP Client
Stdio transport (default):
picoqmd mcp
HTTP transport for networked setups:
picoqmd mcp --http :8181
Any agent that speaks Model Context Protocol can connect. The MCP server exposes the same search tools whether you're on a Mac Studio or a Pi Zero.
MCP Tools Reference
| Tool | Description | Requires Models |
|---|---|---|
search |
BM25 keyword search via SQLite FTS5 with prefix matching, disk-extracted snippets, line citations | No |
vector_search |
Semantic similarity using embeddings | Yes |
deep_search |
Query expansion + fan-out + RRF + re-ranking (noExpand, noRerank to trim stages) |
Yes |
research |
Composite: BM25 + vector in parallel, deduplicated via RRF, one call | Yes |
get |
Retrieve a document by path, #docid, or qmd:// URI, with :from:count line ranges, line-numbered |
No |
multi_get |
Batch retrieve by glob or comma-separated list; oversized files reported as skipped, never silently dropped | No |
status |
Index health, embedding fingerprint, pending counts, stale observation count | No |
Maintenance runs from the CLI: doctor diagnoses the index, cleanup [--dry-run] removes stale or orphaned vectors, and bench <fixture.json> scores search quality per pipeline.
Common parameters across search tools:
| Parameter | Type | Description |
|---|---|---|
query |
string | Search query (required) |
intent |
string | Optional disambiguation hint threaded through expansion, reranking, and snippets |
limit |
int | Max results, default 10 |
collection |
string | Collection name, or a comma-separated list; each is searched separately and the results merged |
minScore |
float | Minimum relevance score 0 to 1 |
maxChars |
int | Truncate response to this many characters (server-side token budget) |
note |
string | Save an observation linked to the top result |
get / multi_get parameters: fromLine, maxLines, lineNumbers (default true), fullPath, maxBytes (multi_get skip threshold, default 64KB).
Two Modes
BM25 only, for edge and constrained devices
picoqmd add ~/notes --no-embed
picoqmd add ~/src --glob "**/*.{go,py,rs,ts,js}" --no-embed
picoqmd search "meeting notes"
No models, no llama.cpp, no downloads. Just Go + SQLite FTS5 with prefix matching. This is the mode for Pi-Zero-class devices where every megabyte counts: keyword search over ~10,000 documents runs in under 30MB of RAM.
Vector + hybrid, for capable hardware
picoqmd add ~/notes # downloads embedding model (~300MB)
picoqmd model download embedding # or: reranker, expansion
picoqmd "semantic search query" # auto-selects best pipeline
picoqmd embed -c big-collection # embed one collection at a time
When you have the RAM, add semantic search with query expansion, RRF fusion, and cross-encoder re-ranking. Still local, still offline. Same models QMD uses:
| Model | Size | Purpose |
|---|---|---|
| embeddinggemma-300M | ~300MB | Document & query embeddings |
| qwen3-reranker-0.6b | ~600MB | Cross-encoder re-ranking |
| qmd-query-expansion-1.7B | ~1GB | Query expansion |
Search Modes
| Mode | Command | What it does |
|---|---|---|
| BM25 | picoqmd search "query" |
Instant keyword search via SQLite FTS5 with prefix matching |
| Vector | picoqmd vsearch "query" |
Semantic similarity using embeddings |
| Hybrid | picoqmd query "query" |
Expansion + fan-out + RRF + re-ranking (--no-expand, --no-rerank to trim) |
| Smart | picoqmd "query" |
Auto-selects best pipeline for available models |
All three accept -c collection or -c colA,colB to scope the search.
Platform Support
| Platform | BM25 | Vector/Hybrid | Binary |
|---|---|---|---|
| Linux arm32 (Pi Zero, Pi 1) | yes | no | ~11MB |
| Linux riscv64 | yes | no | ~10MB |
| Linux arm64 (Pi 3/4/5, SBCs) | yes | yes | ~11MB |
| Linux amd64 | yes | yes | ~11MB |
| macOS arm64 (Apple Silicon) | yes | yes | ~11MB |
| macOS amd64 (Intel) | yes | yes | ~12MB |
Cross-compile for your target in one line:
GOOS=linux GOARCH=arm GOARM=7 go build -ldflags="-s -w" -o picoqmd .
Export / Import: index once, search anywhere
Build a full index (with embeddings) on a capable machine, then transfer it to a tiny device:
# On your workstation
picoqmd add ~/docs && picoqmd export -o docs.tar.gz
# On a Pi Zero / edge device
picoqmd import docs.tar.gz
picoqmd search "deployment guide" # BM25 + precomputed embeddings, no models needed
The exported bundle contains the SQLite database with all embeddings baked in. The edge device gets semantic-quality ranking without downloading a single model.
Remote Search
Don't want to run search on the edge device at all? Forward to a remote instance:
# Server
picoqmd mcp --http :8181
# Edge device
picoqmd search "query" --remote server:8181
File Type Support
Index any text file, not just markdown. Use glob patterns with brace expansion:
picoqmd add . --glob "**/*.md" # markdown only (default)
picoqmd add . --glob "**/*.{go,py,ts,js,rs,md}" # code + docs
picoqmd add . --glob "**/*.{yaml,yml,json,toml}" # config files
PicoQMD automatically skips binary files, files over 1MB, and common noise directories (.git, node_modules, vendor, __pycache__, build, dist, target, etc.).
Use Cases
- QMD alternative on low-spec hardware: the same local hybrid search without the Node.js runtime, on machines from a Pi Zero up
- Claude Code MCP server: fast, token-efficient search over large codebases without spinning up Elasticsearch
- PicoClaw / MiniClaw search tool: give your $10 AI agent fast local search over project docs, wikis, and codebases
- OpenClaw on Raspberry Pi: add document search to your self-hosted AI assistant without eating its RAM budget
- Edge AI knowledge base: deploy searchable documentation to field devices, kiosks, or air-gapped environments
- Offline dev search: index API docs, READMEs, and notes for airplane-mode development
- Token-efficient MCP pipelines: use
researchto cut context window usage by ~50% vs separate search calls
Roadmap
See ROADMAP.md. Matryoshka 768→256 truncation shipped in v0.6.0. Next up: chunk-level incremental re-embedding, recency-aware ranking, binary quantization with two-phase rescoring for very large corpora, and tree-sitter AST chunking for code.
Acknowledgments
PicoQMD is a Go reimplementation of QMD by @tobi, which provides the architecture, hybrid search pipeline, models, and design. Built with yzma (pure-Go llama.cpp bindings) and llama.cpp.
See GUIDE.md for the full user guide, output formats, and configuration.
License
Yorumlar (0)
Yorum birakmak icin giris yap.
Yorum birakSonuc bulunamadi