자습서: Azure AI 검색에서 RAG에 대한 인덱스 설계
인덱스에는 검색 가능한 텍스트, 벡터 콘텐츠, 그리고 구성이 포함됩니다. 응답에 채팅 모델을 사용하는 RAG 패턴에서는 쿼리 시 LLM에 전달할 수 있는 콘텐츠 청크를 중심으로 디자인된 인덱스를 원합니다.
이 자습서에서는 다음을 수행합니다.
- RAG용으로 구축된 인덱스 스키마의 특성 알아보기
- 벡터 및 하이브리드 쿼리를 수용하는 인덱스 만들기
- 벡터 프로필 및 구성 추가
- 구조화된 데이터 추가
- 필터링 추가
필수 조건
Python 확장 및 Jupyter 패키지가 있는 Visual Studio Code. 자세한 내용은 Visual Studio Code의 Python을 참조하세요.
이 연습의 출력은 JSON의 인덱스 정의입니다. 이 시점에서는 Azure AI 검색에 업로드하지 않으므로 이 연습에는 클라우드 서비스나 권한에 대한 요구 사항이 없습니다.
RAG에 대한 스키마 고려 사항 검토
대화형 검색에서 LLM은 검색 엔진이 아닌 사용자가 보는 응답을 작성하므로 검색 결과에 표시할 필드와 개별 검색 문서의 표현 방식이 사용자에게 일관된지 여부를 생각할 필요가 없습니다. 질문에 따라 LLM은 인덱스에서 원본을 그대로 가져온 콘텐츠를 반환할 수도 있고 더 나은 답변을 제공하기 위해 콘텐츠를 다시 패키징할 수도 있습니다.
청크 중심 구성
LLM이 응답을 생성할 때는 메시지 입력을 위해 콘텐츠 청크를 기반으로 작동하며, 인용을 위해 청크의 출처를 알아야 하지만 가장 중요한 것은 메시지 입력의 품질과 사용자 질문과의 관련성입니다. 청크가 하나의 문서에서 오든 천 개의 문서에서 오든 관계없이 LLM은 정보 또는 그라운딩 데이터를 수집하고 시스템 프롬프트에 제공된 지침을 사용하여 응답을 작성합니다.
스키마는 청크에 초점을 두며 각 청크는 RAG 패턴에서 검색 문서를 정의하는 요소입니다. 인덱스를 이름, 설명, 범주, 주소에 대한 균일한 콘텐츠가 포함된 필드와 같이 더 많은 구조를 가진 기존 검색 문서와는 다른 대규모 청크 모음이라고 생각할 수 있습니다.
생성된 데이터로 향상됨
이 자습서에서 샘플 데이터는 NASA Earth Book의 PDF와 콘텐츠로 구성됩니다. 이 콘텐츠는 전 세계의 지리, 국가, 지역에 대한 수많은 참조가 포함된 설명적이고 많은 정보가 담긴 콘텐츠입니다. 모든 텍스트 콘텐츠는 청크로 캡처되지만 위치 이름의 되풀이 인스턴스는 인덱스에 구조를 추가할 수 있는 기회를 만듭니다. 기술을 사용하면 텍스트의 엔터티를 인식하고 쿼리 및 필터에 사용할 인덱스에 캡처할 수 있습니다. 이 자습서에서는 위치 엔터티를 인식하고 추출하여 검색 가능하고 필터링 가능한 locations
필드에 로드하는 엔터티 인식 기술을 포함합니다. 인덱스에 구조화된 콘텐츠를 추가하면 필터링 옵션이 더 많아지고 관련성이 개선되며 더 집중된 답변을 얻을 수 있습니다.
하나 또는 두 개 인덱스의 부모-자식 필드?
청크화된 콘텐츠는 일반적으로 더 큰 문서에서 파생됩니다. 또한 스키마는 청크를 중심으로 구성되지만 여러분은 부모 수준에서 속성과 콘텐츠를 캡처하려고 합니다. 이러한 속성의 예로는 부모 파일 경로, 제목, 작성자, 게시 날짜 또는 요약이 포함될 수 있습니다.
스키마 설계의 변곡점은 부모 및 자식/청크화된 콘텐츠에 대해 두 개의 인덱스를 포함할지, 아니면 각 청크에 대해 부모 요소를 반복하는 단일 인덱스를 포함할지 여부입니다.
이 자습서에서는 모든 텍스트 청크가 한 부모(NASA Earth Book)에서 오기 때문에 부모 필드 수준을 높이는 용도인 별도의 인덱스가 필요하지 않습니다. 그러나 여러 부모 PDF에서 인덱싱하는 경우 부모-자식 인덱스 쌍이 수준별 필드를 캡처한 다음 조회 쿼리를 부모 인덱스로 보내 각 청크와 관련된 필드를 검색할 수 있습니다.
스키마 고려 사항 체크리스트
Azure AI 검색에서 RAG 워크로드에 가장 적합한 인덱스에는 다음과 같은 특성이 있습니다.
쿼리와 관련이 있고 LLM에서 읽을 수 있는 청크를 반환합니다. LLM은 마크업, 중복도, 불완전한 문자열과 같은 특정 수준의 더티 데이터를 청크 단위로 처리할 수 있습니다. 청크는 읽을 수 있고 질문과 관련이 있어야 하지만 본래 그대로일 필요는 없습니다.
문서 청크와 파일 이름, 파일 형식, 제목, 작성자 등의 부모 문서 속성 간에 부모-자식 관계를 유지합니다. 쿼리에 응답하기 위해 인덱스의 어디에서나 청크를 끌어올 수 있습니다. 청크를 제공하는 부모 문서와의 연결은 컨텍스트, 인용 및 후속 쿼리에 유용합니다.
만들려는 쿼리를 수용합니다. 벡터 및 하이브리드 콘텐츠에 대한 필드가 있어야 하며, 이러한 필드는 검색 가능 또는 필터링 가능과 같은 특정 쿼리 동작을 지원하기 위해 특성이 지정되어야 합니다. 한 번에 하나의 인덱스만 쿼리할 수 있으므로(조인 없이) 필드 모음은 검색 가능한 모든 콘텐츠를 정의해야 합니다.
스키마는 플랫(복합 형식 또는 구조가 없음)이거나 LLM으로 보내기 전에 복소수 형식 출력의 형식을 JSON으로 지정해야 합니다. 이 요구 사항은 Azure AI 검색의 RAG 패턴과 관련이 있습니다.
참고 항목
스키마 디자인은 스토리지 및 비용에 영향을 줍니다. 이 연습은 스키마 기본 사항에 중점을 줍니다. 스토리지 및 비용 최소화 자습서에서는 스키마를 다시 검토하여 좁은 데이터 형식, 압축 및 스토리지 옵션이 벡터에서 사용하는 스토리지 양을 크게 줄이는 방법을 알아봅니다.
RAG 워크로드에 대한 인덱스 만들기
LLM에 대한 최소 인덱스는 콘텐츠 청크를 저장하도록 설계되었습니다. 일반적으로 관련성이 높은 결과에 대한 유사성 검색을 원하는 경우 벡터 필드가 포함됩니다. 또한 대화형 검색을 위해 LLM에 대한 사람이 읽을 수 있는 입력을 위한 비벡터 필드도 포함됩니다. 검색 결과의 비벡터 청크화된 콘텐츠는 LLM으로 전송되는 그라운딩 데이터가 됩니다.
Visual Studio Code를 열고 새 파일을 만듭니다. 이 연습에서는 Python 파일 형식일 필요는 없습니다.
다음은 벡터 및 하이브리드 검색을 지원하는 RAG 솔루션에 대한 최소 인덱스 정의입니다. 내용을 검토하여 인덱스 이름, 필드 및 벡터 필드의 구성 섹션과 같은 필수 요소에 대해 알아보세요.
{ "name": "example-minimal-index", "fields": [ { "name": "id", "type": "Edm.String", "key": true }, { "name": "chunked_content", "type": "Edm.String", "searchable": true, "retrievable": true }, { "name": "chunked_content_vectorized", "type": "Edm.Single", "dimensions": 1536, "vectorSearchProfile": "my-vector-profile", "searchable": true, "retrievable": false, "stored": false }, { "name": "metadata", "type": "Edm.String", "retrievable": true, "searchable": true, "filterable": true } ], "vectorSearch": { "algorithms": [ { "name": "my-algo-config", "kind": "hnsw", "hnswParameters": { } } ], "profiles": [ { "name": "my-vector-profile", "algorithm": "my-algo-config" } ] } }
필드에는 키 필드(이 예제의
"id"
)가 포함되어야 하며, 유사성 검색을 위한 벡터 청크 및 LLM에 대한 입력을 위한 비벡터 청크가 포함되어야 합니다.벡터 필드는 쿼리 시 검색 경로를 결정하는 알고리즘과 연결됩니다. 인덱스에는 여러 알고리즘 구성을 지정하기 위한 vectorSearch 섹션이 있습니다. 또한 벡터 필드에는 특정 형식과 모델 차원을 포함하기 위한 추가 특성이 있습니다.
Edm.Single
은 일반적으로 사용되는 LLM에서 작동하는 데이터 형식입니다. 벡터 필드에 대한 자세한 내용은 벡터 인덱스 만들기를 참조하세요.메타데이터 필드는 부모 파일 경로, 생성 날짜 또는 콘텐츠 형식일 수 있으며 필터에 유용합니다.
자습서 소스 코드 및 Earth Book 콘텐츠에 대한 인덱스 스키마는 다음과 같습니다.
기본 스키마와 마찬가지로 청크를 중심으로 구성됩니다.
chunk_id
는 각 청크를 고유하게 식별합니다.text_vector
필드는 청크의 포함입니다. 비벡터chunk
필드는 읽을 수 있는 문자열입니다.title
은 Blob의 고유한 메타데이터 스토리지 경로에 매핑됩니다.parent_id
는 유일한 부모 수준 필드이며 부모 파일 URI의 base64 인코딩 버전입니다.이 자습서 시리즈에
dimensions
사용된 것과 같은 통합 벡터화 워크로드에서 벡터 필드의dimensions
속성은 데이터를 벡터화하는 데 사용되는 포함 기술에 의해 생성된 수와 동일해야 합니다. 이 시리즈에서는 Azure OpenAI에서 text-embedding-3-large 모델을 호출하는 Azure OpenAI 포함 기술을 사용합니다. 기술은 다음 자습서에서 지정됩니다. 벡터 필드와 기술 정의 모두에서 차원을 1024로 설정합니다.스키마에는 인덱싱 파이프라인에서 만들어진 생성된 콘텐츠를 저장하기 위한
locations
필드도 포함되어 있습니다.from azure.identity import DefaultAzureCredential from azure.identity import get_bearer_token_provider from azure.search.documents.indexes import SearchIndexClient from azure.search.documents.indexes.models import ( SearchField, SearchFieldDataType, VectorSearch, HnswAlgorithmConfiguration, VectorSearchProfile, AzureOpenAIVectorizer, AzureOpenAIVectorizerParameters, SearchIndex ) credential = DefaultAzureCredential() # Create a search index index_name = "py-rag-tutorial-idx" index_client = SearchIndexClient(endpoint=AZURE_SEARCH_SERVICE, credential=credential) fields = [ SearchField(name="parent_id", type=SearchFieldDataType.String), SearchField(name="title", type=SearchFieldDataType.String), SearchField(name="locations", type=SearchFieldDataType.Collection(SearchFieldDataType.String), filterable=True), SearchField(name="chunk_id", type=SearchFieldDataType.String, key=True, sortable=True, filterable=True, facetable=True, analyzer_name="keyword"), SearchField(name="chunk", type=SearchFieldDataType.String, sortable=False, filterable=False, facetable=False), SearchField(name="text_vector", type=SearchFieldDataType.Collection(SearchFieldDataType.Single), vector_search_dimensions=1024, vector_search_profile_name="myHnswProfile") ] # Configure the vector search configuration vector_search = VectorSearch( algorithms=[ HnswAlgorithmConfiguration(name="myHnsw"), ], profiles=[ VectorSearchProfile( name="myHnswProfile", algorithm_configuration_name="myHnsw", vectorizer_name="myOpenAI", ) ], vectorizers=[ AzureOpenAIVectorizer( vectorizer_name="myOpenAI", kind="azureOpenAI", parameters=AzureOpenAIVectorizerParameters( resource_url=AZURE_OPENAI_ACCOUNT, deployment_name="text-embedding-3-large", model_name="text-embedding-3-large" ), ), ], ) # Create the search index index = SearchIndex(name=index_name, fields=fields, vector_search=vector_search) result = index_client.create_or_update_index(index) print(f"{result.name} created")
구조화된 콘텐츠를 더욱 유사하게 모방하는 인덱스 스키마의 경우 부모 및 자식(청크) 필드에 대해 별도의 인덱스가 있습니다. 두 인덱스의 인덱싱을 동시에 조정하려면 인덱스 프로젝션이 필요합니다. 쿼리는 자식 인덱스에 대해 실행됩니다. 쿼리 논리에는 부모 인덱스에서 콘텐츠를 검색하는 parent_idt 사용하여 조회 쿼리가 포함됩니다.
자식 인덱스의 필드:
- ID
- chunk
- chunkVectcor
- parent_id
부모 인덱스의 필드("... 중 하나"를 원하는 모든 항목):
- parent_id
- 부모 수준 필드(이름, 제목, 범주)