こんにちは~ 浮田です。
■ TL;DR
- 同じ 2,798 チャンクを両方に入れて測ったら、精度(Recall@10)は S3 Vectors 0.975、次世代版 OpenSearch Serverless 0.981 で差はありませんでした
- 差が出たのはもっと手前です。最初のクエリまでに叩く API は 2 回 対 5 回+ポリシー反映待ち、投入から検索に反映されるまで 0.2 秒 対 15.5 秒、12 分放置したあとの一発目は約 1 秒 対 12 秒
- CWE-79 のような型番の指名は、OpenSearch のキーワード検索なら 8 本すべて当たります。S3 Vectors にはこれを補う手段がありません
- この検証の費用は合計約 36 円。どちらが安いかは、クエリの「数」ではなく「密度」で決まります
- 結論:違いは精度でもコストでもなく「ベクトルの周りに何があるか」。同じ本文を入れても、S3 Vectors では取り出せるだけの荷物、OpenSearch では検索対象になります
■ 目次
- はじめに
- 何が同じで、何が違うのか
- 触って数字で出た違い
- 触れないもの
- で、どっちを使うか
- ハマったところ
- 後片付け
- まとめ
■ はじめに
以前、次世代の OpenSearch Serverless で自分のブログを意味検索できるようにしました。その少し前には Bedrock のナレッジベースで S3 Vectors も触っています。どちらも「ベクトルを入れて、近いものを引く」ことはできる。
料金も両方とも使った分だけ。じゃあ何が違うんでしょうか。
同じベクトルを両方に入れて、投入・検索・精度・費用を並べました。
結論を先に言うと、S3 Vectors は「ベクトルの置き場」で、OpenSearch は「検索エンジン」です。
置き場なので、S3 Vectors にできるのは「入れる」「近いものを引く」「メタデータで絞る」の 3 つだけ。
一方 OpenSearch では、ベクトル検索は検索の種類のひとつにすぎず、同じインデックスに対してキーワード検索も集計もできます。
この違いは機能表を見れば分かります。
ただ、実際に触ると「置き場」と「エンジン」の差はもっと手前で出ました。
作ってから最初のクエリを投げるまで、投入してから検索できるまで、放置したあとの一発目。
なお、コストと精度は掘り始めたら 1 本に収まらなかったので、別の記事に分けています。
この記事は全体を見渡す「違い編」で、このあとコスト編、精度編と続きます。
※「本記事では『次世代版』とは 2026 年 5 月 GA の OpenSearch Serverless のことを指します」)
■ 何が同じで、何が違うのか
まず同じところ。
どちらも 1,024 次元の float の配列を受け取り、cosine で近いものを返します。
埋め込みモデルは自分で選び、自分で呼びます(今回は Titan Text Embeddings V2)。
ここは完全に同じで、片方で作ったベクトルはそのままもう片方に入ります。
違うのは、ベクトルの周りに何があるかです。
S3 Vectors のインデックスに入れられるのは、キーとベクトルとメタデータだけです。
本文も、入れるならメタデータの 1 項目として入れます。
検索は QueryVectors の 1 種類で、返ってくるのはキーと距離(と、頼めばメタデータ)です。
s3v.put_vectors(vectorBucketName=B, indexName="chunks", vectors=[
{"key": "blog:xxx:3", "data": {"float32": vec},
"metadata": {"bucket": 42, "text": "……本文……"}},
])
r = s3v.query_vectors(vectorBucketName=B, indexName="chunks", topK=10,
queryVector={"float32": qvec},
filter={"bucket": {"$lt": 5}})
▲ S3 Vectors:入れるのはキー・ベクトル・メタデータだけ。本文もメタデータの 1 項目
OpenSearch では、ベクトルはドキュメントのフィールドのひとつです。
同じドキュメントに text を text 型で持たせれば全文検索の対象になるし、bucket を integer で持たせれば範囲検索も集計もできます。ベクトル検索はクエリ DSL の knn という 1 句です。
osc.bulk(body=...) # {"key": ..., "bucket": 42, "text": "……本文……", "embedding": vec}
r = osc.search(index="chunks", body={
"size": 10,
"query": {"knn": {"embedding": {
"vector": qvec, "k": 10,
"filter": {"range": {"bucket": {"lt": 5}}}}}}})
▲ OpenSearch:ベクトルはドキュメントのフィールドのひとつ。
knn は match(キーワード検索)や range(範囲指定)と並ぶ検索条件のひとつで、組み合わせて使える
S3 Vectors の text は「取り出せるけど検索できない」荷物です。
OpenSearch の text は検索対象です。
同じ名前のフィールドを同じように入れているのに、片方では中身が見えて、片方では見えない。この記事で言いたい違いは、ほぼこの一点に集約されます。
■ 触って数字で出た違い
同じ条件で両方を作って、投入して、測りました。
- コーパス: 2,798 チャンク。自分の記事を見出し単位で切ったもの 144、e-Gov 法令 API から取った民法・会社法・刑法・労働基準法の条文 2,154(1 条 = 1 チャンク)、日本語 Wikipedia の導入部 500。条文と Wikipedia は母集団を増やすためです
- 埋め込み: Titan Text Embeddings V2、1,024 次元、正規化あり
- リージョン: 東京
- OpenSearch 側: 次世代版の Serverless、ベクトル検索コレクション、最小 0 / 最大 4 OCU。マッピングは何も指定しない既定
- S3 Vectors 側: cosine、本文は非フィルタメタデータ
※コーパス: 検索対象として用意したデータ一式。
正解データ(どれが本当に一番近いか)は numpy の総当たりで作りました。ベクトルは手元にあるので、答え合わせのために別のインデックスを立てる必要はありません。
● 作ってから検索できるまで
|
|
S3 Vectors |
次世代版 |
|
最初のクエリまでに叩いた API |
2 回(バケット、インデックス) |
5 回(ポリシー 3 つ、コレクショングループ、コレクション)+ポリシー反映待ち+インデックス作成 |
|
作成から使えるまで |
即時 |
ACTIVE まで 7.6 秒 |
|
投入(2,798 件) |
46 秒(6 リクエスト) |
62 秒(14 リクエスト) |
|
投入完了から検索できるまで |
0.2 秒 |
15.5 秒 |
S3 Vectors は、バケットを作ってインデックスを作れば終わりです。
ネットワークもアクセスポリシーも IAM だけ。次世代版はコレクショングループ、コレクション、暗号化・ネットワーク・データアクセスの 3 ポリシー、それからインデックスのマッピング。このあたりが「置き場」と「エンジン」の差の、いちばん手前の現れ方です。
「投入完了から検索できるまで」は、投入直後から 1 秒おきに「先頭のベクトルで検索して自分自身が 1 位で返るか」を見た秒数です。S3 Vectorsは投入が終わった時点で返ってきました。次世代版は 15 秒。HNSW のグラフを組む時間がここに乗ります。
投入して即クエリ、という使い方だと、次世代版では最初の十数秒は古い結果が返ります。
● 放置したあとの一発目
次世代版は 10 分放置すると 0 OCU に落ちます( CloudWatch を追って確認しました)。
落ちたあとの一発目には復帰の時間が乗ります。
S3 Vectors は落ちる仕組みがそもそもないので、常に同じはずです。これも測りました。
|
放置 |
S3 Vectors 1 発目 |
次世代版 1 発目 |
S3 Vectors 2 発目 |
次世代版 2 発目 |
|
12 分 |
982 ms |
12,153 ms |
149 ms |
396 ms |
次世代版の一発目は公式に「約 10 秒」とされていて、その間のリクエストは捨てられずにキューに入ります。実測は 12 秒でした。
S3 Vectors の一発目も 1 秒近くかかっていて、公式の「低頻度なら 1 秒以内」の表現どおりです。落ちる仕組みはなくても、しばらく触らないと最初は遅い。
ただ 12 秒と 1 秒では体感が違います。「たまにしか使わないアプリの一発目」は S3 Vectors のほうが楽で、ずっと使い続けるなら次世代版の 2 発目以降(ウォームで p50 60〜77 ms)が効きます。
● 精度
ここは差が出ると思っていました。
結果は Recall@10 で S3 Vectors 0.975、次世代版 0.981。
1,000 件中 6 件の差で、差はないと言っていい数字です。
本番の RAG を想定してメタデータで 25%、5%、1% に絞った場合も、どちらも崩れず 0.99 前後でした。
測り方、フィルタで崩れない理屈、次世代版側で触れるパラメータの話は、精度編でまとめて書きます。
● 型番で引く
意味検索は「だいたいこの辺」を拾うのは得意ですが、文字列を指名するのは苦手です。
ベクトルには文字列が残っていないからです。
S3 Vectors にはこれを補う手段がありません。
OpenSearch には本文の text フィールドがあるので、match_phrase で引けます。
固有名詞や型番を含むクエリを 8 本用意して、上位 5 件に狙ったチャンクが入っていたかを見ました。
|
クエリ |
期待する語 |
S3 Vectors |
次世代版 意味検索 |
次世代版 キーワード検索 |
|
OpenSearch Serverless が 0 OCU に落ちるまでの時間 |
10 分 |
○ |
○ |
○ |
|
S3 Vectors のインデックスで後から変えられない設定 |
次元 |
× |
× |
○ |
|
CWE-79 |
CWE-79 |
○ |
○ |
○ |
|
Titan Embed の最大トークン数 |
8192 |
○ |
○ |
○ |
|
フィルタ付きクエリに必要な権限 |
GetVectors |
○ |
○ |
○ |
|
検索時に候補をいくつ抱えて探索するかのつまみ |
ef_search |
○ |
○ |
○ |
|
東京駅の住所 |
丸の内 |
○ |
○ |
○ |
|
コレクショングループを作るコマンド |
create-collection-group |
○ |
○ |
○ |
意味検索も 8 本中 7 本当たっていて、思ったより健闘しています。
ただ、当たり方の中身を見ると素直には喜べませんでした。そこは別の記事に書きます。キーワード検索は 8 本とも当たります。
意味検索どうし(S3 Vectors と次世代版)は同じ動きで、差はキーワード検索の列にだけ出ます。当たり前の結果ですが、「同じ検索ができる」と思って S3 Vectors を選ぶと、ここで詰まります。
Bedrock のナレッジベースでも、ハイブリッド検索が選べるのは OpenSearch Serverless・RDS・MongoDB のときだけで、S3 Vectors ではセマンティック検索のみです。
● この検証の費用
|
|
内訳 |
USD |
|
次世代版 Search OCU |
0.467 OCU 時間 × $0.334(最大 2 OCU) |
0.156 |
|
次世代版 Indexing OCU |
0.2 OCU 時間 × $0.334(最大 1 OCU) |
0.067 |
|
S3 Vectors 投入 |
66.5 MB × $0.219/GB |
0.015 |
|
S3 Vectors クエリ |
416 回 × $0.0027/1,000 |
0.001 |
|
S3 Vectors ストレージ |
11.5 MB × 1 時間ぶん |
0.000 |
|
合計 |
|
0.239(約 36 円) |
次世代版の OCU 時間は、CloudWatch の 1 分ごとの OCU 数を合計して出しました(1 分 = 1/60 時間として換算)。
単価は東京の $0.334/OCU 時(us-east-1 は $0.24 なので、海外の記事の数字をそのまま使うと 3 割ずれます)。
GPU 加速(Vector Index Acceleration)は別項目で請求されます。
金額そのものより、構造の違いのほうが大事だと思っています。
次世代版は「動いていた時間」に課金され、S3 Vectors は「投げたリクエストと置いたバイト」に課金されます。
1 時間かけて 100 回検索しても、5 分で 100 回検索しても、S3 Vectors は同じ値段です。
次世代版は前者のほうが高い。
逆に、1 時間に 10 万回検索するなら次世代版は OCU が張り付くだけで、S3 Vectors はリクエスト課金が積み上がります。どちらが安いかは、クエリの「数」ではなく「密度」で決まります。
■ 触れないもの
S3 Vectors のインデックスは、作ったあとに次元数と距離関数を変えられません。
HNSW の m や ef_search にあたるものは、そもそも存在しません。
中で何のアルゴリズムが動いているかも公開されていません。返す件数は 1 リクエストで 100 件まで、ページングして 10,000 件まで(2026 年 6 月に 100 件から拡大)。
フィルタに使えるメタデータは 1 ベクトルあたり 2 KB までです。
では OpenSearch なら全部触れるのか、というと、次世代版ではそうでもありませんでした。マッピングをいくつか投げてみると、指定そのものが弾かれる項目があり、Serverless の次世代版は思ったより触れるところが少ない。
何が書けて何が弾かれるか、書けたパラメータが本当に効くかは、別の記事に載せます。
全部触りたければ、Serverless ではなくマネージドドメインです。マネージドドメインなら engine に s3vector を指定して、ストレージだけ S3 Vectors に置くこともできます。「エンジン」と「置き場」を組み合わせる構成も可能です。
■ で、どっちを使うか
読むだけの用途、たまにしか叩かない、キーワードで指名する必要がない。この 3 つが揃うなら S3 Vectors で十分です。
精度は変わらず、置き場として非常に安く、放置しても何も起きません。Bedrock のナレッジベースの裏に置くならなおさらで、ハイブリッド検索を使わないならこちらが楽です。
「これらの文献から文字列で引きたい」「日本語の形態素解析を効かせたい」のどちらかがあるなら、OpenSearch です。
次世代版は放置すれば 0 OCU に落ちるので、以前のように「置いておくだけで月 350 ドル」ではなくなりました。ただし一発目に十数秒かかることと、投入してから検索に反映されるまで十数秒あることは受け入れる必要があります。
■ ハマったところ
精度を測る側で踏んだ罠は精度編にまとめたので、ここでは構築と権限まわりの 4 つです。
1. CLI に create-collection-group がない 手元の AWS CLI が古くて(2.32.14)、create-collection-group がありませんでした。boto3 のほうが先に対応していたので、今回は全部 boto3 で叩いています。ブログ検索編で「コレクショングループが CLI で消せない」と書いたのは、たぶんこれが原因です。
2. フィルタを付けた途端に 403 S3 Vectors でフィルタ付きの QueryVectors を投げるには、s3vectors:QueryVectors に加えて s3vectors:GetVectors の許可が要ります。フィルタなしでは通るので、最初は権限の問題だと気づきませんでした。
3. 法令の条文を入れたら「重複キー」 条文を 1 条 1 ベクトルで S3 Vectors に入れたら弾かれました。本則と附則で条番号が重なります。キーを連番にして解決しました。
4. 作った直後のコレクションに 403 次世代版のデータアクセスポリシーは、反映に 30〜60 秒かかります。作った直後に叩くと 403 で、これも権限の書き方を疑って時間を溶かしました。
■ 後片付け
次世代版はコレクション → コレクショングループ → 3 ポリシーの順で消します。コレクションが消え切る前にグループを消そうとすると弾かれるので、待ってから。S3 Vectors はインデックス → バケットの順です。放置しても OCU は 0 に落ちますが、ストレージの課金は続くので消し忘れないように。
■ まとめ
S3 Vectors と OpenSearch の違いは、精度でもコストでもなく、「ベクトルの周りに何があるか」でした。S3 Vectors はベクトルだけを持ち、近いものを返します。OpenSearch はドキュメントを持ち、その中の 1 フィールドがベクトルです。だから S3 Vectors では本文が荷物になり、OpenSearch では本文が検索対象になります。
数字で見ると、意味検索だけの精度は 0.975 対 0.981 で同じ。差が出たのは、作ってから検索できるまでの手数と、投入が反映されるまでの秒数と、放置後の一発目と、型番で指名したときでした。
私はこの検証で初めて「S3 Vectors は検索エンジンではない」と腑に落ちました。名前に S3 と付いている時点で当たり前なんですが、比較記事を読んでいるうちに、同じ土俵のものだと思い込んでいたようです(笑)
※この記事は 2026 年 9 月時点の仕様・料金に基づきます。数字は私のコーパス(技術記事+法令条文+Wikipedia、2,798 チャンク)での実測値で、データセットによって変わります。単価は東京リージョンのものです。