BGE-M3 임베딩 API

BGE-M3 임베딩 API 사용 가이드

외부에서 이 서버로 텍스트 임베딩을 받아 쓰는 방법입니다. 아래 예제의 주소는 지금 접속한 주소로 자동으로 채워집니다.

API 주소 …
상태 확인 중…

개요

BAAI/bge-m3 모델로 문장·문서를 벡터로 바꿔 주는 서버입니다. 한국어, 영어를 포함해 100개 이상의 언어를 지원하고, 언어가 달라도 뜻이 같으면 가까운 벡터가 나옵니다. RAG, 의미 검색, 중복 탐지, 분류 같은 곳에 씁니다.

1024
dense 벡터 차원
8,192
입력 최대 토큰
100+
지원 언어
3종
dense · sparse · colbert

질의(query)와 문서(passage)에 별도 접두어나 지시문을 붙일 필요가 없습니다. 둘 다 그대로 보내세요.

빠른 시작

터미널에서 바로 확인해 보세요. 인증이 꺼져 있으면 Authorization 줄은 빼도 됩니다.

# 1) 서버 상태
curl {{BASE}}/health

# 2) OpenAI 호환 임베딩 (dense)
curl {{BASE}}/v1/embeddings \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "bge-m3", "input": ["오늘 날씨 어때?", "How is the weather today?"]}'

# 3) dense + sparse 한 번에
curl {{BASE}}/embed \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"texts": ["BGE-M3는 다국어 임베딩 모델입니다."], "dense": true, "sparse": true}'

인증

서버에 API 키가 설정돼 있으면 모든 API 요청에 아래 헤더가 필요합니다. 키는 서버 관리자에게 받으세요.

Authorization: Bearer YOUR_API_KEY

키가 없거나 틀리면 401이 돌아옵니다. /health, /guide, /docs는 키 없이 열립니다.

엔드포인트

POST/v1/embeddings · OpenAI 호환

OpenAI Embeddings API와 형식이 같습니다. OpenAI SDK, LangChain 같은 도구에서 base URL만 바꾸면 그대로 동작합니다. dense 벡터만 반환합니다.

필드타입설명
inputstring | string[]임베딩할 텍스트. 필수. 배열이면 최대 2048개.
modelstring아무 값이나 가능(무시됨). 보통 "bge-m3".
encoding_format"float" | "base64"기본 float. base64는 float32 리틀엔디언 바이트로, 응답이 약 3배 작습니다.
dimensionsint생략하거나 1024만 가능.
{
  "object": "list",
  "data": [
    {"object": "embedding", "index": 0, "embedding": [0.0123, -0.0456, ...]}
  ],
  "model": "BAAI/bge-m3",
  "usage": {"prompt_tokens": 9, "total_tokens": 9}
}

POST/embed · dense / sparse / colbert

BGE-M3의 세 가지 출력을 원하는 조합으로 한 번에 받습니다.

필드타입설명
textsstring | string[]임베딩할 텍스트. 필수. 최대 2048개.
densebool기본 true. 1024차원 벡터.
sparsebool기본 false. 토큰 가중치.
colbertbool기본 false. 토큰별 벡터.
max_lengthint기본 8192. 이보다 긴 입력은 잘립니다. 짧게 잡으면 빨라집니다.
{
  "model": "BAAI/bge-m3",
  "usage": {"tokens": 12},
  "dense":   [[0.0123, -0.0456, ...]],                          // 텍스트당 1024개
  "sparse":  [{"indices": [6, 4865, 1530], "values": [0.08, 0.31, 0.27]}],  // 토큰 ID → 가중치
  "colbert": [[[0.01, ...], [0.02, ...]]]                     // 텍스트당 [토큰 수 × 1024]
}

sparse의 indices/values 형식은 Qdrant, Milvus의 sparse 벡터 형식과 같아서 그대로 넣을 수 있습니다.

기타

경로설명
GET/health서버 상태, 인증 사용 여부. 키 불필요.
GET/v1/modelsOpenAI 형식 모델 목록.
GET/stats대시보드용 지표(JSON). 모니터링 연동에 사용.
GET/dashboard실시간 상태 대시보드.
GET/docsSwagger 자동 문서. 브라우저에서 직접 요청을 보내 볼 수 있습니다.

언어별 예제

Python · requests

import requests

BASE = "{{BASE}}"
HEADERS = {"Authorization": "Bearer YOUR_API_KEY"}

r = requests.post(f"{BASE}/embed", headers=HEADERS, timeout=60, json={
    "texts": ["첫 번째 문서", "두 번째 문서"],
    "dense": True,
    "sparse": True,
})
r.raise_for_status()
data = r.json()
dense = data["dense"]    # [[1024개 float], ...]
sparse = data["sparse"]  # [{"indices": [...], "values": [...]}, ...]
print(len(dense), len(dense[0]), data["usage"])

Python · OpenAI SDK

from openai import OpenAI

client = OpenAI(base_url="{{BASE}}/v1", api_key="YOUR_API_KEY")
resp = client.embeddings.create(model="bge-m3", input=["안녕하세요", "Hello"])
vectors = [d.embedding for d in resp.data]  # 각 1024차원
print(resp.usage.total_tokens)

인증이 꺼져 있어도 SDK는 api_key를 요구하므로 아무 문자열이나 넣으면 됩니다.

LangChain

from langchain_openai import OpenAIEmbeddings

