はじめに
2025年7月15日、Amazon S3 Vectors のプレビュー版がリリースされました。本記事では、Amazon S3 のベクトルバケットとベクトルインデックスの作成や、登録したデータセットに対してベクトル検索を行い LLM で回答を生成する RAG アプリケーションの実装を行います。
S3 Vectors の構築や RAG アプリケーションを構築するうえでポイントとなるものも紹介します。
図表を含む PDF ファイルを使ったRAGの構築については、Amazon S3 Vectors を使って図表を含む PDF ファイルでシンプルな RAG を構築 をご覧ください。
Amazon S3 Vectors とは
Amazon S3 Vectorsは、Amazon S3 にネイティブなベクトル検索機能を提供するサービスです。1 秒未満のクエリレイテンシーをネイティブでサポートする初めてのクラウドオブジェクトストアと謳われています。Amazon S3 上のベクトルバケットとベクトルインデックスを使用するため、Amazon S3 の簡単さ、耐久性、可用性、コスト効率性に、ネイティブのベクトル検索機能を享受することができます。
Amazon OpenSearch ServiceやOpenSearch Service マネージドクラスターとは補完関係にあるといわれており、 OpenSearch Service マネージドクラスターのエンジンとしてS3 Vectorsを使用したり、S3 Vectorsのインデックスをエクスポートして OpenSearch Serverless で利用することも可能です。また、Amazon Bedrock のベクトルデータストアとしても利用することができます。
S3 Vectors では、Amazon Bedrock 以外の埋め込みモデルを使用することも可能です。Amazon Bedrock Knowledge Bases の埋め込みモデルは Amazon Titan 2種類と Cohere の2種類のみ ですが、S3 Vectors では、OpenAI などの埋め込みモデルを使用することも可能です。
参考情報
ベクトルバケットやインデックスの作成、S3 Vectorsのデータの保存、ベクトル検索の実行については、Introducing Amazon S3 Vectors: First cloud storage with native vector support at scale (preview)や、公式ドキュメントのInserting vectors into a vector index、Querying vectorsにチュートリアルがあります。これらのチュートリアルを参考に、本記事では、Amazon Titan Text Embeddings V2を使用して、PDFファイルをベクトル化します。
環境構築
ここでは、すべて us-west-2 リージョンで操作を実施します。他のリージョンを使用する場合は、記事内のリージョン指定を使用するリージョン名に読み替えてください。
ベクトルバケットの作成
Amazon S3 のマネージドコンソールからベクトルバケットを作成します。Vector bucket を選択し、画面右上の Create vector bucket をクリックします。
ベクトルバケット名を入力します。ベクトルバケット名は S3 バケット名と異なり、全世界で一意ではありません。自分の AWS アカウント内の各リージョンで一意である必要があります。その他の命名規則は、Vector bucket naming requirementsを参照してください。抜粋したものは以下のとおりです。
- ベクトルバケット名の長さは 3~63 文字
- ベクトルバケット名には、英小文字 (a-z) 、数字 (0-9) 、ハイフン (-) のみ使用可能
- ベクトルバケット名は、文字または数字ではじまり、文字または数字で終わる
暗号化タイプはデフォルトのままにします。デフォルトでは、Amazon S3 管理キー (SSE-S3) を使用してサーバー側の暗号化が行われます。Create vector bucket をクリックすると、ベクトルバケットが作成されます。
ベクトルバケットの削除
ベクトルバケットは、マネージドコンソールから削除することができません (2025年 8月 14日現在) 。ベクトルバケットを削除する場合は、AWS CLI を使用して削除します。ベクトルバケット内にベクトルインデックスが存在する場合は、事前にすべて削除する必要があります。
aws s3vectors delete-vector-bucket --vector-bucket-name "ベクトルバケット名" --region "リージョン名"
ベクトルインデックスの作成
続いて、ベクトルインデックスの作成を行います。作成したベクトルバケットをクリックし、ベクトルインデックス作成画面を開きます。
ベクトルインデックス名を入力します。ベクトル インデックス名はベクトル バケット内で一意である必要があります。その他の命名規則は、Vector index naming requirementsを参照してください。ルールはベクトルバケット名のものと同じです。
Dimension (次元数) は、使用する埋め込みモデルの次元数を指定します。後述の Amazon Titan Text Embeddings V2 にあわせて 1024 を指定します。
Distance metric (距離メトリック) は、ベクトル検索時に使用する距離メトリックを指定します。ここでは Cosine を選択します。
Additional settings を開き、Non-filterable metadata (フィルタリングできないメタデータ) を設定します。 Non-filterable metadata に指定したキーはクエリ時のメタデータフィルタリングには使用できません。フィルタリング可能なメタデータはデータサイズの上限が低いため、フィルタリングできないメタデータを使って埋め込み時の元データの保存を行います。このメタデータをベクトル検索後に LLM に渡すことで、LLM は回答生成に活用することができます。ここでは、source_text というキーを追加します。Add key をクリックし、source_text と入力します。最後に、Create vector index をクリックします。
ベクトルインデックス名、Dimension 、Distance metric 、Non-filterable metadata のキーはベクトルインデックス作成後に変更することはできません。変更する場合は、新たにベクトルインデックスを作成する必要があります。
ベクトルあたりの「フィルタリング可能なメタデータ」のサイズは、最大 2KB です。従って、フィルタリング可能なメタデータに埋め込み時の元データをそのまま保存すると多くのデータを入れることができません。一方、ベクトルあたりの全メタデータ (「フィルタリング可能なメタデータ」 + 「フィルタリングできないメタデータ」) の合計サイズは最大 40KB です。このため、Non-filterable metadata (フィルタリングできないメタデータ) を使うことで、元データの一部あるいは全部を保存することができます。
制約事項については、Limitations and restrictionsを参照してください。
ベクトルインデックスの削除
ベクトルインデックスは、マネージドコンソールから削除することができません (2025年 8月 14日現在) 。ベクトルインデックスを削除する場合は、AWS CLI を使用して削除します。
aws s3vectors delete-index --vector-bucket-name "ベクトルバケット名" --index-name "ベクトルインデックス名" --region "リージョン名"
PDF ファイルのベクトル化
ここでは、Amazon Bedrock の Amazon Titan Text Embeddings V2 を使用して、PDF ファイルをベクトル化します。ベクトル化の流れは以下のとおりです。テキストのみを埋め込みの対象としているので、今回は画像は対象外です。
- 初期設定:
- チャンクサイズ (1024文字、CHUNK_SIZE で指定)
- オーバーラップ (102文字、OVERLAP で指定)
- S3 Vectors ベクトルバケットとインデックス
- PDF ファイル処理:
- 各 PDF ファイルに対してテキスト抽出・チャンク化・埋め込み生成
- テキスト抽出:
- pypdf ライブラリでページごとのテキスト抽出
- チャンク分割:
- 単語境界を考慮した適切なサイズでの分割
- 埋め込み生成:
- Amazon Titan Text v2 モデルでベクトル化
- S3 Vectors 保存:
- メタデータ付きで S3 Vectors に保存
PDF ファイルを準備
ここでは、総務省発行の情報通信白書令和7年版 PDF版から、第2節 AIの爆発的な進展の動向( https://www.soumu.go.jp/johotsusintokei/whitepaper/ja/r07/pdf/n1120000.pdf )を使用します。
Python 環境の準備
コードの実行環境は以下のとおりです。uvのインストールやvenvの作成は省略します。
- Python 3.12.3
- uv 0.7.4
コード類の配置は以下のとおりです。
.
├── files
│ └── n1120000.pdf
├── requirements.txt
├── simple_embedding.py
└── simple_query.py
boto3
pypdf
1. Python パッケージ
Python パッケージをインストールします。
uv pip install -r requirements.txt
2. システム依存関係
PDF 画像変換のために poppler が必要です。
Ubuntu/Debian
sudo apt-get update
sudo apt-get install poppler-utils
macOS
macOS では、Homebrew を使用してインストールします。
brew install poppler
テキストのベクトル化と S3 Vectors への保存
テキストのベクトル化と S3 Vectors への保存を行うコードは以下のとおりです。事前に、使用するリージョンで Titan Text Embeddings V2 モデルを有効にしておきます。
VECTOR_BUCKET_NAME と INDEX_NAME は、事前に作成したベクトルバケットとインデックスの名前を指定します。
埋め込みと S3 Vectors への保存を行うサンプルコード
import boto3
import json
import os
import time
from pypdf import PdfReader
from typing import List, Dict, Any
# Bedrock と S3 Vectors のクライアントを作成
bedrock_client = boto3.client('bedrock-runtime', region_name='us-west-2')
s3vectors_client = boto3.client('s3vectors', region_name='us-west-2')
# S3 Vectors のベクトルバケットとインデックス
VECTOR_BUCKET_NAME = "ベクトルバケット名"
INDEX_NAME = "ベクトルインデックス名"
# テキスト埋め込みモデル
TEXT_EMBEDDING_MODEL_ID = "amazon.titan-embed-text-v2:0"
# チャンクサイズとオーバーラップ
CHUNK_SIZE = 1024 # chunk size
OVERLAP = int(CHUNK_SIZE * 0.1) # overlap size
# パフォーマンス測定設定
MEASURE_PERFORMANCE = True
def extract_text_from_pdf(pdf_path: str) -> str:
"""
PDFファイルからテキストを抽出
Args:
pdf_path (str): PDFファイルのパス
Returns:
str: 抽出されたテキスト
"""
try:
reader = PdfReader(pdf_path)
text = ""
for page in reader.pages:
text += page.extract_text() + "\n"
return text.strip()
except Exception as e:
print(f"Error extracting text from {pdf_path}: {e}")
return ""
def chunk_text(text: str, chunk_size: int = 1000, overlap: int = 100) -> List[str]:
"""
長いテキストを指定されたサイズのチャンクに分割
Args:
text (str): 分割するテキスト
chunk_size (int): チャンクの最大サイズ(文字数)
overlap (int): チャンク間の重複文字数
Returns:
List[str]: テキストチャンクのリスト
"""
if len(text) <= chunk_size:
return [text]
chunks = []
start = 0
while start < len(text):
end = start + chunk_size
if end >= len(text):
chunks.append(text[start:])
break
# 最後のスペースまで戻って、単語の途中で切れないようにする
last_space = text.rfind(" ", start, end)
if last_space > start:
end = last_space
chunks.append(text[start:end])
start = end - overlap
return chunks
def create_text_embedding(text: str) -> List[float]:
"""
埋め込みモデルを使用してテキストの埋め込みベクトルを作成
Args:
text (str): 埋め込みベクトルを作成するテキスト
Returns:
List[float]: 埋め込みベクトル
"""
try:
payload = json.dumps({
"inputText": text
})
response = bedrock_client.invoke_model(
modelId=TEXT_EMBEDDING_MODEL_ID,
body=payload,
accept='application/json',
contentType='application/json'
)
response_body = json.loads(response['body'].read())
return response_body['embedding']
except Exception as e:
print(f"Error creating text embedding: {e}")
return []
def store_embeddings_to_s3vectors(pdf_path: str, result: Dict[str, Any], store_to_s3: bool = True) -> Dict[str, Any]:
"""
埋め込みベクトルデータをS3Vectorsに保存
Args:
pdf_path (str): PDFファイルのパス
result (Dict[str, Any]): process_pdf_fileの結果
store_to_s3 (bool): S3Vectorsに実際に保存するかどうか
Returns:
Dict[str, Any]: 保存結果の統計情報
"""
if not store_to_s3:
return {"skipped": True, "reason": "store_to_s3=False"}
filename = os.path.basename(pdf_path)
vectors_to_store = []
stats: Dict[str, Any] = {"text_vectors": 0, "total": 0}
# テキストチャンクの埋め込みベクトルを作成
for i, (chunk_info, embedding) in enumerate(zip(result["text_chunks"], result["text_embeddings"])):
vector_key = f"{filename}_text_chunk_{i}"
vectors_to_store.append({
"key": vector_key, # PDFファイル名_テキストチャンク番号
"data": {"float32": embedding}, # 埋め込みベクトル
"metadata": { # メタデータ
"id": vector_key, # PDFファイル名_テキストチャンク番号
"source_file": filename, # PDFファイル名
"type": "text",
"chunk_index": i, # テキストチャンク番号
"chunk_length": chunk_info["length"], # テキストチャンクの長さ
"source_text": chunk_info["text"][:2000], # 埋め込みテキストの一部
"full_text_length": len(chunk_info["text"]) # 埋め込みテキストの全長
}
})
stats["text_vectors"] += 1
stats["total"] = len(vectors_to_store)
if not vectors_to_store:
return {"error": "No vectors to store"}
try:
print(f"\nStoring {stats['total']} vectors to S3Vectors...")
print(f" - Text vectors: {stats['text_vectors']}")
# S3Vectorsに保存
response = s3vectors_client.put_vectors(
vectorBucketName=VECTOR_BUCKET_NAME,
indexName=INDEX_NAME,
vectors=vectors_to_store
)
print(f"✓ Successfully stored {stats['total']} vectors to S3Vectors")
stats["success"] = True
stats["response"] = response
return stats
except Exception as e:
print(f"Error storing vectors to S3Vectors: {e}")
stats["error"] = str(e)
return stats
def process_pdf_file(pdf_path: str, chunk_size: int = 1000, overlap: int = 100) -> Dict[str, Any]:
"""
PDFファイルを処理してテキストを抽出し、埋め込みベクトルを作成
Args:
pdf_path (str): PDFファイルのパス
chunk_size (int): チャンクの最大サイズ(文字数)、デフォルト: 1000
overlap (int): チャンク間の重複文字数、デフォルト: 100
Returns:
Dict[str, Any]: 処理結果(テキストチャンクと埋め込みベクトル)
"""
if not os.path.exists(pdf_path):
print(f"Error: File {pdf_path} does not exist")
return {"error": f"File {pdf_path} does not exist"}
print(f"Processing PDF: {pdf_path}")
result = {
"file_path": pdf_path,
"text_chunks": [],
"text_embeddings": []
}
# PDFからテキストを抽出
text = extract_text_from_pdf(pdf_path)
if not text:
return {"error": "Failed to extract text from PDF"}
print(f"Extracted text length: {len(text)} characters")
# テキストをチャンクに分割
chunks = chunk_text(text, chunk_size=chunk_size, overlap=overlap)
print(f"Split into {len(chunks)} chunks")
# テキストチャンクの埋め込みベクトルを作成
for i, chunk in enumerate(chunks):
print(f"Processing text chunk {i+1}/{len(chunks)}")
text_embedding = create_text_embedding(chunk)
if text_embedding:
result["text_chunks"].append({
"index": i,
"text": chunk,
"length": len(chunk),
"type": "text"
})
result["text_embeddings"].append(text_embedding)
return result
if __name__ == "__main__":
# PDFファイルのパス(filesディレクトリ内のPDFファイルを処理)
pdf_files = [
"files/n1120000.pdf"
]
print(f"\n{'='*60}")
print(f"PDF Processing Configuration:")
print(f" Chunk Size: {CHUNK_SIZE}")
print(f" Overlap: {OVERLAP}")
print('='*60)
for pdf_file in pdf_files:
if os.path.exists(pdf_file):
print(f"\n{'='*50}")
print(f"Processing: {pdf_file}")
start_time = time.time() if MEASURE_PERFORMANCE else None
# PDFファイルを処理してテキストを抽出し、埋め込みベクトルを作成
result = process_pdf_file(
pdf_file,
chunk_size=CHUNK_SIZE,
overlap=OVERLAP
)
# 処理時間を計測
if MEASURE_PERFORMANCE and start_time is not None:
processing_time = time.time() - start_time
print(f"Processing time: {processing_time:.2f} seconds")
if "error" in result:
print(f"Error: {result['error']}")
continue
print(f"Successfully processed {len(result['text_chunks'])} text chunks")
print(f"Generated {len(result['text_embeddings'])} text embeddings")
# サンプル出力を表示
# 最初のテキストチャンクを表示
if result['text_chunks']:
print(f"\nFirst text chunk preview:")
print(f"Text: {result['text_chunks'][0]['text'][:200]}...")
print(f"Text embedding dimension: {len(result['text_embeddings'][0])}")
print(f"First 5 text embedding values: {result['text_embeddings'][0][:5]}")
# S3Vectorsに埋め込みベクトルを保存
storage_result = store_embeddings_to_s3vectors(pdf_file, result, store_to_s3=True)
if "error" in storage_result:
print(f"Storage failed: {storage_result['error']}")
else:
print(f"File not found: {pdf_file}")
コードを実行します。
uv run python ./simple_embedding.py
このコードを実行すると、以下のように処理結果が表示されます。指定したチャンクサイズに基づいて 31個のテキストチャンクが作成されました。それぞれのテキストチャンクに対して Amazon Titan Text Embeddings V2 を使用して埋め込みベクトルが作成され、S3 Vectors に保存されました。
============================================================
PDF Processing Configuration:
Chunk Size: 1024
Overlap: 102
============================================================
==================================================
Processing: files/n1120000.pdf
Processing PDF: files/n1120000.pdf
Extracted text length: 25629 characters
Split into 31 chunks
Processing text chunk 1/31
Processing text chunk 2/31
...
Processing time: 19.77 seconds
Successfully processed 31 text chunks
Generated 31 text embeddings
First text chunk preview:
Text: ୈ2અ AI
AIは爆発的に進化を続けており、大規模言語モデルにおいて巨大な汎用モデルの開発が進展する一
方、新たな技術も日々出現しており、技術変革の可能性が大きい分野であるとも指摘されている。ま
た、巨大な投資が求められるAI分野は、海外のいわゆるビッグテック企業(世界規模で影響力を有す
る巨大デジタル企業群)や巨額な投資を受けたAI人材や技術に優れる海外スタートアップ企業等が主
導している傾...
Text embedding dimension: 1024
First 5 text embedding values: [-0.005605722311884165, 0.12498742341995239, 0.03509175777435303, 0.029809853062033653, 0.011589772067964077]
Storing 31 vectors to S3Vectors...
- Text vectors: 31
✓ Successfully stored 31 vectors to S3Vectors
登録したベクトルの確認
登録したベクトルの確認を行います。ベクトルバケット名とインデックス名は、事前に作成したベクトルバケットとインデックスの名前を指定します。リージョン名は、使用するリージョンを指定します。--max-itemsは、取得するベクトルの最大数を指定します。
aws s3vectors list-vectors --vector-bucket-name "ベクトルバケット名" --index-name "ベクトルインデックス名" --return-metadata --return-data --max-items 1 --region "リージョン名"
このコマンドを実行すると、以下のように登録したベクトルのメタデータが表示されます。(ベクトルデータは一部省略しています。)
source_text には、テキストチャンクの一部が保存されています。このように、テキストチャンクの一部を保存することで、ベクトル検索やベクトルの取得を行った際にテキストチャンクの一部を取得することができます。これにより、ベクトル検索の結果から source_text を取得し LLM に渡すことで、LLM が回答を生成することができます。
{
"vectors": [
{
"key": "n1120000.pdf_text_chunk_15",
"data": {
"float32": [
-0.007006434258073568,
0.09554045647382736,
-0.022282972931861877,
...
]
},
"metadata": {
"type": "text",
"source_text": "。また、近年では、比較的小規模でありな\nがら高性能なモデルの開発も進んでいる(図表Ⅰ-1-2-5)。\n図表Ⅰ-1-2-5 日本の組織による大規模言語モデルの開発事例...",
"full_text_length": 917,
"id": "n1120000.pdf_text_chunk_15",
"chunk_index": 15,
"chunk_length": 917,
"source_file": "n1120000.pdf"
}
}
],
"NextToken": "eyJuZXh0VG9rZW4iOiBudWxsLCAiYm90b190cnVuY2F0ZV9hbW91bnQiOiAxfQ=="
}
ベクトルの削除
登録したベクトルを削除する場合、以下のコマンドを実行します。ベクトルバケット名とインデックス名は、事前に作成したベクトルバケットとインデックスの名前を指定します。リージョン名は、使用するリージョンを指定します。--vector-keyは、削除するベクトルのキーを指定します。ベクトルのキーは、さきほどの出力結果にあるn1120000.pdf_text_chunk_15の箇所です。このコードでは、PDFファイル名_text_chunk_連番の命名規則でキーを設定しています。
aws s3vectors delete-vector --vector-bucket-name "ベクトルバケット名" --index-name "ベクトルインデックス名" --vector-key "ベクトルキー" --region "リージョン名"
ベクトルが数個であればコマンドで削除することができますが、ベクトルが多い場合は、Boto3 の S3 Vectors クライアント を使用して削除するほうが効率的です。
ベクトルの削除を行うサンプルコード
import boto3
# Create AWS clients in the us-west-2 region
s3vectors_client = boto3.client('s3vectors', region_name='us-west-2')
# S3Vectors configuration
VECTOR_BUCKET_NAME = "ベクトルバケット名"
INDEX_NAME = "ベクトルインデックス名"
# Get all vector keys
paginator = s3vectors_client.get_paginator("list_vectors")
pages = paginator.paginate(vectorBucketName=VECTOR_BUCKET_NAME, indexName=INDEX_NAME)
keys = []
for page in pages:
for vector in page["vectors"]:
keys.append(vector["key"])
# Delete each vector key
length = len(keys)
for i, key in enumerate(keys):
print(f"Processing deletion of vector: {key} ({i+1}/{length})")
s3vectors_client.delete_vectors(
vectorBucketName=VECTOR_BUCKET_NAME,
indexName=INDEX_NAME,
keys=[key]
)
コードを実行します。登録したベクトルをすべて削除します。
uv run python ./delete_vectors.py
Processing deletion of vector: n1120000.pdf_text_chunk_15 (1/31)
Processing deletion of vector: n1120000.pdf_text_chunk_21 (2/31)
Processing deletion of vector: n1120000.pdf_text_chunk_7 (3/31)
...
ベクトルの上書き
公式ドキュメントInserting vectors into a vector indexによると、ベクトルインデックスのキーはインデックス内で一意であり、既に存在するキーでデータを登録すると既存のデータを新しいデータで上書きします。キーが一意であれば、ベクトルデータが重複して登録が行われることはありません。
ベクトル検索
Amazon Bedrock の Amazon Titan Text Embeddings V2 で埋め込みを行ったので、同じモデルを使用してクエリをベクトル化します。ベクトル化したクエリを使い、S3 Vectors に登録したベクトルを検索します。検索結果にある source_text を取得し、LLM に渡すことで、ベクトル検索の結果を使って回答を生成します。
クエリのベクトル化とベクトル検索の実行
クエリのベクトル化とベクトル検索を行うコードは以下のとおりです。ベクトルバケット名とインデックス名は、事前に作成したベクトルバケットとインデックスの名前を指定します。リージョン名は、使用するリージョンを指定します。
クエリのベクトル化とベクトル検索の実行を行うサンプルコード
import boto3
import json
bedrock_client = boto3.client('bedrock-runtime', region_name='us-west-2')
s3vectors = boto3.client('s3vectors', region_name='us-west-2')
input_text = "AI活力ランキングについて教えてください。"
request = json.dumps({"inputText": input_text})
# テキスト埋め込みモデルを使用して質問を埋め込みベクトルに変換
response = bedrock_client.invoke_model(
modelId='amazon.titan-embed-text-v2:0',
body=request,
contentType='application/json'
)
# 埋め込みベクトルを抽出
response_body = json.loads(response['body'].read())
embedding = response_body['embedding']
# ベクトル化した質問を使ってベクトル検索
query_response = s3vectors.query_vectors(
vectorBucketName="ベクトルバケット名",
indexName="ベクトルインデックス名",
queryVector={"float32": embedding},
topK=3,
returnDistance=True,
returnMetadata=True
)
# ベクトル検索結果を表示
print("query_response: ", json.dumps(query_response["vectors"], indent=2, ensure_ascii=False))
# ベクトル検索結果一覧から、メタデータに入れたテキストを取得
contexts = [v["metadata"]["source_text"] for v in query_response["vectors"]]
context_str = "\n---\n".join(contexts)
# ベクトル検索結果一覧のテキストをもとに、LLMに質問を投げる
llm_response = bedrock_client.converse(
modelId="us.anthropic.claude-sonnet-4-20250514-v1:0",
system=[
{
"text": context_str
}
],
messages=[
{
"role": "user",
"content": [
{
"text": "<question>" + input_text + "</question>",
}
]
}
],
inferenceConfig={
"maxTokens": 1024,
"temperature": 0.1,
"topP": 0.9
}
)
print("\n" + "*" * 50 + "\n")
# LLMの応答を表示
print("llm_response: ", json.dumps(llm_response['output']['message']['content'], indent=2, ensure_ascii=False))
コードを実行します。
uv run python ./simple_query.py
このコードを実行すると、以下のようにベクトル検索の結果が出力されます。topK=3 を指定しているので、上位 3件のベクトル検索結果が出力されます。そして、ベクトル検索結果一覧から、メタデータにある source_text を LLM が利用し、回答を生成しています。
コード内で、ユーザー質問を設定しています。検索結果には 0.481 と一番近い距離でヒットしたテキストがあり、これに基づいて回答が生成されているようです。
input_text = "AI活力ランキングについて教えてください。"
query_response: [
{
"key": "n1120000.pdf_text_chunk_13",
"metadata": {
"source_text": "ficial Intelligence)が発表した、2023年のAI活力ランキングによれ\nば、日本は総合9位に位置付けられており、米国、中国、英国といった国から水をあけられている( 図\n表Ⅰ-1-2-4) 。また、AIに関する論文数などを基にAI研究力を順位付けしているAIRankingsでは、\nここ数年の上位国は米国、中国、英国、ドイツの順となっており、日本は11~12位で推移している。\nしかし、日本の企業・組織においても、AI開発に向けて様々な動きを見せている。\n*13\t 日本経済新聞「テスラ、ヒト型ロボットを披露 家事など身近な姿を紹介」 (2024年10月11日) 〈 https://www.nikkei.com/article/\nDGXZQOGN114AB0R11C24A0000000/ 〉 (2025年3月19日参照) 、Ledge.ai「OpenAIと提携のFigure、新型ヒューマノイドロボット\n「Figure02」を発表 BMWとの自動車製造の実用化に向けたテストにも成功」 (2024年8月11日) 〈https://ledge.ai/articles/figure02〉、 C N E T\t\nJapan「Agility\tRobotics、二足歩行ロボット「Digit」を刷新」 (2023年4月4日) 〈https://japan.cnet.com/article/35202097/〉 (2025年3月\n19日参照) 、日経クロステック「Boston\tDynamicsに聞く人型ロボ、まずは自動車工場の作業員に」 (2025年3月19日) 〈https://xtech.nikkei.\ncom/atcl/nxt/column/18/03118/00004/〉 (2025年3月19日参照) 、日経クロステック「二足歩行の人型ロボはどこへ向かうのか、AIの進化\nで海外勢の開発競争が再加速」 (2024年7月31日) 〈https://xtech.nikkei.com/atcl/nxt/mag/nmc/18/00011/00263/?P=3〉 (2025年3月19\n日参照) 、日経クロステック「低価格化が後押しする社会実装、工場から家庭まで幅広く用途開拓」 (2025年1月31日)",
"type": "text",
"chunk_index": 13,
"full_text_length": 960,
"source_file": "n1120000.pdf",
"id": "n1120000.pdf_text_chunk_13",
"chunk_length": 960
},
"distance": 0.4814563989639282
},
{
"key": "n1120000.pdf_text_chunk_28",
"metadata": {
"chunk_index": 28,
"source_file": "n1120000.pdf",
"id": "n1120000.pdf_text_chunk_28",
"source_text": "、資料作成等の補助」に生成AIを使用\nしていると回答した割合は、 日本で47.3%( 「業務で使用中」と回答した割合)であった\n*23\n。いずれも、\n他国と比較すると低い割合にとどまっている。\n関連データ 企業における業務での生成AI利用率\u0007\n(業務別・国別)\nURL:\u0007https://www.soumu.go.jp/\njohotsusintokei/whitepaper/ja/r07/\nhtml/datashu.html#f00046(データ集)\n図表Ⅰ-1-2-14 企業における業務での生成AI利用率\n(国別)\n*24\n55.2\n90.6\n90.3\n95.8\n0 20 40 60 80 100\n日本\n米国\nドイツ\n中国\n(%)\n(出典)総務省(2025) 「国内外における最新の情報通信技術の研究開発及び\nデジタル活用の動向に関する調査研究」\n*23\t 企業における個別業務ごとの生成AI利用率については、より推計精度を高めるため、2023年度調査と推計方法を変えたため、2024年度\n調査と2023年度調査とは単純比較はできない。\n*24\t 所属企業のAI活用方針を知っている者に対して、何らかの業務で生成AI利用していると回答した比率を基に推計\n20\n令和7年版 情報通信白書 第Ⅰ部\nୈ\n1\nষ\n「社会基盤」としてのデジタルの浸透・拡大と動向\nAI の爆発的な進展の動向AI の爆発的な進展の動向第第 22 節節\n情通R7_Ⅰ-1-02_第Ⅰ部第1章2節.indd 20情通R7_Ⅰ-1-02_第Ⅰ部第1章2節.indd 20 2025/06/24 15:08:462025/06/24 15:08:46\n生成AI導入に際しての懸念事項について尋ねたところ、日本では、 「効果的な活用方法がわからな\nい」が最も多く、次いで、 「社内情報の漏えい等のセキュリティリスク」 「ランニングコストがかかる」\n「初期コストがかかる」ことが挙げられている(図表Ⅰ-1-2-15)。\n生成AIの活用推進による自社への影響に対する考え方について、日本では、",
"chunk_length": 887,
"type": "text",
"full_text_length": 887
},
"distance": 0.5675384998321533
},
{
"key": "n1120000.pdf_text_chunk_0",
"metadata": {
"source_text": "ୈ2અ AI\nAIは爆発的に進化を続けており、大規模言語モデルにおいて巨大な汎用モデルの開発が進展する一\n方、新たな技術も日々出現しており、技術変革の可能性が大きい分野であるとも指摘されている。ま\nた、巨大な投資が求められるAI分野は、海外のいわゆるビッグテック企業(世界規模で影響力を有す\nる巨大デジタル企業群)や巨額な投資を受けたAI人材や技術に優れる海外スタートアップ企業等が主\n導している傾向が見られる。\nこのような流れの中、AI分野での日本の存在感は世界的にみると必ずしも高いとはいえないものの、\n国内企業・組織によるモデル開発等の技術開発の動きも盛んにおこなわれている。\n今後、AIが更に進化し、あらゆるデジタル分野に浸透・連携することで、デジタル社会を支える基\n盤的要素となる可能性が高まっている。\n 1 1\t AIの技術開発における現状と動向\t AIの技術開発における現状と動向\n\t 激化する世界のAI開発競争\nAIには様々な形態のものがあるが、昨今のAI技術開発や応用において大きな潮流となっている分野\nの一つが、文章や画像、動画等を生成する「生成AI(generative AI) 」であり、その技術の一つが、\n深層学習技術を応用した大規模言語モデル(LLM:Large Language Model)である。2020年に\nOpenAIによって、学習に使われるデータの規模・学習に使われる計算量・モデルのパラメータ数が\n増加すればするほど、LLMの性能が向上するというスケーリング則(Scaling law)が提唱された。例\nえば、OpenAIが2019年に発表したモデルであるGPT-2のパラメータ数が15億だったのに対し、同\n社が2020年に発表したGPT-3のパラメータ数は約120倍の1,750億まで大規模化した。その後も大\n規模化の波は止まらず、2022年4月にGoogleが発表したPaLMのパラメータ数は5,400億まで及ん\nでいる(図表Ⅰ-1-2-1)。\n図表Ⅰ-1-2-1",
"full_text_length": 856,
"source_file": "n1120000.pdf",
"type": "text",
"id": "n1120000.pdf_text_chunk_0",
"chunk_length": 856,
"chunk_index": 0
},
"distance": 0.5704502463340759
}
]
**************************************************
llm_response: [
{
"text": "AI活力ランキングについて説明いたします。\n\n## AI活力ランキングとは\n\nAI活力ランキングは、スタンフォード大学のHuman-Centered AI Institute(HAI:人間中心AI研究所)が発表している、各国のAI分野における総合的な活力・競争力を評価するランキングです。\n\n## 評価項目\n\nこのランキングでは、以下のような多角的な指標を用いて各国のAI活力を評価しています:\n\n- **研究開発力**:AI関連の論文数、引用数、研究の質\n- **人材育成**:AI分野の人材輩出、教育体制\n- **産業応用**:企業でのAI導入・活用状況\n- **投資環境**:AI関連への投資額、スタートアップ支援\n- **政策・制度**:AI戦略、規制環境、政府の取り組み\n- **インフラ**:計算資源、データ基盤の整備状況\n\n## 最新の状況(2023年)\n\n2023年のランキングでは:\n- **上位国**:米国、中国、英国などが上位を占める\n- **日本の順位**:総合9位に位置\n- **特徴**:米国、中国、英国といった国から水をあけられている状況\n\n## 日本の課題と特徴\n\n日本は技術力は高いものの、以下の点で課題があるとされています:\n- 大規模な投資や人材確保での遅れ\n- 産業応用や社会実装のスピード\n- グローバルな競争力の向上\n\nこのランキングは、各国のAI戦略立案や政策決定の重要な参考指標として活用されています。"
}
]
simple_query.py では、ベクトル検索結果一覧からメタデータの source_text からテキストを取得し、それをコンテキストとして LLM に渡しています。
# ベクトル検索結果一覧から、メタデータに入れたテキストを取得
contexts = [v["metadata"]["source_text"] for v in query_response["vectors"]]
context_str = "\n---\n".join(contexts)
これを、Converse API の system パラメータに、ユーザー質問を messages パラメータに渡しています。
system=[
{
"text": context_str
}
],
messages=[
{
"role": "user",
"content": [
{
"text": "<question>" + input_text + "</question>",
}
]
}
],
まとめ
Amazon S3 Vectors を使ったシンプルな RAG を構築しました。今回は、以下の流れで実装を行いました。
- PDF から抽出したテキストを Amazon Bedrock の Amazon Titan Text Embeddings V2 でベクトル化し、S3 Vectors に登録
- クエリをベクトル化し、S3 Vectors に登録したベクトルを検索
- ベクトル検索の結果を使って回答を生成
マネージドな Amazon Bedrock Knowledge Bases を使った RAG に比べ実装の手間はあるものの、Bedrock 埋め込みモデルを利用できる柔軟さや、Amazon S3 の可用性、コスト効率のメリットがあります。
Bedrock Knowledge Bases のベクトルストアとしても利用できるため、コストをおさえつつ RAG を検証したい場合や、OpenSearch 、Aurora ほどのインフラが不要な場合は、Amazon S3 Vectors を使った RAG を検討してみてはいかがでしょうか。

