
話題の組み込み型のベクトルデータベース LanceDB を Amazon S3 と Amazon Bedrock で試してみた
クラウド事業統括本部の石川です。3連休だったので 話題の組み込み型のベクトルデータベース LanceDB を Amazon S3 バックエンドで動かし、Amazon Bedrock と組み合わせてベクトル検索と簡易 RAG を試してみました。
生成 AI アプリケーションでベクトル検索を使いたい場合、Amazon OpenSearch Service や Amazon S3 Vectors など様々な選択肢があります。その中で LanceDB は、SQLite のようにアプリケーションへ組み込めるファイルベースのベクトルデータベースで、データの保存先として Amazon S3 をそのまま指定できるという特徴があります。DB サーバーを一切立てずに、S3 のストレージコストだけでベクトル検索基盤を持てるため、サーバーレス構成と相性の良いアーキテクチャです。
AWS Architecture Blog では、Metagenomi 社が LanceDB + Amazon S3 + AWS Lambda の構成で 35 億件・960 次元のタンパク質ベクトルの検索基盤を構築した事例が紹介されています。
DevelopersIO には LanceDB をローカル環境で動かして仕組みを解説した記事がすでにあります。本記事では S3 バックエンドでの利用と Amazon Bedrock との組み合わせにフォーカスします。
LanceDBとは
LanceDB は、列指向フォーマット「Lance」の上に構築されたオープンソースの組み込み型ベクトルデータベースです。マルチモーダル AI 向けのオープンソースの組み込み型(embedded)検索ライブラリ・ベクトルデータベースです。主な特徴は以下のとおりです。
- サーバーレスで組み込み型:
pip install lancedbだけで導入でき、別途データベースサーバーを立てる必要がありません。データはローカルディレクトリ(またはオブジェクトストレージ)に保存されます - Lance フォーマットが基盤: Rust 製の列指向フォーマット「Lance」の上に構築されており、公式サイトによると Parquet と比較して 100 倍高速なランダムアクセスを実現するとされています
- 多様な検索方式: ベクトル類似検索に加え、全文検索や SQL 構文によるメタデータフィルタをサポートしています
- 自動バージョニング: テーブルへの変更が自動でバージョン管理され、過去の状態に巻き戻せます
- マルチ言語 SDK: Python / TypeScript / Rust の SDK と REST API が提供されています
別途 DB サーバーを起動する必要がなく、アプリケーションのプロセス内で動作し、データはファイルとして保存されます。保存先にはローカルファイルシステムのほか、Amazon S3 などのオブジェクトストレージを指定できます。
S3 を保存先にする場合は、接続文字列を s3://バケット名/パス にするだけです。必要な IAM 権限は s3:PutObject、s3:GetObject、s3:DeleteObject、s3:ListBucket、s3:GetBucketLocation が基本セットとなります。また、現在は S3 のアトミックな書き込みサポートにより、同一テーブルへの同時書き込みも外部のコミットコーディネーターなしで扱えるようになっています。
今回試す構成は以下のとおりです。埋め込み生成に Amazon Titan Text Embeddings V2、RAG の回答生成に Claude Haiku 4.5 を使い、ベクトルの保存と検索は S3 上の LanceDB が担当します。
Lance / LanceDBとは何かについて、より深く知りたい方はこちらのブログがおすすめです。
やってみた
前提条件
- Amazon Bedrock で Titan Text Embeddings V2 と Claude Haiku 4.5 を利用
- 検証環境: ap-northeast-1
- Python 3.14.0、lancedb 0.34.0、pyarrow 25.0.0、boto3 1.43.49
必要なパッケージは uv でプロジェクトローカルにインストールします。
uv init
uv add lancedb pyarrow boto3 pandas
Bedrockモデルの疎通確認
まず、埋め込みモデルが呼び出せることを確認します。
% aws bedrock-runtime invoke-model \
--model-id amazon.titan-embed-text-v2:0 \
--body '{"inputText":"LanceDB のテストです","dimensions":1024,"normalize":true}' \
--cli-binary-format raw-in-base64-out \
--region ap-northeast-1 \
response.json
{
"contentType": "application/json"
}
% python3 -c "import json; d=json.load(open('response.json')); print('embedding_dim:', len(d['embedding'])); print('inputTextTokenCount:', d.get('inputTextTokenCount'))"
embedding_dim: 1024
inputTextTokenCount: 8
1024 次元の埋め込みベクトルが返ってきました。Claude Haiku 4.5(jp.anthropic.claude-haiku-4-5-20251001-v1:0)も同様に converse API で応答することを確認しています。
S3バケットの作成
LanceDB のデータ保存先となるバケットを作成します。バケット名は一意になるよう調整してください。
% aws s3 mb s3://cm-lancedb-blog-20260720 \
--region ap-northeast-1
make_bucket: cm-lancedb-blog-20260720
% aws s3api put-public-access-block \
--bucket cm-lancedb-blog-20260720 \
--public-access-block-configuration BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
暗号化はデフォルトの SSE-S3 のままとし、S3 バージョニングは設定していません。LanceDB は後述のとおり自前でバージョン管理を行うため、S3 側のバージョニングは必須ではありません。
埋め込み生成とLanceDBテーブルの作成
サンプルデータとして、AWS サービスの説明文 40 件を用意しました。以下の JSON です。
sample_docs.json
[
{"id": "doc-01", "title": "Amazon S3", "text": "Amazon S3 は、業界をリードするスケーラビリティ、データ可用性、セキュリティ、パフォーマンスを提供するオブジェクトストレージサービスです。データレイク、バックアップ、アーカイブなど幅広い用途で利用されます。"},
{"id": "doc-02", "title": "AWS Lambda", "text": "AWS Lambda は、サーバーのプロビジョニングや管理なしでコードを実行できるサーバーレスコンピューティングサービスです。イベント駆動型のアプリケーション構築に適しています。"},
{"id": "doc-03", "title": "Amazon EC2", "text": "Amazon EC2 は、クラウド上で仮想サーバーを提供するサービスです。多様なインスタンスタイプから用途に応じた計算リソースを選択できます。"},
{"id": "doc-04", "title": "Amazon DynamoDB", "text": "Amazon DynamoDB は、フルマネージドの NoSQL データベースサービスです。1桁ミリ秒のレイテンシで、あらゆる規模のアプリケーションに対応します。"},
{"id": "doc-05", "title": "Amazon RDS", "text": "Amazon RDS は、リレーショナルデータベースをクラウド上で簡単にセットアップ、運用、スケーリングできるマネージドサービスです。MySQL、PostgreSQL、Oracle などに対応しています。"},
{"id": "doc-06", "title": "Amazon Redshift", "text": "Amazon Redshift は、クラウドデータウェアハウスサービスです。ペタバイト規模のデータに対して高速な分析クエリを実行できます。Serverless オプションも提供されています。"},
{"id": "doc-07", "title": "Amazon Athena", "text": "Amazon Athena は、S3 上のデータに対して標準 SQL でクエリを実行できるサーバーレスのインタラクティブ分析サービスです。スキャンしたデータ量に応じた課金体系です。"},
{"id": "doc-08", "title": "AWS Glue", "text": "AWS Glue は、サーバーレスのデータ統合サービスです。ETL ジョブの実行、データカタログの管理、データ品質の検証などが可能です。"},
{"id": "doc-09", "title": "Amazon Bedrock", "text": "Amazon Bedrock は、主要な AI 企業が提供する高性能な基盤モデルを単一の API で利用できるフルマネージドサービスです。テキスト生成や埋め込みベクトルの生成が可能です。"},
{"id": "doc-10", "title": "Amazon SageMaker", "text": "Amazon SageMaker は、機械学習モデルの構築、トレーニング、デプロイを支援するフルマネージドサービスです。データサイエンティストと開発者向けの統合開発環境を提供します。"},
{"id": "doc-11", "title": "Amazon OpenSearch Service", "text": "Amazon OpenSearch Service は、ログ分析や全文検索、ベクトル検索を実行できるマネージド検索サービスです。k-NN 検索によるセマンティック検索にも対応しています。"},
{"id": "doc-12", "title": "Amazon Kinesis Data Streams", "text": "Amazon Kinesis Data Streams は、ストリーミングデータをリアルタイムに収集・処理できるサービスです。大規模なデータストリームを低レイテンシで扱えます。"},
{"id": "doc-13", "title": "Amazon QuickSight", "text": "Amazon QuickSight は、クラウドネイティブな BI サービスです。ダッシュボードの作成や、自然言語による質問応答機能 Amazon Q in QuickSight を利用できます。"},
{"id": "doc-14", "title": "AWS Step Functions", "text": "AWS Step Functions は、分散アプリケーションのワークフローを視覚的に構築できるサーバーレスオーケストレーションサービスです。Lambda などの AWS サービスを連携させられます。"},
{"id": "doc-15", "title": "Amazon API Gateway", "text": "Amazon API Gateway は、REST API や WebSocket API を作成、公開、管理できるフルマネージドサービスです。Lambda と組み合わせたサーバーレス API の構築によく使われます。"},
{"id": "doc-16", "title": "Amazon CloudFront", "text": "Amazon CloudFront は、低レイテンシでコンテンツを配信するグローバルな CDN サービスです。エッジロケーションを利用して静的・動的コンテンツを高速配信します。"},
{"id": "doc-17", "title": "Amazon VPC", "text": "Amazon VPC は、AWS クラウド内に論理的に分離されたプライベートネットワーク空間を構築するサービスです。サブネットやルートテーブルを自由に設計できます。"},
{"id": "doc-18", "title": "AWS IAM", "text": "AWS IAM は、AWS リソースへのアクセスを安全に管理するサービスです。ユーザー、グループ、ロール、ポリシーによるきめ細かなアクセス制御が可能です。"},
{"id": "doc-19", "title": "Amazon CloudWatch", "text": "Amazon CloudWatch は、AWS リソースとアプリケーションのモニタリングサービスです。メトリクスの収集、ログの分析、アラームの設定ができます。"},
{"id": "doc-20", "title": "AWS CloudFormation", "text": "AWS CloudFormation は、インフラをコードとして管理する IaC サービスです。テンプレートを使って AWS リソースをプロビジョニングできます。"},
{"id": "doc-21", "title": "Amazon ECS", "text": "Amazon ECS は、コンテナ化されたアプリケーションを実行するフルマネージドなコンテナオーケストレーションサービスです。Fargate と組み合わせるとサーバーレスでコンテナを実行できます。"},
{"id": "doc-22", "title": "Amazon EKS", "text": "Amazon EKS は、マネージドな Kubernetes サービスです。Kubernetes コントロールプレーンの運用を AWS に任せて、コンテナワークロードを実行できます。"},
{"id": "doc-23", "title": "AWS Fargate", "text": "AWS Fargate は、サーバーを管理せずにコンテナを実行できるサーバーレスコンピューティングエンジンです。ECS および EKS で利用できます。"},
{"id": "doc-24", "title": "Amazon SQS", "text": "Amazon SQS は、フルマネージドのメッセージキューイングサービスです。マイクロサービス間の疎結合な非同期通信を実現します。"},
{"id": "doc-25", "title": "Amazon SNS", "text": "Amazon SNS は、フルマネージドの Pub/Sub メッセージングサービスです。アプリケーション間通知やモバイルプッシュ通知に利用できます。"},
{"id": "doc-26", "title": "Amazon EventBridge", "text": "Amazon EventBridge は、イベント駆動型アーキテクチャを構築するためのサーバーレスイベントバスサービスです。SaaS アプリケーションとの連携も可能です。"},
{"id": "doc-27", "title": "AWS KMS", "text": "AWS KMS は、暗号化キーの作成と管理を行うマネージドサービスです。多くの AWS サービスと統合されており、保管時のデータ暗号化に利用されます。"},
{"id": "doc-28", "title": "Amazon Aurora", "text": "Amazon Aurora は、クラウド向けに構築された MySQL および PostgreSQL 互換のリレーショナルデータベースです。高い性能と可用性を提供し、Serverless v2 にも対応しています。"},
{"id": "doc-29", "title": "Amazon ElastiCache", "text": "Amazon ElastiCache は、Redis および Memcached 互換のインメモリキャッシュサービスです。データベースの負荷軽減やセッション管理に利用されます。"},
{"id": "doc-30", "title": "AWS Secrets Manager", "text": "AWS Secrets Manager は、データベース認証情報や API キーなどのシークレットを安全に管理するサービスです。シークレットの自動ローテーションに対応しています。"},
{"id": "doc-31", "title": "Amazon S3 Tables", "text": "Amazon S3 Tables は、Apache Iceberg テーブルに最適化されたストレージを提供する S3 の機能です。分析ワークロード向けにテーブルデータを効率的に管理できます。"},
{"id": "doc-32", "title": "AWS Lake Formation", "text": "AWS Lake Formation は、セキュアなデータレイクを構築・管理するサービスです。テーブルや列レベルのきめ細かなアクセス制御を実現します。"},
{"id": "doc-33", "title": "Amazon EMR", "text": "Amazon EMR は、Apache Spark や Hive などのビッグデータフレームワークを実行するマネージドクラスタープラットフォームです。Serverless オプションも利用できます。"},
{"id": "doc-34", "title": "AWS Batch", "text": "AWS Batch は、バッチコンピューティングジョブを効率的に実行するフルマネージドサービスです。ジョブのスケジューリングとコンピューティングリソースの管理を自動化します。"},
{"id": "doc-35", "title": "Amazon Cognito", "text": "Amazon Cognito は、Web およびモバイルアプリケーションの認証、認可、ユーザー管理を提供するサービスです。ソーシャル ID プロバイダーとの連携も可能です。"},
{"id": "doc-36", "title": "AWS AppSync", "text": "AWS AppSync は、GraphQL API を構築するフルマネージドサービスです。リアルタイムデータ同期やオフライン対応のアプリケーション開発を支援します。"},
{"id": "doc-37", "title": "Amazon MSK", "text": "Amazon MSK は、Apache Kafka 互換のフルマネージドストリーミングサービスです。Kafka クラスターの運用負荷を軽減し、ストリーミングアプリケーションを構築できます。"},
{"id": "doc-38", "title": "AWS Amplify", "text": "AWS Amplify は、フルスタックの Web・モバイルアプリケーションを迅速に構築、デプロイできる開発プラットフォームです。ホスティングやバックエンド構築を統合的に支援します。"},
{"id": "doc-39", "title": "Amazon Neptune", "text": "Amazon Neptune は、フルマネージドのグラフデータベースサービスです。ソーシャルネットワークやレコメンデーションなど、関係性の分析に適しています。Neptune Analytics ではベクトル検索もサポートしています。"},
{"id": "doc-40", "title": "Amazon S3 Vectors", "text": "Amazon S3 Vectors は、ベクトルデータの保存とクエリをネイティブにサポートする S3 の機能です。専用のベクトルデータベースを運用せずに、低コストでベクトル検索を実現します。"}
]
このドキュメントを Titan Text Embeddings V2 で埋め込みベクトルに変換し、S3 上に LanceDB テーブルとして保存するスクリプトです。ベクトルの次元数(1024)と型(float32)は PyArrow スキーマの FixedSizeList で固定します。
01_setup_table.py
import json
import os
import time
import boto3
import lancedb
import pyarrow as pa
REGION = "ap-northeast-1"
DB_URI = os.environ["LANCEDB_URI"] # 例: s3://lancedb-blog-xxxx/db
EMBED_MODEL_ID = "amazon.titan-embed-text-v2:0"
bedrock = boto3.client("bedrock-runtime", region_name=REGION)
def embed(text: str) -> list[float]:
body = json.dumps({"inputText": text, "dimensions": 1024, "normalize": True})
res = bedrock.invoke_model(modelId=EMBED_MODEL_ID, body=body)
return json.loads(res["body"].read())["embedding"]
def main():
with open("sample_docs.json") as f:
docs = json.load(f)
print(f"埋め込み生成: {len(docs)} 件")
start = time.perf_counter()
data = [
{
"id": doc["id"],
"title": doc["title"],
"text": doc["text"],
"vector": embed(doc["text"]),
}
for doc in docs
]
print(f"埋め込み生成完了: {time.perf_counter() - start:.1f} 秒")
schema = pa.schema(
[
pa.field("id", pa.string()),
pa.field("title", pa.string()),
pa.field("text", pa.string()),
pa.field("vector", pa.list_(pa.float32(), 1024)),
]
)
db = lancedb.connect(DB_URI, storage_options={"region": REGION})
start = time.perf_counter()
table = db.create_table("aws_services", data=data, schema=schema, mode="overwrite")
print(f"テーブル作成完了: {time.perf_counter() - start:.1f} 秒")
print(f"登録件数: {table.count_rows()}")
if __name__ == "__main__":
main()
実行します。
% export LANCEDB_URI="s3://cm-lancedb-blog-20260720/db"
% uv run python 01_setup_table.py
埋め込み生成: 40 件
埋め込み生成完了: 6.0 秒
[2026-07-20T13:58:44Z WARN lance::dataset::write::insert] No existing dataset at s3://cm-lancedb-blog-20260720/db/aws_services.lance, it will be created
テーブル作成完了: 0.4 秒
登録件数: 40
40 件の埋め込み生成が 6.6 秒、S3 上へのテーブル作成が 0.4 秒で完了しました。注目したいのは認証まわりで、storage_options にはリージョンを指定しただけです。アクセスキーを明示的に渡さなくても、AWS CLI と同じデフォルトの認証情報チェーンがそのまま利用されました。なお、リージョン指定を省略するとバケットのリージョンと不一致になり接続エラーになる場合があるため、明示しておくのが無難です。
ベクトル検索を実行する
作成したテーブルに対して、日本語の自然文クエリで検索してみます。埋め込み生成の embed() 関数は先ほどのスクリプトと同じものです。
02_search.py
"""S3 上の LanceDB テーブルに対してベクトル検索(インデックスなし)を実行する"""
import json
import os
import time
import boto3
import lancedb
REGION = "ap-northeast-1"
DB_URI = os.environ["LANCEDB_URI"]
EMBED_MODEL_ID = "amazon.titan-embed-text-v2:0"
bedrock = boto3.client("bedrock-runtime", region_name=REGION)
def embed(text: str) -> list[float]:
body = json.dumps({"inputText": text, "dimensions": 1024, "normalize": True})
res = bedrock.invoke_model(modelId=EMBED_MODEL_ID, body=body)
return json.loads(res["body"].read())["embedding"]
def main():
db = lancedb.connect(DB_URI, storage_options={"region": REGION})
table = db.open_table("aws_services")
queries = [
"サーバーを管理せずにコンテナを実行したい",
"ログを集めて分析、監視したい",
"ベクトル検索ができるサービスは?",
]
for query in queries:
qvec = embed(query)
start = time.perf_counter()
results = table.search(qvec).limit(3).to_pandas()
elapsed = (time.perf_counter() - start) * 1000
print(f"\nクエリ: {query}(検索時間: {elapsed:.0f} ms ※参考値)")
for _, row in results.iterrows():
print(f" {row['_distance']:.4f} {row['title']}: {row['text'][:45]}...")
if __name__ == "__main__":
main()
% export LANCEDB_URI="s3://cm-lancedb-blog-20260720/db"
% uv run python 02_search.py
クエリ: サーバーを管理せずにコンテナを実行したい(検索時間: 482 ms ※参考値)
1.4603 Amazon ECS: Amazon ECS は、コンテナ化されたアプリケーションを実行するフルマネージドなコンテ...
1.4612 AWS Fargate: AWS Fargate は、サーバーを管理せずにコンテナを実行できるサーバーレスコンピュー...
1.5624 Amazon EKS: Amazon EKS は、マネージドな Kubernetes サービスです。Kuberne...
クエリ: ログを集めて分析、監視したい(検索時間: 165 ms ※参考値)
1.4368 Amazon CloudWatch: Amazon CloudWatch は、AWS リソースとアプリケーションのモニタリングサ...
1.6223 Amazon OpenSearch Service: Amazon OpenSearch Service は、ログ分析や全文検索、ベクトル検索を...
1.7032 AWS Amplify: AWS Amplify は、フルスタックの Web・モバイルアプリケーションを迅速に構築、...
クエリ: ベクトル検索ができるサービスは?(検索時間: 179 ms ※参考値)
1.2319 Amazon OpenSearch Service: Amazon OpenSearch Service は、ログ分析や全文検索、ベクトル検索を...
1.2856 Amazon S3 Vectors: Amazon S3 Vectors は、ベクトルデータの保存とクエリをネイティブにサポート...
1.3629 Amazon Neptune: Amazon Neptune は、フルマネージドのグラフデータベースサービスです。ソーシャ...
3 つのクエリすべてで意味的に妥当なサービスが上位に返ってきました。「サーバーを管理せずにコンテナ」で ECS・Fargate・EKS が並ぶあたり、日本語でもしっかりセマンティック検索が機能しています。検索時間は初回が 482 ms、2 回目以降は 165〜179 ms でした。ローカル PC から S3 へアクセスしているため、この数値はネットワーク環境に依存する参考値です。
なお、この検索はインデックスを作成していない状態のため、全件走査(brute-force)による正確な近傍探索です。_distance は L2 距離で、埋め込み生成時に normalize: true を指定しているためコサイン類似度と同等の順位になります。
S3上のファイルレイアウトを確認する
LanceDB が S3 に何を書き込んだのか見てみます。
% aws s3 ls s3://cm-lancedb-blog-20260720/db/ --recursive --human-readable
2026-07-20 22:58:45 309 Bytes db/__manifest/_transactions/0-1d216e38-0365-48cf-84e5-c032643b3a4d.txn
2026-07-20 22:58:45 692 Bytes db/__manifest/_versions/18446744073709551614.manifest
2026-07-20 22:58:45 277 Bytes db/aws_services.lance/_transactions/0-02fc41eb-5e06-49a4-86df-da4b74c79914.txn
2026-07-20 22:58:45 631 Bytes db/aws_services.lance/_versions/18446744073709551614.manifest
2026-07-20 22:58:45 170.8 KiB db/aws_services.lance/data/1000001001110000101011001c253343bc89df2f778dc015a3.lance
40 件・1024 次元のテーブルの実体は、わずか 5 オブジェクト・合計約 173 KB でした。data/ に実データの .lance ファイル、_versions/ にテーブルのバージョンを管理するマニフェスト、_transactions/ にトランザクションログが置かれる構造です。テーブルへの変更はバージョンとして積み重なっていく設計で、LanceDB 自身がバージョン管理を持っていることがファイルレイアウトからも分かります。
IVF_PQインデックス作成を試してみる
ここまでの検索はインデックスなしの全件走査でしたが、データ件数が増えると検索時間も件数に比例して伸びていきます。そのため大規模データでは、厳密な探索の代わりに精度を少し犠牲にして高速化する ANN(Approximate Nearest Neighbor:近似最近傍探索)インデックスを使うのが一般的です。LanceDB のデフォルトのインデックスタイプは IVF_PQ で、次の 2 つの手法の組み合わせです。
- IVF(転置ファイルインデックス): 全ベクトルを k-means でクラスタリングし、検索時はクエリに近いクラスタだけを走査して探索範囲を絞り込みます。下記コードの
num_partitionsがクラスタ数に対応します - PQ(Product Quantization:直積量子化): 各ベクトルを複数のサブベクトルに分割し、それぞれを少数の代表点に置き換えて圧縮します。メモリ使用量と距離計算のコストを大きく削減できます。
num_sub_vectorsが分割数に対応します
今回のデータはわずか 40 件ですが、ダメ元でインデックスを作成してみます。
03_index.py
import os
import lancedb
REGION = "ap-northeast-1"
DB_URI = os.environ["LANCEDB_URI"]
def main():
db = lancedb.connect(DB_URI, storage_options={"region": REGION})
table = db.open_table("aws_services")
print(f"件数: {table.count_rows()}")
try:
table.create_index(metric="l2", num_partitions=4, num_sub_vectors=16)
print("インデックス作成: 成功")
print(table.list_indices())
except Exception as e:
print(f"インデックス作成: 失敗 ({type(e).__name__})")
print(e)
if __name__ == "__main__":
main()
以下のエラーが返されました。
% export LANCEDB_URI="s3://cm-lancedb-blog-20260720/db"
uv run python 03_index.py
件数: 40
/Users/ishikawa.satoru/workspaces/cc/blog/20260720-lancedb-on-aws/03_index.py:16: DeprecationWarning: The create_index() API with metric/num_partitions parameters is deprecated and will be removed in a future version. Please migrate to the new unified API:
# Old (deprecated):
table.create_index('l2', vector_column_name='my_vector')
# New (recommended):
table.create_index('my_vector', config=IvfPq(distance_type='l2'))
table.create_index(metric="l2", num_partitions=4, num_sub_vectors=16)
インデックス作成: 失敗 (RuntimeError)
lance error: Unprocessable: Not enough rows to train PQ. Requires 256 rows but only 40 available, /Users/runner/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/lance-index-8.0.0/src/vector/pq/builder.rs:179:24
PQ(直積量子化)の学習には最低 256 行が必要とのことです。少量データではインデックスを作らず全件走査に任せるのが正解で、実際に前述のとおり 40 件なら 130 ms 前後で検索できています。インデックス作成を検討するのはデータが数万件を超えてからで良さそうです。
また、実行時に以下の DeprecationWarning が表示されました。lancedb 0.34.0 では metric や num_partitions を直接渡す書き方は非推奨で、config に IvfPq オブジェクトを渡す統一 API へ移行が進んでいます。
DeprecationWarning: The create_index() API with metric/num_partitions parameters is deprecated
# New (recommended):
table.create_index('my_vector', config=IvfPq(distance_type='l2'))
LanceDB + Bedrock(Claude Haiku 4.5)で簡易RAGを試す
最後に、ベクトル検索の結果をコンテキストとして Claude Haiku 4.5 に渡す、シンプルな RAG を動かしてみます。
04_rag.py
import json
import os
import boto3
import lancedb
REGION = "ap-northeast-1"
DB_URI = os.environ["LANCEDB_URI"]
EMBED_MODEL_ID = "amazon.titan-embed-text-v2:0"
CHAT_MODEL_ID = "jp.anthropic.claude-haiku-4-5-20251001-v1:0"
bedrock = boto3.client("bedrock-runtime", region_name=REGION)
def embed(text: str) -> list[float]:
body = json.dumps({"inputText": text, "dimensions": 1024, "normalize": True})
res = bedrock.invoke_model(modelId=EMBED_MODEL_ID, body=body)
return json.loads(res["body"].read())["embedding"]
def main():
db = lancedb.connect(DB_URI, storage_options={"region": REGION})
table = db.open_table("aws_services")
question = "コストを抑えてベクトル検索を実現できる AWS のサービスや構成を教えてください"
results = table.search(embed(question)).limit(3).to_pandas()
context = "\n\n".join(
f"【{row['title']}】{row['text']}" for _, row in results.iterrows()
)
prompt = (
"以下の参考ドキュメントの内容に基づいて質問に回答してください。\n"
"参考ドキュメントに記載のない情報は推測せず、その旨を明記してください。\n\n"
f"# 参考ドキュメント\n{context}\n\n# 質問\n{question}"
)
res = bedrock.converse(
modelId=CHAT_MODEL_ID,
messages=[{"role": "user", "content": [{"text": prompt}]}],
inferenceConfig={"maxTokens": 600, "temperature": 0},
)
print(f"質問: {question}\n")
print("検索でヒットしたドキュメント:")
for _, row in results.iterrows():
print(f" - {row['title']} (distance: {row['_distance']:.4f})")
print("\n回答:")
print(res["output"]["message"]["content"][0]["text"])
usage = res["usage"]
print(f"\ntokens: input={usage['inputTokens']}, output={usage['outputTokens']}")
if __name__ == "__main__":
main()
% export LANCEDB_URI="s3://cm-lancedb-blog-20260720/db"
% uv run python 04_rag.py
質問: コストを抑えてベクトル検索を実現できる AWS のサービスや構成を教えてください
検索でヒットしたドキュメント:
- Amazon S3 Vectors (distance: 1.1685)
- Amazon OpenSearch Service (distance: 1.2089)
- AWS Lambda (distance: 1.2487)
回答:
# 回答
参考ドキュメントに基づいて、コストを抑えてベクトル検索を実現できるAWSのサービスは以下の通りです。
## **Amazon S3 Vectors**
- **最もコスト効率的な選択肢**です
- ベクトルデータの保存とクエリをネイティブにサポート
- 専用のベクトルデータベースを運用する必要がないため、低コストでベクトル検索を実現できます
## **Amazon OpenSearch Service**
- ベクトル検索に対応しており、k-NN検索によるセマンティック検索が可能です
- ただし、マネージドサービスであるため、Amazon S3 Vectorsと比較してコスト面での優位性については、参考ドキュメントに記載されていません
## **推奨構成**
参考ドキュメントの情報のみに基づくと、**Amazon S3 Vectorsの利用が最もコスト効率的**と言えます。
---
**注記:** AWS Lambda との組み合わせなど、より詳細な構成提案については、参考ドキュメントに具体的な記載がないため、推測での回答は控えさせていただきます。
tokens: input=338, output=359
質問の埋め込み → LanceDB で類似検索 → 上位 3 件をコンテキストに回答生成、という RAG の一連の流れが問題なく動作しました。回答も「参考ドキュメントに記載がない情報は推測しない」という指示に忠実です。
考察
実際に試してみて得られた知見をまとめます。
- 導入が非常に手軽: 必要な AWS リソースは S3 バケット 1 つだけです。
pip install lancedbして接続文字列をs3://にするだけで、ローカル開発と同じコードがそのまま S3 バックエンドで動きました。認証もデフォルトの認証情報チェーンが利用されるため、アクセスキーをコードに書く必要はありません - リージョンは明示指定が無難:
storage_options={"region": "ap-northeast-1"}のようにバケットのリージョンを明示することで、リージョン不一致による接続エラーを避けられます - 少量データにインデックスは不要: IVF_PQ インデックスは PQ の学習に最低 256 行が必要でした。今回の 40 件では全件走査でも 130 ms 前後(参考値)で検索でき、インデックスの検討はデータが数万件を超えてからで十分と考えられます
- ストレージ効率が良い: 40 件・1024 次元で約 173 KB・5 オブジェクトでした。S3 のストレージ単価を考えるとベクトルの保管コストはごくわずかです。今回の検証全体でも、Bedrock の呼び出しは合計数千トークン程度、S3 は数十リクエスト程度に収まっています
- 同時書き込みへの対応が進んでいる: 以前は S3 バックエンドでの複数プロセスからの同時書き込みに DynamoDB をコミットストアとして併用する構成が必要でしたが、現在は S3 のアトミックな書き込みサポートにより、外部コーディネーターなしで同時書き込みを扱えると公式ドキュメントに明記されています
- 日本語も実用的: Titan Text Embeddings V2 は英語に最適化されたモデルで、日本語を含む 100 以上の言語はプレビュー扱いですが、今回の日本語ドキュメント・日本語クエリの組み合わせでも妥当な検索結果が得られました
今後は以下も試してみたいところです。
- AWS Lambda からの検索(コンテナイメージでの lancedb パッケージング)
- 数十万件規模のデータでの IVF_PQ インデックス作成と検索性能
- S3 Express One Zone をバックエンドにした場合のレイテンシ改善
LanceDBとAmazon S3 Vectorsの比較
同じ「S3 にベクトルを置く」選択肢として、Amazon S3 Vectors との使い分けも気になるところです。両者の違いを整理すると以下のとおりです。
| 項目 | LanceDB(S3 バックエンド) | Amazon S3 Vectors |
|---|---|---|
| 提供形態 | OSS の組み込み型ライブラリ | S3 のネイティブ機能(フルマネージド) |
| 検索処理の実行場所 | アプリケーションのプロセス内 | AWS 側(QueryVectors API) |
| インデックス | IVF_PQ などを自分で作成・管理 | 自動管理(作成・チューニング不要) |
| スケール | 設計次第(35 億件の構築事例あり) | 最大 20 億ベクトル / インデックス |
| 検索機能 | ベクトル検索+全文検索+SQL フィルタ | ベクトル検索+メタデータフィルタ |
| 距離メトリクス | L2 / cosine / dot | cosine / euclidean |
| テーブルのバージョン管理 | あり(過去バージョンへ巻き戻し可能) | なし |
| コスト | S3 のストレージ+リクエスト料金のみ | ストレージ+PUT+クエリの従量課金 |
| エコシステム | LangChain / LlamaIndex など OSS 連携 | Bedrock Knowledge Bases / OpenSearch 統合 |
| ローカル開発 | 同じコードでローカルファイルシステムでも動作 | AWS 上のみ |
大きな違いは検索処理の実行場所です。S3 Vectors はベクトルの保存も検索も AWS 側が担うフルマネージドな機能であるのに対し、LanceDB は S3 をストレージとしてのみ使い、検索の計算はアプリケーション側で実行します。フルマネージドで運用の手間なくスケールさせたい場合や、Bedrock Knowledge Bases のベクトルストアとして使いたい場合は S3 Vectors が適しています。
LanceDBを選ぶメリット
一方、LanceDB を導入すると次のようなメリットがあります。
- ローカルと同じコードのまま S3 に載る開発体験: 今回の検証のとおり、接続文字列を
s3://に変えるだけでローカル開発のコードがそのまま動きます。単体テストもローカルファイルシステムで完結します - コストが読みやすい: クエリ課金がなく、かかる費用は S3 のストレージとリクエストの実費だけです。今回の 40 件・1024 次元でも約 173 KB でした
- データベースとしての機能が豊富: ベクトル検索に加えて全文検索・SQL 構文のメタデータフィルタ・自動バージョニング(タイムトラベル)を 1 つのライブラリで使えます
- ロックインが緩い: OSS のため、同じコードでローカル・Google Cloud Storage・Azure Blob Storage でも動作し、環境の移行が容易です
- サーバーレス構成に組み込める: AWS Lambda に組み込めば、DB サーバーなしの検索 API を構成できます。冒頭で紹介した Metagenomi 社の 35 億件の事例もこの構成です
逆に、検索がアプリケーション側で実行される構成上、同時アクセスのスケーリングや IVF_PQ インデックスの作成・更新は自分で設計する必要があります。小規模に始めて構成をシンプルに保ちたい、あるいは検索機能ごとアプリケーションに埋め込みたい場合は LanceDB、大規模ベクトルをマネージドに任せたい場合は S3 Vectors、という使い分けになりそうです。
最後に
LanceDB の S3 バックエンドは「S3 バケットを作って接続文字列を変えるだけ」という手軽さで、DB サーバーレスなベクトル検索基盤を実現できました。Bedrock の埋め込みモデルとの組み合わせも boto3 だけで完結し、小規模な RAG であればこの構成で十分成立します。ベクトルデータベースの運用コストを抑えたい方、まずは小さくベクトル検索を始めたい方は、選択肢の一つとして試してみてはいかがでしょうか。









