🤖

社内AI活用推進のためにAWS S3 Vectorsを使ってみました!(データのベクトル化と投入、検索まで)

に公開

はじめに

株式会社フクロウラボで情シスを担当している高島です。
最近はゼロトラスト推進やSaaS運用・改善にとどまらず、事業をドライブするために「攻めの情シス」業務に注力しようとしています。AI推進を行うべく、社内に点在する膨大なナレッジ(Slack、Notion、PDF資料など)をAIエージェントに学習させ、業務効率化を図るミッションを掲げています。

最新のLLMも社内の固有ルールは知らないため、当然「RAG (Retrieval-Augmented Generation)」アーキテクチャの採用を決定しました。RAGアーキテクチャを採用する上で、誰もが直面するのが「ベクトルDBをどうするか」という問題です。
自前でOpenSearchを構築・運用するのはインフラコストが重すぎますし、PineconeのようなフルマネージドSaaSは(非常に優秀ですが)外部サービス連携の手間やAPIキー管理、セキュリティ承認という別のハードルがあります。

そんな中、2025年7月15日AWSよりAmazon S3 Vectorsのプレビュー版がリリースされました!
これはなんだか便利そうだな?ということで早速使用してみましたので、
本記事は、このS3 Vectorsを早速使い、OpenAIでベクトル化したデータをS3に投入 (put_vectors) し、CLIで投入確認 (list_vectors)、Pythonで検索 (query_vectors) するまでの詳細をお伝えさせていただきます。

【Step 1】準備:S3コンソールでインデックスを作成する

通常のS3でのバケット作成と同様にこの作業はAWSコンソールから数クリックで完了し、コードもサーバー構築も不要です。
まず、AWSのサービス一覧からAmazon S3を選択します。
サイドメニューから、「ベクトルバケット」を選択しベクトルバケット名を入力、作成します。ベクトルバケットの作成_1
ベクトルバケットの作成_2

次に、作成したベクトルバケットをクリックし、ベクトルバケットにインデックスを作成します。
インデックスの作成_1

ここで求められるのは、主に以下の3点です。

  1. インデックス名: 任意(例: slack-index
  2. ベクトルの次元数: 使用するEmbeddingモデルに合わせる
    今回はOpenAIの最新モデル text-embedding-3-large を使うため、次元数は 3072 を指定しました。
    text-embedding-3-small を使う場合は、次元数は 1536 を指定します。
  3. 距離メトリック: コサインを選択(テキストの「意味」の類似性(方向)を測るのに最適なため、RAGにおいては主流)
    インデックス作成_2

これだけです。VPCやセキュリティグループの設定に悩む必要は一切ありません。このS3バケットはベクトルデータを格納・検索できる状態になりました。

【Step 2】データ投入:Python (boto3) で put_vectors を叩く

次に、Slackから取得したナレッジ(テキストデータ)をベクトル化し、S3 Vectorsに投入します。今回はPythonスクリプトを実行しました。
核心部は、boto3 (AWS SDK for Python) の s3vectors クライアントを使って、put_vectors APIを直接呼び出す部分です。
SlackReaderでのデータロードや OpenAIEmbedding でのベクトル化の前処理は完了しているものとします)

import boto3
import logging
import os
import hashlib

# --- (前処理:Slackデータロードとベクトル化は完了済みと仮定) ---
# ...
# nodes = [...]  # (例: LlamaIndexでチャンク分割されたSlackメッセージNode)
# vectors = [...] # (例: OpenAIでベクトル化されたベクトルのリスト)
# ...

# --- 1. ★ S3 Vectors クライアントの初期化 ---
# S3 Vectors プレビュー対応リージョンを指定
AWS_REGION = "us-east-1" 
s3vectors_client = boto3.client("s3vectors", region_name=AWS_REGION)

logging.basicConfig(level=logging.INFO) # シンプルなログ設定
logger = logging.getLogger(__name__)

# --- 2. ★ S3 Vectors投入形式への整形 ---
vector_entries = []
for i, node in enumerate(nodes):
    
    # ★ 検索時に一緒に返してほしいリッチな情報を metadata に格納
    metadata = node.metadata.copy() # (Slackの 'ts', 'channel', 'url' など)
    metadata["text_snippet"] = node.get_content()[:500] # 回答生成に使うスニペット
    
    entry = {
        "key": f"slack#{metadata.get('channel')}#{metadata.get('ts')}#{i}", # 一意なキー
        "data": {"float32": [float(x) for x in vectors[i]]}, # ベクトル本体
        "metadata": metadata # ★ 重要なメタデータ
    }
    vector_entries.append(entry)

# --- 3. ★ `put_vectors` APIの実行 ---
if vector_entries:
    logger.info(f"Putting {len(vector_entries)} vector entries...")
    
    # (実際には100件ずつのバッチ処理が必要です)
    s3vectors_client.put_vectors(
        vectorBucketName=os.environ.get("VECTOR_BUCKET_NAME"),
        indexName=os.environ.get("VECTOR_INDEX_NAME"),
        vectors=vector_entries 
    )
    
    logger.info("✅ 投入完了。")
else:
    logger.info("投入するベクトルがありませんでした。")

【Step 3】投入確認:AWS CLI で list-vectors を試す

Step 2でデータが正常に投入された(はずな)ので、インデックスの中身を直接確認します。
get-vectors(キー指定)でも確認できますが、ここではインデックス内のデータを一覧表示する list-vectors コマンドを使ってみました。

