Inference API
Inference API (inference-inference-api)
벡터를 미리 계산해서 저장할 수도 있지만, Qdrant가 서버 쪽에서 직접 임베딩을 생성하게 할 수도 있어요. Qdrant Cloud Inference나 Qdrant의 BM25 모델을 사용할 때는 임베딩이 서버 쪽에서 생성돼요. 이 경우 미리 계산된 벡터 대신, 데이터를 넣거나(ingest) 조회할 때 Inference Object를 전달하게 돼요. Inference Object는 입력으로부터 벡터를 어떻게 생성할지 Qdrant에 알려주는 역할을 해요. 텍스트나 이미지 같은 입력과 함께 사용할 모델을 담고 있죠. API는 세 가지 유형의 Inference Object를 지원해요.
출처: Qdrant 공식문서
-
Document객체 — 텍스트 추론에 사용// Document { // Text input text: "Your text", // Name of the model, to do inference with model: "<the-model-to-use>", // Extra parameters for the model, Optional options: {} } -
Image객체 — 이미지 추론에 사용// Image { // Image input image: "<url>", // Or base64 encoded image // Name of the model, to do inference with model: "<the-model-to-use>", // Extra parameters for the model, Optional options: {} } -
Object객체 — 향후 구현될 수 있는 다른 유형의 입력을 위해 예약된 타입
예를 들어, 아래 코드는 일반적인 벡터 검색 쿼리예요.
POST /collections/<your-collection>/points/query
{
"query": {
"nearest": [0.12, 0.34, 0.56, 0.78, ...]
}
}
client.query_points(
collection_name="{collection_name}",
query=[0.12, 0.34, 0.56, 0.78],
)
import { QdrantClient } from "@qdrant/js-client-rest";
client.query("{collection_name}", {
query: [0.12, 0.34, 0.56, 0.78],
});
use qdrant_client::Qdrant;
use qdrant_client::qdrant::{Query, QueryPointsBuilder};
client
.query(
QueryPointsBuilder::new("{collection_name}")
.query(Query::new_nearest(vec![0.12, 0.34, 0.56, 0.78]))
)
.await?;
import static io.qdrant.client.QueryFactory.nearest;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Points.QueryPoints;
import java.util.List;
client.queryAsync(QueryPoints.newBuilder()
.setCollectionName("{collection_name}")
.setQuery(nearest(List.of(0.12f, 0.34f, 0.56f, 0.78f)))
.build()).get();
using Qdrant.Client;
await client.QueryAsync(
collectionName: "{collection_name}",
query: new float[] { 0.12f, 0.34f, 0.56f, 0.78f }
);
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Query: qdrant.NewQuery(0.12, 0.34, 0.56, 0.78),
})
이 코드는 Inference Object를 사용하는 다음과 같은 코드로 대체할 수 있어요. 벡터 값 대신 텍스트와 모델을 넘기면 되죠.
POST /collections/<your-collection>/points/query
{
"query": {
"nearest": {
"text": "My Query Text",
"model": "<the-model-to-use>"
}
}
}
from qdrant_client import QdrantClient, models
client.query_points(
collection_name="{collection_name}",
query=models.Document(
text="My Query Text",
model="<the-model-to-use>",
),
)
import { QdrantClient } from "@qdrant/js-client-rest";
client.query("{collection_name}", {
query: {
text: 'My Query Text',
model: '<the-model-to-use>',
},
});
use qdrant_client::{
Qdrant,
qdrant::{Document, Query, QueryPointsBuilder},
};
client
.query(
QueryPointsBuilder::new("{collection_name}")
.query(Query::new_nearest(Document {
text: "My Query Text".into(),
model: "<the-model-to-use>".into(),
..Default::default()
}))
.build(),
)
.await?;
import static io.qdrant.client.QueryFactory.nearest;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Points.Document;
import io.qdrant.client.grpc.Points;
client
.queryAsync(
Points.QueryPoints.newBuilder()
.setCollectionName("{collection_name}")
.setQuery(
nearest(
Document.newBuilder()
.setModel("<the-model-to-use>")
.setText("My Query Text")
.build()))
.build())
.get();
using Qdrant.Client;
using Qdrant.Client.Grpc;
await client.QueryAsync(
collectionName: "{collection_name}",
query: new Document() { Model = "<the-model-to-use>", Text = "My Query Text" }
);
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client.Query(context.Background(), &qdrant.QueryPoints{
CollectionName: "{collection_name}",
Query: qdrant.NewQueryNearest(
qdrant.NewVectorInputDocument(&qdrant.Document{
Text: "My Query Text",
Model: "<the-model-to-use>",
}),
),
})
이 경우 Qdrant는 설정된 임베딩 모델을 사용해 Inference Object로부터 벡터를 만든 다음, 그 벡터로 검색 쿼리를 수행해요. 이 모든 과정은 저지연(low-latency) 네트워크 안에서 일어나요.
참고: 인제스트(ingest) 시점에 추론을 사용하면, 추론에 쓰인 입력은 저장되지 않아요. 이 입력을 Qdrant에 영구 보존하고 싶다면, payload에 명시적으로 포함시켜야 해요.
여러 개의 Inference 연산 (Multiple Inference Operations)
하나의 요청 안에서 여러 개의 추론 연산을 실행할 수 있어요. 심지어 모델이 서로 다른 위치에 호스팅되어 있어도 상관없어요. 아래 예제는 하나의 포인트에 대해 세 가지 다른 named vector를 생성해요. Jina AI가 호스팅하는 jina-clip-v2로 이미지 임베딩을, Qdrant Cloud가 호스팅하는 all-minilm-l6-v2로 텍스트 임베딩을, 그리고 Qdrant 클러스터가 로컬에서 실행하는 bm25 모델로 BM25 임베딩을 만드는 코드예요.
PUT /collections/{collection_name}/points?wait=true
{
"points": [
{
"id": 1,
"vector": {
"image": {
"image": "https://qdrant.tech/example.png",
"model": "jinaai/jina-clip-v2",
"options": {
"jina-api-key": "<YOUR_JINAAI_API_KEY>",
"dimensions": 512
}
},
"text": {
"text": "Mars, the red planet",
"model": "sentence-transformers/all-minilm-l6-v2"
},
"bm25": {
"text": "Mars, the red planet",
"model": "qdrant/bm25"
}
}
}
]
}
from qdrant_client import QdrantClient, models
client = QdrantClient(
url="https://xyz-example.qdrant.io:6333",
api_key="<your-qdrant-api-key>",
cloud_inference=True
)
client.upsert(
collection_name="{collection_name}",
points=[
models.PointStruct(
id=1,
vector={
"image": models.Image(
image="https://qdrant.tech/example.png",
model="jinaai/jina-clip-v2",
options={
"jina-api-key": "<your_jinaai_api_key>",
"dimensions": 512
},
),
"text": models.Document(
text="Mars, the red planet",
model="sentence-transformers/all-minilm-l6-v2",
),
"bm25": models.Document(
text="Mars, the red planet",
model="Qdrant/bm25",
),
},
)
],
)
import { QdrantClient } from "@qdrant/js-client-rest";
client.upsert("{collection_name}", {
points: [
{
id: 1,
vector: {
image: {
image: 'https://qdrant.tech/example.png',
model: 'jinaai/jina-clip-v2',
options: {
'jina-api-key': '<your_jinaai_api_key>',
dimensions: 512,
},
},
text: {
text: 'Mars, the red planet',
model: 'sentence-transformers/all-minilm-l6-v2',
},
bm25: {
text: 'Mars, the red planet',
model: 'Qdrant/bm25',
},
},
},
],
});
use qdrant_client::{
Payload, Qdrant,
qdrant::{Document, Image, NamedVectors, PointStruct, UpsertPointsBuilder},
};
use std::collections::HashMap;
let mut jina_options = HashMap::new();
jina_options.insert("jina-api-key".to_string(), "<YOUR_JINAAI_API_KEY>".into());
jina_options.insert("dimensions".to_string(), 512.into());
client
.upsert_points(
UpsertPointsBuilder::new(
"{collection_name}",
vec![PointStruct::new(
1,
NamedVectors::default()
.add_vector(
"image",
Image {
image: Some("https://qdrant.tech/example.png".into()),
model: "jinaai/jina-clip-v2".into(),
options: jina_options,
},
)
.add_vector(
"text",
Document {
text: "Mars, the red planet".into(),
model: "sentence-transformers/all-minilm-l6-v2".into(),
..Default::default()
},
)
.add_vector(
"bm25",
Document {
text: "How to bake cookies?".into(),
model: "qdrant/bm25".into(),
..Default::default()
},
),
Payload::default(),
)],
)
.wait(true),
)
.await?;
import static io.qdrant.client.PointIdFactory.id;
import static io.qdrant.client.ValueFactory.value;
import static io.qdrant.client.VectorFactory.vector;
import static io.qdrant.client.VectorsFactory.namedVectors;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import io.qdrant.client.grpc.Points.Document;
import io.qdrant.client.grpc.Points.Image;
import io.qdrant.client.grpc.Points.PointStruct;
import java.util.List;
import java.util.Map;
client
.upsertAsync(
"{collection_name}",
List.of(
PointStruct.newBuilder()
.setId(id(1))
.setVectors(
namedVectors(
Map.of(
"image",
vector(
Image.newBuilder()
.setModel("jinaai/jina-clip-v2")
.setImage(value("https://qdrant.tech/example.png"))
.putAllOptions(
Map.of(
"jina-api-key",
value("<YOUR_JINAAI_API_KEY>"),
"dimensions",
value(512)))
.build()),
"text",
vector(
Document.newBuilder()
.setModel("sentence-transformers/all-minilm-l6-v2")
.setText("Mars, the red planet")
.build()),
"bm25",
vector(
Document.newBuilder()
.setModel("qdrant/bm25")
.setText("Mars, the red planet")
.build()))))
.build()))
.get();
using Qdrant.Client;
using Qdrant.Client.Grpc;
await client.UpsertAsync(
collectionName: "{collection_name}",
points: new List<PointStruct>
{
new()
{
Id = 1,
Vectors = new Dictionary<string, Vector>
{
["image"] = new Image()
{
Model = "jinaai/jina-clip-v2",
Image_ = "https://qdrant.tech/example.png",
Options = { ["jina-api-key"] = "<YOUR_JINAAI_API_KEY>", ["dimensions"] = 512 },
},
["text"] = new Document()
{
Model = "sentence-transformers/all-minilm-l6-v2",
Text = "Mars, the red planet",
},
["bm25"] = new Document() { Model = "qdrant/bm25", Text = "Mars, the red planet" },
},
},
}
);
import (
"context"
"github.com/qdrant/go-client/qdrant"
)
client.Upsert(context.Background(), &qdrant.UpsertPoints{
CollectionName: "{collection_name}",
Points: []*qdrant.PointStruct{
{
Id: qdrant.NewIDNum(uint64(1)),
Vectors: qdrant.NewVectorsMap(map[string]*qdrant.Vector{
"image": qdrant.NewVectorImage(&qdrant.Image{
Model: "jinaai/jina-clip-v2",
Image: qdrant.NewValueString("https://qdrant.tech/example.png"),
Options: qdrant.NewValueMap(map[string]any{
"jina-api-key": "<YOUR_JINAAI_API_KEY>",
"dimensions": 512,
}),
}),
"text": qdrant.NewVectorDocument(&qdrant.Document{
Model: "sentence-transformers/all-minilm-l6-v2",
Text: "Mars, the red planet",
}),
"my-bm25-vector": qdrant.NewVectorDocument(&qdrant.Document{
Model: "qdrant/bm25",
Text: "Recipe for baking chocolate chip cookies",
}),
}),
},
},
})
하나의 요청에 동일한 Inference Object를 여러 번 지정하면, 추론 서비스는 임베딩을 한 번만 생성하고 결과 벡터를 재사용해요. 이 최적화는 특히 외부 모델 제공자를 사용할 때 유용한데, 지연 시간과 비용을 모두 줄여주거든요.
더 알아보기 (Learn more)
- Qdrant Cloud Inference — 클라우드 추론 서비스
- BM25 모델 — 서버 사이드 BM25 임베딩
- 외부 추론 제공자 — 외부 모델 호스팅 연동