1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

BedrockのToolUseを使って、特定のサイトにフォーカスした回答をさせる

Last updated at Posted at 2024-11-30

背景

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エラーが出てしまい断念。。。

image.png

※1 LambdaからBedrockまでの25KBまで
https://docs.aws.amazon.com/bedrock/latest/userguide/agents-lambda.html#agents-lambda-response
image.png

3. 最後の希望、ToolUseを用いたサイト内の情報読み取り

ToolUseなら、AIエージェントを用いた際に発生したLambdaのレスポンスサイズが大きすぎて発生したエラーが回避できるはずとのことをAWSサポートから教えていただいたので、こちらを実践!

image.png

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」を用いることで
ユーザーのプロンプトに対して、特定のサイトにフォーカスした回答をさせることができました!

記事を見ていただいた方の少しでも助けになれば、うれしいです('ω')ノ

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?