はじめに
Amazon Bedrockのナレッジベースを使ってRAGチャットボットを構築する際、ソースファイルがどれかを参照したり、メタデータフィルタリングをするために、ソースのS3バケットにメタデータを作成する必要があります。
例えば、2024年の記事のみに絞って情報を提供してもらいたい時などに有効です。
メタデータはファイル名.metadata.json
というファイル名でS3の同じ階層においてあげる必要があります。一個一個作るのは面倒なので、自動化した事例を共有します。
Bedrockのメタデータフィルタリングについては以下クラスメソッドさんの記事が参考になるかと思います。
https://dev.classmethod.jp/articles/knowledge-bases-for-amazon-bedrock-metadata-filtering/
自動生成
S3バケットのイベント通知を使ってLambdaを呼び出し、jsonファイルを作成し、S3バケットに配置します。やることとしては非常にシンプルです。
以下SAMテンプレートです。
サンプルテンプレート
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
SourceBucket:
DependsOn: MetadataGeneratorFunctionPermission
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub source-bucket-${AWS::AccountId}
NotificationConfiguration:
LambdaConfigurations:
- Event: 's3:ObjectCreated:*'
Filter:
S3Key:
Rules:
- Name: suffix
Value: .txt
Function: !GetAtt MetadataGeneratorFunction.Arn
# メタデータ生成用Lambda関数
MetadataGeneratorFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: MetadataGenerator
CodeUri: metadata_generator_function/
Handler: app.lambda_handler
Policies:
- Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- s3:GetObject
- s3:PutObject
Resource:
- !Sub arn:aws:s3:::source-bucket-${AWS::AccountId}/*
# S3バケットからLambda関数の呼び出しを許可
MetadataGeneratorFunctionPermission:
Type: AWS::Lambda::Permission
Properties:
Action: 'lambda:InvokeFunction'
FunctionName: !Ref MetadataGeneratorFunction
Principal: 's3.amazonaws.com'
SourceAccount: !Ref 'AWS::AccountId'
SourceArn: !Sub arn:aws:s3:::source-bucket-${AWS::AccountId}
上記の例ではソースはtxtファイルを想定しています。
ファイル拡張子やプレフィックスによって、設定は適宜変更してください。
Lambdaのソースコードは以下の通りです。
MetadataGenerator関数
import json
import boto3
import os
import urllib.parse
import re
s3_client = boto3.client('s3')
def lambda_handler(event, context):
# S3イベントからバケット名とオブジェクトキーを取得
bucket = event['Records'][0]['s3']['bucket']['name']
key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'])
# すでにメタデータファイルの場合は処理しない
if key.endswith('.metadata.json'):
print(f"Skipping metadata file: {key}")
return {
'statusCode': 200,
'body': json.dumps('Skipped metadata file')
}
try:
# プレフィックスから年を抽出
year = extract_year_from_prefix(key)
# メタデータの内容を定義
metadata_content = {
"metadataAttributes": {
"year": year
}
}
# メタデータファイル名を作成
metadata_key = f"{key}.metadata.json"
# メタデータをJSON形式でS3に書き込み
s3_client.put_object(
Bucket=bucket,
Key=metadata_key,
Body=json.dumps(metadata_content, indent=4),
ContentType='application/json'
)
print(f"Created metadata file: {metadata_key} with year: {year}")
return {
'statusCode': 200,
'body': json.dumps('Metadata processing complete')
}
except Exception as e:
print(f"Error processing {key}: {str(e)}")
return {
'statusCode': 500,
'body': json.dumps(f'Error: {str(e)}')
}
def extract_year_from_prefix(key):
"""
キーのプレフィックスから年を数値として抽出する関数
例: "2020/ファイル.txt" → 2020 (整数)
"""
# パスの最初の部分を取得
parts = key.split('/')
if len(parts) > 0:
# 最初の部分が4桁の数字(年)かチェック
if re.match(r'^\d{4}$', parts[0]):
return int(parts[0]) # 文字列から整数に変換
# 年が見つからない場合はデフォルト値を返す
return 0 # 不明な場合は0を返す
作成するリソースも以上で完了です。
これでS3バケットにtxtファイルを配置すると自動でメタデータのファイルが作成されます。
終わりに
やることも内容も非常にシンプルなので、メタデータ手で作成している方は是非参考にしてみてください!
今回はプレフィックスを参照してメタデータの設定を行いましたが、オブジェクトタグを参照させるなどいろいろやり方はあるかと思います!
他にもBedorck関連の記事書いているのでよかったらご覧ください!
弊社では一緒に働く仲間を募集中です!
現在、様々な職種を募集しております。
カジュアル面談も可能ですので、ご連絡お待ちしております!
募集内容等詳細は、是非採用サイトをご確認ください。
https://engineer.po-holdings.co.jp/