검색
검색 (Retrieval)
Retrieval API는 데이터에 대해 시맨틱 검색을 수행하게 해요. 시맨틱 검색은 키워드가 거의 또는 전혀 일치하지 않더라도 의미상 유사한 결과를 표면에 드러내는 기법이에요. Retrieval은 그 자체로도 유용하지만, 모델과 결합해 응답을 종합(synthesize)할 때 특히 강력해요.
출처: 문서
본문
Retrieval API는 데이터의 인덱스 역할을 하는 vector stores로 구동돼요. 이 가이드는 시맨틱 검색을 수행하는 방법을 다루고 vector stores의 세부 사항을 살펴봐요.
Quickstart
- vector store를 만들고 파일을 업로드한다.
from openai import OpenAI
client = OpenAI()
vector_store = client.vector_stores.create( # Create vector store
name="Support FAQ",
)
client.vector_stores.files.upload_and_poll( # Upload file
vector_store_id=vector_store.id,
file=open("customer_policies.txt", "rb")
)
- 검색 쿼리를 보내 관련 결과를 얻는다.
user_query = "What is the return policy?"
results = client.vector_stores.search(
vector_store_id=vector_store.id,
query=user_query,
)
결과를 모델과 함께 쓰는 방법은 응답 종합 섹션을 참고하세요.
시맨틱 검색
시맨틱 검색은 벡터 임베딩을 활용해 의미상 관련 있는 결과를 표면에 드러내는 기법이에요. 중요한 것은 키워드를 거의·전혀 공유하지 않는 결과도 포함해서, 고전적 검색 기법이 놓칠 수 있는 결과를 잡아낸다는 점이에요.
예를 들어 "When did we go to the moon?"에 대한 잠재 결과를 보면:
| 텍스트 | 키워드 유사도 | 시맨틱 유사도 |
|---|---|---|
| The first lunar landing occurred in July of 1969. | 0% | 65% |
| The first man on the moon was Neil Armstrong. | 27% | 43% |
| When I ate the moon cake, it was delicious. | 40% | 28% |
(키워드 유사도는 intersection over union, 시맨틱 유사도는 text-embedding-3-small을 쓴 cosine similarity를 사용.)
가장 관련 있는 결과가 검색 쿼리의 어떤 단어도 포함하지 않는다는 점에 주목하세요. 이 유연성 덕분에 시맨틱 검색은 어떤 크기의 지식 베이스를 쿼리하는 강력한 기법이에요.
시맨틱 검색 수행하기
search 함수로 vector store를 쿼리하고 자연어 query를 지정할 수 있어요. 그러면 각각 관련 청크, 유사도 점수, 출처 파일이 있는 결과 목록을 반환해요.
results = client.vector_stores.search(
vector_store_id=vector_store.id,
query="How many woodchucks are allowed per passenger?",
)
{
"object": "vector_store.search_results.page",
"search_query": "How many woodchucks are allowed per passenger?",
"data": [
{
"file_id": "file-12345",
"filename": "woodchuck_policy.txt",
"score": 0.85,
"attributes": {
"region": "North America",
"author": "Wildlife Department"
},
"content": [
{
"type": "text",
"text": "According to the latest regulations, each passenger is allowed to carry up to two woodchucks."
}
]
}
],
"has_more": false,
"next_page": null
}
응답은 기본적으로 최대 10개 결과를 포함하고, max_num_results 파라미터로 최대 50개까지 설정할 수 있어요.
쿼리 재작성 (Query rewriting)
일부 쿼리 스타일이 더 나은 결과를 내므로, 최적 성능을 위해 쿼리를 자동으로 재작성하는 설정을 제공해요. search를 수행할 때 rewrite_query=true로 설정하면 이 기능을 켤 수 있어요. 재작성된 쿼리는 결과의 search_query 필드에서 볼 수 있어요. 예를 들어 "I'd like to know the height of the main office building."은 "primary office building height"으로, "How do I file a complaint about a service issue?"는 "service complaint filing process"로 재작성돼요.
속성 필터링 (Attribute filtering)
속성 필터링은 특정 날짜 범위로 검색을 제한하는 같은 기준을 적용해 결과를 좁혀요. 시맨틱 검색을 수행하기 전에 파일의 attributes를 기준으로 대상 파일을 고르도록 attribute_filter에서 기준을 정의·결합할 수 있어요.
**비교 필터(comparison filters)**로 파일 attributes의 특정 key를 주어진 value와 비교하고, **복합 필터(compound filters)**로 여러 필터를 and·or로 결합해요.
{
"type": "eq" | "ne" | "gt" | "gte" | "lt" | "lte" | "in" | "nin", // comparison operators
"key": "attributes_key", // attributes key
"value": "target_value" // value to compare against
}
{
"type": "and" | "or", // logical operators
"filters": [...]
}
예시 필터:
Region — region이 us와 같은 파일로 필터.
{
"type": "eq",
"key": "region",
"value": "us"
}
Date range — date가 2024-01-01(gte, unix timestamp 1704067200)에서 2024-03-20(lte, 1710892800) 사이인 파일로 필터.
{
"type": "and",
"filters": [
{ "type": "gte", "key": "date", "value": 1704067200 },
{ "type": "lte", "key": "date", "value": 1710892800 }
]
}
Filenames — in으로 주어진 파일 이름 집합 중 하나와 일치.
{
"type": "in",
"property": "filename",
"value": ["example.txt", "example2.txt"]
}
Exclude filenames — nin으로 초안 파일 제외.
{
"type": "nin",
"property": "filename",
"value": ["draft.txt", "internal_notes.md"]
}
이들(그리고 or의 중첩 필터)을 결합해 프로젝트 코드·기밀도·언어 같은 기준으로 복잡한 필터를 만들 수 있어요.
랭킹 (Ranking)
파일 검색 결과가 충분히 관련 없게 느껴지면 ranking_options를 조정해 응답 품질을 개선할 수 있어요. auto나 default-2024-08-21 같은 ranker를 지정하고, 0.0~1.0 사이의 score_threshold를 설정해요. score_threshold가 높을수록 더 관련 있는 청크로 결과가 제한되지만, 유용한 일부가 제외될 수 있어요. ranking_options.hybrid_search를 제공하면 hybrid_search.embedding_weight(rrf_embedding_weight)와 hybrid_search.text_weight(rrf_text_weight)로 reciprocal rank fusion이 시맨틱 임베딩 매치와 희소 키워드 매치를 어떻게 균형 맞출지 조정할 수 있어요. 전자를 높이면 시맨틱 유사도를, 후자를 높이면 텍스트 겹침을 강조하고, 두 가중치 중 적어도 하나는 0보다 커야 해요.
Vector stores
Vector stores는 Retrieval API와 파일 검색 도구의 시맨틱 검색을 구동하는 컨테이너예요. 파일을 vector store에 추가하면 자동으로 청크로 나뉘고 임베딩되고 인덱싱돼요.
Vector store에는 vector_store.file 객체가 들어 있고, 이 객체는 file 객체에 의해 뒷받침돼요.
| 객체 유형 | 설명 |
|---|---|
file |
Files API로 업로드된 콘텐츠를 나타내요. Vector store와 자주 쓰지만 파인튜닝 등 다른 용도에도 써요. |
vector_store |
검색 가능한 파일의 컨테이너. |
vector_store.file |
청크로 나뉘고 임베딩되어 vector_store와 연결된 file을 구체적으로 나타내는 래퍼 유형. 필터링에 쓰는 attributes 맵을 포함해요. |
가격
모든 vector store의 총 저장 사용량을 기준으로 청구돼요. 파싱된 청크와 대응 임베딩의 크기에 따라 결정돼요.
| 저장 | 비용 |
|---|---|
| 최대 1GB(모든 store 합산) | 무료 |
| 1GB 초과 | $0.10/GB/일 |
비용을 최소화하려면 만료 정책을 참고하세요.
Vector store 연산
API는 vector store의 create, retrieve, update, delete, list 연산을 제공해요.
# Create
client.vector_stores.create(name="Support FAQ", file_ids=["file_123"])
# Retrieve
client.vector_stores.retrieve(vector_store_id="vs_123")
# Update
client.vector_stores.update(vector_store_id="vs_123", name="Support FAQ Updated")
# Delete
client.vector_stores.delete(vector_store_id="vs_123")
# List
client.vector_stores.list()
Vector store 파일 연산
vector_store.file의 create 같은 일부 연산은 비동기라 완료에 시간이 걸릴 수 있어요. create_and_poll 같은 헬퍼 함수로 완료될 때까지 블록하거나 상태를 확인하세요. vector store에서 파일을 제거하는 것은 eventually consistent라, 제거된 파일의 콘텐츠가 짧은 기간 검색 결과에 여전히 포함될 수 있어요.
파일 추가는 vector store ID별로 rate limit이 적용돼요. /vector_stores/{vector_store_id}/files와 /vector_stores/{vector_store_id}/file_batches는 vector store당 300회/분 한도를 공유해요.
# Create (and poll until done)
client.vector_stores.files.create_and_poll(vector_store_id="vs_123", file_id="file_123")
# Upload
client.vector_stores.files.upload_and_poll(vector_store_id="vs_123", file=open("customer_policies.txt", "rb"))
# Retrieve
client.vector_stores.files.retrieve(vector_store_id="vs_123", file_id="file_123")
# Update (attributes)
client.vector_stores.files.update(vector_store_id="vs_123", file_id="file_123", attributes={"key": "value"})
# Delete
client.vector_stores.files.delete(vector_store_id="vs_123", file_id="file_123")
# List
client.vector_stores.files.list(vector_store_id="vs_123")
일괄 연산 (Batch operations)
배치를 만들 때 file_ids(선택적 attributes·chunking_strategy 포함)를 제공하거나, 각 파일에 file_id와 선택적 attributes·chunking_strategy를 담은 객체들로 files 배열을 쓸 수 있어요. 두 옵션은 상호 배타적이라, 모든 파일이 같은 설정을 공유하게 할지 파일별 오버라이드를 쓸지 깔끔하게 제어할 수 있어요. 단일 vector store에 높은 처리량으로 수집할 때는 가능하면 배치 생성을 권장해요. 배치는 한 요청에 최대 500개 파일을 포함할 수 있어, 여러 단일 파일 생성 요청보다 경쟁을 줄이고 종단 간 지연을 개선해요.
client.vector_stores.file_batches.create_and_poll(
vector_store_id="vs_123",
files=[
{"file_id": "file_123", "attributes": {"department": "finance"}},
{
"file_id": "file_456",
"chunking_strategy": {
"type": "static",
"max_chunk_size_tokens": 1200,
"chunk_overlap_tokens": 200
}
}
]
)
배치에는 retrieve, cancel, list_files 연산도 있어요.
속성 (Attributes)
각 vector_store.file는 속성 필터링으로 시맨틱 검색 시 참조할 수 있는 값의 딕셔너리인 attributes를 가질 수 있어요. 딕셔너리는 최대 16개 키를 가질 수 있고, 각각 256자 제한이 있어요.
client.vector_stores.files.create(
vector_store_id="<vector_store_id>",
file_id="file_123",
attributes={
"region": "US",
"category": "Marketing",
"date": 1672531200 # Jan 1, 2023
}
)
만료 정책 (Expiration policies)
vector_store 객체에 expires_after로 만료 정책을 설정할 수 있어요. 벡터 스토어가 만료되면 연결된 모든 vector_store.file 객체가 삭제되고 더 이상 청구되지 않아요.
client.vector_stores.update(
vector_store_id="vs_123",
expires_after={"anchor": "last_active_at", "days": 7}
)
한도
최대 파일 크기는 512MB예요. 각 파일은 파일당 5,000,000 토큰을 넘지 않아야 해요(파일을 붙일 때 자동 계산).
청크 단위 (Chunking)
기본적으로 max_chunk_size_tokens는 800, chunk_overlap_tokens는 400으로 설정돼 있어요. 즉 모든 파일이 800토큰 청크로 나뉘고 연속 청크 사이에 400토큰 겹침으로 인덱싱돼요. 파일을 추가할 때 chunking_strategy를 설정해 조정할 수 있어요. 제한이 있어요. max_chunk_size_tokens는 100~4096 사이여야 하고, chunk_overlap_tokens는 음수가 아니어야 하며 max_chunk_size_tokens / 2를 넘지 않아야 해요.
지원되는 파일 유형
text/ MIME 유형의 인코딩은 utf-8, utf-16, ascii 중 하나여야 해요. 지원 형식에는 .c, .cpp, .cs, .css, .doc, .docx, .go, .html, .java, .js, .json, .md, .pdf, .php, .pptx, .py, .rb, .sh, .tex, .ts, .txt가 있어요.
응답 종합 (Synthesizing responses)
쿼리를 수행한 뒤 그 결과를 바탕으로 응답을 종합하고 싶을 수 있어요. 결과와 원래 쿼리를 모델에 제공해서 근거 있는(grounded) 응답을 받을 수 있어요. 검색 결과를 모델의 input에 포함하고, 관련성이 확실한 텍스트만 전달하도록 하는 것이 좋은 관행이에요.
from openai import OpenAI
client = OpenAI()
user_query = "What is the return policy?"
results = client.vector_stores.search(
vector_store_id=vector_store.id,
query=user_query,
)
이 결과와 원래 쿼리를 프롬프트에 담아 모델이 정확하고 근거 있는 응답을 합성하게 할 수 있어요.