現役のITエンジニアが、 システム開発の現場で求められる知識を発信
記事検索
公開

Knowledge Base × S3 Vectors Index 設定の落とし穴

生成AI関連

今回のポイント

  • S3 Vectors では、フィルタリング可能なメタデータ(filterable metadata)は 2KB までという制限がある
  • Knowledge Base は内部的に AMAZON_BEDROCK_TEXT や AMAZON_BEDROCK_METADATA を自動付与する
  • これらが filterable 扱いになると、チャンク本文がそのままメタデータに入り、2KB制限を超えて同期エラーになる
  • この設定は自分で S3 Vectors Index を作成し、それを Knowledge Base と連携する場合に注意が必要(Knowledge Baseによる自動生成では問題ない)
  • CDK などで Index を作る場合は、metadataConfiguration.nonFilterableMetadataKeys に AMAZON_BEDROCK_TEXT と AMAZON_BEDROCK_METADATA を明示的に指定すること

はじめに

CDK を使って Knowledge Base と S3 Vectors を構築し、デプロイした環境を利用しようとしたところ、データソースの同期がすべて失敗するという事象に遭遇しました。以前の記事では、コンソールから Knowledge Base を作成し、そのまま利用した際には問題なく同期できていました。

生成AI関連
Amazon Bedrock Knowledge Base × S3 VectorsでRAGを作って…
生成AI関連
Amazon Bedrock Knowledge Baseでのメタデータ活用法

しかも今回アップロードしたファイルおよびメタデータは、以前の記事で利用したものとまったく同じ内容です。

  • 同じドキュメント
  • 同じ metadata.json
  • 同じチャンク設定

それにもかかわらず、今回は同期が失敗しました。違いはただ一つです。

  • 以前:Knowledge Base をコンソールから作成(S3 Vectorsは自動作成)
  • 今回:S3 Vectors Index と Knowledge Base をCDKで自作し、それを連携

本記事では、このエラーをどのように切り分け、最終的にどこに原因があったのかを、実際の検証の流れに沿って整理します。

事象:データソースの同期がすべて失敗

同期を実行すると、すべてのデータソースがエラーになりました。

データソースの同期が全滅

データソースの同期が全滅した様子

表示されたエラーメッセージは次の通りです。

Encountered error: Invalid record for key 'fe734cb8-98ff-4720-bd94-01f6ae634119': Filterable metadata must have at most 2048 bytes (Service: S3Vectors, Status Code: 400, Request ID: 8efb6bea-703f-4ff9-8e5b-cbf79762e54c) (SDK Attempt Count: 1). Call to Amazon S3 Vectors did not succeed.

エラーコードによると「Filterable metadata must have at most 2048 bytes」とあり、フィルターに使うメタデータ(filterable metadata)は 2KB 以下である必要があることが分かります。
しかし、登録していた metadata は実測で300B 程度でした。一見すると、2KB 制限に抵触する理由は見当たりません。ここから調査を開始しました。

調査

検証①:ドキュメントサイズが影響しているのか?

エラーメッセージには metadata と書かれていますが、

  • 本文サイズ
  • チャンクサイズ
  • 埋め込み対象テキスト

といった要素が内部的に影響している可能性も考えられます。そこで、極端に小さなドキュメントを用意して検証しました。

ドキュメント1.md

ドキュメント1です。
合言葉は、「犬」です。
ドキュメント1.md.metadata.json

{
	"metadataAttributes": {
		"name": "ドキュメント1",
		"version": 1.3,
		"year": 2024
	}
}

既存データを削除し、このテストデータのみをアップロードして同期を実行します。

テストデータのデータソースの同期に成功

テストデータのデータソースの同期に成功

同期は成功しました。
この時点で分かったのは、

  • 小さなドキュメントでは問題が発生しない
  • 何らかの「サイズ依存の要素」が関係している可能性が高い

ということです。
まだ原因は特定できていませんが、「metadata 単体の問題ではなさそう」という感触が得られました。

検証②:Knowledge Base が自動付与するメタデータを確認する

次に、Knowledge Base から retrieve を実行し、metadata の中身を確認しました。

kb_retrieve.py

import json

import boto3
from dotenv import load_dotenv

load_dotenv()

REGION = "ap-northeast-1"   # ご自身のリージョンに置き換えてください
KNOWLEDGE_BASE_ID = "XXXXXXXXXX"  # ご自身のKnowledge Base IDに置き換えてください


