Skip to content

About

LangChain vector store for TopK: hybrid (BM25 + semantic) search, built-in embeddings, metadata filters and per-tenant partitions in one query.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

Repository files navigation

langchain-topk

LangChain vector store and hybrid search for TopK. Keyword (BM25), semantic, vector and hybrid search, metadata filters, and isolated per-tenant partitions, all in one query against one index. TopK can also embed and rerank text on the server, so you don't need an embedding model.

pip install langchain-topk
from langchain_topk import TopKVectorStore

store = TopKVectorStore(collection_name="docs")  # TopK embeds text for you
store.add_texts(
    ["Error code E-4021 means the payment gateway timed out.",
     "Quarterly revenue grew 12% thanks to cloud sales."],
    metadatas=[{"source": "runbook"}, {"source": "report"}],
)

store.similarity_search("why did checkout fail?", k=3, search_mode="hybrid")

Set TOPK_API_KEY (create one at console.topk.io) and TOPK_REGION (for example aws-us-east-1-elastica, see regions).

What is TopK?

TopK is a hybrid retrieval engine for AI applications and agents. In a typical RAG stack, an embedding service, a vector database, a keyword engine and a reranker are separate pieces glued together in application code. TopK runs all of them in one system:

Typical vector-database setup TopK
Embeddings You run an embedding pipeline and store the vectors semantic_index() embeds and reranks on the server (semantic search)
Hybrid search Vector and keyword searches run separately, then get merged (often with Reciprocal Rank Fusion) One query over one index, so the top results really are the top results (true hybrid search)
Retrieval types Dense vectors, plus keyword search bolted on Dense, sparse and multi-vector search, BM25, regex, filters and custom scoring in one query
Storage Memory- or disk-bound nodes you size and scale Object storage with memory and NVMe caching (architecture), which TopK says costs 10x less
Multi-tenancy A tenant_id filter Fully isolated partitions per tenant (concepts)

TopK publishes these figures: 1B+ documents per partition, under 100ms p99 query latency at 1B documents, and under 1 second data freshness (architecture). Retrieval benchmarks are at topk.io/benchmarks.

Why use TopK with LangChain?

  • No embedding model to run. Leave embedding=None and TopK embeds and reranks text on the server. That means no OpenAI embeddings bill, no extra embedding API key, and no reranker in your chain.
  • Keyword search that actually filters. Agents look up exact strings like error codes, SKUs, function names and ticket IDs. Vector similarity ranks something first even when nothing matches; BM25 returns nothing. search_mode="keyword" gives you that.
  • True hybrid search in one call. search_mode="hybrid" scores dense similarity and BM25 together on the server. Documents with no shared keyword still rank on meaning, and keyword matches get boosted.
  • Agent memory per user or tenant. store.with_partition("user-42") gives each user a fully isolated partition with no cross-tenant filter to forget.
  • It's a standard LangChain VectorStore. It passes LangChain's standard vector store test suite (langchain-tests) in both embedding modes and works with as_retriever(), MMR, get_by_ids and async.

Search modes

Every search method takes search_mode. as_retriever(search_kwargs={...}) passes it through too.

search_mode What it does Use it for
"semantic" TopK's built-in embeddings and reranking (default when embedding=None) Natural-language questions with no embedding model
"vector" Your LangChain Embeddings (default when embedding=... is set) Your own or fine-tuned embedding model
"keyword" BM25 full-text; returns only documents that share a term Exact codes, names, identifiers
"hybrid" Dense + BM25 in one query, weighted by hybrid_alpha (default 0.5) General RAG and agent retrieval
store.keyword_search("E-4021", k=5)                      # BM25 only
store.hybrid_search("payment gateway timeout", alpha=0.7)  # 70% semantic, 30% BM25
store.similarity_search_with_score("revenue", search_mode="semantic")

Dense and BM25 scores sit on different scales. Print a few similarity_search_with_score(..., search_mode="hybrid") results before you tune hybrid_alpha.

Bring your own embeddings

from langchain_openai import OpenAIEmbeddings

