OpenSearch 벡터 스토어

OpenSearch 벡터 스토어 (OpenSearch Vector Store)

이 섹션에서는 OpenSearchVectorStore를 설정해서 문서 임베딩을 저장하고 유사도 검색을 수행하는 방법을 안내해요. OpenSearch는 Elasticsearch에서 포크된 오픈소스 검색·분석 엔진으로, 벡터·어휘·하이브리드 검색을 지원해요. OpenSearch k-NN 기능으로 대규모 데이터셋의 벡터 임베딩을 질의할 수 있어요.

출처: 문서

본문

OpenSearch

이 섹션은 OpenSearchVectorStore를 설정해 문서 임베딩을 저장하고 유사도 검색을 수행하는 방법을 안내해요.

OpenSearch는 원래 Elasticsearch에서 포크된 오픈소스 검색·분석 엔진으로, Apache License 2.0으로 배포돼요. AI 생성 자산의 통합과 관리를 단순화해 AI 애플리케이션 개발을 강화해요. OpenSearch는 벡터 데이터베이스 페이지에 자세히 설명된 대로 고급 벡터 데이터베이스 기능을 활용해 저지연 쿼리와 유사도 검색을 용이하게 하는 벡터·어휘·하이브리드 검색 기능을 지원해요.

OpenSearch k-NN 기능을 사용하면 대규모 데이터셋의 벡터 임베딩을 질의할 수 있어요. 임베딩은 텍스트, 이미지, 오디오, 문서 같은 데이터 객체의 수치 표현이에요. 임베딩은 인덱스에 저장할 수 있고 다양한 유사도 함수를 사용해 질의할 수 있어요.

사전 준비 (Prerequisites)

자동 설정 (Auto-configuration)

참고: Spring AI auto-configuration과 starter 모듈의 아티팩트 이름에 큰 변화가 있었어요. 자세한 내용은 upgrade notes를 참고해 주세요.

Spring AI는 OpenSearch Vector Store에 대한 Spring Boot 자동 설정을 제공해요. 활성화하려면 프로젝트의 Maven pom.xml 파일에 다음 의존성을 추가하세요:

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-vector-store-opensearch</artifactId>
</dependency>

또는 Gradle build.gradle 빌드 파일에:

dependencies {
    implementation 'org.springframework.ai:spring-ai-starter-vector-store-opensearch'
}

참고: self-hosted와 Amazon OpenSearch Service 모두 같은 의존성을 사용해요. 빌드 파일에 Spring AI BOM을 추가하려면 Dependency Management 섹션을 참고해 주세요.

기본값과 구성 옵션을 알려면 벡터 스토어의 구성 파라미터 목록을 살펴보세요. 추가로 구성된 EmbeddingModel 빈이 필요해요. 자세한 내용은 EmbeddingModel 섹션을 참고하세요.

이제 애플리케이션에서 OpenSearchVectorStore를 벡터 스토어로 오토와이어할 수 있어요:

@Autowired VectorStore vectorStore;

// ...

List<Document> documents = List.of(
    new Document("Spring AI rocks!! Spring AI rocks!! Spring AI rocks!!", Map.of("meta1", "meta1")),
    new Document("The World is Big and Salvation Lurks Around the Corner"),
    new Document("You walk forward facing the past and you turn back toward the future.", Map.of("meta2", "meta2")));

// Add the documents to OpenSearch
vectorStore.add(documents);

// Retrieve documents similar to a query
List<Document> results = vectorStore.similaritySearch(SearchRequest.builder().query("Spring").topK(5).build());

구성 프로퍼티 (Configuration Properties)

OpenSearch에 연결하고 OpenSearchVectorStore를 사용하려면 인스턴스의 접근 세부 정보를 제공해야 해요. 간단한 구성은 Spring Boot의 application.yml로 제공할 수 있어요:

spring:
  ai:
    vectorstore:
      opensearch:
        uris: <opensearch instance URIs>
        username: <opensearch username>
        password: <opensearch password>
        index-name: spring-ai-document-index
        initialize-schema: true
        similarity-function: cosinesimil
        read-timeout: <time to wait for response>
        connect-timeout: <time to wait until connection established>
        path-prefix: <custom path prefix>
        ssl-bundle: <name of SSL bundle>
        aws:  # Only for Amazon OpenSearch Service
          host: <aws opensearch host>
          service-name: <aws service name>
          access-key: <aws access key>
          secret-key: <aws secret key>
          region: <aws region>

