Amazon S3 Vectorsについて調査してまとめてみた

目次

Amazon S3 Vectors 概要・仕様まとめ

本書は、Amazon S3 のサーバーレスベクトルストレージ機能である Amazon S3 Vectors の全体概要、アーキテクチャ、全API仕様、メタデータフィルタリング、制約事項、料金、AWS Lambda対応実装例、および公式参照リンクをまとめたドキュメントです。


目次

  1. サービス概要
  2. アーキテクチャと階層構造
  3. 主要コンポーネント詳細
  4. 全API & SDK リファレンス一覧(全19種完全網羅)
  5. メタデータフィルタリング仕様
  6. 制限と制約(Quotas & Limits)
  7. 料金体系(Pricing)
  8. AWS Lambda 実装コード(ベクトルデータ操作 API 5種完全対応)
  9. ユースケースと選定基準
  10. 参照URL・公式リンク集

1. サービス概要

▲ 目次に戻る

Amazon S3 Vectors は、AIエージェント、推論、RAG(Retrieval-Augmented Generation / 検索拡張生成)、セマンティック検索向けに最適化された完全サーバーレスのベクトルストレージ機能です。

主な特徴とメリット

  • 完全サーバーレス・ゼロ管理: インスタンスやクラスタのプロビジョニング、キャパシティプランニング、インデックス再構築のインフラ管理が不要。
  • S3同等の堅牢性と可用性: 99.999999999%(11ナイン)のデータ耐久性と強い整合性(Read-after-write consistency)を提供。
  • レイテンシ特性: アクセス頻度の低いクエリでサブ秒台(1秒未満)、頻度の高いクエリでは最速約100ミリ秒台のレイテンシを実現。
  • 優れたコスト効率: クエリ頻度が中・低頻度なRAG用途において、常時起動型のベクトルDB(Amazon OpenSearch Service、Pinecone等)と比較して最大90%のコスト削減が可能。
  • 一貫したセキュリティモデル: S3既存のバケットポリシーや IAM ポリシーをそのまま適用可能。

2. アーキテクチャと階層構造

▲ 目次に戻る

S3 Vectors は以下の階層構造でリソースを管理します。

AWS Account / Region
 └── Vector Bucket (ベクトル専用バケット)
      └── Vector Index (次元数・距離指標・データ型を設定)
           └── Vectors (Key + float32 Embedding + Metadata)

3. 主要コンポーネント詳細

▲ 目次に戻る

(1) Vector Bucket(ベクトルバケット)

  • ベクトルデータおよびインデックスを格納するための専用バケットタイプ。
  • 通常の S3 オブジェクトバケットとは区別され、専用のバケット管理 API / ポリシーで管理されます。
  • アカウント・リージョンごとに最大 10,000 個まで作成可能(作成・維持費は無料)。

(2) Vector Index(ベクトルインデックス)

  • ベクトルバケット内に作成される検索インデックス。
  • インデックス作成時に以下のパラメーターを設定します:
    • Dimension(次元数): 1 〜 4,096
    • DataType(データ型): float32
    • DistanceMetric(距離指標): COSINE(コサイン類似度)、EUCLIDEAN(ユークリッド距離)、DOT_PRODUCT(内積)
    • NonFilterableMetadataKeys(非フィルタメタデータキー): 検索フィルタには使用しないが、ドキュメント本文等を保存するためのキー(最大10個)。

(3) Vectors(ベクトルデータ)

  • 各ベクトルは以下の3要素で構成されます:
    • Key: インデックス内で一意な識別子(文字列)。
    • Data: float32 配列の数値埋め込みベクトル。
    • Metadata: 属性情報(最大40KB、キー数は最大50個)。

4. 全API & SDK リファレンス一覧(全19種完全網羅)

▲ 目次に戻る

S3 Vectors は専用の名前空間(API: s3vectors:* / boto3: s3vectors)を使用します。
提供されている 全19種のAPI をカテゴリ別に完全に網羅しています。

(1) ベクトルデータ操作 API(5種)

API 名 boto3 メソッド AWS API リファレンス 概要
PutVectors put_vectors PutVectors ベクトルの一括登録・更新(1リクエスト最大500件)
QueryVectors query_vectors QueryVectors クエリベクトルによる類似度検索(ANN)の実行
GetVectors get_vectors GetVectors キー指定によるベクトルデータ・メタデータの取得
DeleteVectors delete_vectors DeleteVectors キー指定によるベクトルの一括削除
ListVectors list_vectors ListVectors インデックス内のベクトルキー一覧の取得(ページネーション対応)

(2) バケット管理 API(4種)

