Vector Embeddings完全ガイド2025 - セマンティック検索の実装と最適化
Vector Embeddingsによるセマンティック検索の仕組みから実装まで徹底解説。OpenAI Embeddings、Pinecone、Chromaを使った高精度検索システムの構築方法と本番運用のベストプラクティス。
Pinecone、Weaviate、Qdrant、ChromaDBを徹底比較。実践的なRAG実装、セマンティック検索、埋め込みモデルの選定から本番運用まで、ベクトルデータベースのすべてを解説します。
2025 年、AIアプリケーションの心臓部となった Vector Database。ChatGPT のような ai アシスタント、画像検索システム、レコメンドエンジン。これらすべての裏側で、高次元ベクトル空間での類似性検索が動いています。
本記事では、主要 4 プラットフォーム(Pinecone、Weaviate、Qdrant、ChromaDB)の徹底比較から、実践的なRAG実装、10億ベクトルを扱うスケーリング戦略まで、Vector Database のすべてを解説します。
チャートを読み込み中...
従来のデータベースは構造化データの完全一致検索に最適化されていますが、ai アプリケーションでは意味的な類似性を扱う必要があります。
// ベクトルの基本概念
interface VectorEmbedding {
// テキスト "犬が走る" を384次元のベクトルに変換
text: string;
vector: number[]; // [0.12, -0.34, 0.56, ..., 0.78] (384次元)
}
// 類似度計算の例
function cosineSimilarity(vec1: number[], vec2: number[]): number {
const dotProduct = vec1.reduce((sum, val, i) => sum + val * vec2[i], 0);
const norm1 = Math.sqrt(vec1.reduce((sum, val) => sum + val * val, 0));
const norm2 = Math.sqrt(vec2.reduce((sum, val) => sum + val * val, 0));
return dotProduct / (norm1 * norm2);
}
// 使用例
const dogRunning = [0.12, -0.34, 0.56, ...]; // "犬が走る"
const catJumping = [0.15, -0.31, 0.52, ...]; // "猫が跳ぶ"
const carDriving = [-0.82, 0.21, -0.15, ...]; // "車が走る"
console.log(cosineSimilarity(dogRunning, catJumping)); // 0.92 (高い類似度)
console.log(cosineSimilarity(dogRunning, carDriving)); // 0.31 (低い類似度)
用途 | 具体例 | 必要な機能 |
---|---|---|
セマンティック検索 | 自然言語での文書検索 | 高速な近傍探索 |
RAG | ChatGPTのような知識拡張 | 大規模ベクトル管理 |
画像検索 | 類似画像の検索 | マルチモーダル対応 |
レコメンデーション | 商品・コンテンツ推薦 | リアルタイム更新 |
異常検知 | 正常パターンからの逸脱検出 | クラスタリング |
顔認識 | 顔の特徴ベクトル比較 | 高精度な距離計算 |
ChatGPTブームでVector DBへの注目急上昇
ハイブリッド検索、マルチモーダル対応が標準化
汎用DBもベクトル機能を追加
特化型DBが独自機能で勝負
AIプラットフォームとの深い統合へ
Pinecone: マネージドサービスで運用負荷を最小化したい場合 Weaviate: オープンソースで柔軟性を重視する場合 Qdrant: 高性能とリソース効率を最優先する場合 ChromaDB: LLM アプリケーションに特化した開発をする場合
// Pinecone実装例
import { PineconeClient } from '@pinecone-database/pinecone';
const pinecone = new PineconeClient();
async function initPinecone() {
await pinecone.init({
apiKey: process.env.PINECONE_API_KEY,
environment: 'us-west4-gcp'
});
// インデックスの作成
await pinecone.createIndex({
name: 'my-rag-index',
dimension: 1536, // OpenAI ada-002の次元数
metric: 'cosine',
pods: 1,
replicas: 1,
pod_type: 'p1.x1'
});
const index = pinecone.Index('my-rag-index');
// ベクトルの挿入
await index.upsert({
vectors: [
{
id: 'doc1',
values: [0.1, 0.2, ...], // 1536次元のベクトル
metadata: {
title: 'AI入門',
content: '人工知能の基礎について...',
source: 'textbook'
}
}
]
});
// 類似検索
const queryResponse = await index.query({
vector: [0.15, 0.25, ...], // クエリベクトル
topK: 10,
includeMetadata: true,
filter: {
source: { $eq: 'textbook' }
}
});
}
特徴:
// Weaviate実装例
import weaviate from 'weaviate-ts-client';
const client = weaviate.client({
scheme: 'http',
host: 'localhost:8080',
});
async function setupWeaviate() {
// スキーマの定義
const schemaConfig = {
class: 'Article',
vectorizer: 'text2vec-openai',
moduleConfig: {
'text2vec-openai': {
model: 'text-embedding-ada-002',
type: 'text'
}
},
properties: [
{
name: 'title',
dataType: ['string'],
},
{
name: 'content',
dataType: ['text'],
}
]
};
await client.schema.classCreator().withClass(schemaConfig).do();
// データの追加(自動ベクトル化)
await client.data.creator()
.withClassName('Article')
.withProperties({
title: 'GraphQLの基礎',
content: 'GraphQLは効率的なAPI設計を可能にします...'
})
.do();
// ハイブリッド検索(ベクトル + キーワード)
const result = await client.graphql
.get()
.withClassName('Article')
.withHybrid({
query: 'API設計の効率化',
alpha: 0.75 // 0=キーワードのみ、1=ベクトルのみ
})
.withFields('title content _additional { score }')
.do();
}
特徴:
// Qdrant実装例(Rust)
use qdrant_client::prelude::*;
use qdrant_client::qdrant::vectors_config::Config;
use qdrant_client::qdrant::{CreateCollection, VectorParams, VectorsConfig};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = QdrantClient::from_url("http://localhost:6334").build()?;
// コレクションの作成
client
.create_collection(&CreateCollection {
collection_name: "products".to_string(),
vectors_config: Some(VectorsConfig {
config: Some(Config::Params(VectorParams {
size: 768,
distance: Distance::Cosine.into(),
..Default::default()
})),
}),
..Default::default()
})
.await?;
// ポイントの挿入
let points = vec![
PointStruct::new(
1,
vec![0.05, 0.61, 0.76, ...], // 768次元
json!({
"name": "高性能ノートPC",
"category": "electronics",
"price": 150000
})
),
];
client
.upsert_points("products", points, None)
.await?;
// フィルタリング付き検索
let search_result = client
.search_points(&SearchPoints {
collection_name: "products".to_string(),
vector: vec![0.11, 0.62, 0.77, ...],
filter: Some(Filter::must([
Condition::range("price", Range {
lt: Some(200000.0),
..Default::default()
}),
])),
limit: 10,
with_payload: Some(true),
..Default::default()
})
.await?;
Ok(())
}
特徴:
# ChromaDB実装例
import chromadb
from chromadb.utils import embedding_functions
# クライアントの初期化
client = chromadb.PersistentClient(path="./chroma_db")
# OpenAI埋め込み関数の設定
openai_ef = embedding_functions.OpenAIEmbeddingFunction(
api_key="your-api-key",
model_name="text-embedding-ada-002"
)
# コレクションの作成
collection = client.create_collection(
name="knowledge_base",
embedding_function=openai_ef,
metadata={"hnsw:space": "cosine"}
)
# ドキュメントの追加
collection.add(
documents=[
"量子コンピュータは従来のコンピュータとは異なる原理で動作します",
"機械学習は大量のデータからパターンを学習します",
"ブロックチェーンは分散型台帳技術です"
],
metadatas=[
{"source": "quantum_textbook", "chapter": 1},
{"source": "ml_guide", "chapter": 3},
{"source": "blockchain_intro", "chapter": 1}
],
ids=["doc1", "doc2", "doc3"]
)
# セマンティック検索
results = collection.query(
query_texts=["量子技術について教えて"],
n_results=2,
where={"chapter": 1}
)
# LangChainとの統合
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings
vectorstore = Chroma(
collection_name="knowledge_base",
embedding_function=OpenAIEmbeddings(),
persist_directory="./chroma_db"
)
# RAGチェーンの構築
from langchain.chains import RetrievalQA
from langchain.llms import OpenAI
qa_chain = RetrievalQA.from_chain_type(
llm=OpenAI(),
retriever=vectorstore.as_retriever(search_kwargs={"k": 3})
)
answer = qa_chain.run("量子コンピュータの応用分野は?")
特徴:
項目 | Pinecone | Weaviate | Qdrant | ChromaDB |
---|---|---|---|---|
最大ベクトル数 | 数十億 | 数十億 | 数十億 | 数百万 |
レイテンシ(p99) | 50ms | 100ms | 10ms | 200ms |
メモリ効率 | 中 | 低 | 高 | 中 |
価格(100万ベクトル) | $70/月 | 自己ホスト | 自己ホスト | 自己ホスト |
セットアップ時間 | 5分 | 30分 | 20分 | 10分 |
スケーリング | 自動 | 手動 | 手動 | 制限あり |
チャートを読み込み中...
// 埋め込みモデルの実装例
import { OpenAI } from 'openai';
import { HuggingFaceInference } from '@huggingface/inference';
class EmbeddingService {
private openai: OpenAI;
private hf: HuggingFaceInference;
constructor() {
this.openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
this.hf = new HuggingFaceInference(process.env.HF_API_KEY);
}
// OpenAI text-embedding-ada-002
async embedWithOpenAI(text: string): Promise<number[]> {
const response = await this.openai.embeddings.create({
model: 'text-embedding-ada-002',
input: text,
});
return response.data[0].embedding; // 1536次元
}
// Sentence Transformers (all-MiniLM-L6-v2)
async embedWithSentenceTransformers(text: string): Promise<number[]> {
const response = await this.hf.featureExtraction({
model: 'sentence-transformers/all-MiniLM-L6-v2',
inputs: text,
});
return response as number[]; // 384次元
}
// 日本語特化モデル
async embedJapanese(text: string): Promise<number[]> {
const response = await this.hf.featureExtraction({
model: 'sonoisa/sentence-bert-base-ja-mean-tokens-v2',
inputs: text,
});
return response as number[];
}
// バッチ処理での最適化
async embedBatch(texts: string[], model: string = 'ada-002'): Promise<number[][]> {
const batchSize = 100;
const embeddings: number[][] = [];
for (let i = 0; i < texts.length; i += batchSize) {
const batch = texts.slice(i, i + batchSize);
if (model === 'ada-002') {
const response = await this.openai.embeddings.create({
model: 'text-embedding-ada-002',
input: batch,
});
embeddings.push(...response.data.map(d => d.embedding));
}
// レート制限対策
await new Promise(resolve => setTimeout(resolve, 100));
}
return embeddings;
}
}
埋め込みモデルの性能は、MTEB リーダーボードで評価されます:
// 埋め込みの品質評価
class EmbeddingEvaluator {
// コサイン類似度の分布を可視化
async evaluateEmbeddingQuality(
embeddings: number[][],
labels: string[]
): Promise<EvaluationResult> {
const similarities: number[][] = [];
// 全ペアの類似度計算
for (let i = 0; i < embeddings.length; i++) {
similarities[i] = [];
for (let j = 0; j < embeddings.length; j++) {
similarities[i][j] = this.cosineSimilarity(
embeddings[i],
embeddings[j]
);
}
}
// 同一カテゴリ内の平均類似度
const intraClassSimilarity = this.calculateIntraClassSimilarity(
similarities,
labels
);
// 異なるカテゴリ間の平均類似度
const interClassSimilarity = this.calculateInterClassSimilarity(
similarities,
labels
);
return {
separation: intraClassSimilarity - interClassSimilarity,
intraClass: intraClassSimilarity,
interClass: interClassSimilarity,
recommendation: this.getRecommendation(
intraClassSimilarity - interClassSimilarity
)
};
}
private getRecommendation(separation: number): string {
if (separation > 0.5) return "優秀:そのまま使用可能";
if (separation > 0.3) return "良好:用途によっては使用可能";
if (separation > 0.1) return "要改善:ファインチューニング推奨";
return "不適切:別のモデルを検討";
}
}
チャートを読み込み中...
// 完全なRAGシステムの実装
import { OpenAI } from 'openai';
import { QdrantClient } from '@qdrant/js-client-rest';
import { Document } from 'langchain/document';
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter';
class RAGSystem {
private openai: OpenAI;
private vectorDB: QdrantClient;
private collectionName = 'knowledge_base';
constructor() {
this.openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
this.vectorDB = new QdrantClient({ url: 'http://localhost:6333' });
}
// ステップ1: ドキュメントの前処理とチャンキング
async preprocessDocuments(documents: string[]): Promise<Document[]> {
const splitter = new RecursiveCharacterTextSplitter({
chunkSize: 1000,
chunkOverlap: 200,
separators: ['\n\n', '\n', '。', '、', ' ', '']
});
const chunks: Document[] = [];
for (const doc of documents) {
const splitDocs = await splitter.createDocuments([doc]);
chunks.push(...splitDocs);
}
return chunks;
}
// ステップ2: チャンクの埋め込みとインデックス作成
async indexDocuments(chunks: Document[]): Promise<void> {
const points = [];
for (let i = 0; i < chunks.length; i++) {
const embedding = await this.createEmbedding(chunks[i].pageContent);
points.push({
id: i,
vector: embedding,
payload: {
text: chunks[i].pageContent,
metadata: chunks[i].metadata
}
});
}
// バッチでVector DBに挿入
await this.vectorDB.upsert(this.collectionName, {
wait: true,
points
});
}
// ステップ3: 検索拡張生成
async generate(query: string, options: RAGOptions = {}): Promise<RAGResponse> {
const {
topK = 5,
temperature = 0.7,
systemPrompt = 'あなたは親切なAIアシスタントです。'
} = options;
// クエリの埋め込み
const queryEmbedding = await this.createEmbedding(query);
// 類似文書の検索
const searchResult = await this.vectorDB.search(this.collectionName, {
vector: queryEmbedding,
limit: topK,
with_payload: true
});
// コンテキストの構築
const context = searchResult
.map((result, index) =>
`[文書${index + 1}]\n${result.payload.text}`
)
.join('\n\n');
// プロンプトの構築
const messages = [
{ role: 'system', content: systemPrompt },
{
role: 'user',
content: `以下の文書を参考に質問に答えてください。
参考文書:
${context}
質問:${query}
回答:`
}
];
// LLMによる回答生成
const completion = await this.openai.chat.completions.create({
model: 'gpt-4',
messages,
temperature,
max_tokens: 1000
});
return {
answer: completion.choices[0].message.content,
sources: searchResult.map(r => ({
text: r.payload.text,
score: r.score,
metadata: r.payload.metadata
})),
usage: completion.usage
};
}
// 高度なRAG手法:HyDE (Hypothetical Document Embeddings)
async generateWithHyDE(query: string): Promise<RAGResponse> {
// ステップ1: 仮想回答の生成
const hypotheticalAnswer = await this.openai.chat.completions.create({
model: 'gpt-3.5-turbo',
messages: [
{
role: 'user',
content: `次の質問に対する詳細な回答を書いてください: ${query}`
}
],
temperature: 0.7
});
// ステップ2: 仮想回答を使って検索
const hydeEmbedding = await this.createEmbedding(
hypotheticalAnswer.choices[0].message.content
);
const searchResult = await this.vectorDB.search(this.collectionName, {
vector: hydeEmbedding,
limit: 5,
with_payload: true
});
// ステップ3: 実際の回答生成
return this.generate(query, { topK: 5 });
}
private async createEmbedding(text: string): Promise<number[]> {
const response = await this.openai.embeddings.create({
model: 'text-embedding-ada-002',
input: text
});
return response.data[0].embedding;
}
}
// シンプルなRAG
async function basicRAG(query: string) {
// 1. クエリを埋め込み
const embedding = await embed(query);
// 2. トップK検索
const docs = await vectorDB.search(embedding, 5);
// 3. LLMに投げる
return await llm.generate(query + docs);
}
// 最適化されたRAG
async function optimizedRAG(query: string) {
// 1. クエリ拡張
const expandedQueries = await expandQuery(query);
// 2. マルチクエリ検索
const allDocs = await Promise.all(
expandedQueries.map(q => vectorDB.search(q, 3))
);
// 3. Re-ranking
const rerankedDocs = await rerank(allDocs, query);
// 4. コンテキスト圧縮
const compressedContext = await compressContext(
rerankedDocs,
query
);
// 5. 構造化プロンプト
return await llm.generate({
systemPrompt: OPTIMIZED_PROMPT,
context: compressedContext,
query: query,
examples: FEW_SHOT_EXAMPLES
});
}
// シンプルなRAG
async function basicRAG(query: string) {
// 1. クエリを埋め込み
const embedding = await embed(query);
// 2. トップK検索
const docs = await vectorDB.search(embedding, 5);
// 3. LLMに投げる
return await llm.generate(query + docs);
}
// 最適化されたRAG
async function optimizedRAG(query: string) {
// 1. クエリ拡張
const expandedQueries = await expandQuery(query);
// 2. マルチクエリ検索
const allDocs = await Promise.all(
expandedQueries.map(q => vectorDB.search(q, 3))
);
// 3. Re-ranking
const rerankedDocs = await rerank(allDocs, query);
// 4. コンテキスト圧縮
const compressedContext = await compressContext(
rerankedDocs,
query
);
// 5. 構造化プロンプト
return await llm.generate({
systemPrompt: OPTIMIZED_PROMPT,
context: compressedContext,
query: query,
examples: FEW_SHOT_EXAMPLES
});
}
// キーワード検索とベクトル検索の組み合わせ
class HybridSearch {
private vectorDB: VectorDatabase;
private textIndex: TextSearchIndex;
async search(
query: string,
options: HybridSearchOptions
): Promise<SearchResult[]> {
const { alpha = 0.7, limit = 10 } = options;
// 並列で両方の検索を実行
const [vectorResults, keywordResults] = await Promise.all([
this.vectorSearch(query, limit * 2),
this.keywordSearch(query, limit * 2)
]);
// スコアの正規化
const normalizedVector = this.normalizeScores(vectorResults);
const normalizedKeyword = this.normalizeScores(keywordResults);
// ハイブリッドスコアの計算
const hybridResults = new Map<string, number>();
for (const result of normalizedVector) {
const vectorScore = result.score * alpha;
hybridResults.set(result.id, vectorScore);
}
for (const result of normalizedKeyword) {
const keywordScore = result.score * (1 - alpha);
const currentScore = hybridResults.get(result.id) || 0;
hybridResults.set(result.id, currentScore + keywordScore);
}
// 結果のソートと返却
return Array.from(hybridResults.entries())
.sort((a, b) => b[1] - a[1])
.slice(0, limit)
.map(([id, score]) => ({
id,
score,
document: this.getDocument(id)
}));
}
// Reciprocal Rank Fusion (RRF)
async searchWithRRF(
query: string,
methods: SearchMethod[]
): Promise<SearchResult[]> {
const k = 60; // RRFパラメータ
const allResults = await Promise.all(
methods.map(method => method.search(query))
);
const rrfScores = new Map<string, number>();
for (const results of allResults) {
results.forEach((result, rank) => {
const score = 1 / (k + rank + 1);
const currentScore = rrfScores.get(result.id) || 0;
rrfScores.set(result.id, currentScore + score);
});
}
return Array.from(rrfScores.entries())
.sort((a, b) => b[1] - a[1])
.map(([id, score]) => ({
id,
score,
document: this.getDocument(id)
}));
}
}
// テキスト + 画像の統合検索
class MultiModalSearch {
private textEncoder: TextEncoder;
private imageEncoder: ImageEncoder;
private vectorDB: VectorDatabase;
async indexMultiModal(items: MultiModalItem[]): Promise<void> {
const embeddings = [];
for (const item of items) {
// テキストと画像を同じ埋め込み空間にマップ
const textEmbedding = item.text
? await this.textEncoder.encode(item.text)
: null;
const imageEmbedding = item.image
? await this.imageEncoder.encode(item.image)
: null;
// 埋め込みの結合または平均
const combinedEmbedding = this.combineEmbeddings(
textEmbedding,
imageEmbedding
);
embeddings.push({
id: item.id,
vector: combinedEmbedding,
payload: {
text: item.text,
imageUrl: item.imageUrl,
metadata: item.metadata
}
});
}
await this.vectorDB.upsert(embeddings);
}
// CLIP-like検索
async searchByText(query: string): Promise<SearchResult[]> {
const queryEmbedding = await this.textEncoder.encode(query);
return this.vectorDB.search(queryEmbedding);
}
async searchByImage(imagePath: string): Promise<SearchResult[]> {
const queryEmbedding = await this.imageEncoder.encode(imagePath);
return this.vectorDB.search(queryEmbedding);
}
// クロスモーダル検索
async crossModalSearch(
query: { text?: string; image?: string }
): Promise<SearchResult[]> {
const embeddings = [];
if (query.text) {
embeddings.push(await this.textEncoder.encode(query.text));
}
if (query.image) {
embeddings.push(await this.imageEncoder.encode(query.image));
}
const combinedQuery = this.combineEmbeddings(...embeddings);
return this.vectorDB.search(combinedQuery);
}
}
インデックス | 特徴 | メモリ使用 | 精度 | 速度 |
---|---|---|---|---|
Flat | 全探索・完全な精度 | O(n) | 100% | 遅い |
HNSW | グラフベース・高速 | O(n*M) | 95-99% | 非常に速い |
IVF | クラスタリング | O(n) | 90-95% | 速い |
LSH | ハッシュベース | O(n) | 80-90% | 速い |
PQ | 量子化・省メモリ | O(n/8) | 85-90% | 中程度 |
// 10億ベクトルを扱うためのシャーディング戦略
class ShardedVectorDatabase {
private shards: VectorDatabaseShard[];
private shardCount: number;
constructor(shardCount: number) {
this.shardCount = shardCount;
this.shards = Array(shardCount)
.fill(null)
.map((_, i) => new VectorDatabaseShard(i));
}
// 一貫性のあるハッシングでシャードを決定
private getShardIndex(id: string): number {
const hash = this.hashString(id);
return hash % this.shardCount;
}
// データの分散インデックス
async indexDistributed(
data: VectorData[],
batchSize: number = 1000
): Promise<void> {
// シャードごとにデータを分類
const shardedData = new Map<number, VectorData[]>();
for (const item of data) {
const shardIndex = this.getShardIndex(item.id);
if (!shardedData.has(shardIndex)) {
shardedData.set(shardIndex, []);
}
shardedData.get(shardIndex)!.push(item);
}
// 並列でシャードにインデックス
await Promise.all(
Array.from(shardedData.entries()).map(
([shardIndex, shardData]) =>
this.shards[shardIndex].indexBatch(shardData, batchSize)
)
);
}
// 分散検索
async searchDistributed(
query: number[],
topK: number
): Promise<SearchResult[]> {
// 各シャードで並列検索
const shardResults = await Promise.all(
this.shards.map(shard =>
shard.search(query, topK)
)
);
// 結果をマージしてトップKを選択
return this.mergeResults(shardResults, topK);
}
// 動的リバランシング
async rebalance(): Promise<void> {
const loadStats = await this.getLoadStatistics();
if (this.needsRebalancing(loadStats)) {
const rebalancePlan = this.createRebalancePlan(loadStats);
await this.executeRebalancing(rebalancePlan);
}
}
}
// スカラー量子化の実装例
class QuantizedVectorIndex {
private quantizationBits: number = 8;
private scale: number[];
private offset: number[];
// float32 → int8 量子化
quantizeVector(vector: number[]): Int8Array {
const quantized = new Int8Array(vector.length);
for (let i = 0; i < vector.length; i++) {
const normalized = (vector[i] - this.offset[i]) / this.scale[i];
const quantizedValue = Math.round(
normalized * (Math.pow(2, this.quantizationBits - 1) - 1)
);
quantized[i] = Math.max(
-128,
Math.min(127, quantizedValue)
);
}
return quantized;
}
// int8 → float32 逆量子化
dequantizeVector(quantized: Int8Array): number[] {
const vector = new Array(quantized.length);
for (let i = 0; i < quantized.length; i++) {
const normalized = quantized[i] / (Math.pow(2, this.quantizationBits - 1) - 1);
vector[i] = normalized * this.scale[i] + this.offset[i];
}
return vector;
}
// 非対称量子化での検索
async searchWithAsymmetricQuantization(
query: number[],
topK: number
): Promise<SearchResult[]> {
// クエリは量子化しない(精度向上)
const results = [];
for (const [id, quantizedVector] of this.quantizedVectors) {
// 量子化空間で直接距離計算
const distance = this.asymmetricDistance(
query,
quantizedVector
);
results.push({ id, distance });
}
return results
.sort((a, b) => a.distance - b.distance)
.slice(0, topK);
}
}
// Vector Database監視システム
class VectorDBMonitoring {
private metrics: MetricsCollector;
async collectMetrics(): Promise<SystemMetrics> {
return {
// パフォーマンスメトリクス
queryLatency: {
p50: await this.metrics.getPercentile('query_latency', 50),
p95: await this.metrics.getPercentile('query_latency', 95),
p99: await this.metrics.getPercentile('query_latency', 99)
},
// インデックスメトリクス
indexMetrics: {
totalVectors: await this.getTotalVectors(),
indexSize: await this.getIndexSizeGB(),
fragmentationRatio: await this.getFragmentation()
},
// システムリソース
resources: {
memoryUsageGB: await this.getMemoryUsage(),
cpuUsagePercent: await this.getCPUUsage(),
diskIOPS: await this.getDiskIOPS()
},
// 品質メトリクス
quality: {
recallAt10: await this.measureRecall(10),
precisionAt10: await this.measurePrecision(10)
}
};
}
// アラート設定
setupAlerts(): void {
this.metrics.createAlert({
name: 'high_query_latency',
condition: 'p99_latency > 100ms',
action: 'notify_oncall'
});
this.metrics.createAlert({
name: 'low_recall',
condition: 'recall_at_10 < 0.9',
action: 'investigate_index_quality'
});
this.metrics.createAlert({
name: 'memory_pressure',
condition: 'memory_usage > 80%',
action: 'scale_up_instance'
});
}
}
// Vector Databaseのバックアップ戦略
class VectorDBBackup {
async performBackup(strategy: BackupStrategy): Promise<BackupResult> {
switch (strategy) {
case 'incremental':
return this.incrementalBackup();
case 'snapshot':
return this.snapshotBackup();
case 'continuous':
return this.continuousReplication();
}
}
// 増分バックアップ
private async incrementalBackup(): Promise<BackupResult> {
const lastBackupTime = await this.getLastBackupTime();
const changes = await this.getChangesSince(lastBackupTime);
// 変更されたベクトルのみバックアップ
const backupData = {
timestamp: new Date(),
type: 'incremental',
vectors: changes.vectors,
deletions: changes.deletions
};
await this.uploadToS3(backupData);
return {
success: true,
backedUpVectors: changes.vectors.length,
backupSize: this.calculateSize(backupData)
};
}
// Point-in-Timeリカバリ
async restoreToPoint(targetTime: Date): Promise<void> {
// 最も近いスナップショットを見つける
const baseSnapshot = await this.findNearestSnapshot(targetTime);
// スナップショットを復元
await this.restoreSnapshot(baseSnapshot);
// 増分バックアップを適用
const incrementals = await this.getIncrementalsSince(
baseSnapshot.timestamp,
targetTime
);
for (const incremental of incrementals) {
await this.applyIncremental(incremental);
}
}
}
知識グラフとベクトル検索の融合
モバイル・IoTデバイスでの実行
量子コンピュータでの超高速検索
自己学習・自己最適化するシステム
Vector Database は、AIアプリケーションの基盤技術として急速に進化しています。適切なプラットフォーム選定、埋め込みモデルの最適化、そして本番環境での運用ノウハウが成功の鍵となります。
重要なのは、ユースケースに応じた技術選定です。小規模な PoC なら ChromaDB、エンタープライズなら Pinecone、高性能が必要なら Qdrant、柔軟性重視なら Weaviate というように、要件に応じて選択しましょう。