spring.ai.vectorstore.opensearch.*로 시작하는 프로퍼티는 OpenSearchVectorStore를 구성하는 데 사용돼요:

Property Description Default Value
spring.ai.vectorstore.opensearch.uris OpenSearch 클러스터 엔드포인트의 URI -
spring.ai.vectorstore.opensearch.username OpenSearch 클러스터 접근용 사용자 이름 -
spring.ai.vectorstore.opensearch.password 지정된 사용자 이름의 비밀번호 -
spring.ai.vectorstore.opensearch.index-name 벡터를 저장할 인덱스의 이름 spring-ai-document-index
spring.ai.vectorstore.opensearch.initialize-schema 필요한 스키마를 초기화할지 여부 false
spring.ai.vectorstore.opensearch.similarity-function 사용할 유사도 함수 (cosinesimil, l1, l2, linf, innerproduct) cosinesimil
spring.ai.vectorstore.opensearch.use-approximate-knn 더 빠른 검색을 위해 근사 k-NN 사용 여부. true면 HNSW 기반 근사 검색, false면 정확한 brute-force k-NN 사용. Approximate k-NN과 Exact k-NN 참고 false
spring.ai.vectorstore.opensearch.dimensions 벡터 임베딩의 차원 수. 근사 k-NN용 인덱스 매핑 생성 시 사용. 설정하지 않으면 임베딩 모델의 차원 사용. 1536
spring.ai.vectorstore.opensearch.mapping-json 인덱스용 커스텀 JSON 매핑. 기본 매핑 생성을 재정의. -
spring.ai.vectorstore.opensearch.read-timeout 상대 엔드포인트의 응답을 기다리는 시간. 0 - 무한. -
spring.ai.vectorstore.opensearch.connect-timeout 연결이 설정될 때까지 기다리는 시간. 0 - 무한. -
spring.ai.vectorstore.opensearch.path-prefix OpenSearch API 엔드포인트용 경로 프리픽스. OpenSearch가 비-루트 경로의 리버스 프록시 뒤에 있을 때 유용. -
spring.ai.vectorstore.opensearch.ssl-bundle SSL 연결 시 사용할 SSL Bundle 이름 -
spring.ai.vectorstore.opensearch.aws.host OpenSearch 인스턴스의 호스트 이름 -
spring.ai.vectorstore.opensearch.aws.service-name AWS 서비스 이름 -
spring.ai.vectorstore.opensearch.aws.access-key AWS 접근 키 -
spring.ai.vectorstore.opensearch.aws.secret-key AWS 비밀 키 -
spring.ai.vectorstore.opensearch.aws.region AWS 지역 -

참고: spring.ai.vectorstore.opensearch.aws.enabled 프로퍼티로 AWS 특화 OpenSearch 자동 설정의 활성화 여부를 제어할 수 있어요.

  • 이 프로퍼티를 false로 설정하면 AWS SDK 클래스가 클래스패스에 있어도 비-AWS OpenSearch 구성이 활성화돼요. 이렇게 하면 AWS SDK가 다른 서비스에 대해 존재하는 환경에서도 self-managed나 타사 OpenSearch 클러스터를 사용할 수 있어요.
  • AWS SDK 클래스가 없으면 비-AWS 구성이 항상 사용돼요.
  • AWS SDK 클래스가 있고 프로퍼티가 설정되지 않았거나 true이면 기본적으로 AWS 특화 구성이 사용돼요.

이 폴백 로직은 사용자가 OpenSearch 통합 유형을 명시적으로 제어할 수 있게 해서, 원치 않을 때 AWS 특화 로직이 실수로 활성화되는 것을 방지해요.

참고: path-prefix 프로퍼티는 OpenSearch가 비-루트 경로를 사용하는 리버스 프록시 뒤에서 실행될 때 커스텀 경로 프리픽스를 지정할 수 있게 해 줘요. 예를 들어 OpenSearch 인스턴스가 example.com/이 아닌 example.com/opensearch/에서 접근 가능하다면 path-prefix: /opensearch로 설정해요.