API 名 boto3 メソッド AWS API リファレンス 概要
CreateVectorBucket create_vector_bucket CreateVectorBucket 新しいベクトルバケットを作成
GetVectorBucket get_vector_bucket GetVectorBucket ベクトルバケットの情報を取得
ListVectorBuckets list_vector_buckets ListVectorBuckets アカウント内のベクトルバケット一覧を取得
DeleteVectorBucket delete_vector_bucket DeleteVectorBucket ベクトルバケットを削除

(3) バケットポリシー管理 API(3種)

API 名 boto3 メソッド AWS API リファレンス 概要
PutVectorBucketPolicy put_vector_bucket_policy PutVectorBucketPolicy ベクトルバケットにリソースベースのアクセスポリシーを設定
GetVectorBucketPolicy get_vector_bucket_policy GetVectorBucketPolicy ベクトルバケットのポリシードキュメントを取得
DeleteVectorBucketPolicy delete_vector_bucket_policy DeleteVectorBucketPolicy ベクトルバケットのポリシーを削除

(4) インデックス管理 API(4種)

API 名 boto3 メソッド AWS API リファレンス 概要
CreateIndex create_index CreateIndex インデックスの新規作成
GetIndex get_index GetIndex インデックスの設定・ステータスを取得
ListIndexes list_indexes ListIndexes バケット内のインデックス一覧を取得
DeleteIndex delete_index DeleteIndex インデックスを削除

(5) タグ管理 API(3種)

API 名 boto3 メソッド AWS API リファレンス 概要
TagResource tag_resource TagResource バケットやインデックスにタグを付与
UntagResource untag_resource UntagResource バケットやインデックスからタグを削除
ListTagsForResource list_tags_for_resource ListTagsForResource リソースに付与されているタグ一覧を取得

5. メタデータフィルタリング仕様

▲ 目次に戻る

🔗 メタデータフィルタリング (Metadata filtering) - ユーザーガイド

(1) 同時評価方式(In-Tandem Filtering)

S3 Vectors は類似度計算とメタデータフィルタリングを同時に実行します。検索後にフィルタで除外する「Post-filtering」とは異なり、条件を満たす上位 $K$ 件を確実に抽出できます。

(2) フィルタ可能 vs フィルタ不可メタデータ

区分 フィルタ可能(Filterable) フィルタ不可(Non-Filterable)
主な用途 カテゴリ、日付、ユーザーID等のクエリ絞り込み条件 ドキュメント本文、長文サマリー等のデータ保持
サイズ上限 最大 2 KB (2,048 bytes) / ベクトル 合計40KBの残枠(最大約38KB) / ベクトル
キー数上限 合計50キー以内 最大 10 キー(インデックス作成時に指定)
クエリ利用 filter 句で指定可能 filter 句では指定不可(取得のみ)

2KB超過エラーへの対策: Amazon Bedrock 等と連携する際、自動付与されるメタデータが 2KB を超えてエラーになるケースがあります。本文等の長文テキストは必ずインデックス作成時に NonFilterableMetadataKeys に指定してください。

(3) サポートされる演算子(MongoDB互換構文)

