はじめに
この記事は、ヌーラボブログリレー2024 for Tech Advent Calendar 2024の2日目の記事です。
前回のブログリレー記事では、Converse APIのTool useのJSONモードを使用して「クエリ拡張」を行い、RAGに対する検索を検証しました。
この記事の発展版も公開しましたので、そちらもご覧ください。
最近、生成AIを活用したチャットボットやRAG(Retrieval-Augmented Generation)システムの実装に取り組む方が増えています。特に、組織固有の文書を検索・活用するシステムは、様々なハンズオンや書籍のサンプルコードを参考に、多くの方が開発に挑戦されているのではないでしょうか。
LLM(大規模言語モデル)の精度は急速に向上しており、比較的シンプルな実装でも複雑なクエリに対して質の高い回答が得られるようになりました。しかし、ユーザーの検索意図に対して的確な検索結果を得られなかったり、関連性の低い情報が返されたりすることがあります。
この課題を解決する手法の一つが「クエリ書き換え」です。クエリ書き換えには以下のようなアプローチがあります。
- クエリ拡張:もとのクエリに関連する用語や同義語を追加する手法
- クエリ分解:複雑なクエリを複数の簡単なクエリに分解する手法
- クエリの簡略化:複雑な検索クエリから重要なキーワードのみを抽出し、シンプルな形に整理する手法
これらの手法を適切に活用することで、より精度の高い検索結果や回答を得ることが期待できます。
本記事では、AWSブログ「RAG の精度を向上させる Advanced RAG on AWS の道標」を参考に、複雑な質問を複数の簡単な質問に分解する「クエリ分解」の再現実装を行い、RAGに対する検索を検証します。比較対象として、もとの質問をそのまま検索した場合と、クエリ分解とクエリ拡張を行った場合の検索結果を比較します。
参考資料
生成AIによる「クエリ拡張」と「クエリ分解」を行う
ここでは、Amazon Bedrock Converse APIのJSONモードを使用して、クエリ拡張とクエリ分解を行います。生成AIに渡すクエリを生成AI自身に作ってもらいます。実際のコードを示す前に、クエリ拡張とクエリ分解の例を以下に示します。
クエリ拡張
クエリ拡張は、もとの検索クエリに関連する用語や同義語を追加することで、検索範囲を適切に広げる手法です。例えば:
もとのクエリ:「現状の生成AIが抱える課題にはどのようなものがあるか」
拡張後のクエリ:「生成AI 規制 法規制 ガイドライン AI規制法 ハルシネーション 著作権 プライバシー 倫理 エネルギー消費」
検索を行う際は、もとのクエリに拡張後のクエリを追加して検索します。もとのクエリに関連する用語や同義語、異なる表現で書かれた関連文書も検索にヒットするようになり、より網羅的な検索結果が期待できます。
クエリ分解
クエリ分解は、複雑なクエリをそれ単体で回答可能な複数の簡単なクエリに分解する手法です。例えば:
もとのクエリ:「現状の生成AIが抱える課題にはどのようなものがあるか」
分解後のクエリ:
- 「生成AIの開発・運用にかかるコストや経済的課題は何か」
- 「生成AIの環境負荷や持続可能性に関する課題は何か」
- 「生成AIのデータセキュリティと個人情報の保護に関する課題は何か」
検索を行う際は、もとのクエリと分解後のクエリのそれぞれで検索し、その結果を組み合わせることで最適な回答を導き出すことが期待できます。
Converse APIのJSONモード
Converse APIのJSONモードについては、Amazon Bedrock Converse API で Claude3 の JSON モードを利用するに以下のような説明があります。
ユーザー側で実際には存在しない「架空のツール」を定義しておき,このツールの入力スキーマとして,求める出力の構造を指定します.この「架空のツール」を Claude3 に送信することで,Claude3 はこのツールが実在すると思い込み,指定されたスキーマに従って JSON 形式の入力を生成します.この生成された JSON 形式のツールの入力が,ユーザーが求める出力そのものとなります.
架空のツールとスキーマの定義で求めるJSON形式の出力を得られるのは、とても便利な機能です。以降で、ツールの定義とその実行結果の出力について説明します。
ツールの定義
JSONモードでは、架空のツールを定義することでツールを実装することなくJSON形式の出力を得ることができます。ClaudeのユーザーガイドにあるTool useの Best practices for tool definitions によると、以下のような点が推奨されています:
- Provide extremely detailed descriptions (非常に詳細な説明を提供する)
- What the tool does (ツールが何をするか)
- When it should be used (ツールを使用するタイミング)
- What each parameter means and how it affects the tool's behavior (各パラメータの意味とツールの動作に与える影響)
- Prioritize descriptions over examples (例よりも説明を優先する)
これらのベストプラクティスを参考に、クエリ拡張とクエリ分解を行うツールを以下のように定義します。それぞれのdescriptionに、ツールの詳細な説明やどのような動作を期待しているかを記述します。
"toolSpec": {
"name": "queries_generator",
"description": "ユーザーが提供する質問に対して、検索効率を高めるためのクエリ拡張とクエリ分解を行う。与えられる質問文に基づいて類義語や日本語と英語の表記揺れを考慮し、多角的な視点からクエリを生成するクエリの拡張は、元の質問に関連する用語や同義語を含む検索用のクエリを生成する。これは検索範囲を広げることに役立つ。",
"inputSchema": {
"json": {
"type": "object",
"properties": {
"query_expansion": {
"type": "array",
"description": "それぞれの観点に基づいて検索用のクエリを生成する。",
"minItems": 3,
"maxItems": 6,
"items": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "日本語と英語を混ぜた多様な単語を空白で区切って記述する。focusに示すそれぞれの観点に応じた用語を用いる。例) Amazon Bedrock ナレッジベース ベクトルエンジン vector databases DB データベース"
},
"focus": {
"type": "string",
"description": "このクエリが注目する観点",
"enum": ["技術的側面", "一般的説明", "政治的説明", "社会的説明", "経済的説明", "その他"]
}
},
"required": ["query", "focus"]
}
},
"query_decomposition": {
"type": "array",
"description": "複雑な質問を複数の簡単な質問に分解し関連度を付けたクエリを生成する。分解したクエリは、それ単体で回答可能なものである。これは、質問をより具体的なものにし、回答の質を高めることに役立つ。ユーザーが提供する質問は、そのままでは回答が難しい場合があるため、このツールを利用することで回答の質を高めることができる。",
"minItems": 3,
"maxItems": 5,
"items": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "複雑な質問をそれ単体で回答可能な粒度のクエリに分解する。"
},
"relevance": {
"type": "integer",
"description": "質問との関連度(1が最高)",
"minimum": 1,
"maximum": 3
}
},
"required": ["query", "relevance"]
}
}
},
"required": ["query_expansion", "query_decomposition"]
}
}
}
ここでは、queries_generator
というツールを定義し、その中にクエリ拡張を行う query_expansion
とクエリ分解を行うquery_decomposition
という二つのプロパティを定義します。
query_expansion
では、以下の二つのプロパティを定義します:
- 出力するクエリの観点を指定する
focus
- クエリ拡張の結果を出力する
query
query_decomposition
では、以下の二つのプロパティを定義します:
- クエリ分解の結果を出力する
query
- クエリとの関連度を示す
relevance
query
、focus
、relevance
の各項目は、必ず出力されるようrequired
を指定します。
これらのプロパティはそれぞれminItems
とmaxItems
を持ち、出力するクエリの数を決定します。前回までの記事では、出力するクエリの数をハードコードしたり、プロンプトで制御したりしていたため、使い勝手の悪いものでした。今回はその点を改良し、より柔軟な出力が行えるようにしました。
ツールの実行と結果の出力
定義したツールを実行し、その結果を出力するコードを以下に示します。次のようにコマンドライン引数として質問を渡すことで、ツールを実行できます。モデルにはClaude 3.5 Sonnet
を使用します。コードを実行する前に、AWS Bedrock でこのモデルを有効化してください。
python ./app.py "現状の生成AIが抱える課題にはどのようなものがあるか。"
コード内で Claude に渡すパラメータを以下のように設定します。生成するクエリにランダム性を持たせるため、temperature
を0.7
に設定します。また、maxTokens
は生成するクエリの最大トークン数を指定します。maxTokens
の値が小さいと、生成するクエリの数がminItems
を下回る可能性があります。それぞれのユースケースに応じて適切な値を検討してください。
inference_config = {
"temperature": 0.7,
"maxTokens": 1000,
}
import json
import logging
import sys
from typing import Any, Dict
import boto3
from botocore.exceptions import ClientError
logging.basicConfig(format="%(asctime)s [%(levelname)s] %(message)s", level=logging.INFO)
logger = logging.getLogger(__name__)
def get_bedrock_client():
return boto3.client(
service_name="bedrock-runtime",
region_name="us-east-1"
)
def load_tool_config() -> Dict[str, Any]:
return {
"tools": [
{
"toolSpec": {
"name": "queries_generator",
"description": "ユーザーが提供する質問に対して、検索効率を高めるためのクエリ拡張とクエリ分解を行う。与えられる質問文に基づいて類義語や日本語と英語の表記揺れを考慮し、多角的な視点からクエリを生成するクエリの拡張は、元の質問に関連する用語や同義語を含む検索用のクエリを生成する。これは検索範囲を広げることに役立つ。",
"inputSchema": {
"json": {
"type": "object",
"properties": {
"query_expansion": {
"type": "array",
"description": "それぞれの観点に基づいて検索用のクエリを生成する。",
"minItems": 3,
"maxItems": 6,
"items": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "日本語と英語を混ぜた多様な単語を空白で区切って記述する。focusに示すそれぞれの観点に応じた用語を用いる。例) Amazon Bedrock ナレッジベース ベクトルエンジン vector databases DB データベース"
},
"focus": {
"type": "string",
"description": "このクエリが注目する観点。",
"enum": ["技術的側面", "一般的説明", "政治的説明", "社会的説明", "経済的説明", "その他"]
}
},
"required": ["query", "focus"]
}
},
"query_decomposition": {
"type": "array",
"description": "複雑な質問を複数の簡単な質問に分解し関連度を付けたクエリを生成する。分解したクエリは、それ単体で回答可能なものである。これは、質問をより具体的なものにし、回答の質を高めることに役立つ。ユーザーが提供する質問は、そのままでは回答が難しい場合があるため、このツールを利用することで回答の質を高めることができる。",
"minItems": 3,
"maxItems": 5,
"items": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "複雑な質問をそれ単体で回答可能な粒度のクエリに分解する。"
},
"relevance": {
"type": "integer",
"description": "質問の優先順位(1が最高)",
"minimum": 1,
"maximum": 3
}
},
"required": ["query", "relevance"]
}
}
},
"required": ["query_expansion", "query_decomposition"]
}
}
}
}
],
"toolChoice": {
"tool": {
"name": "queries_generator",
}
}
}
def query_generator_system_prompt() -> str:
return '''
あなたは高度な検索クエリ最適化エキスパートです。
与えられた質問に対して、クエリの拡張とクエリの分解の2つの最適化を行ってください。
'''
def generate_queries(bedrock_client, model_id, message) -> Dict[str, Any]:
"""入力テキストから検索クエリを生成する"""
system_prompts = [
{
"text": query_generator_system_prompt()
}
]
messages = [
{
"role": "user",
"content": [
{
"text": message,
}
]
}
]
inference_config = {
"temperature": 0.7,
"maxTokens": 1000,
}
try:
return bedrock_client.converse(
system=system_prompts,
messages=messages,
modelId=model_id,
inferenceConfig=inference_config,
toolConfig=load_tool_config(),
)
except ClientError as err:
logger.error("A client error occurred: %s", err.response['Error']['Message'])
raise
except Exception as e:
logger.error("An unexpected error occurred: %s", str(e))
raise
def main():
# コマンドライン引数を取得する
if len(sys.argv) < 2:
print("Usage: python ./app.py <input_text>")
sys.exit(1)
model_id = "anthropic.claude-3-5-sonnet-20240620-v1:0"
message = sys.argv[1]
try:
bedrock_client = get_bedrock_client()
response = generate_queries(
bedrock_client,
model_id,
message,
)
print("Response:" + "\n\n" + json.dumps(response, indent=2, ensure_ascii=False))
except ClientError as err:
logger.error("A client error occurred: %s", err.response['Error']['Message'])
except Exception as e:
logger.error("An unexpected error occurred: %s", str(e))
if __name__ == "__main__":
main()
クエリ拡張とクエリ分解の実行結果
ツールの実行による出力は以下のとおりです。メタデータなどの出力を除いたoutput
のみを抜粋しています。「現状の生成AIが抱える課題にはどのようなものがあるか」という質問から、多くの多様なクエリが生成されていることが分かります。
例えば、「コスト」「計算リソース」「消費電力」「環境負荷」「training」「GPU shortage」というクエリは、生成AIの開発・運用にかかるコストと環境への影響に関する課題を表しています。クエリ拡張により、もとの質問にないキーワードを含むクエリを用いて検索を行うことで、より多面的な情報を取得できます。また、クエリ分解により、質問をより具体的なものにし、回答の質を高めることが期待できます。
出力結果から分かるように、クエリ拡張ではツールで指定した観点に応じたクエリを生成します。クエリ分解では、ユーザーが提供した質問を分解し、その分解したクエリとの関連度を1から3で指定します。このように、ツールの中身を実装することなく、inputSchema
を定義するだけで「クエリ拡張」と「クエリ分解」を行うことができました。
"output": {
"message": {
"role": "assistant",
"content": [
{
"toolUse": {
"toolUseId": "tooluse_SssENtA7Qa2V1BVmkTWb-w",
"name": "queries_generator",
"input": {
"query_expansion": [
{
"query": "生成AI 問題点 課題 hallucination 幻覚 誤情報 精度 generative AI problems challenges limitations",
"focus": "技術的側面"
},
{
"query": "生成AI ChatGPT AI倫理 著作権問題 データプライバシー 個人情報保護 AI regulation",
"focus": "社会的説明"
},
{
"query": "生成AI コスト 計算リソース 消費電力 環境負荷 training cost GPU shortage",
"focus": "経済的説明"
},
{
"query": "生成AI バイアス 差別 偏見 フェイクニュース デジタルデバイド AI bias discrimination",
"focus": "政治的説明"
}
],
"query_decomposition": [
{
"query": "生成AIの技術的な限界や精度の問題点は何か",
"relevance": 1
},
{
"query": "生成AIの利用における倫理的・法的な課題とは",
"relevance": 1
},
{
"query": "生成AIの開発・運用にかかるコストと環境への影響",
"relevance": 2
},
{
"query": "生成AIが社会に与える負の影響やリスク",
"relevance": 2
}
]
}
}
}
]
}
}
toolChoiceの指定
このコードでは、toolConfig
リクエストパラメータでtoolChoice
フィールドを指定することで、ツールの使用をコントロールしています。以下のように明示的にツール名を指定することで、Claude は指定したツールを必ず使用します。(参考: Call a tool with the Converse API, Controlling Claude’s output)
"toolChoice": {
"tool": {
"name": "queries_generator",
}
}
具体的なツール名ではなくany
を指定すると、Claude が複数あるツールの中からどのツールを使用するかを自由に選択します。今回はツールが1つしかないため、queries_generator
を使用することを強制します。
"toolChoice": {
"any" : {}
}
一方、toolChoice
を指定しない場合、もしくは以下のようにauto
を指定すると、Claude がツールを使用するかどうかを自由に選択します。その結果、ツールの実行による出力は以下のようにcontent[]
内にtext
フィールドが追加されます。このtext
フィールドの内容は Chain of Thought(思考の連鎖)と呼ばれ、問題を分析しどのツールを使うのか(あるいは使わないのか)を決定するための段階的な推論を示します。
"toolChoice": {
"auto" : {}
}
"output": {
"message": {
"role": "assistant",
"content": [
{
"text": "生成AIの課題について、多角的な視点からクエリを生成し、体系的に情報を収集していきましょう。"
},
{
"toolUse": {
"toolUseId": "tooluse_SssENtA7Qa2V1BVmkTWb-w",
"name": "queries_generator",
"input": {
"query_expansion": [
{
"query": "生成AI 問題点 課題 hallucination 幻覚 誤情報 精度 generative AI problems challenges limitations",
"focus": "技術的側面"
},
(途中省略)
]
}
}
}
]
}
}
(出典: Controlling Claude’s output)
図にすると、このようになります。toolChoice
の指定によって Claude の挙動が異なることが分かります。この図は Anthropic の APIでのパラメータ指定を示したものであり、Bedrock でのパラメータ指定とは異なります。
PineconeとAmazon Bedrockで構築したRAGに対して、クエリ拡張とクエリ分解で検索を行う
次に、PineconeとAmazon Bedrockで構築したRAGに対して、クエリ拡張とクエリ分解で検索を行ってみます。
RAGの構築手順
RAGの構築手順は、以下の記事を参照してください。
RAGのソースデータファイルには、上記の記事で使用した独立行政法人情報処理推進機構(IPA)のPDFファイル等の他に以下のWebサイトのPDFファイルを使用しました。
また、各ソースデータファイルには以下のようなKnowledge Basesのメタデータファイルを配置します。これにより、Claudeはプロンプトに従って引用元のURLを含む情報を出力することができます。
{
"metadataAttributes": {
"PageURL": "https://www.meti.go.jp/shingikai/mono_info_service/ai_shakai_jisso/20240419_report.html",
"PageTitle": "AI事業者ガイドライン(第1.0版)",
"FileURL": "https://www.meti.go.jp/shingikai/mono_info_service/ai_shakai_jisso/pdf/20240419_3.pdf",
"FileTitle": "「AI事業者ガイドライン(第1.0版)」別添(PDF形式:4,360KB)",
"Category": "gudeline"
}
}
構成図は以下のとおりです。
コード
コードは以下のとおりです。"numberOfResults": 3
は Knowledge Base からの検索結果の数を指定します。1つのクエリに対して3つの検索結果を取得します。
例えば、クエリ拡張で3種類のクエリを生成した場合、もとの質問と合わせて4種類のクエリに対してそれぞれ3件ずつ、合計12件の検索結果を取得します。その結果をもとに回答を生成します。
この処理から分かるとおり、検索結果によっては出力が数万トークンになることがあります。費用の観点から、実際の利用ではユースケースに応じて出力トークン数を抑えるような工夫が必要です。
サンプルコード(長いので閉じています。クリックで展開してください)
import json
import logging
import sys
from typing import Any, Dict, List, Optional
import boto3
from botocore.exceptions import ClientError
logging.basicConfig(format="%(asctime)s [%(levelname)s] %(message)s", level=logging.INFO)
logger = logging.getLogger(__name__)
def get_bedrock_client():
return boto3.client(
service_name="bedrock-runtime",
region_name="us-east-1"
)
def get_bedrock_agent_client():
return boto3.client(
service_name="bedrock-agent-runtime",
region_name="us-east-1"
)
def load_tool_config() -> Dict[str, Any]:
return {
"tools": [
{
"toolSpec": {
"name": "queries_generator",
"description": "ユーザーが提供する質問に対して、検索効率を高めるためのクエリ拡張とクエリ分解を行う。与えられる質問文に基づいて類義語や日本語と英語の表記揺れを考慮し、多角的な視点からクエリを生成するクエリの拡張は、元の質問に関連する用語や同義語を含む検索用のクエリを生成する。これは検索範囲を広げることに役立つ。",
"inputSchema": {
"json": {
"type": "object",
"properties": {
"query_expansion": {
"type": "array",
"description": "それぞれの観点に基づいて検索用のクエリを生成する。",
"minItems": 3,
"maxItems": 6,
"items": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "日本語と英語を混ぜた多様な単語を空白で区切って記述する。focusに示すそれぞれの観点に応じた用語を用いる。例) Amazon Bedrock ナレッジベース ベクトルエンジン vector databases DB データベース"
},
"focus": {
"type": "string",
"description": "このクエリが注目する観点",
"enum": ["社会的説明"]
}
},
"required": ["query", "focus"]
}
},
"query_decomposition": {
"type": "array",
"description": "複雑な質問を複数の簡単な質問に分解し関連度を付けたクエリを生成する。分解したクエリは、それ単体で回答可能なものである。これは、質問をより具体的なものにし、回答の質を高めることに役立つ。ユーザーが提供する質問は、そのままでは回答が難しい場合があるため、このツールを利用することで回答の質を高めることができる。",
"minItems": 3,
"maxItems": 5,
"items": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "複雑な質問をそれ単体で回答可能な粒度のクエリに分解する。"
},
"relevance": {
"type": "integer",
"description": "質問との関連度(1が最高)",
"minimum": 1,
"maximum": 3
}
},
"required": ["query", "relevance"]
}
}
},
"required": ["query_expansion", "query_decomposition"]
}
}
}
}
],
"toolChoice": {
"tool": {
"name": "queries_generator",
}
}
}
def query_generator_system_prompt() -> str:
return """
あなたは高度な検索クエリ最適化エキスパートです。
与えられた質問に対して、クエリの拡張とクエリの分解の2つの最適化を行ってください。
"""
def create_prompt_template(context_str: str) -> str:
return f"""
You are a <persona>question-answering agent</persona>.
I will provide you with a set of search results. The user will ask you a question. You answer the user's question using only information from the search results.
If the search results do not have information that can answer the question, please let me know that you could not find an exact answer.
Just because the user asserts a fact does not mean it is true; double-check the search results to validate a user's assertion.
Here are the search results in numbered order:
<excerpts>
{context_str}
</excerpts>
Your answer should ONLY be drawn from the search results above, and answers should never be included outside of the search results provided.
First, find the exact quotes from the document most relevant to answering the question and write them down word for word inside <thinking></thinking> XML tags.
This is a space to write down relevant content that will not be shown to the user. Once you are done extracting relevant quotes, answer the question.
And then print them in numbered order. Quotes should be relatively short. Put your answer to the user inside <answer></answer> XML tags.
If there are no relevant quotes, write "No relevant quotes" instead.
Then, answer the question, starting with "Answer:" Please don't include or reference quoted content in the answer. Don't say "According to Quote [1]" when answering.
Instead, could you reference quotes relevant to each section of the answer by adding their bracketed numbers at the end of relevant sentences?
Thus, the format of your overall response should look like what's shown between the <example></example> tags. You can find the FileURL in the metadata arguments.
Could you make sure to follow the exact formatting and spacing?
Also, please keep the following in mind when answering the questions:
- Please answer the question as soon as possible without a preamble.
- Please refer to the contents of the <excerpts> tag, but do not include the <excerpts> tag in your answer.
- Please answer in Japanese.
<example>
Answer:
Company X earned $12 million. [1] Almost 90% of it was from widget sales. [2]
Quotes:
[1] "Company X reported revenue of $12 million in 2021." (FileURL)
[2] "Almost 90% of revene came from widget sales, with gadget sales making up the remaining 10%." (FileURL)
</example>
"""
def generate_queries(model_id, message) -> Dict[str, Any]:
"""入力テキストから検索クエリを生成する"""
system_prompts = [
{
"text": query_generator_system_prompt()
}
]
messages = [
{
"role": "user",
"content": [
{
"text": message
}
]
}
]
inference_config = {
"temperature": 0.7,
"maxTokens": 1000,
}
try:
bedrock_client = get_bedrock_client()
return bedrock_client.converse(
system=system_prompts,
messages=messages,
modelId=model_id,
inferenceConfig=inference_config,
toolConfig=load_tool_config() # ツールを使用する
)
except ClientError as err:
logger.error("A client error occurred: %s", err.response['Error']['Message'])
raise
except Exception as e:
logger.error("An unexpected error occurred: %s", str(e))
raise
def extract_queries(input_data):
"""クエリを抽出する"""
return {f"query_{key}": value["query"] for key, value in enumerate(input_data["query_expansion"], 1)}
def extract_decomposition_queries(input_data):
"""分解クエリを抽出する"""
return {f"decomposition_query_{key}": value["query"] for key, value in enumerate(input_data["query_decomposition"], 1)}
def retrieve_results_from_knowledge_base(knowledgebase_id, message: str, response_content: List[Dict]) -> Optional[List[Dict[str, Any]]]:
"""knowledge baseから検索結果を取得する"""
if response_content is None:
logger.warning("No tool use arguments found.")
return None
expansion_queries = extract_queries(response_content)
decomposition_queries = extract_decomposition_queries(response_content)
# ユーザーの質問をクエリに追加する
expansion_queries['query_0'] = message
decomposition_queries['decomposition_query_0'] = message
def execute_retrieval(query: str) -> List[Dict[str, Any]]:
"""指定されたクエリでknowledge baseから検索を実行する"""
try:
bedrock_agent_client = get_bedrock_agent_client()
response = bedrock_agent_client.retrieve(
knowledgeBaseId=knowledgebase_id,
retrievalQuery={
"text": query
},
retrievalConfiguration={
"vectorSearchConfiguration": {
"numberOfResults": 3
}
}
)
return response['retrievalResults']
except ClientError as err:
logger.error("A client error occurred: %s", err.response['Error']['Message'])
raise
except Exception as e:
logger.error("An unexpected error occurred: %s", str(e))
raise
# クエリ拡張でクエリを作成
expansion_results = []
for query_value in expansion_queries.values():
# logger.info("query_value:" + query_value + "\n")
expansion_results.extend(execute_retrieval(query_value))
# クエリ分解でクエリを作成
decomposition_results = []
for decomposition_query_value in decomposition_queries.values():
#logger.info("decomposition_query_value:" + decomposition_query_value + "\n")
decomposition_results.extend(execute_retrieval(decomposition_query_value))
return expansion_results, decomposition_results
def generate_response(model_id, message: str, context_str: str) -> str:
"""検索結果をもとに応答テキストを生成する"""
system_prompts = [
{
"text": create_prompt_template(context_str)
}
]
messages = [
{
"role": "user",
"content": [
{
"text": "<question>" + message + "</question>",
}
]
}
]
inference_config = {
"temperature": 0,
"maxTokens": 4096,
}
try:
bedrock_client = get_bedrock_client()
return bedrock_client.converse(
system=system_prompts,
messages=messages,
modelId=model_id,
inferenceConfig=inference_config,
)
except ClientError as err:
logger.error("A client error occurred: %s", err.response['Error']['Message'])
raise
except Exception as e:
logger.error("An unexpected error occurred: %s", str(e))
raise
def retrieve_and_generate_response(knowledgebase_id, model_id, message):
"""knowledge baseから検索結果を取得して応答テキストを生成する"""
bedrock_agent_client = get_bedrock_agent_client()
model_arn = f'arn:aws:bedrock:us-east-1::foundation-model/{ model_id }'
inference_config = {
"temperature": 0,
"maxTokens": 4096,
}
try:
return bedrock_agent_client.retrieve_and_generate(
input={
"text": "<question>" + message + "</question>",
},
retrieveAndGenerateConfiguration={
"type": 'KNOWLEDGE_BASE',
"knowledgeBaseConfiguration": {
"knowledgeBaseId": knowledgebase_id,
"modelArn": model_arn,
'generationConfiguration': {
'inferenceConfig': {
'textInferenceConfig': inference_config
},
},
'retrievalConfiguration': {
'vectorSearchConfiguration': {
'numberOfResults': 3,
'overrideSearchType': 'SEMANTIC'
}
},
}
}
)
except ClientError as err:
logger.error("A client error occurred: %s", err.response['Error']['Message'])
raise
except Exception as e:
logger.error("An unexpected error occurred: %s", str(e))
raise
def main():
# コマンドライン引数を取得する
if len(sys.argv) < 2:
print("Usage: python3 ./app.py <input_text>")
sys.exit(1)
knowledgebase_id = "xxxxxxxxx"
model_id="anthropic.claude-3-5-sonnet-20240620-v1:0"
message = sys.argv[1]
try:
# Step 1: ユーザーの質問をもとに検索クエリーを生成する
queries_response = generate_queries(
model_id,
message,
)
print("Response:" + "\n\n" + json.dumps(queries_response['output']['message']['content'][0]['toolUse']['input'], indent=2, ensure_ascii=False))
# Step 2: クエリ拡張とクエリ分解によるそれぞれの検索結果を得る
queries = queries_response['output']['message']['content'][0]['toolUse']['input']
expansion_results, decomposition_results = retrieve_results_from_knowledge_base(
knowledgebase_id,
message,
queries,
)
if expansion_results:
# Step 3-1: ユーザーの質問とクエリ拡張による検索結果をもとに応答テキストを生成する
expansion_response = generate_response(
model_id,
message,
expansion_results,
)
expansion_response = expansion_response['output']['message']['content'][0]['text']
print("*" * 50)
print("Response(query_expansion):" + "\n" + expansion_response)
if decomposition_results:
# Step 3-2: ユーザーの質問とクエリ分解による検索結果をもとに応答テキストを生成する
decomposition_response = generate_response(
model_id,
message,
decomposition_results,
)
decomposition_response = decomposition_response['output']['message']['content'][0]['text']
print("*" * 50)
print("Response(query_decomposition):" + "\n" + decomposition_response)
else:
logger.warning("No retrieval results found.")
# Step 3-3: ユーザーの質問のみでknowledge baseから検索結果を取得して応答テキストを生成する
response_data = retrieve_and_generate_response(
knowledgebase_id,
model_id,
message,
)
response_references = ""
for citation in response_data['citations']:
for refs in citation['retrievedReferences']:
uri = refs['metadata']['FileURL']
text = refs['metadata']['FileTitle']
response_references += "\"" + text + "\" "+ "(" + uri + ")"+ "\n"
response_data = response_data['output']['text']
print("*" * 50)
print(
"Response(retrieve_and_generate):" + "\n" + response_data + "\n" +
"references:" + "\n" + response_references
)
except Exception as e:
logger.error(f"Process failed: {str(e)}")
sys.exit(1)
if __name__ == "__main__":
main()
実行結果
今回は、クエリ拡張で生成するキーワードの観点を社会的説明に限定しました。Tool useの設定でfocus
フィールドを以下のように設定しました。
"focus": {
"type": "string",
"description": "このクエリが注目する観点",
"enum": ["社会的説明"]
}
クエリ拡張とクエリ分解で生成したクエリ、もとの質問をそのまま検索した場合とクエリ分解とクエリ拡張を行った場合の検索結果はそれぞれ以下のとおりです。
ユーザーの質問:
"生成AIが社会に与える影響と、それに対して事業者および利用者それぞれが身につけるべきリテ ラシーはなにか? 社会的説明の観点から述べてください。"
回答の出力にある <thinking></thinking>は、Claudeが回答に最も関連のあるドキュメントから引用箇所を見つけた結果を書き留めたものです。最終的な回答は<answer></answer>に出力されています。
クエリ拡張の結果
"query_expansion": [
{
"focus": "社会的説明",
"query": "生成AI 社会的影響 ethical considerations 倫理的配慮 societal impact 技術革新 イノベーション 雇用 プライバシー セキュリティ 教育 医療 creative industries クリエイティブ産業"
},
{
"focus": "社会的説明",
"query": "AI リテラシー デジタルリテラシー 情報リテラシー critical thinking 批判的思考 事業者責任 user responsibility 利用者責任 ethical use of AI AI倫理 data privacy データプライバシー"
},
{
"focus": "社会的説明",
"query": "生成AI ガバナンス AI governance 規制 regulation 法的枠組み legal framework 国際協調 international cooperation 社会的合意形成 public engagement 透明性 accountability 説明責任"
}
]
クエリ分解の結果
"query_decomposition": [
{
"query": "生成AIが社会に与える影響にはどのようなものがあるか?",
"relevance": 1
},
{
"query": "生成AIに関して事業者が身につけるべきリテラシーは何か?",
"relevance": 2
},
{
"query": "生成AIに関して利用者が身につけるべきリテラシーは何か?",
"relevance": 2
},
{
"query": "生成AIの社会的影響と必要なリテラシーについて、社会的説明の観点からどのように考えられるか?",
"relevance": 1
}
]
クエリ拡張を使用した場合の回答
検索キーワードをカバーしつつ「事業者」「利用者」「両者」という視点で回答が生成されました。後述のクエリ分解とは回答の組み立てがやや異なる用です。また、クエリ分解で検索した場合と比較し幅広い文書から検索結果を得たようです。
Response(query_expansion):
<thinking>
Relevant quotes:
- "生成AIの急速な発展・普及などに伴い、AIの技術やシステムが個人や社会に与える潜在的なリスクや課題(Ethical, Legal and Social Issues:ELSI)を分析し、解決策を模索する取組もより活発になっている。"
- "AIの悪用については急速に進んでおり、サイバー攻撃や偽情報による社会混乱や巨大企業の寡占・独占による弊害への対策が求められるとしている。"
- "各主体内のAIに関わる者が、その関わりにおいて十分なレベルのAIリテラシーを確保するために必要な措置を講じる"
- "生成AIの活用拡大によって、AIと人間の作業の棲み分けが変わっていくと想定されるため、新たな働き方ができるよう教育・リスキリング等を検討する"
- "AIを活用するためのリテラシー教育及びスキルとして含めるべき事項"
- "自身の開発するAIが提供・利用された際にどのような影響を与えるか、事前に可能な限り検討し、対応策を講じておくことが重要となる。"
</thinking>
<answer>
生成AIは社会に大きな影響を与えており、その潜在的なリスクや課題(ELSI)の分析と解決策の模索が重要になっています。[1] 特に、サイバー攻撃や偽情報による社会混乱、巨大企業の寡占・独占などの弊害への対策が求められてい> ます。[2]事業者は、自社が開発するAIが社会に与える影響を事前に検討し、適切な対応策を講じる必要があります。[6] また、AIのライフサイクル全体を通じて、データの適正な収集や個人情報・知的財産権の保護など、法令に従った適切な取> り扱いを確保することが重要です。
利用者に関しては、AIと人間の作業の棲み分けが変化していくことを踏まえ、新たな働き方に適応するための教育やリスキリングが必要です。[4]
両者に共通して求められるAIリテラシーとしては、以下のようなものが挙げられます:
- AI、数理、データサイエンスに関する基本的な知識
- データにバイアスが含まれる可能性や、AIの利用によってバイアスが生じる可能性についての理解
- AIやデータの公平性、プライバシー保護、セキュリティ、技術的限界に関する理解
- AIを含むデジタル技術が様々な業務で利用されていることの認識
- AIを活用して問題解決や仮説検証を行うスキル[5]
これらのリテラシーを身につけることで、AIの適切な利用と社会への貢献が可能になります。また、組織は関係者のAIリテラシー向上のために必要な措置を講じることが求められています。[3]
</answer>Quotes:
[1] "生成AIの急速な発展・普及などに伴い、AIの技術やシステムが個人や社会に与える潜在的なリスクや課題(Ethical, Legal and Social Issues:ELSI)を分析し、解決策を模索する取組もより活発になっている。" (https://www.soumu.go.jp/johotsusintokei/whitepaper/ja/r06/pdf/n161000c.pdf)
[2] "AIの悪用については急速に進んでおり、サイバー攻撃や偽情報による社会混乱や巨大企業の寡占・独占による弊害への対策が求められるとしている。" (https://www.soumu.go.jp/johotsusintokei/whitepaper/ja/r06/pdf/n161000c.pdf)
[3] "各主体内のAIに関わる者が、その関わりにおいて十分なレベルのAIリテラシーを確保するために必要な措置を講じる" (https://www.meti.go.jp/shingikai/mono_info_service/ai_shakai_jisso/pdf/20240419_3.pdf)
[4] "生成AIの活用拡大によって、AIと人間の作業の棲み分けが変わっていくと想定されるため、新たな働き方ができるよう教育・リスキリング等を検討する" (https://www.meti.go.jp/shingikai/mono_info_service/ai_shakai_jisso/pdf/20240419_3.pdf)
[5] "AIを活用するためのリテラシー教育及びスキルとして含めるべき事項" (https://www.meti.go.jp/shingikai/mono_info_service/ai_shakai_jisso/pdf/20240419_3.pdf)
[6] "自身の開発するAIが提供・利用された際にどのような影響を与えるか、事前に可能な限り検討し、対応策を講じておくことが重要となる。" (https://www.meti.go.jp/shingikai/mono_info_service/ai_shakai_jisso/pdf/20240419_1.pdf)
クエリ分解を使用した場合の回答
クエリ分解によって「生成AIに関して事業者が身につけるべきリテラシーは何か?」「生成AIに関して利用者が身につけるべきリテラシーは何か?」という2つの質問に分解したことが影響したのか、回答には「事業者が身につけるべきリテラシー」と「利用者が身につけるべきリテラシー」が整理されました。
Response(query_decomposition):
<thinking>
Relevant quotes:
- "生成AIの活用拡大によって、AIと人間の作業の棲み分けが変わっていくと想定されるため、新たな働き方ができるよう教育・リスキリング等を検討する"
- "AIを活用するためのリテラシー教育及びスキルとして含めるべき事項"
- "AI、数理及びデータサイエンスに係る知識"
- "データにバイアスが含まれること、使い方によってはバイアスを生じさせる可能性があること等のAI及びデータの特性への理解"
- "AI又はデータの持つ公平性及びプライバシー保護に関わる課題があること、並びにセキュリティ及びAI技術の限界に関する内容への理解"
- "AIを含むデータ・デジタル技術が様々な業務で利用されていることの理解"
- "AIを「問いを立てる」「仮説を立てる・検証する」等のスキルと掛け合わせることによる、生産性向上等への適切な利用"
- "生成AIが個人でも容易に利用できるようになり、様々な情報を収集、整理したり、文章や画像等のコンテンツをより効率的に制作したりすることができるようになっている一方で、生成AIによって利用者の個人情報が本人の意図に> 関係なく学習されたり、生成された情報の正確性を利用者が確かめることなく発信したりするリスクが高まってきている。"
- "利用者としては、自分自身のプライバシーが漏えいしたり、他人のプライバシーを侵害しないよう配慮してデータを入力し、出力された回答も正確であるかどうか確認する等して、生成AIを適切に利用していくことが重要である。"
</thinking>
<answer>
生成AIは社会に大きな影響を与え、人間と機械の役割分担を変化させる可能性があります。[1] この変化に対応するため、事業者と利用者の両方がAIリテラシーを身につける必要があります。事業者が身につけるべきリテラシー:
- AI、数理、データサイエンスに関する専門知識 [3]
- AIとデータの特性、バイアスの可能性についての理解 [4]
- AIの公平性、プライバシー保護、セキュリティ、技術的限界に関する理解 [5]
- AIを活用した生産性向上のスキル [7]
利用者が身につけるべきリテラシー:
- AIやデジタル技術が様々な業務で利用されていることの理解 [6]
- 個人情報保護とプライバシーに関する意識 [8][9]
- 生成AIの出力の正確性を確認する能力 [9]
- AIを適切に利用するための基本的な知識 [9]
社会的説明の観点からは、AIの影響力と潜在的なリスクを認識し、それに対応できる能力を身につけることが重要です。事業者はAIシステムの開発・運用において責任ある態度を取り、利用者は個人情報保護や情報の正確性確認などの> 基本的なスキルを習得する必要があります。これにより、AIの恩恵を最大限に活用しつつ、社会的な課題やリスクに適切に対処することができます。
</answer>Quotes:
[1] "生成AIの活用拡大によって、AIと人間の作業の棲み分けが変わっていくと想定されるため、新たな働き方ができるよう教育・リスキリング等を検討する" (https://www.meti.go.jp/shingikai/mono_info_service/ai_shakai_jisso/pdf/20240419_3.pdf)
[3] "AI、数理及びデータサイエンスに係る知識" (https://www.meti.go.jp/shingikai/mono_info_service/ai_shakai_jisso/pdf/20240419_3.pdf)
[4] "データにバイアスが含まれること、使い方によってはバイアスを生じさせる可能性があること等のAI及びデータの特性への理解" (https://www.meti.go.jp/shingikai/mono_info_service/ai_shakai_jisso/pdf/20240419_3.pdf)
[5] "AI又はデータの持つ公平性及びプライバシー保護に関わる課題があること、並びにセキュリティ及びAI技術の限界に関する内容への理解" (https://www.meti.go.jp/shingikai/mono_info_service/ai_shakai_jisso/pdf/20240419_3.pdf)
[6] "AIを含むデータ・デジタル技術が様々な業務で利用されていることの理解" (https://www.meti.go.jp/shingikai/mono_info_service/ai_shakai_jisso/pdf/20240419_3.pdf)
[7] "AIを「問いを立てる」「仮説を立てる・検証する」等のスキルと掛け合わせることによる、生産性向上等への適切な利用" (https://www.meti.go.jp/shingikai/mono_info_service/ai_shakai_jisso/pdf/20240419_3.pdf)
[8] "生成AIが個人でも容易に利用できるようになり、様々な情報を収集、整理したり、文章や画像等のコンテンツをより効率的に制作したりすることができるようになっている一方で、生成AIによって利用者の個人情報が本人の意図> に関係なく学習されたり、生成された情報の正確性を利用者が確かめることなく発信したりするリスクが高まってきている。" (https://www.ipa.go.jp/publish/wp-security/eid2eo0000007gv4-att/2024_Chap3.pdf)
[9] "利用者としては、自分自身のプライバシーが漏えいしたり、他人のプライバシーを侵害しないよう配慮してデータを入力し、出力された回答も正確であるかどうか確認する等して、生成AIを適切に利用していくことが重要である。" (https://www.ipa.go.jp/publish/wp-security/eid2eo0000007gv4-att/2024_Chap3.pdf)
もとの質問をそのまま検索した場合の回答
もとの質問文をもとに検索と回答生成をおこなった影響なのか、回答文は箇条書きは用いられず文章主体で組み立てられました。
Response(retrieve_and_generate):
生成AIは社会に大きな影響を与えると予想されています。AIと人間の作業の棲み分けが変化し、新たな働き方が必要になると考えられています。また、AIの活用拡大に伴い、サイバー攻撃や偽情報による社会混乱、巨大企業の寡占・独占による弊害などのリスクも懸念されています。これらの変化に対応するため、事業者と利用者の両方がAIリテラシーを向上させる必要があります。 事業者は、AIシステム・サービスの利用に伴うリスク管理、安全性確保のためのAIガバナンスに関するポリシーを策定し公表する必> 要があります。また、AIの出力の誤りについてステークホルダーからの指摘を受け付け、客観的なモニタリングを実施することが求められます。さらに、ステークホルダーの利益を損なう事態が生じた場合の対応方針を策定し、進捗状> 況を報告することも重要です。
利用者は、AIに関する十分なレベルのリテラシーを確保する必要があります。具体的には、AI、数理、データサイエンスに関する知識、データのバイアスや特性への理解、AIの公平性やプライバシー保護に関する課題の認識、AIの限界> の理解などが含まれます。また、AIを適切に活用するスキルや、環境変化へのレジリエンス向上のための教育も重要です。 社会的説明の観点からは、AIの開発・利用において「人間中心」の原則を重視することが重要です。AIは人々の能力を拡張し、多様な人々の幸せ(well-being)の追求を可能にするものであるべきです。同時に、AIへの過度の依存(自動化バイアス)のリスクにも注意を払い、必要な対策を講じる必要があります。
これらの取り組みを通じて、人間の尊厳が尊重され、多様な背景を持つ人々が多様な幸せを追求でき、持続可能な社会の実現を目指すことが、生成AIの社会実装における重要な課題となります。
references:
"「AI事業者ガイドライン(第1.0版)」チェックリスト及び具体的なアプローチ検討のためのワークシート(別添7A, 7B, 7C)(PDF形式:866KB)" (https://www.meti.go.jp/shingikai/mono_info_service/ai_shakai_jisso/pdf/20240419_5.pdf)
"AIやロボットと協働・共生する未来に向けて(コンヴィヴィアルな関係)" (https://www.soumu.go.jp/johotsusintokei/whitepaper/ja/r06/pdf/n161000c.pdf)
"「AI事業者ガイドライン(第1.0版)」チェックリスト及び具体的なアプローチ検討のためのワークシート(別添7A, 7B, 7C)(PDF形式:866KB)" (https://www.meti.go.jp/shingikai/mono_info_service/ai_shakai_jisso/pdf/20240419_5.pdf)
"「AI事業者ガイドライン(第1.0版)」別添(PDF形式:4,360KB)" (https://www.meti.go.jp/shingikai/mono_info_service/ai_shakai_jisso/pdf/20240419_3.pdf)
"AIやロボットと協働・共生する未来に向けて(コンヴィヴィアルな関係)" (https://www.soumu.go.jp/johotsusintokei/whitepaper/ja/r06/pdf/n161000c.pdf)
まとめ
今回の検証では、多数の関連文書を引用するような多様な質問文を用意できなかったこともあり、クエリ拡張とクエリ分解の結果に大きな違いは見られませんでした。しかし、回答結果に示された引用文書のリストから、それぞれの検索の範囲が異なることが確認できました。質問文の内容や用意する文書の種類によっては、より明確な違いが得られる可能性があります。
「同義語で検索できていれば期待した結果が得られたはず」といった場合や、「質問者が意識していなかった視点での検索結果が欲しい」といった場合に、クエリ拡張やクエリ分解によるRAGの精度向上が期待できそうです。