store = TopKVectorStore(
    collection_name="docs-openai",
    embedding=OpenAIEmbeddings(model="text-embedding-3-small"),
    metric="cosine",  # or "dot_product", "euclidean"
)
store.max_marginal_relevance_search("diverse results please", k=4, fetch_k=20)

In this mode the text field also gets a BM25 index, so "keyword" and "hybrid" work the same way (hybrid combines your vectors with BM25).

Metadata filters

Pass MongoDB-style filters (the usual LangChain convention) to any search method or to delete:

store.similarity_search("refund policy", filter={"source": "faq"})
store.similarity_search("outage", filter={"year": {"$gte": 2024}, "tags": {"$contains": "payments"}})
store.similarity_search("x", filter={"$or": [{"source": "faq"}, {"author.name": {"$starts_with": "Ada"}}]})
store.delete(filter={"source": "stale-import"})

Operators: $eq, $ne, $gt, $gte, $lt, $lte, $in, $nin, $contains (list fields), $starts_with, $exists, $and, $or, $not. Use dots for nested keys.

You can also pass a native TopK expression for anything else TopK supports:

from topk_sdk.query import field

store.similarity_search("x", filter=field("metadata.year").in_([2024, 2025]) & field("metadata.title").regexp_match("^RFC"))

Multi-tenant agent memory with partitions

memories = TopKVectorStore(collection_name="agent-memory")

alice = memories.with_partition("user-alice")
bob = memories.with_partition("user-bob")

alice.add_texts(["Alice prefers window seats and vegetarian meals."])
bob.add_texts(["Bob is allergic to peanuts."])

alice.similarity_search("food preferences", search_mode="hybrid")  # never sees Bob's data

A partition is created on its first write. Reading a partition that was never written returns [].

Use as a retriever or agent tool

retriever = store.as_retriever(search_kwargs={"k": 5, "search_mode": "hybrid", "filter": {"source": "docs"}})
retriever.invoke("how do I rotate API keys?")
from langchain_core.tools import tool

@tool
def search_docs(query: str) -> str:
    """Search the product docs. Good for exact error codes and for natural-language questions."""
    return "\n\n".join(d.page_content for d in store.hybrid_search(query, k=5))

Good to know

  • Reads see your own writes. The store passes the LSN of its last write to every read, so a search right after add_texts includes the new documents.
  • Text limit with server-side embeddings. A semantic_index() field holds up to 4,096 characters per value, so split long documents first (for example with RecursiveCharacterTextSplitter(chunk_size=3000)).
  • Upserts replace whole documents. Adding a document with an existing id overwrites it.
  • Use a new collection name after deleting one. Writes right after recreating a just-deleted collection under the same name can be lost.
  • Metadata comes back exactly as you stored it. Values TopK can't filter on (for example mixed-type lists or custom objects) are still returned, just not filterable.
  • Relevance scores. similarity_score_threshold retrievers work with bring-your-own embeddings. TopK's semantic scores aren't normalized to [0, 1], so pick thresholds from your own data with similarity_search_with_score.

FAQ

Does TopK work with LangChain? Yes. langchain-topk provides TopKVectorStore, a standard LangChain VectorStore that passes the official langchain-tests suite.

Can I do hybrid search (BM25 + vectors) in LangChain without an ensemble retriever? Yes. search_mode="hybrid" ranks dense similarity and BM25 together in a single TopK query. You don't need EnsembleRetriever, Reciprocal Rank Fusion, or a separate BM25 index in memory.

Do I need OpenAI or another embedding provider? No. With embedding=None, TopK embeds and reranks on the server. You can still bring any LangChain Embeddings if you want.

How do I isolate data per user or tenant? Use partitions: store.with_partition("tenant-id"). Each partition is fully isolated and scales independently.

Is TopK open source? The SDKs (topk-io/topk) and this integration are MIT-licensed. TopK itself is a managed service (topk.io), and you can also deploy it in your own VPC.

Development

uv venv && uv pip install -e . --group test
pytest tests/unit_tests
TOPK_API_KEY=... TOPK_REGION=aws-us-east-1-elastica pytest tests/integration_tests  # runs against live TopK

Links

About

LangChain vector store for TopK: hybrid (BM25 + semantic) search, built-in embeddings, metadata filters and per-tenant partitions in one query.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages