ベクターデータベースにより、セマンティック検索が可能になります。つまり、厳密なキーワードではなく意味によってドキュメントを見つけられるのです。LLMと組み合わせることで、RAG(Retrieval-Augmented Generation:検索拡張生成)アプリケーションが実現し、あなた自身のデータから質問に答えられます。では実装してみましょう。
ベクター検索で解決できること
キーワード検索:完全に一致する単語を含むドキュメントを見つけます。
ベクター検索:意味が近いドキュメントを見つけます。
質問: "パスワードをリセットするにはどうすればいいですか?
キーワード検索で見つかるもの: "パスワードリセット手順", "パスワードリセットのページ"
ベクター検索でも見つかるもの: "アカウント復旧", "認証情報を忘れた場合", "ログインの問題"
ドキュメント、カスタマーサポート、ナレッジベースでは、ベクター検索は非常に関連性の高い結果を返します。
埋め込み(Embedding)パイプライン
import OpenAI from 'openai'
const openai = new OpenAI()
async function embedText(text: string): Promise<number[]> {
const response = await openai.embeddings.create({
model: 'text-embedding-3-small', // 1536次元、$0.02/100万トークン
input: text,
})
return response.data[0].embedding
}
// あるいはClaude(Voyage AI経由)で
// voyage-3-lite:大規模なインデックス作成向けに高速かつ低コスト
pgvectorを使ってPostgresにベクターを保存する
-- pgvector拡張を有効化
CREATE EXTENSION IF NOT EXISTS vector;
-- ベクター列を持つテーブル
CREATE TABLE documents (
id BIGSERIAL PRIMARY KEY,
content TEXT NOT NULL,
metadata JSONB,
embedding vector(1536) -- 次元数はモデルに合わせる
);
-- 高速な類似検索用のインデックス
CREATE INDEX ON documents USING ivfflat (embedding vector_cosine_ops)
WITH (lists = 100);
// 埋め込み(embedding)付きでドキュメントを挿入する
async function indexDocument(content: string, metadata: object) {
const embedding = await embedText(content)
await db.$executeRaw`
INSERT INTO documents (content, metadata, embedding)
VALUES (${content}, ${JSON.stringify(metadata)}, ${JSON.stringify(embedding)}::vector)
`
}
セマンティック検索クエリ
async function semanticSearch(query: string, limit = 5) {
const queryEmbedding = await embedText(query)
返却形式: {"translated": "翻訳されたHTML"}const results = await db.$queryRaw<Array<{
id: number
content: string
metadata: object
similarity: number
}>>`
SELECT id, content, metadata,
1 - (embedding <=> ${JSON.stringify(queryEmbedding)}::vector) AS similarity
FROM documents
ORDER BY embedding <=> ${JSON.stringify(queryEmbedding)}::vector
LIMIT ${limit}
`
return results.filter(r => r.similarity > 0.7) // 閾値
}
RAG: データからの質問への回答
async function answerFromDocs(question: string): Promise<string> {
// 1. 関連するドキュメントを見つける
const relevantDocs = await semanticSearch(question, 5)
if (relevantDocs.length === 0) {
return 'その質問に答えるための関連情報が見つかりませんでした。'
}
// 2. 取得したドキュメントからコンテキストを構築する
const context = relevantDocs
.map((doc, i) => `[${i + 1}] ${doc.content}`)
.join('
')
// 3. アースティング(根拠)付きコンテキストで Claude に質問する
const response = await anthropic.messages.create({
model: 'claude-sonnet-4-6',
system: '提供されたコンテキストのみを使って質問に答えてください。コンテキストに答えが含まれていない場合は、それを述べてください。',
messages: [{
role: 'user',
content: `Context:
${context}
Question: ${question}`,
}],
})
return response.content[0].text
}
分割(Chunking)戦略
ドキュメントのチャンク分割は、検索(リトリーバル)の品質に大きく影響します:
function chunkDocument(text: string, chunkSize = 500, overlap = 50): string[] {
const words = text.split(' ')
const chunks: string[] = []
for (let i = 0; i < words.length; i += chunkSize - overlap) {
chunks.push(words.slice(i, i + chunkSize).join(' '))
if (i + chunkSize >= words.length) break
}
return chunks
}
// 各チャンクを個別にインデックスする
for (const chunk of chunkDocument(document)) {
await indexDocument(chunk, { sourceDocId: document.id })
}
管理オプション
pgvector を自分で管理したくない場合:
- Pinecone: 完全マネージド、手厚い無料ティア
- Qdrant: オープンソース、自社ホスティングまたはクラウド
- Supabase Vector: Supabase 上の pgvector
- Neon: Neon 上の pgvector(アプリと同じDB)
whoffagents.com の AI SaaS Starter には、pgvector + Prisma、埋め込みパイプライン、セマンティック検索、RAG パターンを事前構築したベクトル検索モジュールが含まれています。$99 の一回払い。
返却形式: {"translated": "翻訳されたHTML"}



