> ## Documentation Index
> Fetch the complete documentation index at: /docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Qdrant

> Trace Qdrant vector search in a RAG pipeline with OpenInference retriever spans and send them to Arize AX for retrieval observability.

[Qdrant](https://qdrant.tech/) is an open-source vector database for similarity search. Qdrant has no OpenInference auto-instrumentor, so this guide wraps each `query_points` call in a retriever span that records the query and the documents Qdrant returns, using the [OpenInference semantic conventions](https://github.com/Arize-ai/openinference/tree/main/python/openinference-semantic-conventions). The embedding and chat calls around it are captured by [`openinference-instrumentation-openai`](https://github.com/Arize-ai/openinference/tree/main/python/instrumentation/openinference-instrumentation-openai), so Arize AX shows the full retrieval-augmented generation (RAG) flow in one trace.

## Prerequisites

* Python 3.10+
* An Arize AX account ([sign up](https://arize.com/sign-up/))
* An `OPENAI_API_KEY` from the [OpenAI Platform](https://platform.openai.com/api-keys), used for embeddings and the answer
* A running Qdrant server: either a [Qdrant Cloud](https://cloud.qdrant.io/) cluster, or a local container started with `docker run -d -p 6333:6333 qdrant/qdrant` (Podman works too: use `podman run` with the same arguments)

## Launch Arize AX

1. Sign in to your [Arize AX account](https://app.arize.com/).
2. From **Space Settings**, copy your **Space ID** and **API Key**. You will set them as `ARIZE_SPACE_ID` and `ARIZE_API_KEY` below.

## Install

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
pip install arize-otel openinference-instrumentation-openai \
  openinference-semantic-conventions openai qdrant-client
```

## Configure credentials

`QDRANT_URL` points at the local container from the prerequisites. For Qdrant Cloud, set it to your cluster URL and also set `QDRANT_API_KEY`.

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
export ARIZE_SPACE_ID="<your-space-id>"
export ARIZE_API_KEY="<your-api-key>"
export ARIZE_PROJECT_NAME="qdrant-tracing-example"
export OPENAI_API_KEY="<your-openai-api-key>"
export QDRANT_URL="http://localhost:6333"
export QDRANT_API_KEY="<your-qdrant-api-key>"  # optional, Qdrant Cloud only
```

## Setup tracing

```python theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
# instrumentation.py
import os

from arize.otel import register
from openinference.instrumentation.openai import OpenAIInstrumentor

tracer_provider = register(
    space_id=os.environ["ARIZE_SPACE_ID"],
    api_key=os.environ["ARIZE_API_KEY"],
    project_name=os.environ["ARIZE_PROJECT_NAME"],
)

OpenAIInstrumentor().instrument(tracer_provider=tracer_provider)

# A tracer for the spans you create by hand around Qdrant calls.
tracer = tracer_provider.get_tracer(__name__)
print("Arize AX tracing initialized for Qdrant.")
```

## Run Qdrant

The example indexes three short documents, then answers a question in a traced RAG flow: a chain span wraps a retriever span for the Qdrant query and the LLM call that answers from the retrieved text.

```python theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
# example.py

# Importing instrumentation first ensures tracing is set up
# before `openai` is imported.
from instrumentation import tracer

import os

from openai import OpenAI
from openinference.semconv.trace import (
    DocumentAttributes,
    OpenInferenceSpanKindValues,
    SpanAttributes,
)
from qdrant_client import QdrantClient, models

COLLECTION = "arize-qdrant-example"
EMBEDDING_MODEL = "text-embedding-3-small"
DOCUMENTS = [
    "Qdrant is an open-source vector database written in Rust.",
    "Arize AX is a platform for tracing and evaluating AI applications.",
    "The Eiffel Tower in Paris was completed in 1889.",
]
QUESTION = "What programming language is Qdrant written in?"

openai_client = OpenAI()
qdrant = QdrantClient(
    url=os.environ["QDRANT_URL"],
    api_key=os.environ.get("QDRANT_API_KEY"),
)


def embed(texts):
    response = openai_client.embeddings.create(
        model=EMBEDDING_MODEL,
        input=texts,
    )
    return [item.embedding for item in response.data]


# Recreate the collection so the example can run repeatedly.
if qdrant.collection_exists(COLLECTION):
    qdrant.delete_collection(COLLECTION)
qdrant.create_collection(
    collection_name=COLLECTION,
    vectors_config=models.VectorParams(
        size=1536,
        distance=models.Distance.COSINE,
    ),
)
qdrant.upsert(
    collection_name=COLLECTION,
    points=[
        models.PointStruct(id=i, vector=vector, payload={"text": text})
        for i, (text, vector) in enumerate(
            zip(DOCUMENTS, embed(DOCUMENTS))
        )
    ],
)


def retrieve(question, limit=2):
    with tracer.start_as_current_span(
        "qdrant.query_points",
        attributes={
            SpanAttributes.OPENINFERENCE_SPAN_KIND: (
                OpenInferenceSpanKindValues.RETRIEVER.value
            ),
            SpanAttributes.INPUT_VALUE: question,
        },
    ) as span:
        hits = qdrant.query_points(
            collection_name=COLLECTION,
            query=embed([question])[0],
            limit=limit,
        ).points
        # Record each hit as an OpenInference retrieval document.
        for i, hit in enumerate(hits):
            prefix = f"{SpanAttributes.RETRIEVAL_DOCUMENTS}.{i}"
            span.set_attribute(
                f"{prefix}.{DocumentAttributes.DOCUMENT_ID}", str(hit.id)
            )
            span.set_attribute(
                f"{prefix}.{DocumentAttributes.DOCUMENT_CONTENT}",
                hit.payload["text"],
            )
            span.set_attribute(
                f"{prefix}.{DocumentAttributes.DOCUMENT_SCORE}", hit.score
            )
        return [hit.payload["text"] for hit in hits]


with tracer.start_as_current_span(
    "answer_question",
    attributes={
        SpanAttributes.OPENINFERENCE_SPAN_KIND: (
            OpenInferenceSpanKindValues.CHAIN.value
        ),
        SpanAttributes.INPUT_VALUE: QUESTION,
    },
) as root:
    context = "\n".join(retrieve(QUESTION))
    response = openai_client.chat.completions.create(
        model="gpt-6-luna",
        messages=[
            {
                "role": "system",
                "content": (
                    "Answer in one sentence using only this context:\n"
                    f"{context}"
                ),
            },
            {"role": "user", "content": QUESTION},
        ],
    )
    answer = response.choices[0].message.content
    root.set_attribute(SpanAttributes.OUTPUT_VALUE, answer)

print(answer)
```

### Expected output

```text wrap theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
Arize AX tracing initialized for Qdrant.
Qdrant is written in Rust.
```

## Verify in Arize AX

1. Open your Arize AX space and select project **`qdrant-tracing-example`**.
2. You should see a new trace within \~30 seconds rooted at an `answer_question` chain span. It contains a `qdrant.query_points` retriever span that lists the two retrieved documents with their scores (with the question's `CreateEmbeddings` embedding span nested under it), followed by a `ChatCompletion` LLM span. Indexing the documents produces a separate `CreateEmbeddings` trace.
3. If no traces appear, see [Troubleshooting](#troubleshooting).

### Check from the skill, CLI, or SDK

Confirm spans are actually reaching your Arize AX project. Use whichever fits your workflow — the skill and CLI work for any framework; the SDK check is shown for each language.

**Arize skill (agent)**

Install the [Arize Skills](https://github.com/Arize-ai/arize-skills) plugin and let your coding agent check for you:

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
npx skills add Arize-ai/arize-skills
```

Then prompt your agent:

> Use the `arize-trace` skill to export and analyze recent traces from my project. Confirm spans are arriving, and summarize any errors or latency issues.

**AX CLI**

Export recent spans for your project — any rows mean traces are landing:

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
ax spans export "$ARIZE_PROJECT_NAME" --space "$ARIZE_SPACE_ID" \
  --limit 5 --stdout | jq 'length'
```

A non-zero count confirms spans reached Arize AX. Run `ax auth login` first if you have not authenticated. See the [`ax spans` reference](/docs/api-clients/cli/spans).

**SDK**

Query the project's spans and check that at least one came back.

```python Python theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
import os
from arize import ArizeClient

client = ArizeClient(api_key=os.environ["ARIZE_API_KEY"])
resp = client.spans.list(
    project=os.environ["ARIZE_PROJECT_NAME"],
    space=os.environ["ARIZE_SPACE_ID"],
    limit=5,
)
count = len(resp.spans)
print(
    f"{count} span(s) found" if count else "No spans yet — recheck setup"
)
```

```typescript TypeScript theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
// Reads ARIZE_API_KEY from the environment.
import { listSpans } from "@arizeai/ax-client";

const { data: spans } = await listSpans({
  project: process.env.ARIZE_PROJECT_NAME!,
  space: process.env.ARIZE_SPACE_ID!,
  limit: 5,
});
const count = spans.length;
console.log(
  count ? `${count} span(s) found` : "No spans yet — recheck setup",
);
```

```go Go theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
client, err := arize.NewClient(
    arize.Config{APIKey: os.Getenv("ARIZE_API_KEY")},
)
if err != nil {
    log.Fatal(err)
}
resp, err := client.Spans.List(ctx, spans.ListRequest{
    Project: os.Getenv("ARIZE_PROJECT_NAME"),
    Space:   os.Getenv("ARIZE_SPACE_ID"),
    Limit:   5,
})
if err != nil {
    log.Fatal(err)
}
fmt.Printf("%d span(s) found\n", len(resp.Spans))
```

SDK span references: [Python](/docs/api-clients/python/version-8/client-resources/spans.md) · [TypeScript](/docs/api-clients/typescript/version-1/client-resources/spans.md) · [Go](/docs/api-clients/go/version-2/client-resources/spans.md).

## Troubleshooting

* **No traces in Arize AX.** Confirm `ARIZE_SPACE_ID` and `ARIZE_API_KEY` are set in the same shell that runs `example.py`. Enable OpenTelemetry debug logs with `export OTEL_LOG_LEVEL=debug` and re-run.
* **Embedding and chat spans missing, but the retriever span is present.** `OpenAIInstrumentor().instrument(...)` must run before `openai` is imported. Make sure `instrumentation.py` is the first import in your entry point.
* **The retriever span shows no documents.** The span only lists what Qdrant returned. An empty result usually means the collection is empty or was created with a different vector size: `text-embedding-3-small` returns 1,536 dimensions, so the collection's `size` must match.
* **`Connection refused` on port 6333.** The Qdrant container isn't running. Start it with `docker run -d -p 6333:6333 qdrant/qdrant` (or `podman run`), then check `curl http://localhost:6333/readyz`. With Podman on macOS, run `podman machine start` first.
* **`401 Unauthorized` from Qdrant Cloud.** Set `QDRANT_API_KEY` to a key for the same cluster as `QDRANT_URL`.

## Resources

> **[Qdrant Documentation](https://qdrant.tech/documentation/)**

> **[OpenInference Semantic Conventions](https://github.com/Arize-ai/openinference/tree/main/python/openinference-semantic-conventions)**

> **[Manual instrumentation in Arize AX](/docs/ax/instrument/manual-instrumentation.md)**

> **[Qdrant Python client](https://github.com/qdrant/qdrant-client)**
