Valkey - 벡터 스토어
Valkey - 벡터 스토어 (Vector Store)
LiteLLM의 통합 벡터 스토어 API로 이미 Valkey에 인덱싱한 문서를 검색하는 방법을 알아봐요. 가상 키가 Valkey 자격 증명을 보유하지 않아도 데이터스토어에 대해 검색을 실행할 수 있어요.
출처: 문서
본문
LiteLLM은 Valkey에서만 읽어요. 인덱스 구축·로딩은 사용자 몫이에요. 각 검색은 등록한 모델로 쿼리를 임베딩하고, 인덱스에 대해 KNN FT.SEARCH를 실행하며, 다른 제공사와 동일한 OpenAI 형태의 결과를 반환하되 1 - cosine distance로 점수를 매겨 높을수록 가까워요.
빠른 시작
세 가지가 필요해요:
- valkey-search 모듈이 로드된 Valkey 서버
- 임베딩 모델 (문서를 임베딩한 모델)
- 문서에 대한
FT인덱스
1. Valkey에서 벡터 검색 켜기
Valkey 자체는 키-값 저장소이며 벡터를 알지 못해요. 벡터 검색은 FT.* 명령군을 추가하는 모듈 valkey-search에서 오며, 서버에 로드되어야 해요. LiteLLM은 여기에 Python 의존성을 추가하지 않아요. 게이트웨이가 이미 포함한 Redis 클라이언트로 모듈과 통신해요.
로컬에서 가장 빠르게 서버를 얻는 방법은 모듈이 사전 로드된 valkey-bundle 이미지예요:
docker run -d -p 6379:6379 valkey/valkey-bundle:latest
노드 기반 클러스터에서 관리형 ElastiCache for Valkey(Valkey 8.2)와 MemoryDB는 모듈을 이미 제공해요. 클러스터 모드는 문제없어요. LiteLLM은 엔드포인트가 해석되는 노드에 FT.SEARCH를 보내고, valkey-search 자체가 쿼리를 샤드 간에 팬아웃하고 결과를 병합해요. 직접 실행하는 서버에서는 valkey-server --loadmodule /path/to/libsearch.so로 시작해요.
모듈이 없으면 LiteLLM은 정상 연결되지만 이후 모든 검색이 실패해요. 서버가 들어본 적 없는 명령을 거부하기 때문이에요:
litellm.APIConnectionError: unknown command 'FT.SEARCH', with args beginning with:
'my-search-index' '*=>[KNN 3 @embedding $vec AS vector_distance]' ...
2. LiteLLM이 읽는 방식으로 문서 저장
각 문서는 텍스트 필드(text 기본값)와 FLOAT32 벡터 필드(embedding 기본값)를 가진 Valkey HASH이며, 그 키들에 대한 FT.CREATE 인덱스가 FT.SEARCH가 쿼리하는 대상이에요. Valkey hash에는 스키마가 없으므로 LiteLLM은 어떤 필드가 무엇인지 추측할 수 없어요. 등록하는 이름은 데이터와 일치해야 해요.
인덱스 생성:
FT.CREATE my-search-index ON HASH PREFIX 1 kb: \
SCHEMA embedding VECTOR HNSW 6 TYPE FLOAT32 DIM 1536 DISTANCE_METRIC COSINE
DIM은 임베딩 모델의 차원과 같아야 해요(text-embedding-3-small은 1536). LiteLLM에 등록할 것과 같은 모델로 문서를 임베딩하고 쓰세요:
문서를 인덱스에 로드:
import struct
import litellm
from redis import Redis
client = Redis(host="localhost", port=6379)
docs = {
"kb:refunds": "Refunds are issued back to the original payment method and normally settle within five business days.",
"kb:shipping": "Standard shipping takes three to five business days inside the continental United States.",
}
response = litellm.embedding(model="openai/text-embedding-3-small", input=list(docs.values()))
for (key, text), item in zip(docs.items(), response.data):
embedding = item["embedding"]
client.hset(key, mapping={"text": text, "embedding": struct.pack(f"<{len(embedding)}f", *embedding)})
다른 모델은 조용히 잘못된 결과를 반환하고, 다른 차원은 query vector blob size (N) does not match index's expected size (M)로 실패해요.
3. LiteLLM에 인덱스 등록
등록은 LiteLLM에 인덱스 위치와 어떤 모델이 쿼리를 임베딩하는지 알려줘요. Valkey에서는 아무것도 만들지 않아요. 먼저 Models 아래에 제공사 모델 이름(text-embedding-3-small)으로 임베딩 모델을 추가해요. LiteLLM이 그 모델의 자격 증명을 재사용하되 이름은 제공사로 보내기 때문이에요.
Admin UI: Tools > Vector Stores로 가서 Manage Vector Stores 탭으로 이동하고 + Add Vector Store를 클릭해요. 제공사로 Valkey를 골라요. 양식은 기대하는 것을 설명하고 연결 필드를 보여줘요. FT 인덱스 이름을 Vector Store ID로 입력하고, 호스트·포트를 채우고, 이미 인덱스에 있는 벡터를 만든 임베딩 모델을 선택해요. 해시가 다른 이름을 쓰지 않는 한 Text Field와 Vector Field Name은 그대로 둬요. Create 클릭하면 스토어가 테이블에 나타나 검색 준비가 돼요. 참고: 스토어 생성은 연결을 테스트하지 않아요. 잘못된 host·port·인덱스 이름은 첫 검색에서만 보고되므로 Test Vector Store 탭에서 즉시 검색을 실행해 보세요.
config.yaml:
model_list:
- model_name: openai/text-embedding-3-small
litellm_params:
model: openai/text-embedding-3-small
api_key: os.environ/OPENAI_API_KEY
vector_store_registry:
- vector_store_name: support-knowledge-base
litellm_params:
vector_store_id: my-search-index
custom_llm_provider: valkey
valkey_host: my-valkey.example.com
valkey_port: 6379
valkey_password: os.environ/VALKEY_PASSWORD
valkey_ssl: true
litellm_embedding_model: openai/text-embedding-3-small
litellm --config /path/to/config.yaml
인덱스 등록 (Management API):
curl -X POST 'http://localhost:4000/vector_store/new' \
-H "Authorization: Bearer ***" \
-H 'Content-Type: application/json' \
-d '{
"vector_store_id": "my-search-index",
"custom_llm_provider": "valkey",
"vector_store_name": "Support Knowledge Base",
"litellm_params": {
"valkey_host": "my-valkey.example.com",
"valkey_port": 6379,
"litellm_embedding_model": "openai/text-embedding-3-small"
}
}'
스토어는 LiteLLM 데이터베이스에 기록되고 재시작 없이 즉시 검색 가능해요. 나머지 관리 API는 관리형 벡터 스토어(Ma:naged Vector Stores)를 참고해요.
4. 스토어 테스트
Admin UI의 Test Vector Store 탭은 등록된 스토어에 대해 실제 검색을 실행하고 각 히트를 점수와 함께 보여줘요. 연결·인덱스 이름·임베딩 모델이 모두 일치하는지 확인하는 가장 빠른 방법이에요.
HTTP로 같은 검색:
인덱스 검색:
curl -X POST 'http://localhost:4000/v1/vector_stores/my-search-index/search' \
-H "Authorization: Bearer ***" \
-H 'Content-Type: application/json' \
-d '{"query": "how long does a refund take?", "max_num_results": 3}'
{
"object": "vector_store.search_results.page",
"search_query": "how long does a refund take?",
"data": [
{
"score": 0.5899661779400001,
"content": [
{
"text": "Refunds are issued back to the original payment method and normally settle within five business days. Contact support if the money has not arrived after that.",
"type": "text"
}
],
"file_id": "kb:refunds",
"filename": "kb:refunds"
},
{
"score": 0.36476153135300005,
"content": [
{
"text": "Every API key is limited to 600 requests per minute. Requests over the limit return HTTP 429 with a Retry-After header telling you how long to wait.",
"type": "text"
}
],
"file_id": "kb:rate-limits",
"filename": "kb:rate-limits"
}
]
}
file_id와 filename은 히트가 나온 Valkey 키예요. max_num_results는 기본값 10이며 1~50 사이여야 해요.
SDK에서 스토어를 등록하지 않고 연결 설정을 인라인으로 전달할 수도 있어요:
import litellm
response = litellm.vector_stores.search(
vector_store_id="my-search-index",
query="how long does a refund take?",
custom_llm_provider="valkey",
valkey_host="my-valkey.example.com",
litellm_embedding_model="openai/text-embedding-3-small",
max_num_results=3,
)
litellm.vector_stores.asearch가 비동기 버전이에요. 둘 다 proxy가 이미 설치하는 redis 패키지가 필요하며, 순수 SDK 환경에서는 pip install redis를 실행해요.
스토어가 등록되면 벡터 스토어 id를 받는 어떤 LiteLLM 기능이든 사용할 수 있어요. /chat/completions의 RAG는 tools: [{"type": "file_search", "vector_store_ids": ["my-search-index"]}]로요.
설정 레퍼런스
기본값이 이 표의 대부분을 덮어요. 검색이 무엇이든 찾는지 결정하는 것은 인덱스 이름, host, 임베딩 모델, 두 필드 이름이에요.
| 설정 | UI 라벨 | 필수 | 넣을 것 |
|---|---|---|---|
vector_store_id |
Vector Store ID | 예 | FT.CREATE에 전달한 그대로의 FT 인덱스 이름. 자유 형식 라벨이 아님. 알 수 없는 이름은 Index with name '...' not found in database 0으로 실패 |
valkey_host |
Valkey Host | 예 | 스킴·포트 없는 순수 호스트명/ IP. redis://my-valkey.example.com:6379가 아니라 my-valkey.example.com |
valkey_port |
Valkey Port | 아니오 | 기본 6379. 서버가 다른 곳에서 듣는 경우에만 변경 |
valkey_password |
Valkey Password | 아니오 | 서버가 AUTH 요구 시에만. 그 외엔 비움 |
valkey_ssl |
Use TLS | 아니오 | 기본 false. 전송 중 암호화가 있는 ElastiCache·MemoryDB 클러스터에서 true로 (그게 LiteLLM이 rediss://로 연결하게 함) |
litellm_embedding_model |
Embedding Model | 예 | 인덱스의 벡터를 만든 정확한 모델. 다른 모델은 그럴듯하지만 틀린 결과, 다른 차원은 오류 |
valkey_text_field |
Text Field | 아니오 | 기본 text. 읽을 수 있는 텍스트를 담은 해시 필드와 일치해야 함. 아니면 모든 결과가 빈 content로 돌아옴 |
valkey_embedding_field |
Vector Field Name | 아니오 | 기본 embedding. 인덱스가 만들어진 필드와 일치해야 함 |
litellm_embedding_config |
n/a | 아니오 | api_key나 api_base 같은 임베딩 호출의 추가 인자. proxy에서는 보통 생략 가능. LiteLLM이 등록된 모델에서 해석하기 때문 |
문제 해결
unknown command 'FT.SEARCH'는 서버에 벡터 검색이 없다는 뜻이에요.valkey-cli FT._LIST를 실행해 보세요. 모듈이 있는 서버는 인덱스로 응답하고, 없는 서버는 같은 unknown command 오류를 반복해요. 일반valkey/valkey이미지가 흔한 원인이에요.query vector blob size (N) does not match index's expected size (M)는 스토어에 등록된 임베딩 모델이 인덱스가 만들어진 차원과 다른 차원을 반환한다는 뜻이에요. 두 숫자는 바이트 수이므로 4로 나누면 비교되는 차원이 보여요. 그런 다음 인덱스를 만든 모델을 등록하거나 새 차원으로 재구축해요.- 결과가 빈 텍스트로 오면 히트는 실제지만
valkey_text_field가 해시에 없는 필드를 지칭한다는 뜻이에요. 반환된 키 중 하나에서HGETALL을 실행하고 산문을 담은 필드로 설정해요. Connection refused또는 검색이 멈추고 실패하면 보통 잘못된 포트 또는 게이트웨이를 막는 보안 그룹이에요. LiteLLM은 연결에 5초, 명령에 30초를 기다리므로 접근 불가 호스트는 워커를 잡아두지 않고 빠르게 실패해요.- 무작위로 보이는 랭킹은 임베딩 모델을 가리켜요. 문서와 다른 모델로 쿼리를 임베딩해도 오류가 없으므로, 스토어에 등록된 것과 인제스트 작업이 실제로 쓴 것을 비교해 보세요.
미지원
Valkey 벡터 스토어는 검색 전용이에요. LiteLLM은 Valkey에 대해 인덱스 생성(POST /v1/vector_stores), 파일 업로드, /rag/ingest 실행을 할 수 없으며, 그래서 Valkey는 Admin UI의 Create Vector Store 탭에 없어요. FT.CREATE와 HSET로 인덱스를 직접 구축·채워야 해요. 검색의 filters 파라미터도 미구현이며, 전달 시 조용히 무시되지 않고 오류를 발생시켜요.
같은 모듈이 있는 Valkey 서버는 LiteLLM의 의미 캐시를 백업할 수도 있는데, 이는 LiteLLM이 만들고 쓰는 자체 인덱스를 가진 별도 기능이에요.
더 알아보기 (Learn more)
- Valkey 문서
- LiteLLM 벡터 스토어 레지스트리
- LiteLLM RAG