ragless
File-based retrieval for RAG, with no vector database.
ragless indexes a folder of text files (or in-memory strings) and lets you
search them with BM25 keyword search, fuzzy string matching, and optional
local semantic embeddings -- combined with Reciprocal Rank Fusion. Everything
lives on disk as plain files (or in memory): no Postgres/pgvector, no
Pinecone/Weaviate/Chroma server, nothing to run.
from ragless import Retriever
r = Retriever("./docs")
for hit in r.search("how do I configure retries", mode="hybrid"):
print(f"{hit.path}:{hit.start_line}-{hit.end_line} score={hit.score:.3f}")
print(hit.text)
Install
pip install ragless # BM25 + fuzzy (no heavy deps)
pip install 'ragless[semantic]' # + local embeddings and cross-encoder rerank
Quickstart
from ragless import Retriever
# Index a directory of files.
r = Retriever("./docs")
results = r.search("installation steps", mode="hybrid", top_k=5)
# Or index in-memory text directly -- no files required.
r = Retriever(
[
"ragless indexes folders of text files for retrieval.",
"BM25, fuzzy matching, and semantic search are combined with RRF.",
]
)
results = r.search("how are results combined?")
for hit in results:
print(hit.path, hit.start_line, hit.end_line, hit.score)
print(hit.text)
Search modes
r.search("query", mode="hybrid") # fuse every configured engine (default)
r.search("query", mode="bm25") # keyword search only
r.search("query", mode="fuzzy") # fuzzy string matching only
r.search("query", mode="semantic") # embeddings only (requires the extra)
Semantic search and caching
from ragless import Retriever
r = Retriever(
"./docs",
engines=["bm25", "fuzzy", "semantic"],
cache_dir="./.ragless_cache", # embeddings cached here as .npy/.json
)
r.search("query", mode="hybrid")
Embeddings are cached per source file, keyed by the file's mtime/size and the embedder's name. Editing one file only re-embeds that file's chunks on the next run; untouched files are loaded straight from the cache.
Cross-encoder rerank
r.search("query", mode="hybrid", rerank=True)
Reranking takes the top fused candidates and rescoring them with a small
local cross-encoder (also via fastembed, under the semantic extra) for
higher precision at the top of the list.
Filters
# Only chunks whose metadata says category="guide"
r.search("query", filters={"category": "guide"})
# Only chunks from markdown files
r.search("query", path_filter="*.md")
# Per-document metadata, assigned when building the index
r = Retriever(
"./docs",
metadata=lambda path: {"category": "guide"} if path and "guide" in path else {},
)
Pluggable components
Chunkers, retrievers, and embedders are all swappable:
from ragless import Retriever, Chunker, BaseRetriever
from ragless.chunking import LineChunker
r = Retriever("./docs", chunker=LineChunker(lines_per_chunk=60, overlap=10))
# Bring your own retriever/embedder implementation:
r = Retriever("./docs", engines={"bm25": MyRetriever(), "fuzzy": FuzzyRetriever()})
Implement ragless.chunking.Chunker, ragless.retrievers.base.BaseRetriever,
or ragless.embedders.base.Embedder to plug in your own.
Why
Most "RAG" tutorials reach for a vector database before checking whether they need one. For a folder of a few thousand files, plain keyword search (BM25), fuzzy matching, and a small local embedding model get you most of the way there, with:
- No server to run, no API keys, no network calls (semantic search runs a small model locally via fastembed).
- No ingestion pipeline: point it at a directory and it chunks and indexes on construction.
- Caching as plain sidecar files (
.npy+.json) next to your index, not a database you have to back up and migrate. - Every result keeps the file path and line range it came from.
| ragless | vector DB (Pinecone/Chroma/pgvector) | |
|---|---|---|
| Infra | none | a running database/service |
| Setup | Retriever("./docs") |
provision, configure, ingest |
| Keyword search | built in (BM25) | usually bolted on separately |
| Fuzzy/typo tolerance | built in | usually absent |
| Good for | a few thousand files, local tools, CLIs, CI | large, constantly-changing, multi-tenant corpora |
| Cold start | instant | needs the service up and reachable |
If you need to search tens of millions of chunks with concurrent writers
across a fleet of machines, use a vector database. If you need to search the
docs folder sitting next to your code, ragless is simpler.
How it works
- Load -- read files from a directory (or accept raw in-memory strings). Binary files and undecodable text are skipped automatically.
- Chunk -- split each document into overlapping line-based windows,
keeping the file path and 1-indexed
start_line/end_linefor every chunk (ragless.chunking.LineChunker, pluggable). - Index -- each configured engine builds its own index over the chunks:
BM25Retriever: classic Okapi BM25 over tokenized text (NumPy).FuzzyRetriever:rapidfuzzstring similarity.SemanticRetriever: dense embeddings (fastembed) with cosine similarity (NumPy), cached to disk as.npy/.jsonsidecar files.
- Search -- query every configured engine, fuse their rankings with Reciprocal Rank Fusion, apply metadata/path filters, and optionally rerank the top candidates with a cross-encoder.
Status
0.1.0 -- the API may still shift. Feedback and issues welcome.
License
MIT
Metadata
Release files for ragless 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| ragless-0.1.0.tar.gz | 28.7 kB | With provenance | Details for ragless-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| ragless-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for ragless-0.1.0-py3-none-any.whl |
Total release size: 49.8 kB
Release files / ragless-0.1.0.tar.gz
| Download URL | ragless-0.1.0.tar.gz |
|---|---|
| Size | 28.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8b6f9a00d0cd6114b63c84c4df0c8227541e7782708f476ad1de2d549da547c6
|
|
BLAKE2b-256 checksum How to use checksums |
8176c910f2339d857b7a2696590130f7a5c6acf5a4f1b52b8d4cffab55cdb67d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.
Transparency logRelease files / ragless-0.1.0-py3-none-any.whl
| Download URL | ragless-0.1.0-py3-none-any.whl |
|---|---|
| Size | 21.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2efb766f4acf0e5cc808ba04cafd44803155de41ae39aadd70727c9dc4b20d6f
|
|
BLAKE2b-256 checksum How to use checksums |
1d2f083c1b703483b30c77bc42eadd40bb0a2783a81f5d1dee28da3912310b20
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.
Transparency logSecurity for ragless 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for ragless-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 12, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.