背景
Bedrockを使用して通常のLLMだけで回答させるのではなく、特定のサイトにフォーカスした回答をさせたくなった。
そこで!
1.システムプロンプトに特定のサイトのURLを埋め込む
BedrockのシステムプロンプトにURLを埋め込んで
「このサイトの内容に従って回答して」というプロンプトを埋め込んでみた。
ただ、 BedrockのプロンプトにURLを埋め込んでもURLの内容を読み取るということはまでは出来ないという結果に。。。
※システムプロンプト
'あなたは優秀なAIアシスタントです。ユーザーからの質問や指示に対して、以下の手順で適切な回答を提供してください。'\
'1.依頼内容が実績と予測の差について、Web(https://playvalorant.com/ja-jp/news/game-updates/valorant-patch-notes-9-10/)内を検索し推測を行い、結果をユーザーに提供してください。'\
'Web内のどの文章を引用したか、参考にした文章全体を末尾に「引用:」として記載してください。'\
'また、回答の最後にURLを「■参照元:<url>」と、ファイルを「■参照元:<file>」と出力してください。'\
'2.回答する際は、ユーザーの知識レベルや関心に合わせて、技術的な詳細の量を調整するなど、わかりやすく親しみやすい言葉遣いを心がけてください。'\
'以上の手順に沿って、ユーザーの期待に応える高品質なアシスタントサービスを提供してください。'}]
2.AIエージェントを使ったサイト内の情報読み取り
プロンプトにURLの情報を書き込んで、サイトの情報を読み取ってもらうことはできないので、AIエージェントを作成し、エージェントからLambdaを起動し、Lambdaでサイトの情報を読み取ってそれを用いて、特定のサイトにフォーカスした回答ができるようにしたい!
今度は、AIエージェント内で起動したLambdaからのレスポンスのサイズが大きすぎて※1エラーが出てしまい断念。。。
※1 LambdaからBedrockまでの25KBまで
https://docs.aws.amazon.com/bedrock/latest/userguide/agents-lambda.html#agents-lambda-response
3. 最後の希望、ToolUseを用いたサイト内の情報読み取り
ToolUseなら、AIエージェントを用いた際に発生したLambdaのレスポンスサイズが大きすぎて発生したエラーが回避できるはずとのことをAWSサポートから教えていただいたので、こちらを実践!
ToolUseとは
今更ですが、、、AWS Bedrock での Tool Use (ツール使用) 機能は、大規模言語モデル (LLM) が外部ツールやAPIを利用できるようにする機能になります。
→ 「3. 最後の希望、ToolUseを用いたサイト内の情報読み取り」に記載したイラストにあるインターネットと通信している部分が、外部ツール/APIを使用している部分になります。
「3. 最後の希望、ToolUseを用いたサイト内の情報読み取り」の実践
ToolUseなしの動作確認
比較として、まずToolUseなし(Bedrock->プレイグラウンド->Chat / Text)の場合の動作確認を行います。
※ 僕がValorantというゲームが好きなので、、最近発表されたValorantのパッチノート9.10について質問してみます。
◆ユーザープロンプト
valorant9.10の目玉の情報を教えて
◆回答
Sunsetの追加って、1年前の話ですよ・・・
それにToolUseなしだと当たり障りがなく、具体性のない回答が目立ちます。
Valorant パッチ9.10の主な更新内容は以下の通りです:
1. 新マップ「Sunset」の追加
- ロサンゼルスを舞台にした新マップ
- 3レーン構造の戦術的なマップデザイン
2. プレミアの機能強化
- チーム検索システムの改善
- マッチメイキングの最適化
3. バランス調整
- エージェントの微調整
- 武器の調整
4. バグ修正と安定性の向上
このパッチでは特に新マップ「Sunset」の追加が大きな目玉となっています。ロサンゼルスの雰囲気を反映した華やかなデザインと、戦略性の高いレイアウトが特徴です。
これらの情報は私の知る限りですが、詳細な変更点については公式パッチノートで確認することをお勧めします。
ConverseAPI/ToolUse実装
ConverseAPIとは、Bedrockの「プレイグラウンド->Chat / Text」で使用されているAPIで
ConverseAPIを使用することで「プレイグラウンド->Chat / Text」と同様のプロンプトのやり取りができるようになります。
そして、ConverseAPIには上記で説明したToolUseを組み込むことができるので、それを実現したコードが下記となります。
※ 参考ドキュメント
[1]ConverseAPI/ConverseStreamAPIの使用例
https://docs.aws.amazon.com/ja_jp/bedrock/latest/userguide/tool-use-examples.html
import logging
import json
import boto3
import requests
from bs4 import BeautifulSoup
import time
from botocore.exceptions import ClientError
class ValorantContentsNotFoundError(Exception):
"""Raised when a radio station isn't found."""
pass
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
def get_valorant_patch_contents():
"""Returns valorant patch note information
"""
print("get_valorant_patch_contents start")
valorant_patch_contents = ""
call_url = "https://playvalorant.com/ja-jp/news/game-updates/valorant-patch-notes-9-10/"
response = requests.get(call_url)
soup = BeautifulSoup(response.content, "html.parser")
texts = []
for tmp_content in soup.find_all("div", id="__next"):
# get_textはHTML タグを取り除き、純粋なテキスト部分だけを返す。例: <p>Hello, <b>World</b>!</p> → Hello, World!
# 前後の空白を取り除く場合は strip=True を指定します。
text = tmp_content.get_text(strip=True)
if text:
texts.append(text)
valorant_patch_contents = texts
return valorant_patch_contents
def generate_text(bedrock_client, model_id, tool_config, input_text):
"""Generates text using the supplied Amazon Bedrock model. If necessary,
the function handles tool use requests and sends the result to the model.
Args:
bedrock_client: The Boto3 Bedrock runtime client.
model_id (str): The Amazon Bedrock model ID.
tool_config(dict): The tool configuration.
input_text (str): The input text.
Returns:
Nothing.
"""
logger.info("Generating text with model %s", model_id)
# Create the initial message from the user input.
messages = [{
"role": "user",
"content": [{"text": input_text}]
}]
response = bedrock_client.converse(
modelId=model_id,
messages=messages,
toolConfig=tool_config
)
output_message = response['output']['message']
messages.append(output_message)
stop_reason = response['stopReason']
print("stop_reason:" + stop_reason)
if stop_reason == 'tool_use':
# Tool use requested. Call the tool and send the result to the model.
tool_requests = response['output']['message']['content']
for tool_request in tool_requests:
if 'toolUse' in tool_request:
tool = tool_request['toolUse']
logger.info("Requesting tool %s. Request: %s", tool['name'], tool['toolUseId'])
text = "valorant_patch_note"
if text in tool['name']:
tool_result = {}
try:
valorant_patch_contents = get_valorant_patch_contents()
# print("valorant_patch_contents:" + ", ".join([str(item) for item in valorant_patch_contents]))
tool_result = {
"toolUseId": tool['toolUseId'],
"content": [{"json": {"valorant_patch_contents": valorant_patch_contents}}]
}
except ValorantContentsNotFoundError as err:
tool_result = {
"toolUseId": tool['toolUseId'],
"content": [{"text": err.args[0]}],
"status": 'error'
}
tool_result_message = {
"role": "user",
"content": [
{
"toolResult": tool_result
}
]
}
messages.append(tool_result_message)
# Too many request countermeasure -> 1 mininute wait
time.sleep(60)
# Send the tool result to the model.
response = bedrock_client.converse(
modelId=model_id,
messages=messages,
toolConfig=tool_config
)
output_message = response['output']['message']
# print the final response from the model
for content in output_message['content']:
print(json.dumps(content, indent=4, ensure_ascii=False))
else:
# Not use tool use
# print the final response from the model
for content in output_message['content']:
print(json.dumps(content, indent=4, ensure_ascii=False))
def main():
"""
Entrypoint for tool use example
"""
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
model_id = "anthropic.claude-3-5-sonnet-20240620-v1:0"
input_text = "valorant9.10の目玉の情報を教えて"
# TODO:valorantのパッチノートをiunputChemaで指定して、好きなやつを見れるようにしたい
tool_config = {
"tools": [
{
"toolSpec": {
"name": "valorant_patch_note_9_10",
"description": "valorantのパッチノート9.10に関連する情報",
"inputSchema": {
"json": {
"type": "object",
"properties": {},
"required": []
}
}
}
}
]
}
bedrock_client = boto3.client(service_name='bedrock-runtime')
try:
print(f"Quiestion: {input_text}")
generate_text(bedrock_client, model_id, tool_config, input_text)
except ClientError as err:
message = err.response['Error']['Message']
logger.error("A client error occurred: %s", message)
print(f"A client error occured: {message}")
else:
print(f"Finished generating text with model {model_id}.")
if __name__ == "__main__":
main()
実行結果
パッチ9.10の内容にフォーカスした回答ができていて
ToolUseなしの結果と比較すると、回答の具体性が段違い。
パッチノート9.10の主な目玉情報をまとめてお伝えします:
1. 新たなアーマー回復シールドの追加:
- コスト:650クレジット
- ダメージ吸収量:25 HP
- 回復プール:50 HP
- 体力へのダメージが発生する前までに受けたダメージの100%を吸収し、
しばらくすると回復プール内に残っているポイントを消費してシールドが回復します。
2. エージェントのアップデート:
- サイファー: トラップワイヤーの調整、アルティメットコストの増加
- セージ: ヒーリングオーブとスロウオーブの強化、アルティメットコストの減少
- オーメン: ダークカバーの調整
- フェニックス: 大幅な能力調整と強化
3. ゲームシステムのアップデート:
- バトルレポートの改善:
ユーティリティの使用に関する統計データがラウンド終了後に表示されるように変更
- スタン効果の調整:
敵のダッシュ速度を50%低下させるように
- スモークのビジュアルアップデート:
消えるタイミングが分かりやすくなるよう改善
4. 武器のアップデート:
- ピストルとライフルの精度低下の調整
- フレンジーの弾数増加(13→15)、ゴーストの弾数減少(15→13)
- ファントムのダメージ減衰の調整
これらの変更は、ゲームバランスの調整とプレイ体験の向上を目的としています。
特に新しい回復シールドの追加や、フェニックスの大幅な調整は、
ゲームプレイに大きな影響を与える可能性があります。
まとめ
いろいろ紆余曲折ありましたが、「ConverseAPI / ToolUse」を用いることで
ユーザーのプロンプトに対して、特定のサイトにフォーカスした回答をさせることができました!
記事を見ていただいた方の少しでも助けになれば、うれしいです('ω')ノ