Java 클라이언트 라이브러리와 함께 Azure Cosmos DB 벡터 검색을 사용합니다. 애플리케이션에서 벡터 데이터를 효율적으로 저장하고 쿼리합니다.
이 빠른 시작 가이드에서는 텍스트 임베딩-3-small 모델의 벡터와 함께 JSON 파일에 있는 샘플 호텔 데이터 세트를 사용합니다. 데이터 세트에는 호텔 이름, 위치, 설명 및 벡터 포함이 포함됩니다.
GitHub에서 리소스 프로비저닝을 사용하여 샘플 코드를 찾습니다.
필수 조건
Azure 구독
- Azure 구독이 없는 경우 체험 계정 만들기
기존 Azure Cosmos DB의 리소스 데이터 평면에 대한 접근
- 리소스가 없는 경우 새 리소스를 만듭니다.
- 클라이언트 IP 주소에 대한 액세스를 허용하도록 구성된 방화벽
- 할당된 RBAC(역할 기반 액세스 제어) 역할:
- Cosmos DB 기본 제공 데이터 기여자 (데이터 평면)
- 역할 ID:
00000000-0000-0000-0000-000000000002
-
- 구성된 사용자 지정 도메인
- 할당된 RBAC(역할 기반 액세스 제어) 역할:
- Cognitive Services OpenAI 사용자
- 역할 ID:
5e0bd9bd-7b93-4f28-af87-19fc36ad61bd
-
text-embedding-3-small배포된 모델
벡터를 사용하여 데이터 파일 만들기
호텔 데이터 파일에 대한 새 데이터 디렉터리를 만듭니다.
mkdir data벡터가 있는 raw 데이터 파일을
data디렉터리에 다운로드합니다.curl -o data/HotelsData_toCosmosDB_Vector.json https://raw.githubusercontent.com/Azure-Samples/cosmos-db-vector-samples/refs/heads/main/data/HotelsData_toCosmosDB_Vector.json
Java 프로젝트 만들기
데이터 디렉터리와 동일한 수준에서 프로젝트에 대한 새 형제 디렉터리를 만들고 Visual Studio Code에서 엽니다.
mkdir vector-search-quickstart cd vector-search-quickstart code .Maven 구성을 사용하여
pom.xml프로젝트 루트에 파일을 만듭니다.<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example.cosmos.samples</groupId> <artifactId>nosql-vector-search-sample</artifactId> <version>1.0.0</version> <name>Azure Cosmos DB NoSQL Vector Search - Java</name> <properties> <maven.compiler.release>21</maven.compiler.release> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!-- Azure Cosmos DB SDK (NoSQL API) --> <dependency> <groupId>com.azure</groupId> <artifactId>azure-cosmos</artifactId> <version>4.69.0</version> </dependency> <!-- Azure Identity (DefaultAzureCredential) --> <dependency> <groupId>com.azure</groupId> <artifactId>azure-identity</artifactId> <version>1.18.1</version> </dependency> <!-- Azure OpenAI (embeddings) --> <dependency> <groupId>com.azure</groupId> <artifactId>azure-ai-openai</artifactId> <version>1.0.0-beta.16</version> </dependency> <!-- Jackson (JSON serialization) --> <dependency> <groupId>tools.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>3.0.3</version> </dependency> <!-- Suppress noisy SDK logging --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-nop</artifactId> <version>2.0.17</version> <scope>runtime</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>3.5.0</version> <configuration> <mainClass>com.example.cosmos.vectorsearch.VectorSearch</mainClass> </configuration> </plugin> </plugins> </build> </project>키 종속성:
- azure-identity - 암호 없는(관리 ID) 연결에 대한 인증 라이브러리 Azure
- azure-cosmos - 데이터베이스 작업을 위한 Azure Cosmos DB 클라이언트 라이브러리
- azure-ai-openai - 임베딩 생성을 위한 Azure OpenAI SDK
- slf4j-nop - 런타임 시 시끄러운 SDK 로깅을 억제합니다.
원본 디렉터리 구조를 만듭니다.
mkdir -p src/main/java/com/example/cosmos/vectorsearch환경 변수에
.env대한 파일을 프로젝트 루트에 만듭니다.# Azure OpenAI Embedding Settings AZURE_OPENAI_EMBEDDING_MODEL=text-embedding-3-small AZURE_OPENAI_EMBEDDING_API_VERSION=2024-08-01-preview AZURE_OPENAI_EMBEDDING_ENDPOINT= # Cosmos DB configuration AZURE_COSMOSDB_ENDPOINT= # Data file DATA_FILE_WITH_VECTORS=../data/HotelsData_toCosmosDB_Vector.json FIELD_TO_EMBED=Description EMBEDDED_FIELD=DescriptionVector EMBEDDING_DIMENSIONS=1536 # Vector search algorithm: diskann or quantizedflat VECTOR_ALGORITHM=diskann.env파일의 자리 표시자 값을 사용자 정보로 바꿉니다.-
AZURE_OPENAI_EMBEDDING_ENDPOINT: Azure OpenAI 리소스 엔드포인트 URL 주소 -
AZURE_COSMOSDB_ENDPOINT: Azure Cosmos DB의 끝점 URL
비고
Java 샘플은
System.getenv()사용하여 환경 변수를 읽습니다. 셸 세션에서 이러한 변수를 내보내거나azd env get-values를 사용하여 설정해야 합니다. 이 파일은.env참조 템플릿으로 사용되며 애플리케이션에서 자동으로 로드되지 않습니다.-
문서 스키마 이해
애플리케이션을 빌드하기 전에 벡터가 Azure Cosmos DB 문서에 저장되는 방법을 이해합니다. 각 호텔 문서에는 다음이 포함됩니다.
-
표준 필드:
HotelId,HotelName,DescriptionCategory등 -
벡터 필드:
DescriptionVector- 호텔 설명의 의미 체계적 의미를 나타내는 1536 부동 소수점 숫자의 배열입니다.
호텔 문서 구조의 간소화된 예는 다음과 같습니다.
{
"HotelId": "1",
"HotelName": "Stay-Kay City Hotel",
"Description": "This classic hotel is fully-refurbished...",
"Rating": 3.6,
"DescriptionVector": [
-0.04886505,
-0.02030743,
0.01763356,
...
// 1536 dimensions total
]
}
포함 저장에 대한 핵심 사항은 다음과 같습니다.
- 벡터 배열은 문서에 표준 JSON 배열로 저장됩니다.
-
벡터 정책은 경로(
/DescriptionVector), 데이터 형식(), 차원(float321536) 및 거리 함수(코사인)를 정의합니다. - 인덱싱 정책은 효율적인 유사성 검색을 위해 벡터 필드에 벡터 인덱스 만들기
- 삽입 성능을 최적화하려면 표준 인덱싱에서 벡터 필드를 제외해야 합니다.
이러한 정책은 이 샘플 프로젝트의 거리 메트릭 에 대한 Bicep 템플릿에 정의됩니다. 벡터 정책 및 인덱싱에 대한 자세한 내용은 Azure Cosmos DB의 벡터 검색을 참조하세요.
벡터 검색을 위한 코드 파일 만들기
src/main/java/com/example/cosmos/vectorsearch 디렉터리에 두 개의 Java 원본 파일을 만듭니다.
touch src/main/java/com/example/cosmos/vectorsearch/VectorSearch.java
touch src/main/java/com/example/cosmos/vectorsearch/Utils.java
벡터 검색을 위한 코드 만들기
다음 코드를 파일에 붙여넣습니다 VectorSearch.java .
package com.example.cosmos.vectorsearch;
import com.azure.ai.openai.OpenAIClient;
import com.azure.ai.openai.models.EmbeddingItem;
import com.azure.ai.openai.models.EmbeddingsOptions;
import com.azure.cosmos.CosmosClient;
import com.azure.cosmos.CosmosContainer;
import com.azure.cosmos.models.CosmosQueryRequestOptions;
import com.azure.cosmos.models.SqlParameter;
import com.azure.cosmos.models.SqlQuerySpec;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Azure Cosmos DB NoSQL vector search sample — Java port of nosql-vector-search-typescript.
*
* Demonstrates:
* - Passwordless authentication with DefaultAzureCredential
* - Bulk insert of hotel data with pre-computed embeddings
* - Vector similarity search using VectorDistance() SQL function
* - DiskANN and QuantizedFlat algorithm selection via environment variable
*/
public final class VectorSearch {
private static final String SAMPLE_QUERY =
"quintessential lodging near running trails, eateries, retail";
private static final Set<String> VALID_ALGORITHMS = Set.of("diskann", "quantizedflat");
private static final Map<String, String> ALGORITHM_CONTAINERS = Map.of(
"diskann", "hotels_diskann",
"quantizedflat", "hotels_quantizedflat"
);
private static final Map<String, String> ALGORITHM_DISPLAY = Map.of(
"diskann", "DiskANN",
"quantizedflat", "QuantizedFlat"
);
public static void main(String[] args) {
try {
new VectorSearch().run();
} catch (Exception e) {
System.err.println("App failed: " + e.getMessage());
e.printStackTrace();
System.exit(1);
}
System.exit(0);
}
private void run() throws Exception {
// ── Configuration ───────────────────────────────────────────────
var algorithm = Utils.envOrDefault("VECTOR_ALGORITHM", "diskann").trim().toLowerCase();
var dbName = Utils.envOrDefault("AZURE_COSMOSDB_DATABASENAME", "Hotels");
var dataFile = Utils.requireEnv("DATA_FILE_WITH_VECTORS");
var embeddedField = Utils.requireEnv("EMBEDDED_FIELD");
var deployment = Utils.requireEnv("AZURE_OPENAI_EMBEDDING_MODEL");
var distanceFunction = Utils.envOrDefault("VECTOR_DISTANCE_FUNCTION", "cosine");
if (!VALID_ALGORITHMS.contains(algorithm)) {
throw new IllegalArgumentException(
"Invalid algorithm '" + algorithm + "'. Must be one of: " +
String.join(", ", VALID_ALGORITHMS));
}
var containerName = ALGORITHM_CONTAINERS.get(algorithm);
var algorithmDisplay = ALGORITHM_DISPLAY.get(algorithm);
// ── Clients ─────────────────────────────────────────────────────
OpenAIClient aiClient = Utils.createOpenAIClient();
CosmosClient dbClient = Utils.createCosmosClient();
try {
var database = dbClient.getDatabase(dbName);
System.out.println("Connected to database: " + dbName);
CosmosContainer container = database.getContainer(containerName);
System.out.println("Connected to container: " + containerName);
System.out.println("\n[Algorithm] Vector Search Algorithm: " + algorithmDisplay);
System.out.println("[Distance] Distance Function: " + distanceFunction);
// Verify container exists
container.read();
// ── Load & Insert Data ──────────────────────────────────────
var dataPath = Path.of(dataFile);
var data = Utils.readJsonFile(dataPath);
Utils.insertData(container, data);
// ── Generate Query Embedding ────────────────────────────────
var embeddingOptions = new EmbeddingsOptions(List.of(SAMPLE_QUERY));
var embeddingResult = aiClient.getEmbeddings(deployment, embeddingOptions);
List<Float> embedding = embeddingResult.getData().get(0).getEmbedding();
// Convert Float list to List<Double> for Cosmos DB parameter binding
var embeddingDoubles = new ArrayList<Double>(embedding.size());
for (var f : embedding) {
embeddingDoubles.add(f.doubleValue());
}
// ── Build & Execute Vector Search Query ─────────────────────
var safeField = Utils.validateFieldName(embeddedField);
var queryText = "SELECT TOP 5 c.HotelName, c.Description, c.Rating, " +
"VectorDistance(c." + safeField + ", @embedding) AS SimilarityScore " +
"FROM c " +
"ORDER BY VectorDistance(c." + safeField + ", @embedding)";
System.out.println("\n--- Executing Vector Search Query ---");
System.out.println("Query: " + queryText);
System.out.println("Parameters: @embedding (vector with " + embeddingDoubles.size() + " dimensions)");
System.out.println("--------------------------------------\n");
var sqlQuery = new SqlQuerySpec(
queryText,
List.of(new SqlParameter("@embedding", embeddingDoubles))
);
var queryOptions = new CosmosQueryRequestOptions();
@SuppressWarnings("unchecked")
var resultPages = container.queryItems(sqlQuery, queryOptions, Map.class);
var results = new ArrayList<Map<String, Object>>();
var requestCharge = 0.0;
for (var page : resultPages.iterableByPage()) {
requestCharge += page.getRequestCharge();
for (var item : page.getResults()) {
@SuppressWarnings("unchecked")
var typedItem = (Map<String, Object>) item;
results.add(typedItem);
}
}
Utils.printSearchResults(results, requestCharge);
} finally {
dbClient.close();
}
}
}
이 코드:
-
DiskANN환경 변수에서quantizedFlat벡터 알고리즘 또는VECTOR_ALGORITHM벡터 알고리즘 중 하나를 구성합니다. - 암호 없는 인증을 사용하여 Azure OpenAI 및 Azure Cosmos DB에 연결합니다.
- JSON 파일에서 미리 벡터화된 호텔 데이터를 로드합니다.
- 적절한 컨테이너에 데이터를 삽입합니다.
- 자연어 쿼리(
quintessential lodging near running trails, eateries, retail)에 대한 포함을 생성합니다. - SQL 쿼리를
VectorDistance실행하여 유사성 점수로 순위가 가장 유사한 상위 5개 호텔을 검색합니다. - 누락된 클라이언트, 잘못된 알고리즘 선택 및 존재하지 않는 컨테이너/데이터베이스에 대한 오류를 처리합니다.
코드를 이해하기: Azure OpenAI를 사용하여 임베딩 생성
이 코드는 쿼리 텍스트에 대한 임베딩을 생성합니다.
EmbeddingsOptions options = new EmbeddingsOptions(
List.of(queryText) // Array of description strings to embed
);
Embeddings embeddings = openAIClient.getEmbeddings(model, options);
List<Float> queryVector = embeddings.getData().get(0).getEmbedding();
이 Azure OpenAI SDK 호출은 "달리기 코스 근처의 특색 있는 숙소"와 같은 텍스트를 그 의미를 포착하는 1536 차원 벡터로 변환합니다. 포함 생성에 대한 자세한 내용은 Azure OpenAI embeddings 설명서를 참조하세요.
코드 이해: Azure Cosmos DB에 벡터 저장
벡터 배열이 있는 모든 문서는 executeBulkOperations에서 Utils.insertData() 메서드를 사용하여 대규모로 삽입됩니다. 각 문서는 각 문서의 파티션 키 값을 사용하여 PartitionKeyBuilder 대량 만들기 작업에 매핑됩니다. 유틸리티는 총 RU 사용량과 함께 삽입, 건너뛰기 및 실패한 수를 추적합니다.
이렇게 하면 미리 생성된 DescriptionVector 배열을 포함한 호텔 문서가 컨테이너에 삽입됩니다. 모든 문서 데이터를 안전하게 전달할 수 있으며 클라이언트 라이브러리는 일괄 처리 및 재시도를 처리합니다.
코드 이해: 벡터 유사성 검색 실행
이 코드는 함수를 사용하여 벡터 검색을 수행합니다.VectorDistance
String queryText = String.format(
"SELECT TOP 5 c.HotelName, c.Description, c.Rating, " +
"VectorDistance(c.%s, @embedding) AS SimilarityScore " +
"FROM c ORDER BY VectorDistance(c.%s, @embedding)",
embeddedField, embeddedField
);
SqlQuerySpec querySpec = new SqlQuerySpec(queryText,
new SqlParameter("@embedding", queryVector));
CosmosPagedIterable<ObjectNode> results = container.queryItems(
querySpec, new CosmosQueryRequestOptions(), ObjectNode.class);
이 코드는 VectorDistance 함수를 사용하여 쿼리의 포함 벡터(@embedding)를 각 문서의 저장된 벡터 필드(DescriptionVector)와 비교하여 이름 및 유사성 점수가 가장 유사한 상위 5개 호텔을 반환하는 매개 변수가 있는 SQL 쿼리를 작성합니다. 쿼리 포함은 삽입을 방지하기 위해 매개 변수로 전달되며 이전 Azure OpenAI embeddings 호출에서 가져옵니다.
이 쿼리에서 반환하는 내용:
- 벡터 거리를 기준으로 가장 유사한 호텔 상위 5개
- 호텔 속성:
HotelName,DescriptionRating -
SimilarityScore: 각 호텔이 쿼리와 얼마나 유사한지를 나타내는 숫자 값입니다. - 유사성 순으로 정렬된 결과
함수에 대한 VectorDistance 자세한 내용은 VectorDistance 설명서를 참조하세요.
유틸리티 함수 만들기
다음 코드를 Utils.java에 붙여넣으세요.
package com.example.cosmos.vectorsearch;
import com.azure.ai.openai.OpenAIClientBuilder;
import com.azure.ai.openai.OpenAIClient;
import com.azure.cosmos.CosmosClient;
import com.azure.cosmos.CosmosClientBuilder;
import com.azure.cosmos.CosmosContainer;
import com.azure.cosmos.models.CosmosBulkOperations;
import com.azure.cosmos.models.CosmosItemOperation;
import com.azure.cosmos.models.PartitionKey;
import com.azure.cosmos.models.PartitionKeyBuilder;
import com.azure.identity.DefaultAzureCredentialBuilder;
import tools.jackson.databind.json.JsonMapper;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Shared utilities for Azure Cosmos DB NoSQL vector search sample.
* Provides authentication, bulk insert, field validation, and result formatting.
*/
public final class Utils {
private static final JsonMapper JSON_MAPPER = JsonMapper.builder().build();
private Utils() {
// utility class
}
// ── Authentication ──────────────────────────────────────────────────
/**
* Create an Azure OpenAI client using DefaultAzureCredential (passwordless).
*/
public static OpenAIClient createOpenAIClient() {
var endpoint = requireEnv("AZURE_OPENAI_EMBEDDING_ENDPOINT");
var credential = new DefaultAzureCredentialBuilder().build();
return new OpenAIClientBuilder()
.endpoint(endpoint)
.credential(credential)
.buildClient();
}
/**
* Create a Cosmos DB client using DefaultAzureCredential (passwordless).
*/
public static CosmosClient createCosmosClient() {
var endpoint = requireEnv("AZURE_COSMOSDB_ENDPOINT");
var credential = new DefaultAzureCredentialBuilder().build();
return new CosmosClientBuilder()
.endpoint(endpoint)
.credential(credential)
.buildClient();
}
// ── Data Loading ────────────────────────────────────────────────────
/**
* Read a JSON array file and return its contents as a list of maps.
*/
@SuppressWarnings("unchecked")
public static List<Map<String, Object>> readJsonFile(Path filePath) throws IOException {
System.out.println("Reading JSON file from " + filePath);
var bytes = Files.readAllBytes(filePath);
return JSON_MAPPER.readValue(bytes, List.class);
}
// ── Bulk Insert ─────────────────────────────────────────────────────
/**
* Insert documents into a Cosmos DB container using bulk operations.
* Skips insert if the container already contains data.
*
* @return summary with counts and RU charge
*/
public static BulkInsertResult insertData(CosmosContainer container,
List<Map<String, Object>> data) {
// Check existing document count
var existingCount = getDocumentCount(container);
if (existingCount > 0) {
System.out.println("Container already has " + existingCount + " documents. Skipping insert.");
return new BulkInsertResult(0, 0, 0, (int) existingCount, 0.0);
}
System.out.println("Inserting " + data.size() + " items using bulk operations...");
// Build bulk create operations
var operations = new ArrayList<CosmosItemOperation>();
for (var item : data) {
// Cosmos DB requires an "id" field — map HotelId to id
var doc = new java.util.HashMap<>(item);
var hotelId = String.valueOf(doc.get("HotelId"));
doc.put("id", hotelId);
operations.add(CosmosBulkOperations.getCreateItemOperation(doc,
new PartitionKeyBuilder().add(hotelId).build()));
}
var inserted = 0;
var failed = 0;
var skipped = 0;
var totalRUs = 0.0;
var startTime = System.currentTimeMillis();
System.out.println("Starting bulk insert (" + operations.size() + " items)...");
var responses = container.executeBulkOperations(operations);
for (var response : responses) {
var statusCode = response.getResponse().getStatusCode();
totalRUs += response.getResponse().getRequestCharge();
if (statusCode >= 200 && statusCode < 300) {
inserted++;
} else if (statusCode == 409) {
skipped++;
} else {
failed++;
}
}
var durationSec = (System.currentTimeMillis() - startTime) / 1000.0;
System.out.printf("Bulk insert completed in %.2fs%n", durationSec);
System.out.printf("%nInsert Request Charge: %.2f RUs%n%n", totalRUs);
return new BulkInsertResult(data.size(), inserted, failed, skipped, totalRUs);
}
private static long getDocumentCount(CosmosContainer container) {
var result = container.queryItems(
"SELECT VALUE COUNT(1) FROM c",
new com.azure.cosmos.models.CosmosQueryRequestOptions(),
Long.class
);
for (var count : result) {
return count;
}
return 0;
}
// ── Field Name Validation ───────────────────────────────────────────
/**
* Validates a field name to prevent NoSQL injection when building queries
* with string interpolation.
*
* @param fieldName the field name to validate
* @return the validated field name
* @throws IllegalArgumentException if the field name contains unsafe characters
*/
public static String validateFieldName(String fieldName) {
if (!fieldName.matches("^[A-Za-z_][A-Za-z0-9_]*$")) {
throw new IllegalArgumentException(
"Invalid field name: \"" + fieldName + "\". " +
"Field names must start with a letter or underscore " +
"and contain only letters, numbers, and underscores.");
}
return fieldName;
}
// ── Output Formatting ───────────────────────────────────────────────
/**
* Print search results in a consistent tabular format.
*/
public static void printSearchResults(List<Map<String, Object>> results, double requestCharge) {
System.out.println("\n--- Search Results ---");
if (results == null || results.isEmpty()) {
System.out.println("No results found.");
return;
}
for (var i = 0; i < results.size(); i++) {
var r = results.get(i);
var name = r.get("HotelName");
var score = r.get("SimilarityScore");
System.out.printf("%d. %s, Score: %.4f%n", i + 1, name, ((Number) score).doubleValue());
}
System.out.printf("%nVector Search Request Charge: %.2f RUs%n%n", requestCharge);
}
// ── Environment Helpers ─────────────────────────────────────────────
public static String requireEnv(String key) {
var value = System.getenv(key);
if (value == null || value.isBlank()) {
throw new IllegalStateException("Required environment variable not set: " + key);
}
return value;
}
public static String envOrDefault(String key, String defaultValue) {
var value = System.getenv(key);
return (value != null && !value.isBlank()) ? value : defaultValue;
}
// ── Result Record ───────────────────────────────────────────────────
public record BulkInsertResult(int total, int inserted, int failed, int skipped, double requestCharge) {}
}
이 유틸리티 클래스는 다음과 같은 주요 함수를 제공합니다.
-
createOpenAIClient/createCosmosClient: DefaultAzureCredential을 사용하여 암호 없는 인증으로 Azure OpenAI와 Azure Cosmos DB의 클라이언트를 생성하십시오. 두 리소스 각각에서 RBAC를 활성화하고 Azure CLI에 로그인하십시오. -
insertData: 대량 작업을 사용하여 일괄 처리로 데이터를 Azure Cosmos DB 컨테이너에 삽입하고 총 RU 사용량과 함께 삽입, 건너뛰기 및 실패한 수를 추적합니다. -
printSearchResults: 점수 및 호텔 이름을 포함하여 벡터 검색 결과를 출력합니다. -
validateFieldName: 삽입을 방지하기 위해 데이터에 필드 이름이 있는지 확인합니다.
Azure CLI를 사용하여 인증
앱이 Azure 리소스에 안전하게 액세스할 수 있도록 애플리케이션을 실행하기 전에 Azure CLI에 로그인합니다.
az login
이 코드는 로컬 개발자 인증을 사용하여 Azure Cosmos DB와 createOpenAIClient, createCosmosClient, Utils.java의 Azure OpenAI에 액세스합니다. 이 함수들은 DefaultAzureCredential을 사용하는 azure-identity에 의존하며, 정렬된 자격 증명 공급자 체인을 탐색하여 로컬 개발을 위해 Azure CLI 자격 증명으로 해결합니다.
Azure ID 라이브러리를 사용하여 Azure 서비스에 Java 앱을 인증하는 방법에 대해 자세히 알아봅니다.
애플리케이션 빌드 및 실행
Maven을 사용하여 애플리케이션을 빌드하고 실행합니다.
Linux/macOS:
VECTOR_ALGORITHM=diskann mvn compile exec:java
윈도우:
set VECTOR_ALGORITHM=diskann && mvn compile exec:java
앱 로깅 및 출력은 다음을 보여줍니다.
- 데이터 삽입 상태
- 벡터 인덱스 만들기
- 호텔 이름 및 유사성 점수가 있는 검색 결과
Connected to database: Hotels
Connected to container: hotels_diskann
📊 Vector Search Algorithm: DiskANN
📏 Distance Function: cosine
Reading JSON file from ../data/HotelsData_toCosmosDB_Vector.json
Inserting 50 items using bulk operations...
Starting bulk insert (50 items)...
Bulk insert completed in 3.41s
Insert Request Charge: 6805.25 RUs
--- Executing Vector Search Query ---
Query: SELECT TOP 5 c.HotelName, c.Description, c.Rating, VectorDistance(c.DescriptionVector, @embedding) AS SimilarityScore FROM c ORDER BY VectorDistance(c.DescriptionVector, @embedding)
Parameters: @embedding (vector with 1536 dimensions)
--------------------------------------
--- Search Results ---
1. Royal Cottage Resort, Score: 0.4991
2. Country Comfort Inn, Score: 0.4786
3. Nordick's Valley Motel, Score: 0.4635
4. Economy Universe Motel, Score: 0.4461
5. Roach Motel, Score: 0.4388
Vector Search Request Charge: 5.33 RUs
거리 메트릭
Azure Cosmos DB는 벡터 유사성을 위해 세 가지 거리 함수를 지원합니다.
| Distance 함수 | 점수 범위 | 해석 | 적합한 대상 |
|---|---|---|---|
| 코사인 (기본값) | 0.0에서 1.0까지 | 점수가 높을수록(1.0에 가까울수록) 더 큰 유사성을 나타냅니다. | 일반 텍스트 유사성, Azure OpenAI 임베딩(이 퀵스타트 가이드에서 사용) |
| 유클리단 (L2) | 0.0에서 ∞ | 낮을수록 더 유사함 | 공간 데이터( 크기가 중요한 경우) |
| 닷 제품 | -∞에서 +∞까지 | 더 높은 = 더 유사 | 벡터 크기가 정규화된 경우 |
거리 함수는 컨테이너를 만들 때 벡터 포함 정책에 설정됩니다. 이는 샘플 리포지토리의 인프라스트럭처에서 제공됩니다. 컨테이너 정의의 일부로 정의됩니다.
{
name: 'hotels_diskann'
partitionKeyPaths: [
'/HotelId'
]
indexingPolicy: {
indexingMode: 'consistent'
automatic: true
includedPaths: [
{
path: '/*'
}
]
excludedPaths: [
{
path: '/_etag/?'
}
{
path: '/DescriptionVector/*'
}
]
vectorIndexes: [
{
path: '/DescriptionVector'
type: 'diskANN'
}
]
}
vectorEmbeddingPolicy: {
vectorEmbeddings: [
{
path: '/DescriptionVector'
dataType: 'float32'
dimensions: 1536
distanceFunction: 'cosine'
}
]
}
}
이 Bicep 코드는 벡터 검색 기능을 사용하여 호텔 문서를 저장하기 위한 Azure Cosmos DB 컨테이너 구성을 정의합니다.
| 재산 | Description |
|---|---|
partitionKeyPaths |
HotelId을 기준으로 문서를 분할하여 분산 저장합니다. |
indexingPolicy |
쓰기 성능을 최적화하기 위해 시스템 /* 필드와 배열을 제외한 모든 문서 속성(_etag)에서 DescriptionVector 자동 인덱싱을 구성합니다. 벡터 필드는 특수 구성을 대신 사용하기 때문에 표준 인덱싱이 vectorIndexes 필요하지 않습니다. |
vectorIndexes |
효율적인 유사성 검색을 위해 경로에 /DescriptionVector DiskANN 또는 quantizedFlat 인덱스 중 하나를 만듭니다. |
vectorEmbeddingPolicy |
벡터 필드의 특성을 정의합니다: 1536차원의 데이터 유형(float32)는 모델 출력(text-embedding-3-small)과 일치하며 쿼리 수행 시 벡터 간 유사성을 측정하는 거리 함수로 코사인을 사용합니다. |
유사성 점수 해석
예제에서는 코사인 유사성을 사용하여 출력합니다.
- 0.4991 (로얄 코티지 리조트) - 가장 높은 유사성, "달리기 트레일, 식당, 소매 근처 숙박"에 대한 최고의 일치
- 0.4388 (바퀴벌레 모텔) - 유사성이 낮고 관련성이 낮지만 일치하는 항목이 적습니다.
- 1.0에 가까운 점수는 더 강력한 의미 체계 유사성을 나타냅니다.
- 0에 가까운 점수는 유사성이 거의 없음을 나타냅니다.
Important
- 절대 점수 값은 포함 모델 및 데이터에 따라 달라집니다.
- 절대 임계값이 아닌 상대 순위 에 집중
- Azure OpenAI 임베딩은 코사인 유사성과 가장 잘 작동합니다.
거리 함수에 대한 자세한 내용은 거리 함수란?
Visual Studio Code에서 데이터 보기 및 관리
Visual Studio Code에서 Cosmos DB 확장을 선택하여 Azure Cosmos DB 계정에 연결합니다.
Hotels 데이터베이스에서 데이터 및 인덱스를 봅니다.
자원을 정리하세요
NoSQL 계정에 대한 API가 더 이상 필요하지 않은 경우 해당 리소스 그룹을 삭제할 수 있습니다.