emb = OpenAIEmbeddings(
    model="bge-m3",
    base_url="{{BASE}}/v1",
    api_key="YOUR_API_KEY",
    check_embedding_ctx_length=False,  # 필수: 토큰 ID 대신 원문 텍스트를 보내도록
)
doc_vecs = emb.embed_documents(["문서 1", "문서 2"])
query_vec = emb.embed_query("질문")

check_embedding_ctx_length=False를 빼면 LangChain이 tiktoken 토큰 ID 배열을 보내서 422 오류가 납니다.

JavaScript · Node.js 18+

const BASE = "{{BASE}}";

const res = await fetch(`${BASE}/v1/embeddings`, {
  method: "POST",
  headers: { "Content-Type": "application/json", Authorization: "Bearer YOUR_API_KEY" },
  body: JSON.stringify({ model: "bge-m3", input: ["안녕하세요", "Hello"] }),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const { data } = await res.json();
console.log(data[0].embedding.length); // 1024

다른 도메인의 웹페이지(브라우저)에서 직접 호출하는 것은 CORS가 열려 있지 않아 막힙니다. 백엔드 서버에서 호출하세요. API 키를 브라우저 코드에 넣지 않는 것도 같은 이유로 중요합니다.

유사도 계산

import numpy as np

def dense_score(q, d):
    """dense: 정규화돼 있으므로 내적 = 코사인 유사도 (-1 ~ 1)"""
    return float(np.dot(q, d))

def sparse_score(q, d):
    """sparse: 두 텍스트에 공통으로 있는 토큰의 가중치 곱의 합"""
    dw = dict(zip(d["indices"], d["values"]))
    return sum(v * dw.get(i, 0.0) for i, v in zip(q["indices"], q["values"]))

def colbert_score(q, d):
    """colbert: 질의 토큰마다 문서 토큰과의 최대 유사도를 구해 평균 (MaxSim)"""
    q, d = np.asarray(q), np.asarray(d)
    return float((q @ d.T).max(axis=1).mean())

# 세 점수를 섞는 하이브리드 예 (BGE-M3 저자들이 쓴 가중치)
score = 0.4 * dense_score(qd, dd) + 0.2 * sparse_score(qs, ds) + 0.4 * colbert_score(qc, dc)

Qdrant 하이브리드 검색 (dense + sparse)

import requests
from qdrant_client import QdrantClient, models

BASE, HEADERS = "{{BASE}}", {"Authorization": "Bearer YOUR_API_KEY"}

def embed(texts):
    r = requests.post(f"{BASE}/embed", headers=HEADERS, timeout=120,
                      json={"texts": texts, "dense": True, "sparse": True})
    r.raise_for_status()
    return r.json()

qd = QdrantClient(url="http://localhost:6333")
qd.create_collection(
    "docs",
    vectors_config={"dense": models.VectorParams(size=1024, distance=models.Distance.COSINE)},
    sparse_vectors_config={"sparse": models.SparseVectorParams()},
)

docs = ["BGE-M3는 100개 이상의 언어를 지원한다.", "Qdrant는 벡터 데이터베이스다."]
e = embed(docs)
qd.upsert("docs", points=[
    models.PointStruct(
        id=i,
        vector={"dense": e["dense"][i], "sparse": models.SparseVector(**e["sparse"][i])},
        payload={"text": t},
    )
    for i, t in enumerate(docs)
])

q = embed(["다국어 임베딩 모델"])
hits = qd.query_points(
    "docs",
    prefetch=[
        models.Prefetch(query=q["dense"][0], using="dense", limit=20),
        models.Prefetch(query=models.SparseVector(**q["sparse"][0]), using="sparse", limit=20),
    ],
    query=models.FusionQuery(fusion=models.Fusion.RRF),  # 두 결과를 순위 기반으로 합침
    limit=5,
)
for p in hits.points:
    print(round(p.score, 3), p.payload["text"])

바로 테스트

두 문장의 dense 임베딩을 받아 코사인 유사도를 계산합니다. 이 서버의 /v1/embeddings를 실제로 호출합니다.

참고로 유사도는 대략 0.75 이상이면 같은 뜻, 0.5 안팎이면 관련 있음, 0.4 이하는 거의 무관한 경우가 많습니다. 데이터에 따라 다르니 검색 임계값은 직접 확인해서 정하세요.

제한과 팁

항목값설명
요청당 텍스트 수최대 2048개넘으면 413. 나눠서 보내세요.
텍스트 길이최대 8192 토큰넘는 부분은 오류 없이 잘립니다. 긴 문서는 미리 청크로 나누는 것이 검색 품질에도 좋습니다.
요청 본문 크기10 MB외부 도메인(프록시) 기준. 넘으면 413.
응답 대기 시간60초프록시 기준. 아주 큰 배치는 시간 초과(504)가 날 수 있으니 나눠 보내세요.

오류 코드

코드의미해결
400빈 입력, 지원하지 않는 dimensions, 출력 종류를 하나도 고르지 않음detail 메시지를 확인하세요.
401API 키 없음 또는 틀림Authorization: Bearer 키 헤더를 확인하세요.
413텍스트 2048개 초과, 또는 본문 10MB 초과요청을 나눠 보내세요.
422JSON 형식 오류(필드 이름/타입)예: input에 토큰 ID 배열을 보낸 경우(LangChain 설정 확인).
502서버가 꺼져 있거나 재시작 중잠시 후 다시 시도하세요. 계속되면 관리자에게 알려 주세요.
504처리 시간 60초 초과배치를 줄이세요.