def make_kb_client():
    session = boto3.Session()
    return session.client("bedrock-agent-runtime", region_name=REGION)


def retrieve_documents(query: str) -> None:
    bedrock_client = make_kb_client()

    response = bedrock_client.retrieve(
        knowledgeBaseId=KNOWLEDGE_BASE_ID,
        retrievalQuery={"text": query},
        retrievalConfiguration={
            "vectorSearchConfiguration": {
                "numberOfResults": 3,
            }
        },
    )

    print("----------- retrieve -----------")
    print(json.dumps(response, indent=2, ensure_ascii=False))
    print("--------------------------------")


if __name__ == "__main__":
    retrieve_documents("test")
実行結果

----------- retrieve -----------
{
  "ResponseMetadata": {
    (省略)
  },
  "retrievalResults": [
    {
      "content": {
        "text": "ドキュメント1です。 合言葉は、「犬」です。",
        "type": "TEXT"
      },
      "location": {
        "s3Location": {
          "uri": "s3://(省略)"
        },
        "type": "S3"
      },
      "metadata": {
        "name": "ドキュメント1",
        "x-amz-bedrock-kb-source-file-modality": "TEXT",
        "year": 2024.0,
        "version": 1.3,
        "x-amz-bedrock-kb-chunk-id": "c8f9c02a-a1b0-431b-a11c-35ff1691e36a",
        "x-amz-bedrock-kb-data-source-id": "H4YXHZENRS"
      },
      "score": 0.5249484777450562
    }
  ]
}
--------------------------------

ここで、自分で設定していない以下のキーが付与されていることが分かります。

  • x-amz-bedrock-kb-chunk-id
  • x-amz-bedrock-kb-data-source-id
  • x-amz-bedrock-kb-source-file-modality

つまり、Knowledge Base は内部的にメタデータを自動付与しているということです。ただし、これらは ID やファイル種別といった管理情報であり、ドキュメント本文のサイズには依存しません。そのため、これらが追加されたとしても 2KB を超えるとは考えにくい状況でした。

検証③:S3 Vectors に格納された実データを直接確認する

次に、S3 Vectors から直接ベクトルデータを取得しました。

get_metadata.py

import json

import boto3
from dotenv import load_dotenv

load_dotenv()

REGION = "ap-northeast-1"   # ご自身のリージョンに置き換えてください
VECTOR_BUCKET = "xxxx-xxxxxxxxx-xxxxx"   # ご自身のS3 Vectorバケット名に置き換えてください
VECTOR_INDEX_NAME = "yyyy-yyyyyyyyy-yyyyy"   # ご自身のS3 Vectorインデックス名に置き換えてください


def get_vectors_with_metadata(limit: int = 5):
    client = boto3.client("s3vectors", region_name=REGION)

    response = client.list_vectors(
        vectorBucketName=VECTOR_BUCKET,
        indexName=VECTOR_INDEX_NAME,
        maxResults=limit,
    )

    for vector in response.get("vectors", []):
        key = vector["key"]

        # 実データ取得
        item = client.get_vectors(
            vectorBucketName=VECTOR_BUCKET,
            indexName=VECTOR_INDEX_NAME,
            keys=[key],
            returnData=True,
            returnMetadata=True,
        )

        print(json.dumps(item, indent=2, ensure_ascii=False))


if __name__ == "__main__":
    get_vectors_with_metadata()
実行結果

