はじめに:RAGインフラの「肥大化」と「複雑性」を解消する
RAG(検索拡張生成)システムを構築する際、外部のマネージドベクトル検索サービス(Pinecone、Qdrant、Milvus等)を導入すると、インフラコストの増加やネットワークレイテンシ、セキュリティ上の懸念が生じます。
SQLiteの公式拡張 sqlite-vec と標準の全文検索エンジン FTS5 を組み合わせることで、単一のSQLiteファイル(数MB〜数GB)だけで完結する超高速ハイブリッドRAGを構築できます。
1. ハイブリッド検索(ベクトル + BM25)の優位性
- ベクトル検索(埋め込み): 概念や意味の類似性(「安い宿」→「格安ホテル」)を捉えるのが得意。
- BM25全文検索(FTS5): 固有の型番、専門用語、JANコード(「C3000HG」「4969363045300」)の完全一致に強い。
- ハイブリッド融合(RRF: Reciprocal Rank Fusion): 両方の検索スコアを統合し、圧倒的な検索精度を実現。
2. Pythonでの実装例
import sqlite3
import sqlite_vec
from sentence_transformers import SentenceTransformer
# 1. DB接続と拡張のロード
db = sqlite3.connect("knowledge.db")
db.enable_load_extension(True)
sqlite_vec.load(db)
db.enable_load_extension(False)
# 2. テーブル作成(ベクトルテーブル + FTS5テーブル)
db.execute("CREATE VIRTUAL TABLE IF NOT EXISTS vec_docs USING vec0(embedding float[384])")
db.execute("CREATE VIRTUAL TABLE IF NOT EXISTS fts_docs USING fts5(doc_id, content)")
# 3. ドキュメントの登録
model = SentenceTransformer("intfloat/multilingual-e5-small")
doc_text = "シマノ 24ヴァンフォード C3000HG は超軽量な汎用スピニングリールです。"
embedding = model.encode(doc_text).tolist()
cur = db.cursor()
cur.execute("INSERT INTO fts_docs(doc_id, content) VALUES (1, ?)", (doc_text,))
cur.execute("INSERT INTO vec_docs(rowid, embedding) VALUES (1, ?)", (embedding,))
db.commit()
# 4. ハイブリッド検索クエリ
query = "ヴァンフォードの型番"
q_emb = model.encode(query).tolist()
# ベクトル近傍検索
res_vec = cur.execute("SELECT rowid, distance FROM vec_docs WHERE embedding MATCH ? ORDER BY distance LIMIT 5", (q_emb,)).fetchall()
# FTS5キーワード検索
res_fts = cur.execute("SELECT doc_id, rank FROM fts_docs WHERE fts_docs MATCH ? ORDER BY rank LIMIT 5", (query,)).fetchall()
サーバーレス・ローカル環境でミリ秒単位で動作するため、デスクトップアプリやエッジ環境でのRAG実装に最適です。