다음 유사도 함수를 사용할 수 있어요:

  • cosinesimil - 기본값, 대부분의 사용 사례에 적합. 벡터 간 코사인 유사도를 측정.
  • l1 - 벡터 간 맨해튼 거리.
  • l2 - 벡터 간 유클리드 거리.
  • linf - 벡터 간 체비쇼프 거리.

수동 구성 (Manual Configuration)

Spring Boot 자동 설정 대신 OpenSearch 벡터 스토어를 수동 구성할 수 있어요. 이를 위해 프로젝트에 spring-ai-opensearch-store를 추가해야 해요:

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-opensearch-store</artifactId>
</dependency>

또는 Gradle build.gradle 빌드 파일에:

dependencies {
    implementation 'org.springframework.ai:spring-ai-opensearch-store'
}

참고: 빌드 파일에 Spring AI BOM을 추가하려면 Dependency Management 섹션을 참고해 주세요.

OpenSearch 클라이언트 빈을 만드세요:

@Bean
public OpenSearchClient openSearchClient() {
    RestClient restClient = RestClient.builder(
        HttpHost.create("http://localhost:9200"))
        .build();

    return new OpenSearchClient(new RestClientTransport(
        restClient, new JacksonJsonpMapper()));
}

그런 다음 빌더 패턴으로 OpenSearchVectorStore 빈을 만드세요:

@Bean
public VectorStore vectorStore(OpenSearchClient openSearchClient, EmbeddingModel embeddingModel) {
    return OpenSearchVectorStore.builder(openSearchClient, embeddingModel)
        .index("custom-index")                // Optional: defaults to "spring-ai-document-index"
        .similarityFunction("l2")             // Optional: defaults to "cosinesimil"
        .useApproximateKnn(true)              // Optional: defaults to false (exact k-NN)
        .dimensions(1536)                     // Optional: defaults to 1536 or embedding model's dimensions
        .initializeSchema(true)               // Optional: defaults to false
        .batchingStrategy(new TokenCountBatchingStrategy()) // Optional: defaults to TokenCountBatchingStrategy
        .build();
}

// This can be any EmbeddingModel implementation
@Bean
public EmbeddingModel embeddingModel() {
    return new OpenAiEmbeddingModel(OpenAiEmbeddingOptions.builder().apiKey(System.getenv("OPENAI_API_KEY")).build());
}

메타데이터 필터링 (Metadata Filtering)

OpenSearch에서도 일반적이고 휴대 가능한 메타데이터 필터를 활용할 수 있어요.

예를 들어 텍스트 표현 언어를 사용할 수 있어요:

vectorStore.similaritySearch(
    SearchRequest.builder()
        .query("The World")
        .topK(TOP_K)
        .similarityThreshold(SIMILARITY_THRESHOLD)
        .filterExpression("author in ['john', 'jill'] && 'article_type' == 'blog'").build());

또는 Filter.Expression DSL로 프로그래밍 방식으로:

FilterExpressionBuilder b = new FilterExpressionBuilder();

vectorStore.similaritySearch(SearchRequest.builder()
    .query("The World")
    .topK(TOP_K)
    .similarityThreshold(SIMILARITY_THRESHOLD)
    .filterExpression(b.and(
        b.in("author", "john", "jill"),
        b.eq("article_type", "blog")).build()).build());

참고: 이 (휴대 가능한) 필터 표현식은 자동으로 고유한 OpenSearch Query string query로 변환돼요.

예를 들어 이 휴대 가능한 필터 표현식:

author in ['john', 'jill'] && 'article_type' == 'blog'

은 고유한 OpenSearch 필터 형식으로 변환돼요:

(metadata.author:john OR jill) AND metadata.article_type:blog

네이티브 클라이언트 접근 (Accessing the Native Client)

OpenSearch Vector Store 구현은 getNativeClient() 메서드를 통해 내부 네이티브 OpenSearch 클라이언트(OpenSearchClient)에 접근을 제공해요:

OpenSearchVectorStore vectorStore = context.getBean(OpenSearchVectorStore.class);
Optional<OpenSearchClient> nativeClient = vectorStore.getNativeClient();

if (nativeClient.isPresent()) {
    OpenSearchClient client = nativeClient.get();
    // Use the native client for OpenSearch-specific operations
}

네이티브 클라이언트는 VectorStore 인터페이스로는 노출되지 않는 OpenSearch 특화 기능과 작업에 접근할 수 있게 해 줘요.

더 알아보기 (Learn more)