{
  "ResponseMetadata": {
    "RequestId": "a9c00bfb-c746-42eb-a557-7e58abc904b4",
    "HostId": "",
    "HTTPStatusCode": 200,
    "HTTPHeaders": {
      "date": "Thu, 12 Feb 2026 09:00:11 GMT",
      "content-type": "application/json",
      "content-length": "22584",
      "connection": "keep-alive",
      "x-amz-request-id": "a9c00bfb-c746-42eb-a557-7e58abc904b4",
      "access-control-allow-origin": "*",
      "vary": "origin, access-control-request-method, access-control-request-headers",
      "access-control-expose-headers": "*"
    },
    "RetryAttempts": 0
  },
  "vectors": [
    {
      "key": "c8f9c02a-a1b0-431b-a11c-35ff1691e36a",
      "data": {
        "float32": [
          -0.0544898696243763,
          (1,024次元、省略)
          -0.06755758076906204
        ]
      },
      "metadata": {
        "version": 1.3,
        "x-amz-bedrock-kb-data-source-id": "H4YXHZENRS",
        "AMAZON_BEDROCK_METADATA": "{\"text\":null,\"author\":null,\"createDate\":\"2026-02-12T08:39:51Z\",\"modifiedDate\":\"2026-02-12T08:39:51Z\",\"source\":{\"sourceLocation\":\"s3://(省略)/ドキュメント1.md\",\"sourceType\":null},\"descriptionText\":null,\"pageNumber\":null,\"pageSizes\":null,\"graphDocument\":{\"entities\":[]},\"parentText\":null,\"relatedContents\":null,\"sourceDocumentId\":\"9sOnR6eTCvL7d7hbp62iaQt0A0dvqA1AqZYGjyaIjbauPuGsUG6w/CzDfkL9zyII\",\"additionalMetadata\":null}",
        "AMAZON_BEDROCK_TEXT": "ドキュメント1です。 合言葉は、「犬」です。",
        "name": "ドキュメント1",
        "x-amz-bedrock-kb-source-file-modality": "TEXT",
        "year": 2024.0
      }
    }
  ]
}

ここで決定的な情報が見つかります。

"AMAZON_BEDROCK_METADATA": "…",
"AMAZON_BEDROCK_TEXT": "ドキュメント1です。 合言葉は、「犬」です。"

AMAZON_BEDROCK_METADATAには、多くの内部利用データ、そしてAMAZON_BEDROCK_TEXT には本文の全文が格納されていました(おそらく、チャンク分割された後の値)。
この時点で初めて、

  • 本文が metadata として保存されている
  • それが filterable metadata として扱われている可能性がある

という仮説にたどり着きました。

S3 Vectors の制限仕様を確認する

公式ドキュメントによると、S3 Vectors には以下の制限があります。

ベクトルあたりのメタデータの合計: 最大 40 KB (フィルタリング可能 + フィルタリング不可)
ベクトルあたりのフィルタリング可能なメタデータ: 最大 2 KB

また、S3 Vectorsのベストプラクティスには、下記のように記載があります。

ベクトルインデックスのフィルタリング不可能なメタデータフィールドの設定
ベクトルインデックスを作成する際は、フィルタリング不可能なメタデータキーとしてフィルタリングを必要としないメタデータフィールドを設定します。例えば、ベクトル埋め込みのテキストチャンクは、参照のみが必要な場合、フィルタリング不可能なメタデータフィールドとして保存します。詳細については、「フィルタリング不可能なメタデータ」を参照してください。

つまり、Knowledge Baseと連携させる場合、自動付与されるメタデータを把握し、自身でフィルタリング不可能なメタデータ(non-filterable metadata)を設定しなければならないのです。

今回の設定では、「チャンクサイズ:3,072 トークン」としていたので、仮に 1文字1バイトとしても2KB を超えてしまいます。全ドキュメントで同期が失敗していた理由は、これが原因である可能性が高いと考えられました。

なぜ以前は問題が起きなかったのか?

以前の記事では、まったく同じファイルと metadata を使用していました。
違いは、S3 Vectors Index の作成方法です。

  • 以前:Knowledge Base 作成時に自動生成された Index
  • 今回:CDK で自分で作成した Index

以前の環境の Index 設定を確認すると、次の設定が含まれていました。

get_s3_vector_setting.py

import json

import boto3
from dotenv import load_dotenv

load_dotenv()

REGION = "ap-northeast-1"   # ご自身のリージョンに置き換えてください
VECTOR_BUCKET = "xxxx-xxxxxxxxx-xxxxx"   # ご自身のS3 Vectorバケット名に置き換えてください
VECTOR_INDEX_NAME = "yyyy-yyyyyyyyy-yyyyy"   # ご自身のS3 Vectorインデックス名に置き換えてください


def get_vector_index_metadata():
    client = boto3.client("s3vectors", region_name=REGION)

    response = client.get_index(
        vectorBucketName=VECTOR_BUCKET,
        indexName=VECTOR_INDEX_NAME,
    )

    print(json.dumps(response, indent=2, default=str))


if __name__ == "__main__":
    get_vector_index_metadata()
実行結果