演算子 説明 正しい構文例
$eq 完全一致 {"category": {"$eq": "technical"}}
$ne 不一致 {"status": {"$ne": "deleted"}}
$gt / $gte より大きい / 以上 {"year": {"$gte": 2025}}
$lt / $lte より小さい / 以下 {"price": {"$lte": 1000}}
$in 配列内のいずれかに一致 {"tag": {"$in": ["aws", "cloud"]}}
$nin 配列内のいずれにも一致しない {"dept": {"$nin": ["hr", "legal"]}}
$exists フィールドの存在確認 {"author": {"$exists": true}}
$and / $or 論理AND / 論理OR(複数条件指定時は必須 {"$and": [{"category": {"$eq": "technical"}}, {"year": {"$gte": 2026}}]}

[!WARNING]
複数条件指定時の重要ルール(Invalid filter エラーの防止):
S3 Vectors では、トップレベルに複数のキーを並べる暗黙的 AND(例: {"category": {...}, "year": {...}})は構文エラー(Invalid filter)となります。
複数のフィルタ条件を指定する場合は、必ず {"$and": [ {条件1}, {条件2} ]} または {"$or": [ ... ]} の配列形式でラップしてください。

必要な IAM 権限: メタデータフィルタを指定する場合、または検索結果にメタデータを含めて取得(returnMetadata=True)する場合は、s3vectors:QueryVectors に加えて s3vectors:GetVectors 権限が必要です。


6. 制限と制約(Quotas & Limits)

▲ 目次に戻る

🔗 制限と制約 (Limitations and restrictions) - ユーザーガイド

項目 上限・仕様値
ベクトルバケット数 10,000 / リージョン / アカウント
ベクトルインデックス数 10,000 / ベクトルバケット
ベクトル格納数 最大 20億 (2 Billion) / インデックス
次元数 (Dimension) 1 〜 4,096 次元
サポートデータ型 float32
距離指標 COSINE, EUCLIDEAN, DOT_PRODUCT
総メタデータサイズ 最大 40 KB / ベクトル
フィルタ可能メタデータサイズ 最大 2 KB (2,048 bytes) / ベクトル
メタデータキー数 合計最大 50 キー / ベクトル
非フィルタメタデータキー数 最大 10 キー / インデックス
PutVectors バッチ件数 最大 500 件 / 1リクエスト
QueryVectors topK 取得件数 最大 100 件 / 1クエリ
リクエストレート制限 約 1,000 write req/sec/index(超過時は 429 TooManyRequestsException

7. 料金体系(Pricing)

▲ 目次に戻る

🔗 Amazon S3 料金表 - AWS 公式

S3 Vectors は完全な従量課金制です(プロビジョンド料金なし)。

課金項目 単価の目安(us-east-1等) 課金対象
ベクトルバケット作成・維持 無料 ($0.00) バケット自体の維持コストなし
論理ストレージ料金 約 $0.06 / GB-月 ベクトルデータおよびメタデータの合計容量
データ書き込み (PUT) 約 $0.20 / GB アップロードされたデータ転送量
クエリ API 料金 約 $2.50 / 100万クエリ API リクエスト回数
クエリデータ処理料金 インデックスサイズに応じた従量課金 1,000万件超の大規模インデックス向けに最大80%割引

8. AWS Lambda 実装コード(ベクトルデータ操作 API 5種完全対応)

▲ 目次に戻る

S3 Vectors の 「ベクトルデータ操作 API(5種)」 である PutVectors, QueryVectors, GetVectors, DeleteVectors, ListVectors の全操作に対応した AWS Lambda 実装です。
boto3 の仕様(引数名が lowerCamelCase: vectorBucketName, indexName 等)に完全準拠しており、AWS Lambda コンソールに直接貼り付けて即座に実行できます。


(1) Lambda 実行に必要な IAM ポリシー(IAM Policy)

Lambda 関数の実行ロール(Execution Role)にアタッチする IAM ポリシーです。
※ AWS マネージドポリシー AWSLambdaBasicExecutionRole に加え、以下の S3 Vectors 権限が必要です。

① 最小権限ポリシー(推奨・特定バケット/インデックス限定)

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "S3VectorsDataPlanePermissions",
      "Effect": "Allow",
      "Action": [
        "s3vectors:PutVectors",
        "s3vectors:QueryVectors",
        "s3vectors:GetVectors",
        "s3vectors:DeleteVectors",
        "s3vectors:ListVectors"
      ],
      "Resource": [
        "arn:aws:s3vectors:*:*:bucket/my-knowledge-vector-bucket",
        "arn:aws:s3vectors:*:*:bucket/my-knowledge-vector-bucket/index/*"
      ]
    },
    {
      "Sid": "CloudWatchLogsLogging",
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:*:*:log-group:/aws/lambda/*"
    }
  ]
}

[!IMPORTANT]
QueryVectors 実行時の必須権限:
s3vectors:QueryVectors でメタデータフィルタを使用する場合、または検索結果にメタデータを含めて取得(returnMetadata=True)する場合は、s3vectors:QueryVectors だけでなく s3vectors:GetVectors の権限も必須となります。

② 開発・検証用ポリシー(全リソース対象)

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "S3VectorsDataPlaneAllAccess",
      "Effect": "Allow",
      "Action": [
        "s3vectors:PutVectors",
        "s3vectors:QueryVectors",
        "s3vectors:GetVectors",
        "s3vectors:DeleteVectors",
        "s3vectors:ListVectors"
      ],
      "Resource": "*"
    }
  ]
}

(2) AWS Lambda ハンドラーコード (Python)

"""
AWS Lambda 関数: Amazon S3 Vectors ベクトルデータ操作ハンドラー
- 対象API(5種):
    1. PutVectors    : ベクトルの一括登録・更新(最大500件/リクエスト)
    2. QueryVectors  : 類似度検索 (ANN) + In-Tandem メタデータフィルタリング
    3. GetVectors    : キー指定によるベクトルデータ・メタデータの取得
    4. DeleteVectors : キー指定によるベクトルデータの一括削除
    5. ListVectors   : インデックス内のベクトルキー一覧取得(ページネーション対応)
- 全API呼び出しおよびLambdaリクエスト/レスポンスの全量ログ出力を実装
- boto3 S3Vectors 仕様準拠(引数名は lowerCamelCase)
- API Gateway プロキシ統合と Lambda 直接呼び出しの両方に対応
"""

import json
import os
import logging
from typing import Any, Dict, List
import boto3
from botocore.exceptions import ClientError, ParamValidationError

# CloudWatch Logs へのログ出力設定
logger = logging.getLogger()
logger.setLevel(logging.INFO)

# デフォルト設定値(Lambda環境変数から取得、未指定時はフォールバック)
DEFAULT_REGION = os.environ.get("AWS_REGION", "ap-northeast-1")
DEFAULT_BUCKET_NAME = os.environ.get("VECTOR_BUCKET_NAME", "my-knowledge-vector-bucket")
DEFAULT_INDEX_NAME = os.environ.get("VECTOR_INDEX_NAME", "document-embeddings-index")

# boto3 s3vectors クライアント初期化
# ※ コールドスタート対策としてハンドラー外で一度だけ初期化し接続を再利用
# ※ boto3 >= 1.39.16 が必要
s3vectors_client = boto3.client("s3vectors", region_name=DEFAULT_REGION)

def build_response(status_code: int, body: Dict[str, Any]) -> Dict[str, Any]:
    """
    API Gateway プロキシ統合互換のレスポンス辞書を構築する共通ヘルパー関数。
    返却するレスポンスの全量(ステータスコード、ヘッダー、ボディ)をログに出力する。

    Args:
        status_code (int): HTTPステータスコード (200, 400, 500等)
        body (Dict[str, Any]): レスポンスボディとして返却する辞書データ

    Returns:
        Dict[str, Any]: API Gateway 仕様に準拠したレスポンス辞書
    """
    response = {
        "statusCode": status_code,
        "headers": {
            "Content-Type": "application/json",
            "Access-Control-Allow-Origin": "*",
            "Access-Control-Allow-Headers": "Content-Type,Authorization",
            "Access-Control-Allow-Methods": "OPTIONS,POST,GET"
        },
        "body": json.dumps(body, ensure_ascii=False, default=str)
    }
    # レスポンス全量のログ出力
    logger.info(f"[Lambda Response Output] {json.dumps(response, ensure_ascii=False, default=str)}")
    return response

# ==============================================================================
# 1. PutVectors API: ベクトルのバッチ登録・更新
# ==============================================================================
def handle_put_vectors(bucket_name: str, index_name: str, vectors: List[Dict[str, Any]]) -> Dict[str, Any]:
    """
    指定されたインデックスへベクトルデータを一括登録(UPSERT)する。

    実装意図:
    - S3 Vectors の仕様上、1リクエストあたりの最大登録数は500件のため事前にバリデーションを実施。
    - 各ベクトルの float32 埋め込みデータが不正な形式でないかを検査し、純粋な float 型リストへ変換。
    - boto3 引数名は lowerCamelCase (vectorBucketName, indexName, vectors) を指定。
    - S3 Vectors API 送信リクエスト全量および受信レスポンス全量をログ出力。
    """
    if not vectors:
        raise ValueError("Parameter 'vectors' is required and must not be empty.")
    if len(vectors) > 500:
        raise ValueError(f"Vector batch size ({len(vectors)}) exceeds the maximum allowed limit of 500.")

    formatted_vectors = []
    for item in vectors:
        key = item.get("key")
        data = item.get("data", {})
        metadata = item.get("metadata", {})

        # 必須属性の存在チェック
        if not key or "float32" not in data:
            raise ValueError(f"Each vector item must contain 'key' and 'data.float32'. Invalid item: {item}")

        # 数値リストを確実に float 型へキャスト
        float_values = [float(val) for val in data["float32"]]

        formatted_vectors.append({
            "key": str(key),
            "data": {"float32": float_values},
            "metadata": metadata
        })

    # APIリクエスト全量の構築とログ出力
    api_request_payload = {
        "vectorBucketName": bucket_name,
        "indexName": index_name,
        "vectors": formatted_vectors
    }
    logger.info(f"[PutVectors API Request] Payload: {json.dumps(api_request_payload, ensure_ascii=False, default=str)}")

    # API呼び出し
    response = s3vectors_client.put_vectors(**api_request_payload)

    # APIレスポンス全量のログ出力
    logger.info(f"[PutVectors API Response] Payload: {json.dumps(response, ensure_ascii=False, default=str)}")

    return {
        "action": "put_vectors",
        "processedCount": len(formatted_vectors),
        "rawApiResponse": response
    }

# ==============================================================================
# 2. QueryVectors API: 類似度検索 (ANN) + メタデータフィルタ
# ==============================================================================
def handle_query_vectors(
    bucket_name: str,
    index_name: str,
    query_vector: List[float],
    top_k: int = 5,
    metadata_filter: Dict[str, Any] = None,
    return_distance: bool = True,
    return_metadata: bool = True
) -> Dict[str, Any]:
    """
    クエリベクトルに基づく近似最近傍探索 (ANN) を実行する。

    実装意図:
    - S3 Vectors は探索とフィルタリングを同時に実行する In-Tandem Filtering を採用。
    - topK は 1〜100 の範囲に正規化して API 制約違反を防止。
    - メタデータフィルタやメタデータ返却には s3vectors:GetVectors 権限が必要。
    - S3 Vectors API 送信リクエスト全量および受信レスポンス全量をログ出力。
    """
    if not query_vector:
        raise ValueError("Parameter 'queryVector' is required for similarity search.")

    float_query = [float(val) for val in query_vector]
    top_k_clamped = min(max(1, int(top_k)), 100)

    query_params: Dict[str, Any] = {
        "vectorBucketName": bucket_name,
        "indexName": index_name,
        "queryVector": {"float32": float_query},
        "topK": top_k_clamped,
        "returnDistance": return_distance,
        "returnMetadata": return_metadata
    }

    # フィルタ条件が存在する場合の正規化とパラメータ設定
    if metadata_filter:
        # S3 Vectors の仕様上、トップレベルに複数条件がある場合は {"$and": [...]} 配列でラップが必要
        # 例: {"category": {"$eq": "technical"}, "year": {"$gte": 2026}} -> {"$and": [{"category": ...}, {"year": ...}]}
        if (
            isinstance(metadata_filter, dict)
            and len(metadata_filter) > 1
            and "$and" not in metadata_filter
            and "$or" not in metadata_filter
        ):
            logger.info("[QueryVectors] Auto-wrapping top-level multi-key filter into '$and' array for S3 Vectors compliance.")
            normalized_filter = {
                "$and": [{k: v} for k, v in metadata_filter.items()]
            }
        else:
            normalized_filter = metadata_filter

        query_params["filter"] = normalized_filter

    # APIリクエスト全量のログ出力
    logger.info(f"[QueryVectors API Request] Payload: {json.dumps(query_params, ensure_ascii=False, default=str)}")

    # API呼び出し
    response = s3vectors_client.query_vectors(**query_params)

    # APIレスポンス全量のログ出力
    logger.info(f"[QueryVectors API Response] Payload: {json.dumps(response, ensure_ascii=False, default=str)}")

    return {
        "action": "query_vectors",
        "resultsCount": len(response.get("vectors", [])),
        "vectors": response.get("vectors", []),
        "rawApiResponse": response
    }

# ==============================================================================
# 3. GetVectors API: キー指定によるベクトル・メタデータ取得
# ==============================================================================
def handle_get_vectors(
    bucket_name: str,
    index_name: str,
    keys: List[str],
    return_data: bool = True,
    return_metadata: bool = True
) -> Dict[str, Any]:
    """
    キーを指定してベクトルデータおよびメタデータを直接取得する。

    実装意図:
    - returnData / returnMetadata フラグにより、ベクトル数値データやメタデータの取得有無を制御可能。
    - 取得成功したベクトルリストと、インデックス内に存在しなかった notFoundKeys の双方を返却。
    - S3 Vectors API 送信リクエスト全量および受信レスポンス全量をログ出力。
    """
    if not keys:
        raise ValueError("Parameter 'keys' list is required and must not be empty.")

    api_request_payload = {
        "vectorBucketName": bucket_name,
        "indexName": index_name,
        "keys": keys,
        "returnData": return_data,
        "returnMetadata": return_metadata
    }
    # APIリクエスト全量のログ出力
    logger.info(f"[GetVectors API Request] Payload: {json.dumps(api_request_payload, ensure_ascii=False, default=str)}")

    # API呼び出し
    response = s3vectors_client.get_vectors(**api_request_payload)

    # APIレスポンス全量のログ出力
    logger.info(f"[GetVectors API Response] Payload: {json.dumps(response, ensure_ascii=False, default=str)}")

    return {
        "action": "get_vectors",
        "vectors": response.get("vectors", []),
        "notFoundKeys": response.get("notFoundKeys", []),
        "rawApiResponse": response
    }

# ==============================================================================
# 4. DeleteVectors API: キー指定によるベクトル一括削除
# ==============================================================================
def handle_delete_vectors(bucket_name: str, index_name: str, keys: List[str]) -> Dict[str, Any]:
    """
    指定されたキーに一致するベクトルデータをインデックスから削除する。

    実装意図:
    - 削除対象キー配列を受け取り、一括削除を実行して結果ステータスを返却。
    - S3 Vectors API 送信リクエスト全量および受信レスポンス全量をログ出力。
    """
    if not keys:
        raise ValueError("Parameter 'keys' list is required for deletion.")

    api_request_payload = {
        "vectorBucketName": bucket_name,
        "indexName": index_name,
        "keys": keys
    }
    # APIリクエスト全量のログ出力
    logger.info(f"[DeleteVectors API Request] Payload: {json.dumps(api_request_payload, ensure_ascii=False, default=str)}")

    # API呼び出し
    response = s3vectors_client.delete_vectors(**api_request_payload)

    # APIレスポンス全量のログ出力
    logger.info(f"[DeleteVectors API Response] Payload: {json.dumps(response, ensure_ascii=False, default=str)}")

    return {
        "action": "delete_vectors",
        "deletedKeys": keys,
        "rawApiResponse": response
    }

# ==============================================================================
# 5. ListVectors API: ベクトル一覧の取得(ページネーション完全対応)
# ==============================================================================
def handle_list_vectors(
    bucket_name: str,
    index_name: str,
    next_token: str = None,
    max_results: int = 100,
    return_data: bool = False,
    return_metadata: bool = False,
    fetch_all: bool = False,
    max_total_results: int = 1000
) -> Dict[str, Any]:
    """
    インデックス内に登録されているベクトル一覧をページネーション取得する。

    実装意図:
    - S3 Vectors の ListVectors レスポンス仕様は `vectors` キーにリスト([{"key": "..."}, ...])が格納される。
    - maxResults は 1〜500 の範囲に正規化(S3 Vectors の単一リクエスト上限は 500 件)。
    - returnData / returnMetadata フラグにより、キーだけでなくベクトル数値やメタデータも取得可能(要 s3vectors:GetVectors 権限)。
    - fetch_all=True を指定した場合、nextToken を自動走査して最大 max_total_results 件まで一括取得可能。
    """
    page_size = min(max(1, int(max_results)), 500)

    # 1. 全件自動取得モード (fetch_all=True)
    if fetch_all:
        all_vectors = []
        current_token = next_token
        page_count = 0

        logger.info(f"[ListVectors (fetchAll)] Starting auto-pagination on {bucket_name}/{index_name} (max_total={max_total_results})")

        while True:
            params: Dict[str, Any] = {
                "vectorBucketName": bucket_name,
                "indexName": index_name,
                "maxResults": page_size,
                "returnData": return_data,
                "returnMetadata": return_metadata
            }
            if current_token:
                params["nextToken"] = current_token

            # APIリクエスト全量のログ出力
            logger.info(f"[ListVectors API Request (Page {page_count + 1})] Payload: {json.dumps(params, ensure_ascii=False, default=str)}")
            response = s3vectors_client.list_vectors(**params)

            # APIレスポンス全量のログ出力
            logger.info(f"[ListVectors API Response (Page {page_count + 1})] Payload: {json.dumps(response, ensure_ascii=False, default=str)}")

            page_vectors = response.get("vectors", [])
            all_vectors.extend(page_vectors)
            page_count += 1
            current_token = response.get("nextToken")

            # ページごとの累積件数と進行状況をログ出力
            logger.info(f"[ListVectors (fetchAll Progress)] Page {page_count}: received {len(page_vectors)} vectors, totalCount: {len(all_vectors)}, nextToken: {current_token}")

            # 終了条件: 次ページトークンが存在しない、または安全上限件数に到達
            if not current_token or len(all_vectors) >= max_total_results:
                break

        logger.info(f"[ListVectors (fetchAll Completed)] totalPages: {page_count}, totalCount: {len(all_vectors)}, nextToken: {current_token}")
        extracted_keys = [item["key"] for item in all_vectors if isinstance(item, dict) and "key" in item]
        return {
            "action": "list_vectors",
            "mode": "fetchAll",
            "totalPages": page_count,
            "totalCount": len(all_vectors),
            "keys": extracted_keys,
            "vectors": all_vectors,
            "nextToken": current_token,
            "hasMore": bool(current_token)
        }

    # 2. 単一ページ取得モード (fetch_all=False)
    params: Dict[str, Any] = {
        "vectorBucketName": bucket_name,
        "indexName": index_name,
        "maxResults": page_size,
        "returnData": return_data,
        "returnMetadata": return_metadata
    }
    if next_token:
        params["nextToken"] = next_token

    # APIリクエスト全量のログ出力
    logger.info(f"[ListVectors API Request] Payload: {json.dumps(params, ensure_ascii=False, default=str)}")

    # API呼び出し
    response = s3vectors_client.list_vectors(**params)

    # APIレスポンス全量のログ出力
    logger.info(f"[ListVectors API Response] Payload: {json.dumps(response, ensure_ascii=False, default=str)}")

    vectors = response.get("vectors", [])
    extracted_keys = [item["key"] for item in vectors if isinstance(item, dict) and "key" in item]
    next_page_token = response.get("nextToken")

    return {
        "action": "list_vectors",
        "mode": "singlePage",
        "count": len(vectors),
        "keys": extracted_keys,
        "vectors": vectors,
        "nextToken": next_page_token,
        "hasNextPage": bool(next_page_token),
        "rawApiResponse": response
    }

# ==============================================================================
# メインエントリポイント: lambda_handler
# ==============================================================================
def lambda_handler(event: Dict[str, Any], context: Any) -> Dict[str, Any]:
    """
    AWS Lambda メインハンドラー
    - API Gateway プロキシペイロードおよびダイレクト呼び出しイベントを自動識別・パース
    - 要求された action に応じて各ベクトルデータ操作ハンドラーへルーティング
    """
    # Lambda呼び出しイベント全量のログ出力
    logger.info(f"[Lambda Event Input] {json.dumps(event, ensure_ascii=False, default=str)}")

    try:
        # 1. リクエストボディのパース処理
        payload = event
        if "body" in event and event["body"] is not None:
            if isinstance(event["body"], str):
                try:
                    payload = json.loads(event["body"])
                except json.JSONDecodeError:
                    return build_response(400, {"error": "Malformed JSON in request body."})
            elif isinstance(event["body"], dict):
                payload = event["body"]

        # クエリパラメータが存在する場合はマージ(GETリクエスト対応)
        if "queryStringParameters" in event and event["queryStringParameters"]:
            payload.update(event["queryStringParameters"])

        # 2. アクションパラメータの検証
        action = payload.get("action")
        if not action:
            return build_response(400, {
                "error": "Missing 'action' parameter.",
                "supported_actions": [
                    "put_vectors",
                    "query_vectors",
                    "get_vectors",
                    "delete_vectors",
                    "list_vectors"
                ]
            })

        # 3. 対象バケット名・インデックス名の決定
        bucket_name = payload.get("vectorBucketName", DEFAULT_BUCKET_NAME)
        index_name = payload.get("indexName", DEFAULT_INDEX_NAME)

        # 4. 各アクションへのディスパッチ
        if action == "put_vectors":
            vectors = payload.get("vectors", [])
            result = handle_put_vectors(bucket_name, index_name, vectors)

        elif action == "query_vectors":
            query_vector = payload.get("queryVector")
            top_k = payload.get("topK", 5)
            metadata_filter = payload.get("filter")
            return_distance = payload.get("returnDistance", True)
            return_metadata = payload.get("returnMetadata", True)
            result = handle_query_vectors(
                bucket_name=bucket_name,
                index_name=index_name,
                query_vector=query_vector,
                top_k=top_k,
                metadata_filter=metadata_filter,
                return_distance=return_distance,
                return_metadata=return_metadata
            )

        elif action == "get_vectors":
            keys = payload.get("keys", [])
            return_data = bool(payload.get("returnData", True))
            return_metadata = bool(payload.get("returnMetadata", True))
            result = handle_get_vectors(
                bucket_name=bucket_name,
                index_name=index_name,
                keys=keys,
                return_data=return_data,
                return_metadata=return_metadata
            )

        elif action == "delete_vectors":
            keys = payload.get("keys", [])
            result = handle_delete_vectors(bucket_name, index_name, keys)

        elif action == "list_vectors":
            next_token = payload.get("nextToken")
            max_results = int(payload.get("maxResults", 100))
            return_data = bool(payload.get("returnData", False))
            return_metadata = bool(payload.get("returnMetadata", False))
            fetch_all = bool(payload.get("fetchAll", False))
            max_total_results = int(payload.get("maxTotalResults", 1000))
            result = handle_list_vectors(
                bucket_name=bucket_name,
                index_name=index_name,
                next_token=next_token,
                max_results=max_results,
                return_data=return_data,
                return_metadata=return_metadata,
                fetch_all=fetch_all,
                max_total_results=max_total_results
            )

        else:
            return build_response(400, {
                "error": f"Unsupported action: '{action}'",
                "supported_actions": [
                    "put_vectors", "query_vectors", "get_vectors", "delete_vectors", "list_vectors"
                ]
            })

        return build_response(200, result)

    except ValueError as ve:
        # クライアント側の入力値バリデーションエラー (400 Bad Request)
        logger.error(f"[Validation Error] {ve}")
        return build_response(400, {"error": str(ve)})

    except ParamValidationError as pve:
        # boto3 のパラメータ型/キー名不正エラー (400 Bad Request)
        logger.error(f"[boto3 ParamValidationError] {pve}")
        return build_response(400, {
            "error": "Parameter validation failed",
            "detail": str(pve)
        })

    except ClientError as ce:
        # AWS S3 Vectors サービス側のエラー (403/404/429/500 等)
        error_code = ce.response.get("Error", {}).get("Code", "UnknownClientError")
        error_message = ce.response.get("Error", {}).get("Message", str(ce))
        status_code = ce.response.get("ResponseMetadata", {}).get("HTTPStatusCode", 500)

        logger.error(f"[AWS ClientError] Code: {error_code}, Message: {error_message}, FullResponse: {json.dumps(ce.response, ensure_ascii=False, default=str)}")
        return build_response(status_code, {
            "error": "AWS Service Error",
            "code": error_code,
            "message": error_message,
            "rawErrorResponse": ce.response
        })

    except Exception as e:
        # 予期せぬ内部サーバーエラー (500 Internal Server Error)
        logger.error(f"[Internal Server Error] {e}", exc_info=True)
        return build_response(500, {"error": "Internal server error", "detail": str(e)})

(3) Lambda テストイベント例(JSON: ベクトルデータ操作 API 5種)

AWS Lambda コンソールの「テストイベント」作成時にそのまま使用できる5種類すべてのJSON定義です。

put_vectors(ベクトルのバッチ登録・更新)

{
  "action": "put_vectors",
  "vectorBucketName": "my-knowledge-vector-bucket",
  "indexName": "document-embeddings-index",
  "vectors": [
    {
      "key": "doc-001",
      "data": {
        "float32": [0.015, -0.023, 0.045, 0.012, 0.089, -0.054, 0.033, 0.011]
      },
      "metadata": {
        "category": "technical",
        "department": "engineering",
        "year": 2026,
        "raw_text": "Amazon S3 Vectors はサーバーレスのベクトルストレージです。"
      }
    },
    {
      "key": "doc-002",
      "data": {
        "float32": [0.020, -0.018, 0.040, 0.015, 0.075, -0.060, 0.028, 0.019]
      },
      "metadata": {
        "category": "financial",
        "department": "accounting",
        "year": 2025,
        "raw_text": "2025年度の財務レポート概要です。"
      }
    }
  ]
}

query_vectors(類似度検索 + メタデータフィルタ)

{
  "action": "query_vectors",
  "vectorBucketName": "my-knowledge-vector-bucket",
  "indexName": "document-embeddings-index",
  "queryVector": [0.015, -0.023, 0.045, 0.012, 0.089, -0.054, 0.033, 0.011],
  "topK": 5,
  "returnDistance": true,
  "returnMetadata": true,
  "filter": {
    "$and": [
      { "category": { "$eq": "technical" } },
      { "year": { "$gte": 2026 } }
    ]
  }
}

get_vectors(キー指定によるベクトル取得)

{
  "action": "get_vectors",
  "vectorBucketName": "my-knowledge-vector-bucket",
  "indexName": "document-embeddings-index",
  "keys": ["doc-001", "doc-002"],
  "returnData": true,
  "returnMetadata": true
}

list_vectors(ベクトル一覧取得・ページネーション例)

パターンA: 単一ページ取得(初回リクエスト)

{
  "action": "list_vectors",
  "vectorBucketName": "my-knowledge-vector-bucket",
  "indexName": "document-embeddings-index",
  "maxResults": 10,
  "returnData": false,
  "returnMetadata": false
}

パターンB: 次ページ取得(レスポンスの nextToken を指定)

{
  "action": "list_vectors",
  "vectorBucketName": "my-knowledge-vector-bucket",
  "indexName": "document-embeddings-index",
  "maxResults": 10,
  "nextToken": "eyJleGNsdXNpdmVTdGFydEtleSI6ICJkb2MtMDEwIn0="
}

パターンC: 全件自動取得モード(fetchAll: true

{
  "action": "list_vectors",
  "vectorBucketName": "my-knowledge-vector-bucket",
  "indexName": "document-embeddings-index",
  "fetchAll": true,
  "maxTotalResults": 500
}

delete_vectors(キー指定によるベクトル一括削除)

{
  "action": "delete_vectors",
  "vectorBucketName": "my-knowledge-vector-bucket",
  "indexName": "document-embeddings-index",
  "keys": ["doc-001", "doc-002"]
}

9. ユースケースと選定基準

▲ 目次に戻る

比較項目 Amazon S3 Vectors Amazon OpenSearch Service / 専用ベクトルDB
アーキテクチャ 完全サーバーレス クラスタ / インスタンスプロビジョニング
維持費用 従量課金のみ ($0〜) 常時起動インスタンス費用が発生
レイテンシ 100ms 〜 サブ秒台 数ミリ秒 (Single-digit ms)
全文検索連携 ベクトル検索に特化 BM25全文検索 + ベクトルのハイブリッド検索
主な適合ケース 社内文書RAG、ナレッジベース、中低頻度検索、アーカイブ リアルタイムレコメンド、高QPS EC検索、高度な複合検索

10. 参照URL・公式リンク集

▲ 目次に戻る

(1) ユーザーガイド & サービス仕様

(2) Python SDK (boto3) リファレンス

(3) AWS REST API リファレンス