add qdrant_store.py

This commit is contained in:
2026-05-28 07:32:54 +00:00
parent c1bd989a11
commit fcd621f85e
+110
View File
@@ -0,0 +1,110 @@
"""
Qdrant vector store wrapper used by the knowledgebase search tool.
The implementation is intentionally simple it creates an inmemory Qdrant client,
creates a collection named ``knowledge`` and exposes two public methods:
* :py:meth:`add_documents` add a list of LangChain ``Document`` objects to the store.
* :py:meth:`similarity_search` perform a semantic search and return the top *k*
documents.
Only the packages listed in ``requirements.txt`` are imported, so the file is fully
selfcontained.
"""
from __future__ import annotations
import os
from pathlib import Path
from typing import List
from langchain_core.documents import Document
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams
# ---------------------------------------------------------------------------
# Configuration constants they are kept in a small module so that the rest of
# the code can simply ``import qdrant_store``.
# ---------------------------------------------------------------------------
QDRANT_COLLECTION = "knowledge"
EMBEDDING_DIMENSION = 1536 # default for OpenAI embeddings used by LangChain
# ---------------------------------------------------------------------------
# QdrantStore thin wrapper around the official client.
# ---------------------------------------------------------------------------
class QdrantStore:
"""A minimal wrapper around :class:`qdrant_client.QdrantClient`.
The store is created in memory (``:memory:``) so that it works out of the box
without a running Qdrant server. For production use you would replace the
``client = QdrantClient(":memory:")`` line with a connection string to a
real instance.
"""
def __init__(self) -> None:
self.client: QdrantClient = QdrantClient(":memory:")
# Create collection if it does not exist yet.
collections = self.client.get_collections().collections
if all(c.name != QDRANT_COLLECTION for c in collections):
self.client.create_collection(
name=QDRANT_COLLECTION,
vectors_config=VectorParams(size=EMBEDDING_DIMENSION, distance=Distance.COSINE),
)
# ---------------------------------------------------------------------
def add_documents(self, docs: List[Document]) -> None:
"""Add a list of :class:`langchain_core.documents.Document` objects.
The documents are indexed using the default LangChain embedding model
(OpenAI embeddings). ``Document`` already contains a ``metadata``
dictionary we preserve it unchanged.
"""
if not docs:
return
# Convert to Qdrant payload format.
points = []
for doc in docs:
point_id = str(doc.metadata.get("id", os.urandom(8).hex()))
points.append(
{
"id": point_id,
"vector": doc.embedding, # ``embedding`` is set by LangChain
"payload": {"content": doc.page_content, **doc.metadata},
}
)
self.client.upsert(collection_name=QDRANT_COLLECTION, points=points)
# ---------------------------------------------------------------------
def similarity_search(self, query: str, k: int = 5) -> List[Document]:
"""Return the top *k* documents most similar to ``query``.
The method uses the same embedding model that LangChain would use for
vectorisation this keeps the semantic space consistent.
"""
# ``client.search`` expects a vector; we let Qdrant compute it via its
# builtin OpenAI embeddings if available. For simplicity we ask the
# client to embed the query itself.
results = self.client.search(
collection_name=QDRANT_COLLECTION,
query_vector=query, # ``query`` is a string Qdrant will embed it
limit=k,
)
docs: List[Document] = []
for hit in results:
payload = hit.payload or {}
content = payload.get("content", "")
metadata = {k: v for k, v in payload.items() if k != "content"}
docs.append(Document(page_content=content, metadata=metadata))
return docs
# ---------------------------------------------------------------------------
# Singleton instance the rest of the project imports ``store`` directly.
# ---------------------------------------------------------------------------
store = QdrantStore()
__all__ = ["QdrantStore", "store"]