作成手順
この『その1』では下の1,2だけ。
- VPC(virtual private cloud)作成
- Aurora Serverless v2でPostgreSQL作成
- (ec2インスタンス作成<==不要だったので使わない(参考にメモとして残した)
- s3に読み込ますpdfを.mdに変換してUploadする
- bedrockでvector storeを作り、そのapiも作る
- bedrockでllm apiを作る
- lambdaで上記2つのapiを使って簡単な質問を送る
- Webページに入力された質問をLambdaに送るAPI GatewayでREST APIを作る
- AmplifyにAngularベースのWebページを作り、REST APIに質問を送る様にする
s3にファイルをUploadする
s3にbucketを作成して、その下に7つのpdfファイルをUploadしました。
これだけです。
BedrockでVector store作成
- Bedrockページ左メニューの『Knowledge Bases』に入り
- 『Create』〜『Knowledge Base with vector store』
- 『Knowledge Base name』を入れる
- 私の場合。『MyKnowlegeBase–FP0H』とした
- IAM permissionsで『Create and use a new service role』を選択し、Service role nameはそのまま『AmazonBedrockExecutionRoleForKnowledgeBase_dylea』とした。
- 『Data source details』で『Amazon s3』を選択し『Next』
- 画面の左下の『Add data source』をします。
- Data sourceは1つのファイルで良いし、ディレクトリで良い。 ディレクトリの場合は、その下のファイル全てが対象になります。 ディレクトリを指定していると後ほど、Add documentで10個までファイルを追加できます。(ファイルはData sourceと同じディレクトリ内である必要があります)
- 今回はs3をData sourceにしていますが、Customで選択していると
- 直接ファイルをUploadできる(5MBまで。50MBまで拡大可能)
- Inlineで文字入力できる(制限は不明)
- Data source name ーーー『MyKnowledgeBase-FP0H-source』
- Data source location 『This AWS account』
- Parsing strategy 『Amazon Bedrock default parser』
- Chunk strategy size 500 overwrap 15%(私の場合はこうしましたが、300,20%でもいいと思います)
- 今回はs3をData sourceにしていますが、Customで選択していると
- Data sourceは4つ(計5つ)までで追加できるみたいです。
- Data sourceは1つのファイルで良いし、ディレクトリで良い。 ディレクトリの場合は、その下のファイル全てが対象になります。 ディレクトリを指定していると後ほど、Add documentで10個までファイルを追加できます。(ファイルはData sourceと同じディレクトリ内である必要があります)
- 『Next』
- Embedding model 『Cohere Multilingal v3』—1024次元
- このembedding modelはRequestしないと使用できない。Syncできない。
- Bedrock左メニューからFoundation modelの下のModel catalogをクリック
- Cohere multilingaulをクリック
- 画面上のEmbed Multilingaulのすぐ下に『Available to request』があるのでクリック
- ポップアップが開くので、『Request model access』をクリック
- 使いたいmodelを選択して、
- (Anthropic製を選択した場合は、会社名、業種、用途などを記入して)
- 『Submit』
- TitanなどはすぐにAccess grantedになったが、CohereとAnthropicはIn Progressのまま。3分くらいでCohereもAnthropicもAccess grantedになった。
- このembedding modelはRequestしないと使用できない。Syncできない。
- 『Use an existing vector store』とし
- Vector storeでAuroraを選択
- Amazon Aurora DB Cluster ARNはAurora DBのインスタンスの親のARNをコピペする
- Table nameは、スキーマ名.テーブル名で記入する。 『bedrock_knowledgebase.table_1』
- 『その1』で最後に作った2つ目のSecret ARNをコピペする。
- 暗号化はしていないのでKMS Keyは空白のまま
- Index filed mapping(CREATE TABLEで指定した通り)
- Vector field nameは、embedding
- Text field nameは、chunks
- Bedrock-managed metadata fieldは、metadata
- Custom metadataはテーブルに作っていないので空白のまま。
- Primary keyは、id
- 『Next』
- 『Create knowledge base』
- 『AmazonBedrokSecretPolicyForKnowledgeBase_dylea does not exit』というエラーが表示されたが、何故か次の日見たらKnowledgebaseが作成されていて、availableとなっていた。
- Knowledge baseに入り、Data sourceを選択して、『Sync』を実行。 (Sync completed for data source…)
- すると5~6分でCompleteとなり、Data source overviewの下のDocumentsに7つのファイル名の横にStatusとして『INDEXED』と表示されたら完成です。
実際テストしてみます。
- Data sourceからKnowledge baseに戻り、右上の『Test knowledge Base』をクリック
- まず、Knowledgebaseができているか確認するため、『Retrieval only: data sources』を選択
- 右下に入力フィールドがあるので、何か問い合わせてみます。
- PreviewにSoure chunksの初期値の5個の返答が表示されたら
Sourceの追加
MyKnowledgeBase-FP0Hに、pdfを追加していく。
- Bedrock画面の左メニューのKnowledge Basesを選択して
- ファイルを追加したいKnowledgebaseを選択する
- Data sourceを選択する
- Add document
- S3 locationはCurrent AWS accountとあひて、Browse s3でファイルを選択する。
- これはData sourceがs3の場合です。
- またファイルはData sourceのディレクトリ内になければなりません。
- 『Add』
*通常、Syncを実行したらData sourceで指定したディレクトリ内の全てのファイルが読み込まれる。 しかしその後ファイルを追加して、特定のファイルだけ読み込ます時にこのAdd documentを使用するのだと思います。
Lambdaからアクセス
retrieve_and_generate()
Lambdaで簡単に作るなら上記コマンドで試します。
- Lambdaからアクセスするには、以下のコード。
- knowledeBaseIdはあなたのKnowledgebase IDに置き換えてください。
- service_nameでもわかるように、awsが用意してくれているRAG用のAgentを使います。
- これでeventのpromptに質問を入れてLambdaに渡すと簡単に答えを取得できます。
import json import boto3 bedrock_client = boto3.client(service_name='bedrock-agent-runtime') def lambda_handler(event, context): try: prompt = event.get('prompt') response = bedrock_client.retrieve_and_generate( input={"text": prompt}, retrieveAndGenerateConfiguration={ "type": 'KNOWLEDGE_BASE', "knowledgeBaseConfiguration": { "knowledgeBaseId": "NNI3CR9IKF", "modelArn": "arn:aws:bedrock:ap-northeast-1::foundation-model/anthropic.claude-3-5-sonnet-20240620-v1:0", }, }, ) text = response["output"]["text"] print(text) return { 'statusCode': 200, 'body': json.dumps({ 'input_prompt': prompt, 'generated_text': text }) } except Exception as e: return { 'statusCode': 500, 'body': json.dumps({ 'error': str(e) }) }
TestのEvent JSONには以下を作って、key2をpromptに変えて、質問を記述しています。 これでTestとすれば、以下のJSONがlambda_handlerにeventとして渡されるので、event.get(“prompt”)で質問を取り出せます。
{ "message": "大阪はどんなところですか", "prompt": "Ethernetの使い方を教えて", "key3": "value3" }
カスタマイズ
上記retrieve_and_generateは簡単ですが、精度を上げたかったりする場合には、Knowledgebaseからの取り出し方や、フィルタやLLMへの使い方も変わる場合があります。 その場合は、Knowledgebaseから取り出し、加工してLLMに渡すなど個別に実行する必要があります。 Knowledgebaseから取り出すだけの場合はretrieveで、LLMを実行するだけの場合はinvoke_modelで実行します。
retrieve (Knowledgebaseから取り出し)
- デバッグする際もKnowlodgebaseからの返信内容を確認しなければなりません。
- しかしprint(response)としても、途中で切れるし、7個指定しても2個しか表示されないし、とてもデバッグできる感じではありません。 しかもresponseの中身は{Dict[List{Dict}]}となっています。
- それを確認するために参考として7個のprint文を記入しています。
- 中身の確認は、result_list以下でちゃんと7個表示されました。 私の場合Chunkは500にしているので、長すぎるため先頭の100文字だけprintしています。
import json import boto3 bedrock_client = boto3.client(service_name='bedrock-agent-runtime') import pprint def lambda_handler(event, context): try: prompt = {"text": event.get('prompt')} response = bedrock_client.retrieve( retrievalQuery=prompt, knowledgeBaseId="NNI3CR9IKF", retrievalConfiguration={ "vectorSearchConfiguration":{ "numberOfResults":7 } } ) print(type(response)) print(list(response.keys())) print(type(response["retrievalResults"])) print(type(response["retrievalResults"][0])) print(len(response["retrievalResults"])) print(list(response["retrievalResults"][0].keys())) results_list = response["retrievalResults"] print(f"\n全部で {len(results_list)} 件のデータがあります。\n") for i, result in enumerate(results_list, 1): print(f"\n--- 結果 {i}/{len(results_list)} ---") # content内のtextとscoreを表示する例 content = result.get('content', {}) text = content.get('text', '') score = result.get('score', 'N/A') print(f"Score: {score}") print(f"Text preview: {text[:100]}...") # 先頭100文字だけ表示 print("-" * 50) # 区切り線 return { 'statusCode': 200, 'body': json.dumps({ 'input_prompt': prompt, 'generated_text': text }) } except Exception as e: return { 'statusCode': 500, 'body': json.dumps({ 'error': str(e) }) }
Text Completion(invoke_model)
import json import boto3 bedrock_client = boto3.client(service_name='bedrock-runtime') def lambda_handler(event, context): # 以下でevent["prompt"]の中身を書き換えている。 # あるバージョンでは先頭の\n\nHuman:と末尾の\n\nAssistant:が必要と書いてあったので一応メモっとく。 # prompt = event.get("prompt", "\n\nHuman:クリスマスについて小学生でもわかるように説明してください。\n\nAssistant:") try: model_id = 'anthropic.claude-3-5-sonnet-20240620-v1:0' system_prompt = "必ず日本語で答えてください" prompt=event.get("message") messages = [{"role": "user", "content": prompt}] max_tokens = 1000 body = json.dumps( { "anthropic_version": "bedrock-2023-05-31", "max_tokens": max_tokens, "system": system_prompt, "messages": messages } ) response = bedrock_client.invoke_model(body=body, modelId=model_id) # Decode the response body. model_response = json.loads(response["body"].read()) # Extract and print the response text. response_text = model_response["content"][0]["text"] print(response_text) return { 'statusCode': 200, 'body': json.dumps({ 'input_prompt': prompt, 'generated_text': response_text }) } except Exception as e: return { 'statusCode': 500, 'body': json.dumps({ 'error': str(e) }) }
TestのEvent JSONには以下を作って、key1をmessageに変えて、prompt(=質問する文字列)を記述しています。 これでTestとすれば、以下のJSONがlambda_handlerにeventとして渡されるので、event.get(“message”)で質問を取り出せます。
{ "message": "大阪はどんなところですか", "key2": "value2", "key3": "value3" }
単純な質問でも返信に11秒強かかります。 しかしLambdaの初期Timeoutは3秒なのでタイムアウトエラーにならないように、Lambda関数のConfigurationでTimeoutを20秒に設定します。
使用可能モデル
- 東京リージョンでは『anthropic.claude-3-5-sonnet-20240620-v1:0』しか使えない。
- 最初はどこかのサンプルでanthropic.claude-3-5-sonnet-20240229-v1:0と記されてたので、これを使ってみたが、model ID on-demand throughput isn’t supported. となる。 もしかしたら常に最新のモデルIDを取得して記述しなければならないかもしれない。
- Amazon Bedrock でサポートされている基盤モデルに記載されている。
- Sonnet3.5 v2を使う場合。(2025/5現在)
- Amazon Bedrock でサポートされている基盤モデルでは、東京リージョンで3.5v2を使うのは『*』マークが記されている。 これは東京リージョンからus-west-2などのクロスリージョンを指定しないと使用できないという事。 Sonnet3.7は東京リージョンでは使えないので、Lambdaごとus-west-2で作成しないといけない。
- Bedrockで、us-west-2リージョンに切り替えて、Model catalogからそれらのモデルの使用申請(Modify model access)して、Access grantedになったら使用できる。
- その際、Lamdaは東京リージョンで起動して、以下のようにbedrock-runtimeにus-west-2を書き込むとSonnet3.7を使うことができる。
- bedrock_client = boto3.client(service_name=’bedrock-runtime’, region_name=’us-west-2′)
その他
Aurora databaseの右にRecommendationsのカラムに3 Informationalとあったので開いてみると、1つがRead Onlyのインスタンスが存在しないとあり、それをクリックすると、Add at least another DB instance to the DB cluster to improve availability and performance.と書かれてあったので、『Apply』ボタンを押し追加。 Availableまで5分くらいかかった。
ちょっと長いので、『その1』はここまででおしまい。 続きは『その2』へ。
コメント