Elasticsearch 벡터 스토어
Elasticsearch 벡터 스토어 (Elasticsearch Vector Store)
이 섹션에서는 Elasticsearch VectorStore를 설정해서 문서 임베딩을 저장하고 유사도 검색을 수행하는 방법을 안내해요. Elasticsearch는 Apache Lucene 라이브러리 기반의 오픈소스 검색·분석 엔진으로, 벡터 스토어로 활용할 수 있어요.
출처: 문서
본문
Elasticsearch
이 섹션은 Elasticsearch VectorStore를 설정해 문서 임베딩을 저장하고 유사도 검색을 수행하는 방법을 안내해요.
Elasticsearch는 Apache Lucene 라이브러리 기반의 오픈소스 검색·분석 엔진이에요.
사전 준비 (Prerequisites)
실행 중인 Elasticsearch 인스턴스. 다음 옵션이 가능해요:
자동 설정 (Auto-configuration)
참고: Spring AI auto-configuration과 starter 모듈의 아티팩트 이름에 큰 변화가 있었어요. 자세한 내용은 upgrade notes를 참고해 주세요.
Spring AI는 Elasticsearch Vector Store에 대한 Spring Boot 자동 설정을 제공해요. 활성화하려면 프로젝트의 Maven pom.xml 또는 Gradle build.gradle 빌드 파일에 다음 의존성을 추가하세요:
- Maven
- Gradle
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-elasticsearch</artifactId>
</dependency>
dependencies {
implementation 'org.springframework.ai:spring-ai-starter-vector-store-elasticsearch'
}
참고: spring-boot 3.3.0 이전 버전에서는 버전 8.13.3 이상의 elasticsearch-java 의존성을 명시적으로 추가해야 해요. 그렇지 않으면 사용되는 이전 버전이 수행되는 쿼리와 호환되지 않아요:
- Maven
- Gradle
<dependency> <groupId>co.elastic.clients</groupId> <artifactId>elasticsearch-java</artifactId> <version>8.13.3</version> </dependency>
dependencies { implementation 'co.elastic.clients:elasticsearch-java:8.13.3' }
참고: 빌드 파일에 Spring AI BOM을 추가하려면 Dependency Management 섹션을 참고해 주세요.
참고: 빌드 파일에 Maven Central 및/또는 Snapshot 저장소를 추가하려면 Artifact Repositories 섹션을 참고해 주세요.
벡터 스토어 구현은 필요한 스키마를 초기화할 수 있지만, 적절한 생성자에서 initializeSchema boolean을 지정하거나 application.properties 파일에서 …initialize-schema=true를 설정해 선택해야 해요. 또는 초기화를 해제하고 Elasticsearch 클라이언트로 인덱스를 수동으로 만들 수도 있어요. 이는 인덱스에 고급 매핑이나 추가 구성이 필요한 경우 유용해요.
참고: 이것은 호환성을 깨는 변경이에요! 이전 버전의 Spring AI에서는 이 스키마 초기화가 기본으로 일어났어요.
기본값과 구성 옵션을 알려면 벡터 스토어의 구성 파라미터 목록을 살펴보세요. 이 프로퍼티들은 ElasticsearchVectorStoreOptions 빈을 구성해서도 설정할 수 있어요. 추가로 구성된 EmbeddingModel 빈이 필요해요. 자세한 내용은 EmbeddingModel 섹션을 참고하세요.
이제 애플리케이션에서 ElasticsearchVectorStore를 벡터 스토어로 오토와이어할 수 있어요.
@Autowired VectorStore vectorStore;
// ...
List <Document> documents = List.of(
new Document("Spring AI rocks!! Spring AI rocks!! 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 Elasticsearch
vectorStore.add(documents);
// Retrieve documents similar to a query
List<Document> results = this.vectorStore.similaritySearch(SearchRequest.builder().query("Spring").topK(5).build());
구성 프로퍼티 (Configuration Properties)
Elasticsearch에 연결하고 ElasticsearchVectorStore를 사용하려면 인스턴스의 접근 세부 정보를 제공해야 해요. 간단한 구성은 Spring Boot의 application.yml로 제공할 수 있어요.
spring:
elasticsearch:
uris: <elasticsearch instance URIs>
username: <elasticsearch username>
password: <elasticsearch password>
ai:
vectorstore:
elasticsearch:
initialize-schema: true
index-name: custom-index
dimensions: 1536
similarity: cosine
spring.elasticsearch.*로 시작하는 Spring Boot 프로퍼티는 Elasticsearch 클라이언트를 구성하는 데 사용돼요:
| Property | Description | Default Value |
|---|---|---|
spring.elasticsearch.connection-timeout |
Elasticsearch와 통신할 때 사용하는 연결 타임아웃. | 1s |
spring.elasticsearch.password |
Elasticsearch 인증용 비밀번호. | - |
spring.elasticsearch.username |
Elasticsearch 인증용 사용자 이름. | - |
spring.elasticsearch.uris |
사용할 Elasticsearch 인스턴스의 쉼표로 구분된 목록. | http://localhost:9200 |
spring.elasticsearch.path-prefix |
Elasticsearch로 보내는 모든 요청의 경로에 추가되는 프리픽스. | - |
spring.elasticsearch.restclient.sniffer.delay-after-failure |
실패 후 예약된 sniff 실행의 지연. | 1m |
spring.elasticsearch.restclient.sniffer.interval |
연속적인 일반 sniff 실행 사이의 간격. | 5m |
spring.elasticsearch.restclient.ssl.bundle |
SSL bundle 이름. | - |
spring.elasticsearch.socket-keep-alive |
클라이언트와 Elasticsearch 사이의 소켓 keep alive 활성화 여부. | false |
spring.elasticsearch.socket-timeout |
Elasticsearch와 통신할 때 사용하는 소켓 타임아웃. | 30s |
spring.ai.vectorstore.elasticsearch.*로 시작하는 프로퍼티는 ElasticsearchVectorStore를 구성하는 데 사용돼요:
| Property | Description | Default Value |
|---|---|---|
spring.ai.vectorstore.elasticsearch.initialize-schema |
필요한 스키마를 초기화할지 여부 | false |
spring.ai.vectorstore.elasticsearch.index-name |
벡터를 저장할 인덱스의 이름 | spring-ai-document-index |
spring.ai.vectorstore.elasticsearch.dimensions |
벡터의 차원 수 | 1536 |
spring.ai.vectorstore.elasticsearch.similarity |
사용할 유사도 함수 | cosine |
spring.ai.vectorstore.elasticsearch.embedding-field-name |
검색할 벡터 필드의 이름 | embedding |
다음 유사도 함수를 사용할 수 있어요:
cosine- 기본값, 대부분의 사용 사례에 적합. 벡터 간 코사인 유사도를 측정.l2_norm- 벡터 간 유클리드 거리. 값이 낮을수록 유사도가 높음.dot_product- 정규화된 벡터(예: OpenAI 임베딩)에 가장 좋은 성능.
각각에 대한 자세한 내용은 dense vector에 대한 Elasticsearch Documentation을 참고하세요.
메타데이터 필터링 (Metadata Filtering)
Elasticsearch에서도 일반적이고 휴대 가능한 메타데이터 필터를 활용할 수 있어요.
예를 들어 텍스트 표현 언어를 사용할 수 있어요:
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());
참고: 이 (휴대 가능한) 필터 표현식은 자동으로 고유한 Elasticsearch Query string query로 변환돼요.
예를 들어 이 휴대 가능한 필터 표현식:
author in ['john', 'jill'] && 'article_type' == 'blog'
은 고유한 Elasticsearch 필터 형식으로 변환돼요:
(metadata.author:john OR jill) AND metadata.article_type:blog
수동 구성 (Manual Configuration)
Spring Boot 자동 설정 대신 Elasticsearch 벡터 스토어를 수동 구성할 수 있어요. 이를 위해 프로젝트에 spring-ai-elasticsearch-store를 추가해야 해요:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-elasticsearch-store</artifactId>
</dependency>
또는 Gradle build.gradle 빌드 파일에:
dependencies {
implementation 'org.springframework.ai:spring-ai-elasticsearch-store'
}
Elasticsearch Rest5Client 빈을 만드세요. 커스텀 Rest5Client 구성에 대한 더 깊이 있는 정보는 Elasticsearch Documentation을 읽어보세요.
@Bean
public Rest5Client restClient() {
return Rest5Client.builder(new HttpHost("<host>", 9200, "http"))
.setDefaultHeaders(new Header[]{
new BasicHeader("Authorization", "Basic <encoded username and password>")
})
.build();
}
그런 다음 빌더 패턴으로 ElasticsearchVectorStore 빈을 만드세요:
@Bean
public VectorStore vectorStore(Rest5Client restClient, EmbeddingModel embeddingModel) {
ElasticsearchVectorStoreOptions options = new ElasticsearchVectorStoreOptions();
options.setIndexName("custom-index"); // Optional: defaults to "spring-ai-document-index"
options.setSimilarity(COSINE); // Optional: defaults to COSINE
options.setDimensions(1536); // Optional: defaults to model dimensions or 1536
return ElasticsearchVectorStore.builder(restClient, embeddingModel)
.options(options) // Optional: use custom options
.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());
}
네이티브 클라이언트 접근 (Accessing the Native Client)
Elasticsearch Vector Store 구현은 getNativeClient() 메서드를 통해 내부 네이티브 Elasticsearch 클라이언트(ElasticsearchClient)에 접근을 제공해요:
ElasticsearchVectorStore vectorStore = context.getBean(ElasticsearchVectorStore.class);
Optional<ElasticsearchClient> nativeClient = vectorStore.getNativeClient();
if (nativeClient.isPresent()) {
ElasticsearchClient client = nativeClient.get();
// Use the native client for Elasticsearch-specific operations
}
네이티브 클라이언트는 VectorStore 인터페이스로는 노출되지 않는 Elasticsearch 특화 기능과 작업에 접근할 수 있게 해 줘요.