はじめに
Amazon Q Business は、企業内データソースに対してRAGベースの自律的なQ&Aを提供するAWS マネージドAI Agentです。単なる検索エンジンではなく、複数データソースを横断して回答を生成・引用まで行う Agentic AI として、社内ナレッジ活用・カスタマーサポート・業務自動化の文脈で採用が増えています。
本記事は実際のboto3 API検証と設計パターンの整理をもとに、提案・デリバリーで繰り返し参照できるリファレンスとしてまとめたものです。「どのデータソースをどのパターンで繋ぐか」「アクセス制御をどう設計するか」の判断軸を体系化します。
Amazon Q Business のアーキテクチャ全体像
Amazon Q Business は以下の4層で構成されます。
【データソース層】
S3(ドキュメント) Athena(構造化) Salesforce / SharePoint カスタムAPI
| | | |
+-------------------+----------------------+----------------------+
|
【インジェスト層】
ネイティブコネクタ / カスタムコネクタ
(スケジュール同期 or オンデマンド)
|
【インデックス層】
Amazon Q Index
チャンク化 / 埋め込み / ACL付与
|
【アプリケーション層】
+------------------+------------------+
| |
Web Experience ChatSync API
(チャット UI) (プログラム呼び出し)
各層の役割:
| 層 | 役割 | 設計上の主な判断ポイント |
|---|---|---|
| データソース層 | 元データの置き場所・形式 | 構造化 vs 非構造化 / 更新頻度 / ACL要件 |
| インジェスト層 | データの取り込み・正規化 | ネイティブコネクタ vs カスタムAPI |
| インデックス層 | チャンク化・ベクトル化・ACL付与 | チャンクサイズ / メタデータ設計 |
| アプリケーション層 | 検索・回答生成・ユーザー提供 | 認証方式 / 回答スタイル |
Amazon Q Business が Agentic AI である理由: ユーザーの質問に対して、Agentが自律的に複数ソースを横断検索 → 関連チャンクを選択 → 回答を合成 → 引用を付与、という多段階の判断を人手を介さず実行します。
Agentic RAG の動作シーケンス
ユーザー: 「Masks の在庫が発注点を下回っている。過去の緊急発注手順を教えて」
|
| ChatSync API
v
Amazon Q Agent
|
+-- [1] クエリ解析: 「在庫不足 + 緊急発注手順」と判断
|
+-- [2] インデックス横断検索(自律)
| S3(調達マニュアル.pdf) ← 「緊急発注手順」にヒット
| Athena(在庫履歴テーブル)← 「Masks の過去発注履歴」にヒット
|
+-- [3] 関連チャンクのスコアリング・選択(自律)
| 上位チャンクを選択し、回答合成に渡す
|
+-- [4] 回答合成(自律)
| 「手順書p.3に基づき、緊急発注はXXX。Masksの直近発注は〇月〇日」
|
+-- [5] 引用付与(自律)
sourceAttributions: [調達マニュアル.pdf#p3, 在庫履歴#row-47]
このシーケンスはユーザーが1回質問するだけで Agent が自律的に実行します。以下は ChatSync API を使って実際にこの動作を確認した例です。
import boto3
qb = boto3.client('qbusiness', region_name='us-east-1')
response = qb.chat_sync(
applicationId='app-xxxxxxxx',
userId='user@example.com',
userMessage='Masksの在庫が発注点を下回っています。過去の緊急発注手順を教えてください'
)
print(response['systemMessage'])
# → "調達マニュアルの手順に基づき、緊急発注は購買部門へのメール申請が必要です(マニュアル p.3)。
# Masksの直近発注は2026年2月14日で、200個を発注済みです(在庫履歴より)。"
for source in response.get('sourceAttributions', []):
print(f"引用: {source['title']} — {source['url']}")
# → 引用: 調達マニュアル.pdf — s3://company-docs/procurement/manual.pdf
# → 引用: 在庫発注履歴 — athena://inventory-db/order_history#row-47
レスポンス例(sourceAttributions 抜粋):
{
"systemMessage": "調達マニュアルの手順に基づき、緊急発注は購買部門へのメール申請が必要です(マニュアル p.3)。Masksの直近発注は2026年2月14日で、200個を発注済みです(在庫履歴より)。",
"sourceAttributions": [
{
"title": "調達マニュアル.pdf",
"url": "s3://company-docs/procurement/manual.pdf",
"citationNumber": 1,
"snippet": "緊急発注が必要な場合は、購買部門(purchase@example.com)へ件名「緊急発注申請」でメール申請…"
},
{
"title": "在庫発注履歴",
"url": "athena://inventory-db/order_history",
"citationNumber": 2,
"snippet": "2026-02-14 | Masks | 200個 | 発注済み | 担当: 田中"
}
]
}
sourceAttributions に複数ソース(S3文書+Athenaクエリ結果)が含まれていることが、Agent が複数データソースを横断して自律的に回答生成した証拠です。
データソース分類と選定フレームワーク
Amazon Q Business のデータソースは3パターンに分類できます。
データ特性
|
+-- 非構造化ドキュメント系 --> パターン1: ネイティブコネクタ(S3 / SharePoint / Confluence)
|
+-- 構造化DB / DWH系 --> パターン2: Athena経由(S3 + Glue Catalog)
|
+-- SaaS / CRM / ITSM系 --> パターン3: SaaS ネイティブコネクタ(Salesforce / ServiceNow)
|
+-- 既存システム / API系 --> パターン4: カスタムコネクタ(BatchPutDocument API)
選定マトリクス
| 要件 | パターン1 S3/ファイル | パターン2 Athena | パターン3 SaaS | パターン4 カスタム |
|---|---|---|---|---|
| 対応データ形式 | PDF/Word/HTML/CSV | SQL結果(テキスト変換) | SaaS固有オブジェクト | 任意 |
| リアルタイム性 | 低(スケジュール同期) | 低〜中 | 中(スケジュール同期) | 高(即時反映可) |
| ACL引き継ぎ | S3バケットポリシー | なし(別途設計) | SaaS権限を引き継ぎ可 | 自由に設定可 |
| 設定難易度 | 低 | 中 | 中〜高(OAuth設定) | 高 |
| ユースケース例 | 社内規程・マニュアル | 売上レポート・ログ | 案件管理・チケット | 基幹システム連携 |
パターン1 — S3 ネイティブコネクタ
構成
社内文書(PDF / Word / HTML)
|
v アップロード
S3 Bucket
/docs/ /manuals/
|
| スケジュール同期 or オンデマンド
v
S3 Data Source Connector <-- metadata/ (属性JSON)
|
v チャンク化 / 埋め込み
Amazon Q Index
boto3 実装
import boto3
qb = boto3.client('qbusiness', region_name='us-east-1')
response = qb.create_data_source(
applicationId='app-xxxxxxxx',
indexId='idx-xxxxxxxx',
displayName='社内文書-S3',
type='S3',
configuration={
'S3Configuration': {
'bucketName': 'company-knowledge-bucket',
'inclusionPrefixes': ['docs/', 'manuals/'],
'exclusionPatterns': ['*.tmp', '*/archive/*'],
'documentsMetadataConfiguration': {
'S3Prefix': 'metadata/'
}
}
},
syncSchedule='cron(0 2 * * ? *)', # 毎日 02:00 UTC
roleArn='arn:aws:iam::123456789012:role/QBusinessDataSourceRole'
)
data_source_id = response['dataSourceId']
# 手動同期
qb.start_data_source_sync_job(
applicationId='app-xxxxxxxx',
indexId='idx-xxxxxxxx',
dataSourceId=data_source_id
)
メタデータスキーマ設計
S3オブジェクトと同名の .metadata.json をメタデータプレフィックス下に置くことで属性フィルタリングが可能になります。
{
"_source_uri": "https://s3.amazonaws.com/bucket/docs/hr-policy.pdf",
"_category": "HR",
"_last_updated_at": "2026-04-01T00:00:00Z",
"department": "人事部",
"confidentiality": "internal"
}
設計上の注意点:
– _ プレフィックスはシステム予約属性。カスタム属性はプレフィックスなしで定義する
– ファイルソース系データセットは describe_data_set 系APIで列情報を取得できない(File source type is not supported in Public API)。属性確認は get_index または list_data_sources 経由で行う
パターン2 — Athena 経由の構造化データ
構成
RDS / Redshift
(構造化データ)
|
| ETL / Export
v
S3 (CSV / Parquet)
|
v
Glue Catalog
(スキーマ定義)
|
v
Athena
(SQL変換クエリ)
|
| クエリ結果をMarkdown / JSONドキュメント化
v
S3 (docs/)
|
v
S3 Connector --> Q Index
構造化データをRAGに乗せる際の主な課題は「テーブルの行をどう文書化するか」です。単純なCSVダンプではなく、行ごとに文脈を持つドキュメントに変換する必要があります。
import boto3
athena = boto3.client('athena', region_name='ap-northeast-1')
s3 = boto3.client('s3')
def export_as_documents(query: str, output_bucket: str, output_prefix: str):
response = athena.start_query_execution(
QueryString=query,
ResultConfiguration={
'OutputLocation': f's3://{output_bucket}/athena-results/'
}
)
# ポーリングで完了待ち(省略)
results = athena.get_query_results(
QueryExecutionId=response['QueryExecutionId']
)
columns = [col['Label'] for col in results['ResultSet']['ResultSetMetadata']['ColumnInfo']]
for i, row in enumerate(results['ResultSet']['Rows'][1:]):
values = {col: row['Data'][j].get('VarCharValue', '') for j, col in enumerate(columns)}
# 行をMarkdown形式のドキュメントに整形
doc_content = '\n'.join([f'**{k}**: {v}' for k, v in values.items()])
s3.put_object(
Bucket=output_bucket,
Key=f'{output_prefix}/row-{i}.md',
Body=doc_content.encode('utf-8')
)
注意: 大量レコードを全行ドキュメント化するとインデックスコストが増大します。集約・サマリー化するか、カスタムコネクタ(パターン4)でフィルタ条件を付けて件数を絞るアプローチが現実的です。
パターン3 — SaaS ネイティブコネクタ(Salesforce)
SaaS側の権限(ACL)をそのままQ Businessに引き継げる点が最大のメリットです。
構成
Salesforce IAM Identity Center
(Account / Case / KB) (SAML連携)
| |
| OAuth 2.0 認証 | ユーザーコンテキスト
v v
Salesforce Connector --> Q Index --> Q Application
|
v
ユーザーが閲覧可能な
ドキュメントのみ回答
Secrets Manager でクレデンシャル管理
import boto3, json
sm = boto3.client('secretsmanager', region_name='us-east-1')
qb = boto3.client('qbusiness', region_name='us-east-1')
secret_arn = sm.create_secret(
Name='qbusiness/salesforce-credentials',
SecretString=json.dumps({
'clientId': 'YOUR_SF_CLIENT_ID',
'clientSecret': 'YOUR_SF_CLIENT_SECRET',
'username': 'admin@example.com',
'password': 'PASSWORD',
'securityToken': 'SECURITY_TOKEN'
})
)['ARN']
qb.create_data_source(
applicationId='app-xxxxxxxx',
indexId='idx-xxxxxxxx',
displayName='Salesforce-KB',
type='SALESFORCE',
configuration={
'SalesforceConfiguration': {
'serverUrl': 'https://yourinstance.salesforce.com',
'secretArn': secret_arn,
'crawlKnowledgeArticles': True,
'crawlAttachments': False,
'standardObjectConfigurations': [
{
'name': 'KNOWLEDGE_ARTICLE',
'query': "ArticleType = 'FAQ__kav'",
'fieldMappings': [
{
'dataSourceFieldName': 'Title',
'indexFieldName': '_document_title',
'indexFieldType': 'STRING'
}
]
}
]
}
},
roleArn='arn:aws:iam::123456789012:role/QBusinessSalesforceRole'
)
SaaS コネクタ共通の注意点:
– 初回同期は全量取得のため、大規模なSaaSは数時間かかることがある
– SaaS側のAPI Rate Limitに注意(Salesforceは query() の同時呼び出し制限あり)
– ACL引き継ぎはSaaS側の権限変更が次回同期まで反映されない点を考慮する
パターン4 — カスタムコネクタ(BatchPutDocument API)
既存システム連携や独自フォーマットのデータには batch_put_documents APIが最も柔軟です。
処理フロー
基幹システム / API
|
| データ取得(差分 / 全量)
v
Lambda(変換・整形)
- テキスト化
- メタデータ付与
- ACL設定
|
v
batch_put_documents()
|
+-- 成功 --> Q Index に反映
|
+-- 失敗 --> 失敗IDをリトライキューへ
(SQS / DynamoDB)
実装
import boto3
qb = boto3.client('qbusiness', region_name='us-east-1')
def ingest_documents(application_id: str, index_id: str, documents: list):
formatted = []
for doc in documents:
formatted.append({
'id': doc['id'],
'title': doc['title'],
'content': {
'blob': doc['content'].encode('utf-8')
},
'contentType': 'PLAIN_TEXT',
'documentAttributes': [
{'name': '_category', 'value': {'stringValue': doc.get('category', 'general')}},
{'name': '_last_updated_at', 'value': {'dateValue': doc['updated_at']}},
{'name': 'source_system', 'value': {'stringValue': doc.get('source', 'unknown')}},
],
'accessConfiguration': {
'accessControls': [
{
'principals': [
{'user': {'id': uid, 'type': 'USER'}}
for uid in doc.get('allowed_users', [])
],
'memberRelation': 'OR'
}
],
'memberRelation': 'OR'
}
})
# 10件ずつバッチ処理(API上限)
for i in range(0, len(formatted), 10):
response = qb.batch_put_documents(
applicationId=application_id,
indexId=index_id,
documents=formatted[i:i+10]
)
for failed in response.get('failedDocuments', []):
print(f"失敗: {failed['id']} — {failed['error']['errorMessage']}")
アクセス制御設計パターン
Amazon Q Business のアクセス制御は2軸で設計します。
軸1: アプリケーションレベル認証
認証方式の選択
|
+-- 社員 / パートナーがブラウザから使う
| --> IAM Identity Center + SAML連携
|
+-- 社内システムからAPIで呼び出す
| --> IAM認証
|
+-- 外部公開(顧客向け)
--> Cognito + カスタムUI
(Q Business Web Experience は使わない)
軸2: ドキュメントレベルACL
| ACLパターン | 設定方法 | 適用ケース | 注意点 |
|---|---|---|---|
| 全員閲覧可 | accessConfiguration を省略 |
社内共通ナレッジ・FAQ | デフォルト動作 |
| 部門グループ制御 | GroupId でグループ指定 |
部門ドキュメント | IAM Identity Centerのグループと紐付け |
| ユーザー個別制御 | UserId で個人指定 |
個人プロジェクト | ユーザー数が多いと管理コスト増 |
| SaaS ACL継承 | ネイティブコネクタが自動設定 | Salesforce / SharePoint | 権限変更は次回同期まで遅延あり |
提案・デリバリー時の考慮点
コスト設計
| 要素 | 課金単位 | 設計上の考慮点 |
|---|---|---|
| ユーザーサブスクリプション | ユーザー数 × 月額 | Lite vs Pro の機能差を確認してから提案 |
| インデックスストレージ | 格納データ量 | exclusionPatterns で不要ドキュメントを除外 |
| データ同期 | API呼び出し回数 | 毎時 vs 日次でコスト差が大きい |
| ChatSync API | クエリ数 | アプリ側でキャッシュ設計を検討 |
同期戦略の選定
更新頻度による分岐
|
+-- 月次以下 --> フル同期(週次スケジュール)
|
+-- 日次程度 --> 増分同期(_last_updated_at でフィルタ)
|
+-- 時間単位 --> オンデマンド同期(EventBridge Trigger + batch_put_documents)
|
+-- 分単位 --> ストリーミング(Kinesis + Lambda + batch_put_documents)
増分同期の実装例:
from datetime import datetime, timezone
def incremental_sync(application_id: str, index_id: str, source_api):
last_sync = get_last_sync_timestamp() # DynamoDB等から取得
updated_docs = source_api.get_documents_since(last_sync)
if updated_docs:
ingest_documents(application_id, index_id, updated_docs)
deleted_ids = source_api.get_deleted_since(last_sync)
if deleted_ids:
qb.batch_delete_document(
applicationId=application_id,
indexId=index_id,
documentIdList=deleted_ids
)
save_last_sync_timestamp(datetime.now(timezone.utc))
インデックス容量設計
| インデックスタイプ | ドキュメント上限 | 推奨用途 |
|---|---|---|
| STARTER | 10万文書 | PoC・小〜中規模 |
| ENTERPRISE | 100万文書 | 大規模エンタープライズ |
STARTERで開始してENTERPRISEにアップグレードする段階的アプローチが提案フェーズでは現実的です。
監視設計
def monitor_sync_jobs(application_id, index_id, data_source_id):
response = qb.list_data_source_sync_jobs(
applicationId=application_id,
indexId=index_id,
dataSourceId=data_source_id,
maxResults=5
)
for job in response['history']:
m = job.get('metrics', {})
print(f"状態: {job['status']} | 追加: {m.get('documentsAdded',0)} | "
f"更新: {m.get('documentsModified',0)} | 失敗: {m.get('documentsFailed',0)}")
同期失敗率が継続上昇する場合、データソース側のAPI制限・認証トークン期限切れ・ドキュメントサイズ超過のいずれかが原因であることがほとんどです。
落とし穴まとめ
| 項目 | 落とし穴 | 対処 |
|---|---|---|
| ファイルソース | S3直接アップロード系は describe_data_set で列情報取得不可 |
get_index / list_data_sources で確認 |
| SaaS ACL遅延 | SaaS側の権限剥奪が次の同期まで反映されない | 機密文書は batch_delete_document で即時削除する仕組みを併用 |
| 大量ドキュメント | 全行ドキュメント化するとコスト・精度が悪化 | 集約・サマリー化またはフィルタ条件で件数を絞る |
| インデックス上限 | STARTERの10万文書上限に予告なく到達するとインジェスト停止 | get_index で documentCount を定期監視 |
| batch_put_documents 上限 | 1コールで10件まで | ループ処理とエラーハンドリングを必ず実装 |
| リージョン差 | ap-northeast-1 は一部機能の提供が遅延するケースあり | デリバリー前に最新の対応リージョンを公式ドキュメントで確認 |
| cron タイムゾーン | cron式はUTC。JST 09:00 に同期したい場合は cron(0 0 * * ? *) |
タイムゾーン変換ミスに注意 |
まとめ
Amazon Q Business のデータソース連携は「何を・いつ・誰に見せるか」の3軸で設計パターンが決まります。
| 軸 | 判断基準 | 推奨パターン |
|---|---|---|
| What(データ種別) | 非構造化 | S3 ネイティブコネクタ |
| 構造化DB | Athena経由 → S3変換 | |
| SaaS | SaaS ネイティブコネクタ | |
| 既存システム | BatchPutDocument カスタム | |
| When(更新頻度) | 月次以下 | スケジュール同期 |
| 時間単位以上 | オンデマンド + EventBridge | |
| Who(アクセス制御) | 全社共通 | ACL省略(デフォルト) |
| 部門・個人 | GroupId / UserId 指定 | |
| SaaS権限継承 | ネイティブコネクタの自動ACL |
Quick Flows を使ったフロー自動化と本記事のデータ連携パターンを組み合わせると、データ取得 → AI判断 → アクション通知 のエンドツーエンドなAgentic AIパイプラインが構成できます。
Amazon Quick Flows によるAI業務フロー自動化