Amazon Q in Connectのタグによるナレッジ絞り込みのため、スクリプトでフォルダ配下の全ファイルを一括タグ付けしてみた
はじめに
Amazon Q in Connect は、コンタクトセンター業務を AI で支援するマネージドサービスです。
本サービスには、データソースにタグを付与し、問い合わせに関連するドキュメントのみを絞り込む「コンテンツセグメンテーション」という機能があります。これにより、以下のメリットが得られます。
- 回答精度の向上:関連性の高い情報のみを検索対象にするため、より正確な回答が可能
- 応答時間の短縮:検索対象が減ることで、必要な情報へ迅速にアクセス
- 顧客満足度の向上:的確でスピーディーな回答により、顧客の課題を早期に解決
利用シーンを具体的にイメージしてみます。金融機関のコンタクトセンターで「クレジットカードの利用限度額を上げたい」という問い合わせが来た場合、通常は「口座開設」「ローン」「投資商品」などを含むナレッジベース全体が検索対象になります。
しかし、コンテンツセグメンテーションを用いて「お問い合わせ種別:クレジットカード関連」というタグが付いたドキュメントのみに絞れば、AIチャットボットは関連度の高い情報だけを即座に参照でき、顧客へ迅速かつ的確に回答できます。
以前の記事では、Amazon Q in Connectセルフサービスやコンテンツ検索機能において、コンテンツセグメンテーション機能を使ってナレッジ参照先を動的に変える方法を検証しました。
しかし、検証の過程で課題が明らかになりました。現在のタグ付け方法はファイル単位での手動設定となっており、フォルダ単位で大量のファイルに一括でタグを付与したい場合、手作業では非効率的です。
そこで今回は、S3フォルダ配下の全ファイルに対してタグを一括付与するスクリプトを作成し、効率的なタグ管理方法を検証してみました。
前提条件
本記事は、以下の前回記事の続編として書かれています。Amazon Q in Connectの基本的なセットアップ(インスタンス作成、ナレッジベースとS3連携)が完了していることを前提とします。
検証用ファイルの準備
前回記事では、検証用に以下3つのファイルをS3バケットに配置しました
s3://<bucket-name>/
├── test1/
│ └── classmethod.html … クラスメソッド公式サイトのトップページ
├── test2/
│ └── tokyo.txt … 本社所在地を「東京」と記載
└── test3/
└── sapporo.txt … 本社所在地を「札幌」と記載
追加FAQファイルの作成
今回は、コンテンツセグメンテーションの効果をより詳細に検証するため、test2とtest3フォルダに追加のFAQファイルを作成しました。
AWS CloudShellで以下のFAQも作成しました。あくまでもサンプルです。
BUCKET_NAME="cm-hirai-amazon-connect-q" && \
\
echo "=== Creating all FAQ files ===" && \
\
# 東京オフィス FAQファイル
cat << 'EOF' > /tmp/tokyo-access-faq.txt
東京オフィス アクセス・施設情報 FAQ
Q1. 東京オフィスの住所を教えてください
A1. 〒105-0003 東京都港区西新橋1-1-1 日比谷フォートタワー26階です。
Q2. 最寄り駅はどこですか?
A2. JR新橋駅から徒歩5分です。
Q3. 東京オフィスの営業時間は?
A3. 平日9:00〜18:00です。
Q4. 東京オフィスでの面談は可能ですか?
A4. はい、事前予約にて会議室での面談が可能です。
Q5. 東京オフィスへの問い合わせ電話番号は?
A5. 0120-991-668(代表)です。
EOF
\
cat << 'EOF' > /tmp/tokyo-services-faq.txt
東京オフィス サービス FAQ
Q1. 東京オフィスのサービス内容は?
A1. AWSコンサルティング、システム開発、データ分析を提供しています。
Q2. 東京オフィスのエンジニア数は?
A2. 約500名のエンジニアが在籍しています。
Q3. 東京オフィスでの技術研修は?
A3. AWS認定資格取得支援を定期的に開催しています。
EOF
\
# 札幌オフィス FAQファイル
cat << 'EOF' > /tmp/sapporo-access-faq.txt
札幌オフィス アクセス・施設情報 FAQ
Q1. 札幌オフィスの住所を教えてください
A1. 〒060-0003 北海道札幌市中央区北3条西1-1-1 札幌ブリックキューブ10階です。
Q2. 最寄り駅はどこですか?
A2. JR札幌駅から徒歩8分です。
Q3. 札幌オフィスの営業時間は?
A3. 平日9:00〜18:00です。
Q4. 札幌オフィスでの面談は可能ですか?
A4. はい、事前予約にて会議室での面談が可能です。
Q5. 札幌オフィスへの問い合わせ電話番号は?
A5. 011-200-7891(札幌オフィス直通)です。
EOF
\
cat << 'EOF' > /tmp/sapporo-services-faq.txt
札幌オフィス サービス FAQ
Q1. 札幌オフィスのサービス内容は?
A1. 北海道地域向けにAWSコンサルティング、システム開発を提供しています。
Q2. 札幌オフィスのエンジニア数は?
A2. 約150名のエンジニアが在籍しています。
Q3. 札幌オフィスの特色は?
A3. 北海道の自然環境の中で地域密着型のサポートを提供しています。
EOF
\
echo "✅ All FAQ files created" && \
echo "" && \
echo "=== Uploading to S3 ===" && \
\
# test2へアップロード
aws s3 cp /tmp/tokyo-access-faq.txt s3://$BUCKET_NAME/test2/ && \
aws s3 cp /tmp/tokyo-services-faq.txt s3://$BUCKET_NAME/test2/ && \
\
# test3へアップロード
aws s3 cp /tmp/sapporo-access-faq.txt s3://$BUCKET_NAME/test3/ && \
aws s3 cp /tmp/sapporo-services-faq.txt s3://$BUCKET_NAME/test3/ && \
\
echo "" && \
echo "=== Verifying uploaded files ===" && \
echo "test2/ files:" && \
aws s3 ls s3://$BUCKET_NAME/test2/ && \
echo "" && \
echo "test3/ files:" && \
aws s3 ls s3://$BUCKET_NAME/test3/ && \
\
rm -f /tmp/tokyo-*.txt /tmp/sapporo-*.txt && \
echo "" && \
echo "✅ All files uploaded and cleaned up"
✅ All FAQ files created
=== Uploading to S3 ===
upload: ../../tmp/tokyo-access-faq.txt to s3://cm-hirai-amazon-connect-q/test2/tokyo-access-faq.txt
upload: ../../tmp/tokyo-services-faq.txt to s3://cm-hirai-amazon-connect-q/test2/tokyo-services-faq.txt
upload: ../../tmp/sapporo-access-faq.txt to s3://cm-hirai-amazon-connect-q/test3/sapporo-access-faq.txt
upload: ../../tmp/sapporo-services-faq.txt to s3://cm-hirai-amazon-connect-q/test3/sapporo-services-faq.txt
=== Verifying uploaded files ===
test2/ files:
2025-04-10 06:15:46 0
2025-09-18 01:29:15 585 tokyo-access-faq.txt
2025-09-18 01:29:16 414 tokyo-services-faq.txt
2025-09-17 07:16:46 136 tokyo.txt
test3/ files:
2025-04-10 06:24:05 0
2025-09-18 01:29:17 618 sapporo-access-faq.txt
2025-09-18 01:29:18 435 sapporo-services-faq.txt
2025-09-17 07:17:11 142 sapporo.txt
✅ All files uploaded and cleaned up
期待される検証結果
ファイル追加により、以下のようなコンテンツセグメンテーションのテストが可能になります。
テストケース1: 「東京オフィスの電話番号を教えて」
address_tokyo
フィルター → 0120-991-668と回答address_sapporo
フィルター → 関連情報なしのため回答不可
テストケース2: 「札幌オフィスの住所を教えて」
address_sapporo
フィルター → 札幌の住所と回答address_tokyo
フィルター → 関連情報なしのため回答不可
テストケース3: 「エンジニア数はどのくらい?」
address_tokyo
フィルター → 約500名と回答address_sapporo
フィルター → 約150名と回答
現在のタグ状態の確認
まず、現在のタグの確認を行います。KNOWLEDGE_BASE_ID
は、aws qconnect list-knowledge-bases
コマンドで確認できます。
test2フォルダのタグ確認は以下のコマンドです。
KNOWLEDGE_BASE_ID="abf9f3c4-6cc7-40ab-876e-ef80e1797497" && \
BUCKET_NAME="cm-hirai-amazon-connect-q" && \
FOLDER_PREFIX="test2/" && \
EXPECTED_TAGS='{"category":"product_info","content_type":"address_tokyo"}' && \
\
echo "=== 📋 Checking tags for folder: $FOLDER_PREFIX ===" && \
echo "🎯 Expected tags: $EXPECTED_TAGS" && \
echo "🔍 Fetching content list from knowledge base..." && \
CONTENTS=$(aws qconnect list-contents --knowledge-base-id $KNOWLEDGE_BASE_ID) && \
\
aws s3 ls s3://$BUCKET_NAME/$FOLDER_PREFIX --recursive | awk '{print $4}' | \
while read s3_key; do \
# フォルダ自体はスキップ
if [[ "$s3_key" == *"/" ]]; then \
echo "📁 Skipping folder: $s3_key" && \
continue; \
fi && \
\
echo "" && \
echo "📄 Processing file: $s3_key" && \
CONTENT_ARN=$(echo "$CONTENTS" | jq -r --arg key "$s3_key" \
'.contentSummaries[] | select(.metadata."s3.object.key" == $key) | .contentArn') && \
\
if [ ! -z "$CONTENT_ARN" ] && [ "$CONTENT_ARN" != "null" ]; then \
echo " 🔗 ARN: $CONTENT_ARN" && \
TAGS=$(aws qconnect list-tags-for-resource --resource-arn "$CONTENT_ARN" 2>/dev/null) && \
\
if [ $? -eq 0 ]; then \
ACTUAL_TAGS=$(echo "$TAGS" | jq -c '.tags // {}') && \
echo " 📋 Current tags: $ACTUAL_TAGS" && \
\
# タグ比較(JSONの順序に関係なく比較)
TAG_MATCH=$(echo "$TAGS" | jq -r --argjson expected "$EXPECTED_TAGS" \
'if .tags == $expected then "true" else "false" end') && \
\
if [ "$TAG_MATCH" = "true" ]; then \
echo " ✅ Tags match expected values"; \
else \
echo " ❌ Tags do not match expected values"; \
fi; \
else \
echo " ❌ Failed to get tags"; \
fi; \
else \
echo " ❌ Content ARN not found"; \
fi; \
done && \
echo "" && \
echo "=== ✅ Completed checking folder: $FOLDER_PREFIX ==="
=== 📋 Checking tags for folder: test2/ ===
🎯 Expected tags: {"category":"product_info","content_type":"address_tokyo"}
🔍 Fetching content list from knowledge base...
📁 Skipping folder: test2/
📄 Processing file: test2/tokyo-access-faq.txt
🔗 ARN: arn:aws:wisdom:ap-northeast-1:540835513398:content/abf9f3c4-6cc7-40ab-876e-ef80e1797497/877a6b39-40a8-48f9-8dd4-914894454d3c
📋 Current tags: {}
❌ Tags do not match expected values
📄 Processing file: test2/tokyo-services-faq.txt
🔗 ARN: arn:aws:wisdom:ap-northeast-1:540835513398:content/abf9f3c4-6cc7-40ab-876e-ef80e1797497/4cceb8d8-1769-4058-83eb-db7afbd29439
📋 Current tags: {}
❌ Tags do not match expected values
📄 Processing file: test2/tokyo.txt
🔗 ARN: arn:aws:wisdom:ap-northeast-1:540835513398:content/abf9f3c4-6cc7-40ab-876e-ef80e1797497/f2a97425-5d3c-48d0-9a9d-b4cd2ed990e1
📋 Current tags: {"content_type":"address_tokyo","category":"product_info"}
✅ Tags match expected values
=== ✅ Completed checking folder: test2/ ===
実行結果から、一部のファイルがタグ付けされていないことが確認できました。
フォルダ配下ファイルの一括タグ付け
test2フォルダ配下のファイルにタグ付けを行います。
KNOWLEDGE_BASE_ID="abf9f3c4-6cc7-40ab-876e-ef80e1797497" && \
BUCKET_NAME="cm-hirai-amazon-connect-q" && \
FOLDER_PREFIX="test2/" && \
TAGS='{"category":"product_info","content_type":"address_tokyo"}' && \
\
echo "=== 🏷️ Tagging files in folder: $FOLDER_PREFIX ===" && \
echo "🎯 Tags to apply: $TAGS" && \
echo "🔍 Fetching content list from knowledge base..." && \
CONTENTS=$(aws qconnect list-contents --knowledge-base-id $KNOWLEDGE_BASE_ID) && \
\
aws s3 ls s3://$BUCKET_NAME/$FOLDER_PREFIX --recursive | awk '{print $4}' | \
while read s3_key; do \
# フォルダ自体はスキップ
if [[ "$s3_key" == *"/" ]]; then \
echo "📁 Skipping folder: $s3_key" && \
continue; \
fi && \
\
echo "" && \
echo "📄 Processing file: $s3_key" && \
CONTENT_ARN=$(echo "$CONTENTS" | jq -r --arg key "$s3_key" \
'.contentSummaries[] | select(.metadata."s3.object.key" == $key) | .contentArn') && \
\
if [ ! -z "$CONTENT_ARN" ] && [ "$CONTENT_ARN" != "null" ]; then \
echo " 🔗 ARN: $CONTENT_ARN" && \
aws qconnect tag-resource --resource-arn "$CONTENT_ARN" --tags "$TAGS" && \
echo " ✅ Successfully tagged: $(basename "$s3_key")"; \
else \
echo " ❌ Content ARN not found for: $s3_key"; \
fi; \
done && \
echo "" && \
echo "=== ✅ Completed tagging folder: $FOLDER_PREFIX ==="
=== 🏷️ Tagging files in folder: test2/ ===
🎯 Tags to apply: {"category":"product_info","content_type":"address_tokyo"}
🔍 Fetching content list from knowledge base...
📁 Skipping folder: test2/
📄 Processing file: test2/tokyo-access-faq.txt
🔗 ARN: arn:aws:wisdom:ap-northeast-1:540835513398:content/abf9f3c4-6cc7-40ab-876e-ef80e1797497/877a6b39-40a8-48f9-8dd4-914894454d3c
✅ Successfully tagged: tokyo-access-faq.txt
📄 Processing file: test2/tokyo-services-faq.txt
🔗 ARN: arn:aws:wisdom:ap-northeast-1:540835513398:content/abf9f3c4-6cc7-40ab-876e-ef80e1797497/4cceb8d8-1769-4058-83eb-db7afbd29439
✅ Successfully tagged: tokyo-services-faq.txt
📄 Processing file: test2/tokyo.txt
🔗 ARN: arn:aws:wisdom:ap-northeast-1:540835513398:content/abf9f3c4-6cc7-40ab-876e-ef80e1797497/f2a97425-5d3c-48d0-9a9d-b4cd2ed990e1
✅ Successfully tagged: tokyo.txt
test3フォルダの場合、以下のコマンドです。
KNOWLEDGE_BASE_ID="abf9f3c4-6cc7-40ab-876e-ef80e1797497" && \
BUCKET_NAME="cm-hirai-amazon-connect-q" && \
FOLDER_PREFIX="test3/" && \
TAGS='{"category":"product_info","content_type":"address_sapporo"}' && \
\
echo "=== 🏷️ Tagging files in folder: $FOLDER_PREFIX ===" && \
echo "🎯 Tags to apply: $TAGS" && \
echo "🔍 Fetching content list from knowledge base..." && \
CONTENTS=$(aws qconnect list-contents --knowledge-base-id $KNOWLEDGE_BASE_ID) && \
\
aws s3 ls s3://$BUCKET_NAME/$FOLDER_PREFIX --recursive | awk '{print $4}' | \
while read s3_key; do \
# フォルダ自体はスキップ
if [[ "$s3_key" == *"/" ]]; then \
echo "📁 Skipping folder: $s3_key" && \
continue; \
fi && \
\
echo "" && \
echo "📄 Processing file: $s3_key" && \
CONTENT_ARN=$(echo "$CONTENTS" | jq -r --arg key "$s3_key" \
'.contentSummaries[] | select(.metadata."s3.object.key" == $key) | .contentArn') && \
\
if [ ! -z "$CONTENT_ARN" ] && [ "$CONTENT_ARN" != "null" ]; then \
echo " 🔗 ARN: $CONTENT_ARN" && \
aws qconnect tag-resource --resource-arn "$CONTENT_ARN" --tags "$TAGS" && \
echo " ✅ Successfully tagged: $(basename "$s3_key")"; \
else \
echo " ❌ Content ARN not found for: $s3_key"; \
fi; \
done && \
echo "" && \
echo "=== ✅ Completed tagging folder: $FOLDER_PREFIX ==="
=== 🏷️ Tagging files in folder: test3/ ===
🎯 Tags to apply: {"category":"product_info","content_type":"address_sapporo"}
🔍 Fetching content list from knowledge base...
📁 Skipping folder: test3/
📄 Processing file: test3/sapporo-access-faq.txt
🔗 ARN: arn:aws:wisdom:ap-northeast-1:540835513398:content/abf9f3c4-6cc7-40ab-876e-ef80e1797497/d05b21f2-6353-4f55-8608-3e8ab61b8d1c
✅ Successfully tagged: sapporo-access-faq.txt
📄 Processing file: test3/sapporo-services-faq.txt
🔗 ARN: arn:aws:wisdom:ap-northeast-1:540835513398:content/abf9f3c4-6cc7-40ab-876e-ef80e1797497/6d8c5764-eb7c-4935-a6e1-c7d4ff9d2c7c
✅ Successfully tagged: sapporo-services-faq.txt
📄 Processing file: test3/sapporo.txt
🔗 ARN: arn:aws:wisdom:ap-northeast-1:540835513398:content/abf9f3c4-6cc7-40ab-876e-ef80e1797497/a1759f6f-0acb-4a0c-bbee-644065ad3a3c
✅ Successfully tagged: sapporo.txt
=== ✅ Completed tagging folder: test3/ ===
実行結果により、全てのファイルに対して適切なタグが付与されました。
タグの一括削除機能
参考として、タグの削除についても説明します。test2フォルダの場合、以下のコマンドで一括削除が可能です。
KNOWLEDGE_BASE_ID="abf9f3c4-6cc7-40ab-876e-ef80e1797497" && \
BUCKET_NAME="cm-hirai-amazon-connect-q" && \
FOLDER_PREFIX="test2/" && \
\
echo "=== 🗑️ Removing all tags from folder: $FOLDER_PREFIX ===" && \
echo "🔍 Fetching content list from knowledge base..." && \
CONTENTS=$(aws qconnect list-contents --knowledge-base-id $KNOWLEDGE_BASE_ID) && \
\
aws s3 ls s3://$BUCKET_NAME/$FOLDER_PREFIX --recursive | awk '{print $4}' | \
while read s3_key; do \
# フォルダ自体はスキップ
if [[ "$s3_key" == *"/" ]]; then \
echo "📁 Skipping folder: $s3_key" && \
continue; \
fi && \
\
echo "" && \
echo "📄 Processing file: $s3_key" && \
CONTENT_ARN=$(echo "$CONTENTS" | jq -r --arg key "$s3_key" \
'.contentSummaries[] | select(.metadata."s3.object.key" == $key) | .contentArn') && \
\
if [ ! -z "$CONTENT_ARN" ] && [ "$CONTENT_ARN" != "null" ]; then \
echo " 🔗 ARN: $CONTENT_ARN" && \
\
# 現在のタグを取得
CURRENT_TAGS=$(aws qconnect list-tags-for-resource --resource-arn "$CONTENT_ARN" 2>/dev/null) && \
if [ $? -eq 0 ]; then \
CURRENT_TAG_KEYS=$(echo "$CURRENT_TAGS" | jq -r '.tags | keys[]' | tr '\n' ' ') && \
CURRENT_TAG_COUNT=$(echo "$CURRENT_TAGS" | jq '.tags | length') && \
echo " 📋 Current tags: $(echo "$CURRENT_TAGS" | jq -c '.tags // {}')" && \
\
if [ "$CURRENT_TAG_COUNT" -gt 0 ] && [ ! -z "$CURRENT_TAG_KEYS" ]; then \
echo " 🗑️ Removing tag keys: $CURRENT_TAG_KEYS" && \
aws qconnect untag-resource --resource-arn "$CONTENT_ARN" --tag-keys $CURRENT_TAG_KEYS && \
echo " ✅ Successfully removed all tags from: $(basename "$s3_key")"; \
else \
echo " ℹ️ No tags to remove from: $(basename "$s3_key")"; \
fi; \
else \
echo " ❌ Failed to get current tags for: $(basename "$s3_key")"; \
fi; \
else \
echo " ❌ Content ARN not found for: $s3_key"; \
fi; \
done && \
echo "" && \
echo "=== ✅ Completed removing all tags from folder: $FOLDER_PREFIX ==="
=== 🗑️ Removing all tags from folder: test2/ ===
🔍 Fetching content list from knowledge base...
📁 Skipping folder: test2/
📄 Processing file: test2/tokyo-access-faq.txt
🔗 ARN: arn:aws:wisdom:ap-northeast-1:540835513398:content/abf9f3c4-6cc7-40ab-876e-ef80e1797497/877a6b39-40a8-48f9-8dd4-914894454d3c
📋 Current tags: {"content_type":"address_tokyo","category":"product_info"}
🗑️ Removing tag keys: category content_type
✅ Successfully removed all tags from: tokyo-access-faq.txt
📄 Processing file: test2/tokyo-services-faq.txt
🔗 ARN: arn:aws:wisdom:ap-northeast-1:540835513398:content/abf9f3c4-6cc7-40ab-876e-ef80e1797497/4cceb8d8-1769-4058-83eb-db7afbd29439
📋 Current tags: {"content_type":"address_tokyo","category":"product_info"}
🗑️ Removing tag keys: category content_type
✅ Successfully removed all tags from: tokyo-services-faq.txt
📄 Processing file: test2/tokyo.txt
🔗 ARN: arn:aws:wisdom:ap-northeast-1:540835513398:content/abf9f3c4-6cc7-40ab-876e-ef80e1797497/f2a97425-5d3c-48d0-9a9d-b4cd2ed990e1
📋 Current tags: {"content_type":"address_tokyo","category":"product_info"}
🗑️ Removing tag keys: category content_type
✅ Successfully removed all tags from: tokyo.txt
=== ✅ Completed removing all tags from folder: test2/ ===
このスクリプトにより、フォルダ内の全ファイルから既存のタグを一括で削除できます。
動作検証
タグ付け完了後、Amazon Q in Connect セルフサービス上で以下のテストケースを実行し、コンテンツセグメンテーションの動作を確認しました。
検証方法
Connectフローの[Lambda関数]ブロックにおいて、タグフィルターパラメータを以下のように設定してテストを実行しました。
address_tokyo
フィルター{"tagCondition":{"key":"content_type","value":"address_tokyo"}}
address_sapporo
フィルター{"tagCondition":{"key":"content_type","value":"address_sapporo"}}
検証結果
想定通り、タグフィルタリングによって期待した回答が得られました。
テストケース1: 「東京オフィスの電話番号を教えて」
address_tokyo
フィルター → 0120-991-668と回答address_sapporo
フィルター → 関連情報なしのため回答不可
テストケース2: 「札幌オフィスの住所を教えて」
address_sapporo
フィルター → 札幌の住所と回答address_tokyo
フィルター → 関連情報なしのため回答不可
テストケース3: 「エンジニア数はどのくらい?」
address_tokyo
フィルター → 約500名と回答address_sapporo
フィルター → 約150名と回答
address_tokyo フィルタを適用した場合の回答
address_sapporo フィルタを適用した場合の回答
タグの永続性について
検証の過程で興味深い特性を発見しました。タグ付け後、同じファイル名でS3にアップロードし直しても、タグは削除されず、タグの値も変わりませんでした。
この特性により、ファイル更新時にタグの再設定作業は不要ですが、意図的にタグを変更したい場合は明示的な削除・再付与が必要となります。
まとめ
今回、Amazon Q in ConnectのコンテンツセグメンテーションにおけるS3フォルダ配下の全ファイルへの一括タグ付けスクリプトを作成・検証しました。
このスクリプトにより、以下のメリットが得られます。
- 運用効率の向上:大量ファイルへの手動タグ付け作業から解放
- 一貫性の確保:フォルダ単位での統一されたタグ管理が可能
- スケーラビリティ:ファイル数に関係なく一括処理が可能
ナレッジベースを運用する際の効率的なタグ管理方法として、ぜひご活用ください。
参考