{
  "ResponseMetadata": {
    "RequestId": "91663b82-5b43-449b-a3c4-db78501f1a71",
    "HostId": "",
    "HTTPStatusCode": 200,
    "HTTPHeaders": {
      "date": "Thu, 12 Feb 2026 09:19:43 GMT",
      "content-type": "application/json",
      "content-length": "491",
      "connection": "keep-alive",
      "x-amz-request-id": "91663b82-5b43-449b-a3c4-db78501f1a71",
      "access-control-allow-origin": "*",
      "vary": "origin, access-control-request-method, access-control-request-headers",
      "access-control-expose-headers": "*"
    },
    "RetryAttempts": 0
  },
  "index": {
    "vectorBucketName": "bedrock-knowledge-base-t3s18v",
    "indexName": "bedrock-knowledge-base-default-index",
    "indexArn": "arn:aws:s3vectors:(省略)",
    "creationTime": "2026-01-23 14:34:43+09:00",
    "dataType": "float32",
    "dimension": 3072,
    "distanceMetric": "euclidean",
    "metadataConfiguration": {
      "nonFilterableMetadataKeys": [
        "AMAZON_BEDROCK_TEXT",
        "AMAZON_BEDROCK_METADATA"
      ]
    },
    "encryptionConfiguration": {
      "sseType": "AES256"
    }
  }
}

たしかに、nonFilterableMetadataKeysの設定が存在しています。一方、エラーが発生していた環境では、nonFilterableMetadataKeysが空になっていました。

ここで、同期エラーの原因が特定できました。

CDKで作成する場合の解決方法

CDK で Index を作成する際には、以下のようにnonFilterableMetadataKeysを追加します。


const vectorIndex = new s3vectors.CfnIndex(this, "TestVectorIndex", {
    vectorBucketArn: vectorBucket.attrVectorBucketArn,
    indexName: vectorIndexName,
    dataType: "float32",
    dimension: 1024,
    distanceMetric: "cosine",
    metadataConfiguration: {
        nonFilterableMetadataKeys: [
            "AMAZON_BEDROCK_TEXT",
            "AMAZON_BEDROCK_METADATA",
        ],
    },
});

CDKの再デプロイ後、Index 設定を確認し、再度同期を実行します。

データソースの同期に成功

データソースの同期に成功

無事、同期は成功しました。

まとめ

今回の事象から得られたことは、次の通りです。

  • S3 Vectors には filterable metadata の 2KB 制限がある
  • Knowledge Base は本文を metadata として保持している
  • Index を自作する場合、nonFilterableMetadataKeys を明示しないと、Knowledge Baseとの同期が失敗する可能性が高い

IaC でインフラを構築する場合、「コンソールでは簡単に作れたから、CDKでも特に意識しなくて問題ないはず」という前提は成立しないことがあります。デフォルト設定の違いが、思わぬ挙動差につながることもあるため、仕様の確認と設定値の明示は重要です。

生成AI活用支援サービスのご紹介

Tech Funでは、お客様のフェーズに合わせ、生成AI活用に向けた支援を3つのパックでご提供しています。

  1. 無料診断パック:業務・プロセスの現状を無料で診断し、生成AI活用の可能性をレポートします。
  2. 検証(PoC)パック:診断で有効性が確認された業務を対象に、プロトタイプ構築を支援します。
  3. コンサルティングサービス:生成AI導入戦略の策定から運用体制構築までを包括的に支援します。

生成AIに限らず、Web・業務システム開発やインフラ設計など、技術領域を問わずご相談を承っています。「何から始めれば良いか分からない」という段階でも構いませんので、ぜひお気軽にお問い合わせください。

執筆・編集

Tech Fun Magazine R&Dチーム
Tech Funの生成AI研究に携わるエンジニアが、最新のAIモデル動向やプロンプト設計、実業務への応用手法など、生成AIに特化した知見を執筆・編集しています。
モデル評価や業務シナリオに応じたAI活用設計など、日々のR&D活動で得られる実践的なノウハウをわかりやすく紹介します。

ARTICLE
生成AI関連記事一覧

生成AI関連

Knowledge Base × S3 Vectors In…

生成AI関連

RAGの評価指標 ─ 何を・どう測るかを整理する

生成AI関連

Guardrails for Amazon Bedrock …

生成AI関連

生成AIに精度を求めすぎない

生成AI関連

Amazon Bedrock Knowledge Baseで…

生成AI関連

はじめての人のための:AI・機械学習・統計・生成AI・AGI…

生成AI関連

Amazon Bedrock Knowledge Base …

生成AI関連

NotebookLMで議事録活用【活用編】

生成AI関連

生成AIシステムへのガードレール導入のポイント

記事一覧を見る