임베딩 및 재순위화 API¶
mlxcel은 OpenAI 호환 임베딩, llama-server 고유 임베딩, Jina/Cohere/TEI 호환 재순위화 경로를 제공합니다. 이 경로들은 전용 모델 작업자를 사용하며 생성 작업자와 독립적입니다.
경로 표¶
| 방식 | 경로 | 계약 |
|---|---|---|
POST | /v1/embeddings | OpenAI 요청·응답 형식 |
POST | /embedding | llama-server 고유 임베딩 요청과 배열 응답 |
POST | /embeddings | /embedding과 같은 고유 형식이며 /v1/embeddings의 별칭이 아님 |
POST | /v1/rerank | 재순위화 처리기 |
POST | /v1/reranking | 같은 재순위화 처리기 |
POST | /rerank | 같은 재순위화 처리기 |
POST | /reranking | 같은 재순위화 처리기 |
GET | /v1/models | 불러온 대화·임베딩·재순위화 모델 식별자 목록 |
재순위화 응답 형식은 경로가 아니라 문서 목록 필드로 결정합니다. documents를 보내면 Jina/Cohere 형식이고 texts를 보내면 TEI 호환 배열입니다.
모델 제공자 불러오기¶
# 임베딩 전용
mlxcel-server -m sentence-transformers/all-MiniLM-L6-v2 --port 8080
# 교차 인코더 재순위화 전용
mlxcel-server -m BAAI/bge-reranker-v2-m3 --port 8080
# 대화와 두 보조 제공자를 함께 실행
mlxcel-server \
-m mlx-community/Qwen3-4B-4bit \
--embedding-model Qwen/Qwen3-Embedding-0.6B \
--reranker-model mlx-community/Qwen3-Reranker-0.6B-4bit \
--port 8080
-m에 지정한 임베딩 체크포인트나 레이블 하나짜리 ForSequenceClassification 재순위화 모델은 자동 감지하며 이때 대화 모델은 불러오지 않습니다. 생성형 Qwen3와 Qwen3-VL 재순위화 모델은 설정만으로 생성 모델과 구분할 수 없으므로 --reranker-model이 필요합니다. 생성형 재순위화 전용 서버에서는 같은 체크포인트를 -m과 --reranker-model에 모두 지정하십시오.
POST /v1/embeddings¶
요청¶
{
"model": "선택 사항인 서빙 모델 식별자",
"input": ["query: 벡터 검색", "passage: 최근접 이웃 검색"],
"encoding_format": "float",
"dimensions": 256,
"instruction": "선택 사항인 모델 계열별 질의 지시문",
"embd_normalize": 2,
"user": "받지만 사용하지 않음"
}
input은 다섯 가지 형태를 받습니다.
| 형태 | 예시 | 동작 |
|---|---|---|
| 문자열 | "hello" | 텍스트 입력 하나 |
| 문자열 목록 | ["a", "b"] | 텍스트 입력 여러 개 |
| 토큰 ID 배열 | [101, 7592, 102] | 특수 토큰을 추가하지 않고 그대로 사용 |
| 토큰 ID 배열 목록 | [[101, 102], [101, 7592, 102]] | 토큰 시퀀스 여러 개를 그대로 사용 |
| 형식 지정 항목 목록 | 텍스트·이미지 객체 목록 | 순서를 보존하며 이미지는 멀티모달 임베딩 모델(Qwen3-VL-Embedding, Llama-Nemotron-VL-Embed, ColIdefics3, ColQwen2.5, vision tower가 있는 EmbeddingGemma 2 체크포인트)에서만 허용 |
형식 지정 항목의 실제 JSON은 다음과 같습니다.
{
"input": [
{"type": "text", "text": "빨간색 차트"},
{"type": "image_url", "image_url": {"url": "file:///data/chart.png"}}
]
}
model을 넣으면 현재 제공 중인 임베딩 모델 식별자와 같아야 합니다. encoding_format의 기본값은 float이며 base64도 가능합니다. dimensions는 1 이상 모델의 원래 출력 폭 이하여야 합니다. instruction은 모델 계열별 텍스트 형식 함수로 전달하며 별도 규칙이 없는 계열은 무시합니다. embd_normalize는 이 요청에 한해 서버·체크포인트 정규화를 덮어씁니다. -1은 정규화 안 함, 0은 절댓값 최댓값 기반 int16 범위 조정, 1은 L1, 2는 L2, 2보다 크면 해당 p-노름입니다.
이미지 URL에는 data:image/...;base64, URI, file:// URL, HTTP(S) URL, 로컬 경로를 쓸 수 있으며 공통 미디어 제한과 프로세스의 네트워크·파일 시스템 권한을 적용합니다.
밀집 벡터 응답¶
{
"object": "list",
"data": [
{"object": "embedding", "index": 0, "embedding": [0.01, 0.02, 0.03]}
],
"model": "all-MiniLM-L6-v2",
"usage": {"prompt_tokens": 7, "total_tokens": 7}
}
각 항목은 요청 순서를 보존합니다. 토큰 사용량은 특수 토큰을 포함한 비패딩 토큰 수입니다. total_tokens와 prompt_tokens는 같습니다.
다중 벡터와 base64 응답¶
후기 상호작용 모델은 유효 토큰마다 한 행씩 embedding: [[...], [...]]를 반환합니다. encoding_format: "base64"를 쓰면 행 우선 순서의 리틀 엔디언 f32 바이트를 표준 패딩 base64로 인코딩합니다. 다중 벡터 항목에는 "shape": [토큰 수, 차원 수]가 붙고 밀집 벡터에는 필요하지 않습니다.
POST /embedding 및 /embeddings¶
llama-server 고유 경로는 input 또는 예전 content 필드를 받습니다. 같은 모델을 실행하지만 /v1/embeddings와 의도적으로 다른 형식을 반환합니다.
curl -s http://localhost:8080/embedding \
-H 'Content-Type: application/json' \
-d '{"content":"hello"}'
최상위 값은 배열이고 embedding은 한 단계 더 중첩됩니다. OpenAI 방식의 object/data/model/usage를 기대하는 클라이언트는 /v1/embeddings를 호출해야 합니다.
POST /v1/rerank 및 별칭¶
요청¶
{
"model": "선택 사항인 서빙 재순위화 모델 식별자",
"query": "벡터 검색이란 무엇인가?",
"documents": [
"벡터 검색은 가까운 임베딩을 찾는다.",
"컴파일러는 소스 코드를 번역한다."
],
"top_n": 1,
"return_documents": true,
"instruction": "선택 사항이며 생성형 재순위화 모델만 사용",
"user": "받지만 사용하지 않음"
}
query와 각 문서는 문자열 또는 text, image, image_url 가운데 하나 이상을 가진 객체일 수 있습니다.
{
"query": {"text": "매출 차트를 찾아라", "image_url": {"url": "query.png"}},
"documents": [
{"text": "분기별 매출", "image": "chart.png"},
{"image": {"url": "photo.png"}}
],
"top_n": 2,
"return_documents": true
}
두 이미지 필드를 모두 보내면 image가 우선합니다. 이미지는 URL 문자열이나 {"url": "..."}로 씁니다. 멀티모달 재순위화 모델에서는 이미지만 있는 객체도 유효합니다. 텍스트 전용 모델은 이미지를 거부합니다. top_n은 1 이상이어야 합니다. 시퀀스 분류기는 지시문을 넣을 프롬프트가 없으므로 instruction을 거부합니다.
Jina/Cohere 응답¶
documents 필드를 쓰면 다음 형식을 선택합니다.
{
"model": "bge-reranker-v2-m3",
"object": "list",
"results": [
{
"index": 0,
"relevance_score": 0.9948,
"document": "벡터 검색은 가까운 임베딩을 찾는다."
}
],
"usage": {"prompt_tokens": 31, "total_tokens": 31}
}
document는 return_documents 또는 return_text가 참일 때만 들어가며 요청의 원래 항목을 그대로 돌려줍니다. 결과는 점수가 높은 순, 같은 점수에서는 원래 인덱스가 작은 순으로 정렬한 뒤 top_n까지 자릅니다.
TEI 응답¶
documents 없이 texts를 쓰면 Text Embeddings Inference 호환 배열을 반환합니다. return_text가 원문 반환 여부를 정합니다.
{
"query": "벡터 검색이란 무엇인가?",
"texts": ["벡터 검색은 가까운 임베딩을 찾는다.", "컴파일러는 소스 코드를 번역한다."],
"return_text": true
}
[
{"index": 0, "score": 0.9948, "text": "벡터 검색은 가까운 임베딩을 찾는다."},
{"index": 1, "score": 0.0012, "text": "컴파일러는 소스 코드를 번역한다."}
]
documents와 texts를 모두 보내면 documents가 우선하며 Jina/Cohere 응답을 반환합니다.
오류 계약¶
오류에는 mlxcel의 구조화된 오류 응답을 사용합니다. 중요한 상태 코드는 다음과 같습니다.
| 상태 | 유형 | 대표 원인 |
|---|---|---|
400 | invalid_request_error | 빈 입력, 잘못된 토큰 ID, 지원하지 않는 이미지, 차원·인코딩·모델 불일치, 빈 문서 목록, 잘못된 top_n, 분류기에 전달한 지시문 |
401 | 인증 오류 | API 키가 설정되어 있는데 Bearer 토큰이 없거나 틀림 |
501 | not_implemented | 요청한 종류의 모델 제공자를 불러오지 않음 |
503 | server_busy | 크기가 제한된 작업자 큐가 가득 참 |
504 | server_timeout | 설정된 제한 시간 안에 작업자가 응답하지 않음 |
500 | server_error | 로딩·추론 실패, 패닉, 잘못된 수치 결과 |
서버 옵션¶
| 옵션 | 기본값 | 의미 |
|---|---|---|
--embedding-model PATH_OR_REPO | 미지정 | 주 모델 옆에 임베딩 모델을 불러옴 |
--reranker-model PATH_OR_REPO | 미지정 | 주 모델 옆에 재순위화 모델을 불러옴. 생성형 모델에는 필수 |
--embedding-batch-size N | 16 | 임베딩 순전파 한 번에 넣는 텍스트 수 |
--embedding-max-length N | 자동 결정 | 자동 계산한 임베딩 길이 상한을 더 낮춤 |
--embedding-queue-depth N | 8 | 각 임베딩·재순위화 작업자 큐의 상한 |
--embedding-request-timeout-secs N | 120 | 두 작업자 종류가 공유하는 응답 제한 시간 |
--rerank-batch-size N | 0 | 종류별 기본값: 텍스트 쌍 8개 또는 멀티모달 쌍 2개 |
--pooling mean\|cls\|last | 체크포인트·계열 | 밀집 풀링 덮어쓰기. rank는 재순위화 모드이고 none은 거부 |
--embd-normalize N | 체크포인트 | 벡터 정규화 덮어쓰기 |
--embedding, --embeddings | 꺼짐 | 임베딩 호환 제한 모드 선택. 임베딩 모델이 있어야 함 |
--rerank, --reranking | 꺼짐 | 재순위화 호환 제한 모드 선택. 재순위화 모델이 있어야 함 |
임베딩 작업자와 재순위화 작업자는 각각 전용 스레드에서 모델, 토크나이저, MLX 배열을 소유합니다. 한 프로세스는 임베딩 제공자 하나와 재순위화 제공자 하나를 지원합니다. 큐 크기와 제한 시간의 설정 이름은 공유하지만 각 작업자의 자체 큐와 요청 수명에 적용됩니다.
클라이언트 예시¶
OpenAI Python SDK¶
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8080/v1", api_key="none")
response = client.embeddings.create(
model="Qwen3-Embedding-0.6B",
input=["벡터 검색이란 무엇인가?", "벡터 검색은 가까운 임베딩을 찾는다."],
)
print(response.data[0].embedding[:8])
httpx를 사용한 재순위화¶
import httpx
response = httpx.post(
"http://localhost:8080/v1/rerank",
json={
"query": "벡터 검색이란 무엇인가?",
"documents": [
"벡터 검색은 가까운 임베딩을 찾는다.",
"컴파일러는 소스 코드를 번역한다.",
],
"top_n": 1,
"return_documents": True,
},
)
response.raise_for_status()
print(response.json()["results"])
모델 계약, 풀링 규칙, 다중 벡터 최대 유사도 합산, 평가, 인덱스 설계는 임베딩과 재순위화를 참고하십시오.