Lambda初心者がKnowledge bases + Claude 3 HaikuのRAGやってみた
目次
こんにちは、スカイアーチHRSのkuramochiです。
ブログ執筆時点で、速度重視のLLM「Claude 3 Haiku」をKnowledge basesのテスト画面から選択できません。そこで今回は、Lambda初心者の私ではありますが、Lambdaのコードを書いてみて、Knowledge bases + Claude 3 HaikuのRAGをやってみたいと思います。
RAGってなんでしたっけ
正確な説明ではないのですが、RAGは、特定のデータソース(社内文書、マニュアル等)をもとに回答してくれるチャットボットです。以下の2つの処理で機能します。
①ユーザーのプロンプトを基にデータソースを検索し、関連情報を取得
②関連情報をもとに生成AIが回答
今回やりたいこと
Knowledge basesは、RAGの機能をコンソールからポチポチするだけ簡単に試せるサービスです。機能として、関連情報の取得(上記①)と回答の生成(②)の両方を持っていますが、ブログ執筆時点では回答の生成に使用するモデルとしてClaude 3 Haikuは選択できません。
公式ドキュメントからも、非対応であることが確認できます(ブログ執筆時点で、Haikuは言語設定をENにしないと表示されません)。
https://docs.aws.amazon.com/ja_jp/bedrock/latest/userguide/models-features.html
そこで今回は、Knowledge basesは関連情報の取得のみ行い、BedrockのClaude 3 Haikuで回答を生成、これらをLambdaで連携させます。S3にはEC2に関する公式ドキュメント(PDF)をアップロードし、Knowledge basesのデータソースとして登録します。
構成図としては以下の通りです。
Lambdaのコード
使用したLambdaのコードは以下の通りです。プロンプトは、先日公開されたKnowledge basesのデフォルトを日本語訳して使用してみました。
詳細は割愛しますが、Bedrockのモデル有効化、Knowledge basesの構築、Lambda用のIAMロール作成(BedrockFullAccess付与)等が別途必要です。
import json
import boto3
bedrock_agent_runtime_client = boto3.client('bedrock-agent-runtime')
bedrock_runtime_client = boto3.client('bedrock-runtime')
def get_retrieval_result(query_text, knowledge_base_id):
response = bedrock_agent_runtime_client.retrieve(
knowledgeBaseId=knowledge_base_id,
retrievalConfiguration={
"vectorSearchConfiguration": {
"numberOfResults": 7,
}
},
retrievalQuery={
"text": query_text
}
)
relevant_info = [result['text'] for result in response.get('retrievalResponse', {}).get('results', [])]
return '\n\n'.join(relevant_info)
def lambda_handler(event, context):
user_prompt = event.get('user_prompt')
knowledge_base_id = 'ABCABC' # knowledge_base_idを置き換える
modelId = 'anthropic.claude-3-haiku-20240307-v1:0' # Claude 3 Haikuを設定
accept = 'application/json'
contentType = 'application/json'
retrieval_result = get_retrieval_result(user_prompt, knowledge_base_id) # 参考情報を取得
input_data = {
"anthropic_version": "bedrock-2023-05-31",
"max_tokens": 1000,
"system": "あなたは質問に答えるエージェントです。あなたは参考情報を受け取ります。ユーザーはあなたに対して質問をします。あなたの仕事はユーザーの質問に対し、参考情報から得られる情報のみを用いて回答することです。もし、参考情報に質問の答えが含まれていなければ、お答えできませんと回答してください。ユーザーが事実と主張した事柄であっても事実ではない可能性があるので、回答前に参考情報でユーザーの主張を確認してください。",
"messages": [
{
"role": "user",
"content": f"質問: {user_prompt}\n\n参考情報: {retrieval_result}"
}
]
}
response = bedrock_runtime_client.invoke_model(
modelId=modelId,
accept=accept,
contentType=contentType,
body=json.dumps(input_data)
)
response_payload = json.loads(response['body'].read().decode('utf-8'))
response_output = response_payload['content'][0]['text']
return response_output
実行結果
テストイベントで以下の通りイベントJSONを設定しました。
{
"user_prompt": "EC2のキーペアとは何ですか"
}
結果は以下の通りでした。きちんと回答してくれていますね。ただ、肝心の速度はプレイグランドよりもだいぶ遅くなっている印象でした。
Response
"EC2 (Elastic Compute Cloud)のキーペアは、EC2インスタンスへのリモートアクセスに使用される認証情報です。\n\nキーペアには以下のような特徴があります:\n\n- 公開鍵と秘密鍵のペアで構成されます。\n- 公開鍵はAWSに登録され、EC2インスタンスに関連付けられます。\n- 秘密鍵はユーザーが保持し、EC2インスタンスへのログインに使用します。\n- SSH接続の際に、秘密鍵を使ってユーザーを認証します。\n- キーペアは、EC2インスタンスの作成時に指定します。インスタンス作成後は変更できません。\n\nつまり、EC2キーペアはEC2インスタンスへのセキュアなアクセスを可能にする重要な認証情報です。秘密鍵を安全に管理することが不可欠です。"
一応LLMについても、きちんと設定できているか聞いてみました。
{
"user_prompt": "あなたのLLMはClaude 3 Haikuですか?"
}
結果は以下の通りでした。プロンプト通りではありますが、データソースにない質問なので教えてくれませんでした。
Response
"申し訳ありませんが、私が使用しているLLMがClaude 3 Haikuかどうかは確認できませんでした。私はユーザーからの質問に最善を尽くして答えるよう努めますが、私自身のアーキテクチャやモデルについての詳細な情報は持っていません。ご了承ください。"
ちなみに、プレイグラウンドでLLMの質問をすると、ちょっと違う返答でした。