aws s3vectors list-vectors \
    --vector-bucket-name test-bucket \
    --index-name slack-index \
    --return-data \
    --return-metadata

実行結果

コマンドを実行すると、インデックスに格納されているキーと、その中身(ベクトルとメタデータ)が一覧で返ってきました。

{
    "vectors": [
        {
            "key": "slack#C01234#1678886400.000100#0",
            "data": {
                "float32": [0.012, -0.045, ... (3072次元続く) ...]
            },
            "metadata": {
                "ts": "1678886400.000100",
                "channel": "C01234",
                "source": "slack",
                "url": "[https://your-slack.com/archives/C01234/p1678886400000100](https://your-slack.com/archives/C01234/p1678886400000100)",
                "text_snippet": "【総務】経費精算の締め日は毎月25日です。遅れないようご注意ください。"
            }
        },
        {
            "key": "slack#C05678#1678000000.000200#0",
            "data": { "...": "..." },
            "metadata": { "...": "..." }
        }
    ],
    "nextToken": "..." 
}

【Step 4】検索テスト:query_vectors の呼び出し (Python)

インデックスにデータがあることを確認できたので、いよいよRAGの心臓部である「意味による類似検索」を行います。
Step 3ではCLIを使いましたが、ここではAIエージェント(Webアプリ)のバックエンドで動かすことを想定し、Python (boto3) で query_vectors APIを呼び出すサンプルコードを紹介します。

import boto3
import os
from llama_index.embeddings.openai import OpenAIEmbedding

# --- (クライアントとモデルの初期化は完了済みと仮定) ---
AWS_REGION = "us-east-1"
s3vectors_client = boto3.client("s3vectors", region_name=AWS_REGION)
embed_model = OpenAIEmbedding(model="text-embedding-3-large", api_key=os.environ.get("OPENAI_API_KEY"))

# 環境変数から取得する代わりに、サンプルとしてハードコード
VECTOR_BUCKET_NAME = "test-bucket" 
VECTOR_INDEX_NAME = "slack-index"

def search_knowledge(query_text: str):
    """
    ユーザーの質問テキストを受け取り、S3 Vectorsで検索して
    関連するノード(メタデータ含む)を返す関数
    (エラーハンドリング省略版)
    """
    if not query_text:
        return "質問が入力されていません。"

    # --- 1. 質問をベクトル化 ---
    query_vector = embed_model.get_text_embedding(query_text)

    # --- 2. ★ `query_vectors` APIの実行 ---
    response = s3vectors_client.query_vectors(
        vectorBucketName=VECTOR_BUCKET_NAME,
        indexName=VECTOR_INDEX_NAME,
        queryVector={'float32': query_vector}, # 検索ベクトルを指定
        topK=5,                                # 上位5件を取得
        returnMetadata=True                    # ★ metadataを必ずTrueにする
    )
    
    nodes = response.get('vectors', [])
    if not nodes:
        return "参照ソースが見つかりませんでした。"
    
    # 検索結果(ノードのリスト)を返す
    return nodes

# --- 実行デモ ---
user_question = "経費精算の締め日はいつ?"
search_results = search_knowledge(user_question)

# 検索結果(JSONライクな辞書のリスト)を表示
print(search_results)

実行結果 (コンソール出力例)

上記の print(search_results) を実行すると、Step 3のCLI実行時とほぼ同じJSON構造(key, score, metadata を持つ辞書のリスト)がPythonで取得できます。

[
    {
        "key": "slack#C01234#1678886400.000100#0",
        "score": 0.895,
        "metadata": {
            "ts": "1678886400.000100",
            "channel": "C01234",
            "text_snippet": "【総務】経費精算の締め日は毎月25日です。遅れないようご注意ください。"
        }
    },
    ... (他4件の結果) ...
]

最後に

最後までお読みいただきありがとうございました!
今回S3 Vectorsを試してみて、AI推進としてRAG構築を始めるハードルが、思ったよりもずっと低かったと感じました。
理由の一つとして、開発・認証がAWSエコシステムで完結する点にあります。

  • boto3 で完結: 開発者は、使い慣れた boto3 (AWS SDK) で client("s3vectors") を呼び出すだけです。
  • IAMロールで完結: 認証もIAMロール/ポリシーで一元管理できます。
    Pineconeのような外部SaaSや、自前でOpenSearchクラスターを導入する場合、新しいSDKの学習、APIキーの管理、あるいはDB自体のインフラ運用といった「RAGの本質ではない作業」が発生してしまいます。
    S3 Vectorsを選択すると、これら「別のベクトルDBを学び、設置する」必要がなくなり、設計が非常にシンプルになりました。
    その結果、本来やるべき「どのデータをベクトル化するか」「どういうmetadataを持たせるか」「どう検索(query_vectors)するか」といった、AIエージェントの精度向上に直結する業務にフォーカスすることができるのではないでしょうか。

(本記事執筆時点では)S3 Vectorsはまだ us-east-1 us-east-2 us-west-2 eu-central-1 ap-southeast-2 でのみ利用可能ですが、AWSネイティブでRAGを構築したいチームにとって、非常に強力な選択肢になると存じます。(個人的にはこれからのベクトルDBの主流になるのではと感じています!)
この記事が、AI推進を担当されている情シスの方や、RAG構築に悩むエンジニアの方の一助となれば幸いです。

フクロウラボ エンジニアブログ

Discussion