For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to the page URL.
기본 탐색 메뉴
Theme

검색

의미적 유사도를 바탕으로 데이터를 검색하세요.

Retrieval API를 사용하면 데이터에서 시맨틱 검색을 수행할 수 있습니다. 시맨틱 검색은 검색어와 일치하는 키워드가 거의 없거나 전혀 없어도 의미가 비슷한 결과를 찾습니다. 검색 자체로도 유용하지만, OpenAI 모델이 검색 결과를 종합해 응답을 작성할 때 특히 효과적입니다.

Retrieval API의 검색 방식

Retrieval API는 데이터의 인덱스 역할을 하는 벡터 저장소를 사용합니다. 이 가이드에서는 시맨틱 검색을 수행하는 방법과 벡터 저장소의 작동 방식을 살펴봅니다.

빠른 시작

  • 벡터 저장소를 만들고 파일을 업로드하세요.

  • 파일이 포함된 벡터 저장소 만들기
    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,
    )

    OpenAI 모델에서 검색 결과를 활용하는 방법은 검색 결과를 종합해 응답 작성하기를 참고하세요.

    시맨틱 검색

    시맨틱 검색은 벡터 임베딩을 활용해 의미상 관련 있는 결과를 찾는 기법입니다. 검색어와 겹치는 키워드가 거의 없거나 전혀 없어 기존 검색 기법으로는 놓치기 쉬운 결과도 찾을 수 있습니다.

    예를 들어 "When did we go to the moon?"(우리는 언제 달에 갔나요?)이라는 쿼리로 검색했을 때 나올 수 있는 결과를 살펴보세요.

    텍스트 키워드 유사도 의미적 유사도
    The first lunar landing occurred in July of 1969. (최초의 달 착륙은 1969년 7월에 이루어졌다.) 0% 65%
    The first man on the moon was Neil Armstrong. (달에 처음 발을 디딘 사람은 닐 암스트롱이었다.) 27% 43%
    When I ate the moon cake, it was delicious. (내가 먹은 월병은 맛있었다.) 40% 28%

    (키워드 유사도는 합집합 대비 교집합의 비율로 계산하고, 의미적 유사도는 코사인 유사도를 사용해 text-embedding-3-small이 생성한 임베딩을 비교한 값입니다.)

    가장 관련성이 높은 결과의 영어 원문에는 검색 쿼리에 쓰인 단어가 하나도 없습니다. 이처럼 시맨틱 검색은 표현이 달라도 관련 내용을 찾을 수 있어 규모에 관계없이 지식 베이스를 검색하는 데 효과적입니다.

    시맨틱 검색에는 벡터 저장소가 사용됩니다. 벡터 저장소는 가이드 뒷부분에서 자세히 다루고, 여기서는 시맨틱 검색이 어떻게 작동하는지 살펴봅니다.

    시맨틱 검색 수행하기

    search 함수를 호출할 때 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."
            },
            {
              "type": "text",
              "text": "Ensure that the woodchucks are properly contained during transport."
            }
          ]
        },
        {
          "file_id": "file-67890",
          "filename": "transport_guidelines.txt",
          "score": 0.75,
          "attributes": {
            "region": "North America",
            "author": "Transport Authority"
          },
          "content": [
            {
              "type": "text",
              "text": "Passengers must adhere to the guidelines set forth by the Transport Authority regarding the transport of woodchucks."
            }
          ]
        }
      ],
      "has_more": false,
      "next_page": null
    }

    기본적으로 응답에는 결과가 최대 10개 포함됩니다. max_num_results 매개변수로 최대 50개까지 설정할 수 있습니다.

    쿼리 재작성

    같은 내용이라도 쿼리를 어떻게 표현하느냐에 따라 더 나은 검색 결과를 얻을 수 있습니다. 검색 성능을 높이도록 쿼리를 자동으로 재작성하려면 rewrite_query=true로 설정한 뒤 search를 실행하세요.

    재작성된 쿼리는 결과의 search_query 필드에서 확인할 수 있습니다.

    원래 쿼리 재작성된 쿼리
    본사 건물의 높이가 얼마나 되는지 알고 싶습니다. 본사 건물 높이
    위험물 운송 시 지켜야 할 안전 규정은 무엇인가요? 위험물 안전 규정
    서비스 문제에 대해 어떻게 불만을 접수하나요? 서비스 불만 접수 절차

    속성 필터링

    속성 필터링을 사용하면 특정 기간에 해당하는 파일만 검색하는 등 조건을 적용해 결과를 좁힐 수 있습니다. filters에서 조건을 정의하고 조합하면 시맨틱 검색을 수행하기 전에 파일 속성에 따라 검색 대상을 제한할 수 있습니다.

    비교 필터를 사용하면 특정 key에 대해 파일의 attributes에 저장된 값을 지정한 value와 비교할 수 있습니다. 복합 필터를 사용하면 여러 필터를 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": [...]
    }

    다음은 필터 사용 예시입니다.

    아래 파일명 예시에서는 사용자 지정 filename 속성의 값을 비교합니다. 각 벡터 저장소 파일에 이 속성을 {"filename": "example.txt"}와 같이 명시적으로 설정해야 합니다. 필터가 업로드된 파일의 이름을 자동으로 비교하지는 않습니다.

    특정 지역으로 필터링
    {
      "type": "eq",
      "key": "region",
      "value": "us"
    }

    검색 순위 조정

    파일 검색 결과의 관련성이 낮다면 ranking_options를 조정해 응답 품질을 개선할 수 있습니다. ranker에 auto 또는 default-2024-08-21 등을 지정하고 score_threshold를 0.0에서 1.0 사이의 값으로 설정할 수 있습니다. score_threshold를 높이면 관련성이 높은 청크만 결과에 포함되지만, 유용할 수 있는 청크도 일부 제외될 수 있습니다. ranking_options.hybrid_search를 지정한 경우에는 hybrid_search.embedding_weight(rrf_embedding_weight)와 hybrid_search.text_weight(rrf_text_weight)도 조정할 수 있습니다. 이 가중치는 역순위 융합에서 임베딩 기반 의미 검색과 희소 키워드 검색의 결과를 각각 얼마나 반영할지 결정합니다. 의미적 유사도를 강조하려면 첫 번째 가중치를, 텍스트의 일치도를 강조하려면 두 번째 가중치를 높이세요. 두 가중치 중 적어도 하나는 0보다 커야 합니다.

    벡터 저장소

    벡터 저장소는 Retrieval API와 파일 검색 도구에서 시맨틱 검색에 사용하는 컨테이너입니다. 벡터 저장소에 파일을 추가하면 파일이 자동으로 청크로 나뉘고, 각 청크의 임베딩이 생성되며 인덱싱이 이루어집니다.

    벡터 저장소에는 vector_store_file 객체가 들어 있으며, 각 객체는 file 객체를 기반으로 합니다.

    객체 유형
    설명
    file Files API로 업로드한 콘텐츠를 나타냅니다. 주로 벡터 저장소와 함께 사용하며 파인튜닝 등 다른 용도로도 사용합니다.
    vector_store 검색할 수 있는 파일을 담는 컨테이너입니다.
    vector_store.file 청크 분할과 임베딩 생성을 거친 file이 vector_store에 연결된 상태를 나타내는 래퍼 유형입니다.
    필터링에 사용하는 attributes 맵을 포함합니다.

    요금

    요금은 모든 벡터 저장소의 총 스토리지 사용량을 기준으로 부과됩니다. 사용량은 파일을 파싱해 얻은 청크와 해당 임베딩의 크기로 계산합니다.

    스토리지 사용량 요금
    1GB까지(전체 저장소 합계) 무료
    1GB 초과분 GB당 하루 0.10달러

    비용을 최소화하는 방법은 만료 정책을 참고하세요.

    벡터 저장소 작업

    벡터 저장소 생성
    client.vector_stores.create(
        name="Support FAQ",
        file_ids=["file_123"]
    )

    벡터 저장소 파일 작업

    create로 vector_store.file을 생성하는 작업 등 일부 작업은 비동기로 실행되므로 완료까지 시간이 걸릴 수 있습니다. create_and_poll과 같은 헬퍼 함수를 사용하면 작업이 완료될 때까지 기다릴 수 있습니다. 또는 작업 상태를 직접 확인할 수 있습니다. 벡터 저장소에서 파일을 삭제하면 변경 사항이 시차를 두고 반영되므로 삭제한 파일의 내용이 잠시 검색 결과에 나타날 수 있습니다.

    파일 추가 요청 한도는 벡터 저장소 ID별로 적용됩니다. /vector_stores/{vector_store_id}/files와 /vector_stores/{vector_store_id}/file_batches에 보내는 요청은 두 엔드포인트를 합해 벡터 저장소당 분당 300건까지 허용됩니다.

    벡터 저장소 파일 생성
    client.vector_stores.files.create_and_poll(
        vector_store_id="vs_123",
        file_id="file_123"
    )

    배치 작업

    배치 생성
    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",
                    "static": {
                        "max_chunk_size_tokens": 1200,
                        "chunk_overlap_tokens": 200
                    }
                }
            }
        ]
    )

    배치를 생성할 때는 file_ids를 전달하고 필요에 따라 attributes와 chunking_strategy 중 하나 또는 둘 다 지정할 수 있습니다. 또는 files 배열에 파일별 객체를 담아 전달할 수 있습니다. 각 객체에는 file_id가 필수이며 attributes와 chunking_strategy는 선택 사항입니다. 두 방식은 함께 사용할 수 없습니다. 모든 파일에 같은 설정을 적용할지, 파일마다 설정을 다르게 지정할지에 따라 한 가지 방식을 선택하세요.

    하나의 벡터 저장소에 파일을 추가할 때 처리량을 높이려면 가능하면 배치를 생성하는 것이 좋습니다. 배치를 사용하면 한 번의 요청에 최대 500개의 파일을 포함할 수 있습니다. 일반적으로 파일마다 별도의 생성 요청을 보낼 때보다 리소스 경합이 줄고 전체 처리 시간이 짧아집니다.

    속성

    각 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
        }
    )

    만료 정책

    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개 이하여야 하며, 파일을 첨부할 때 자동으로 계산됩니다.

    청크 분할

    기본적으로 max_chunk_size_tokens는 800으로, chunk_overlap_tokens는 400으로 설정됩니다. 따라서 모든 파일은 800토큰 단위의 청크로 나뉘어 인덱싱되며, 연속된 두 청크 사이에는 400토큰이 겹칩니다.

    벡터 저장소에 파일을 추가할 때 chunking_strategy를 설정하면 청크 분할 방식을 조정할 수 있습니다. 다음 제한 사항이 적용됩니다.

    • max_chunk_size_tokens는 100 이상 4096 이하여야 합니다.
    • chunk_overlap_tokens는 0 이상이어야 하며 max_chunk_size_tokens / 2를 초과하지 않아야 합니다.

    검색 결과를 종합해 응답 작성하기

    쿼리를 실행한 뒤 검색 결과를 종합해 응답을 작성하려면 OpenAI 모델에 검색 결과와 원래 쿼리를 함께 전달하세요. 그러면 검색 결과에 근거한 응답을 받을 수 있습니다.

    검색 쿼리를 실행해 결과 가져오기
    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,
    )
    검색 결과를 종합해 응답 작성하기
    # Use results and user_query from the preceding search step.
    formatted_results = format_results(results.data)
    
    "\n".join("\n".join(c.text for c in result.content) for result in results.data)
    
    completion = client.chat.completions.create(
        model="gpt-6-astra",
        messages=[
            {
                "role": "developer",
                "content": "Produce a concise answer to the query based on the provided sources.",
            },
            {
                "role": "user",
                "content": f"Sources: {formatted_results}\n\nQuery: '{user_query}'",
            },
        ],
    )
    
    print(completion.choices[0].message.content)
    "Our return policy allows returns within 30 days of purchase."

    위 예시에서 사용한 format_results 함수는 다음과 같이 구현할 수 있습니다.

    검색 결과의 형식을 지정하는 함수 예시
    def format_results(results):
        formatted_results = ""
        for result in results.data:
            formatted_result = (
                f"<result file_id='{result.file_id}' file_name='{result.filename}'>"
            )
            for part in result.content:
                formatted_result += f"<content>{part.text}</content>"
            formatted_results += formatted_result + "</result>"
        return f"<sources>{formatted_results}</sources>"