Skip to content

Qdrant operations

Qdrant is the vector store the ingestion job writes to and the API reads from. The connection is configured by two required environment variables, provisioned through the encrypted service env:

VariableMeaningExample
QDRANT_URLQdrant endpointhttp://localhost:6333
QDRANT_COLLECTIONCollection name(set per environment)

In the commands below, $QDRANT_URL and $QDRANT_COLLECTION refer to those values.

Run Qdrant

Qdrant runs as a Docker container. The project's tooling starts it with the k-qdrant script from the Kalisio development workspace (scripts/run_tests.sh calls it), persisting data to ./qdrant_data/:

bash
k-qdrant

k-qdrant is a thin wrapper over the standalone command below. The image version defaults to v1.18.0 (override with QDRANT_VERSION) and the data directory to ./qdrant_data (override with QDRANT_DATA_DIR):

bash
docker run -d --rm --name qdrant \
  -p 6333:6333 -p 6334:6334 \
  -v "$(pwd)/qdrant_data:/qdrant/storage" \
  qdrant/qdrant:v1.18.0

REST is on port 6333 (gRPC on 6334). Check it is up:

bash
curl -s http://localhost:6333/healthz

TIP

Data lives in ./qdrant_data/. Removing that directory deletes every collection — a full reset.

Web UI (dashboard)

Qdrant ships a built-in dashboard:

http://localhost:6333/dashboard

It lists collections, shows each collection's configuration (vector size, distance, point count), lets you browse and filter points, and includes a console for running REST requests interactively.

Inspect from the command line (REST API)

List collections:

bash
curl -s $QDRANT_URL/collections | jq

Collection configuration and point count:

bash
curl -s $QDRANT_URL/collections/$QDRANT_COLLECTION | jq

Exact point count:

bash
curl -s -X POST $QDRANT_URL/collections/$QDRANT_COLLECTION/points/count \
  -H 'Content-Type: application/json' -d '{"exact": true}' | jq

List points with their payload (omit vectors for readability):

bash
curl -s -X POST $QDRANT_URL/collections/$QDRANT_COLLECTION/points/scroll \
  -H 'Content-Type: application/json' \
  -d '{"limit": 5, "with_payload": true, "with_vector": false}' | jq

Fetch one point by id:

bash
curl -s $QDRANT_URL/collections/$QDRANT_COLLECTION/points/<id> | jq

Inspect from Python

The project wraps Qdrant in utils/vectordb.py, which reads QDRANT_URL / QDRANT_COLLECTION from the runtime config:

python
from utils import vectordb

vectordb.count()                       # indexed point count (0 if the collection is absent)
for payload in vectordb.iter_payloads():   # every stored payload, paged
    print(payload["repository"], payload["source_path"], payload["chunk_index"])

Or the client directly:

python
from qdrant_client import QdrantClient

client = QdrantClient(url="http://localhost:6333")
client.count("<collection>", exact=True)
records, _ = client.scroll("<collection>", limit=5, with_payload=True, with_vectors=False)

What is stored per point

Each point is one chunk: its embedding vector plus a payload. The payload fields (built in utils/vectordb.py):

FieldMeaning
textthe chunk text
source_pathrepo-relative file path
repositoryrepository name (kdk, kano, …)
file_typefile extension without the dot
chunk_indexchunk position within the file
breadcrumbheading / symbol context
commit_historyrecent commit subjects for the file
text_sha1 / file_sha1hashes for idempotent upsert and change detection

Vectors use cosine distance; the vector size is the embedding model's dimension (1024 for the selected Qwen3 model). The point id is deterministic — uuid5(repository:source_path:chunk_index:sha1) — so re-indexing the same chunk overwrites its point instead of creating a duplicate.

Verify data after ingestion

After running the ingestion job (python -m ingestion.main):

  1. Count — the point count should be greater than zero:

    bash
    curl -s -X POST $QDRANT_URL/collections/$QDRANT_COLLECTION/points/count \
      -H 'Content-Type: application/json' -d '{"exact": true}' | jq
  2. Confirm a repository is present — scroll with a payload filter:

    bash
    curl -s -X POST $QDRANT_URL/collections/$QDRANT_COLLECTION/points/scroll \
      -H 'Content-Type: application/json' -d '{
        "filter": {"must": [{"key": "repository", "match": {"value": "kdk"}}]},
        "limit": 3, "with_payload": true, "with_vector": false
      }' | jq
  3. Confirm a specific file — replace the filter key with source_path.

  4. Check retrieval end to end — query the API and inspect the returned sources and scores. /search requires a JWT when auth is enabled (the default), so send a bearer token — or run the API with KNOWLEDGE_AUTH_ENABLED=false for a local check:

    bash
    curl -s -X POST http://localhost:8187/search \
      -H 'Authorization: Bearer <token>' \
      -H 'Content-Type: application/json' -d '{"query": "how to add a layer", "top_k": 5}' | jq

Locate points with filters

Qdrant filters match on payload fields. Reusable must clauses:

TargetFilter clause
One repository{"key": "repository", "match": {"value": "kano"}}
One file{"key": "source_path", "match": {"value": "kdk/docs/api/map/services.md"}}
One file type{"key": "file_type", "match": {"value": "vue"}}

Delete

Delete points by filter — for example, drop one repository's chunks:

bash
curl -s -X POST $QDRANT_URL/collections/$QDRANT_COLLECTION/points/delete \
  -H 'Content-Type: application/json' \
  -d '{"filter": {"must": [{"key": "repository", "match": {"value": "kano"}}]}}'

Delete specific points by id (same endpoint):

bash
curl -s -X POST $QDRANT_URL/collections/$QDRANT_COLLECTION/points/delete \
  -H 'Content-Type: application/json' -d '{"points": ["<id>", "<id>"]}'

Delete the whole collection (to re-index from scratch):

bash
curl -s -X DELETE $QDRANT_URL/collections/$QDRANT_COLLECTION

The ingestion job recreates the collection on its next run. In Python, vectordb.ensure_collection(vector_size, recreate=True) drops and recreates it.

WARNING

Deleting by repository removes every chunk from that repository; re-run ingestion to restore it. Deleting the collection (or the qdrant_data/ volume) removes all indexed data.