Amazon Q in ConnectでServiceNowをナレッジベースとし、タグで検索対象を絞り込んでみた

Amazon Q in ConnectでServiceNowをナレッジベースとし、タグで検索対象を絞り込んでみた

2025.11.17

はじめに

Amazon Q in Connect は、コンタクトセンター業務を AI で支援するマネージドサービスです。
本サービスには、データソースにタグを付与し、問い合わせに関連するドキュメントだけを絞り込める「コンテンツセグメンテーション」という機能があります。これにより次のメリットが得られます。

  • 回答精度の向上 : 関連性の高い情報だけを検索対象にするため、より正確な回答が可能
  • 応答時間の短縮 : 検索対象が減ることで、必要な情報へ迅速にアクセス
  • 顧客満足度の向上 : 的確でスピーディーな回答により、顧客の課題を早期に解決

以前、ナレッジベースのソースをS3バケットで試しました。

https://dev.classmethod.jp/articles/amazon-q-in-connect-content-tagging-segmentation/

今回は、ナレッジベースのソースをServiceNowでコンテンツセグメンテーションを試してみます。

前提条件

ServiceNowのナレッジ記事の確認

ServiceNowのトップページから、ナレッジに遷移します。
cm-hirai-screenshot 2025-10-22 10.45.54

ナレッジをクリックします。
cm-hirai-screenshot 2025-10-22 10.46.40
クラスメソッドのオフィス住所として、東京と札幌の住所情報の2つのナレッジ記事を作成しています。

cm-hirai-screenshot 2025-10-22 10.51.44

それぞれの記事は、公開済みの記事です。
cm-hirai-screenshot 2025-10-22 10.51.10

札幌オフィス情報のナレッジ記事番号は、KB0010006です。後で利用します。

コンテンツのタグ付け手順

まず、Connectインスタンスと紐づいている対象のナレッジベースのknowledgeBaseIdを取得します。

AWS CloudShellで以下のコマンドを実行します。

$ aws qconnect list-knowledge-bases
{
    "knowledgeBaseSummaries": [
        {
            "knowledgeBaseId": "bbe5faac-a562-41ee-b50b-b53716fbc54c",
            "knowledgeBaseArn": "arn:aws:wisdom:ap-northeast-1:アカウントID:knowledge-base/bbe5faac-a562-41ee-b50b-b53716fbc54c",
            "name": "integration-servicenow-hirai-2-2",
            "knowledgeBaseType": "EXTERNAL",
            "status": "ACTIVE",
            "sourceConfiguration": {
                "appIntegrations": {
                    "appIntegrationArn": "arn:aws:app-integrations:ap-northeast-1:アカウントID:data-integration/9a19f3eb-979d-4652-97fd-ec73b01de3fa",
                    "objectFields": [
                        "number",
                        "workflow_state",
                        "active",
                        "short_description",
                        "sys_mod_count",
                        "sys_id",
                        "wiki",
                        "sys_updated_on",
                        "text"
                    ]
                }

対象のナレッジベース名前は、マネジメントコンソール上の[統合]から確認できます。

cm-hirai-screenshot 2025-11-17 7.59.46
統合名

次に、ListContents APIにて、対象ナレッジベースのコンテンツリストを取得します。ここで言うコンテンツとは、ServiceNowに保存されている各ナレッジ記事のことです。

各コンテンツのcontentArnをコピーしておきます。ナレッジ記事の数が多いため、今回は、札幌オフィス情報に絞るため、ナレッジ記事番号KB0010006のみを出力させます。

$ aws qconnect list-contents --knowledge-base bbe5faac-a562-41ee-b50b-b53716fbc54c --query "contentSummaries[?metadata.number=='KB0010006']"
[
    {
        "contentArn": "arn:aws:wisdom:ap-northeast-1:アカウントID:content/bbe5faac-a562-41ee-b50b-b53716fbc54c/95786efd-0a48-4956-b8ff-377bc67d8e0a",
        "contentId": "95786efd-0a48-4956-b8ff-377bc67d8e0a",
        "knowledgeBaseArn": "arn:aws:wisdom:ap-northeast-1:アカウントID:knowledge-base/bbe5faac-a562-41ee-b50b-b53716fbc54c",
        "knowledgeBaseId": "bbe5faac-a562-41ee-b50b-b53716fbc54c",
        "name": "KB0010006",
        "revisionId": "OGU0MjAyNWQ2OWUyMzIzNjY1OGYyZGY1MGNlNTVlNjc0NmM1OWQxYTkxMjY3OTMwZjZmYjkyNDk2ODFjMWQzYg==",
        "title": "札幌オフィス",
        "contentType": "application/x.wisdom-json;source=servicenow",
        "status": "ACTIVE",
        "metadata": {
            "active": "true",
            "aws:wisdom:externalVersion": "3",
            "number": "KB0010006",
            "short_description": "札幌オフィス",
            "sys_mod_count": "3",
            "workflow_state": "Published"
        },
        "tags": {}
    }
]

TagResource APIにて、先程取得したコンテンツそれぞれにタグ付けを実行します。

なお、TagResource APIのリクエストパラメータresourceArnは、先ほど取得した各コンテンツのcontentArnです。

ここでは categorycontent_type の 2 種類のタグを付けます。

$ aws qconnect tag-resource \
  --resource-arn "arn:aws:wisdom:ap-northeast-1:アカウントID:content/bbe5faac-a562-41ee-b50b-b53716fbc54c/95786efd-0a48-4956-b8ff-377bc67d8e0a" \
  --tags '{"category":"product_info","content_type":"address_sapporo"}'

コンテンツがタグ付けされているかも確認できます。

$ aws qconnect list-tags-for-resource \
--resource-arn "arn:aws:wisdom:ap-northeast-1:アカウントID:content/bbe5faac-a562-41ee-b50b-b53716fbc54c/95786efd-0a48-4956-b8ff-377bc67d8e0a"

{
    "tags": {
        "content_type": "address_sapporo",
        "category": "product_info"
    }
}

https://awscli.amazonaws.com/v2/documentation/api/latest/reference/qconnect/list-contents.html
https://awscli.amazonaws.com/v2/documentation/api/latest/reference/qconnect/tag-resource.html
https://awscli.amazonaws.com/v2/documentation/api/latest/reference/qconnect/list-tags-for-resource.html

Lambdaを作成する

コンタクトフローから呼び出し、Amazon Q in Connect の「セッション」にタグフィルターを設定する Lambda 関数を作成します。

  • 名前 : cm-hirai-q-in-connect-session-tag-filter-updater
  • ランタイム : Python 3.13
  • 付与するロール : AWSLambdaBasicExecutionRole + 下記インラインポリシー
インラインポリシー
{
    "Version": "2012-10-17",
    "Statement":[
        {
            "Effect": "Allow",
            "Action": [
                "connect:DescribeContact",
                "wisdom:UpdateSession"
            ],
            "Resource": "*"
        }
    ]
}
import json
import boto3
from botocore.exceptions import ClientError

connect_client = boto3.client('connect')
qconnect_client = boto3.client('qconnect')

def lambda_handler(event, context):
    print('Event Received: ' + json.dumps(event, ensure_ascii=False))

    # Connectコンタクトデータとパラメータの取得
    contact_data = event.get('Details', {}).get('ContactData', {})
    tag_filter = event.get('Details', {}).get('Parameters', {}).get('tagFilter')
    print("Tag Filter: ", json.dumps(tag_filter))

    # Amazon Connect Contactの情報を取得
    try:
        contact_response = connect_client.describe_contact(
            InstanceId=contact_data.get('InstanceARN'),
            ContactId=contact_data.get('ContactId')
        ).get("Contact", {})

        print(f"Describe ContactId: {contact_data.get('ContactId')}: {json.dumps(contact_response, default=str)}")
    except ClientError as e:
        error_message = f"ClientError - DescribeContact for ContactId: {contact_data.get('ContactId')} - {e}"
        print(error_message)
        return {"success": False}

    # Amazon Q in Connect セッション情報の抽出
    wisdom_info = contact_response.get("WisdomInfo", {})
    session_arn = wisdom_info.get("SessionArn")

    if not session_arn:
        error_message = f"No Amazon Q in Connect session found for ContactId: {contact_data.get('ContactId')}"
        print(error_message)
        return {"success": False}

    # セッションARNからアシスタントIDとセッションIDを抽出
    try:
        arn_parts = session_arn.split('/')
        assistant_id = arn_parts[1]
        session_id = arn_parts[2]
    except (IndexError, AttributeError) as e:
        error_message = f"Error parsing session ARN: {session_arn} - {e}"
        print(error_message)
        return {"success": False}

    # タグフィルターの処理とセッションの更新
    try:
        # タグフィルターが文字列の場合、JSONオブジェクトに変換
        if isinstance(tag_filter, str):
            try:
                tag_filter_json = json.loads(tag_filter)
            except json.JSONDecodeError:
                error_message = f"Invalid JSON in tagFilter parameter: {tag_filter}"
                print(error_message)
                return {
                    "statusCode": 400,
                    "body": error_message
                }
        else:
            tag_filter_json = tag_filter

        # セッションにタグフィルターを適用
        update_session_response = qconnect_client.update_session(
            assistantId=assistant_id,
            sessionId=session_id,
            tagFilter=tag_filter_json
        ).get("session", {})

        print("Update Session Response: ", json.dumps(update_session_response))
        return {"success": True}
    except Exception as e:
        print(f"Error: {str(e)}")
        return {"success": False}

この関数は以下の処理を行います

  1. コンタクトフローから渡された Contact情報とtagFilter パラメータを受け取る
  2. DescribeContact で Wisdom セッション ARN を取得
  3. ARN から assistantId と sessionId を分解
  4. UpdateSession API に tagFilter を渡してセッションを更新
    → 以降の検索対象が、指定タグを持つコンテンツだけに絞られる

Connectフロー

検証用のフローは下図のとおりです。

cm-hirai-screenshot 2025-04-10 17.29.16

(クリックで展開)
{
  "Version": "2019-10-30",
  "StartAction": "62416ee0-7b7d-406f-8df5-4888d812fa9a",
  "Metadata": {
    "entryPointPosition": {
      "x": 290.4,
      "y": 53.6
    },
    "ActionMetadata": {
      "fba2c05b-c6a2-4795-acaf-200e9f6c39da": {
        "position": {
          "x": 631.2,
          "y": 52.8
        }
      },
      "62416ee0-7b7d-406f-8df5-4888d812fa9a": {
        "position": {
          "x": 404.8,
          "y": 52
        }
      },
      "10d508f7-76b0-4ff6-b084-59ed78c9e1a0": {
        "position": {
          "x": 176.8,
          "y": 238.4
        },
        "children": [
          "b3ab6dc2-902c-411a-b211-7c3128e8abfa"
        ],
        "parameters": {
          "WisdomAssistantArn": {
            "displayName": "arn:aws:wisdom:ap-northeast-1:111111111111:assistant/a1793008-f4de-481b-a9ed-3697ef373ff2"
          }
        },
        "fragments": {
          "SetContactData": "b3ab6dc2-902c-411a-b211-7c3128e8abfa"
        }
      },
      "b3ab6dc2-902c-411a-b211-7c3128e8abfa": {
        "position": {
          "x": 176.8,
          "y": 238.4
        },
        "dynamicParams": []
      },
      "90537ffa-b186-47ec-938f-cca6dc4286db": {
        "position": {
          "x": 404,
          "y": 236.8
        },
        "parameters": {
          "LambdaFunctionARN": {
            "displayName": "cm-hirai-q-in-connect-session-tag-filter-updater"
          },
          "LambdaInvocationAttributes": {
            "tagFilter": {
              "useJson": true
            }
          }
        },
        "dynamicMetadata": {
          "tagFilter": false
        }
      },
      "433d0be0-48cb-44da-a24f-6aa745e3f242": {
        "position": {
          "x": 853.6,
          "y": 224.8
        }
      },
      "d7f03407-1f71-4852-9ca0-1a8e00d17409": {
        "position": {
          "x": 630.4,
          "y": 234.4
        },
        "parameters": {
          "QueueId": {
            "displayName": "cm-hirai"
          }
        },
        "queue": {
          "text": "cm-hirai"
        }
      },
      "8e1110a3-e055-4bf4-9c5a-2b5c2b48f6bc": {
        "position": {
          "x": 863.2,
          "y": 454.4
        }
      }
    },
    "Annotations": [],
    "name": "cm-hirai-q-in-connect-session-tag-filter-updater",
    "description": "",
    "type": "contactFlow",
    "status": "published",
    "hash": {}
  },
  "Actions": [
    {
      "Parameters": {
        "RecordingBehavior": {
          "RecordedParticipants": [
            "Agent",
            "Customer"
          ],
          "ScreenRecordedParticipants": [
            "Agent"
          ],
          "IVRRecordingBehavior": "Disabled"
        },
        "AnalyticsBehavior": {
          "Enabled": "True",
          "AnalyticsLanguage": "ja-JP",
          "AnalyticsRedactionBehavior": "Disabled",
          "AnalyticsRedactionResults": "None",
          "ChannelConfiguration": {
            "Chat": {
              "AnalyticsModes": []
            },
            "Voice": {
              "AnalyticsModes": [
                "PostContact"
              ]
            }
          }
        }
      },
      "Identifier": "fba2c05b-c6a2-4795-acaf-200e9f6c39da",
      "Type": "UpdateContactRecordingBehavior",
      "Transitions": {
        "NextAction": "10d508f7-76b0-4ff6-b084-59ed78c9e1a0"
      }
    },
    {
      "Parameters": {
        "FlowLoggingBehavior": "Enabled"
      },
      "Identifier": "62416ee0-7b7d-406f-8df5-4888d812fa9a",
      "Type": "UpdateFlowLoggingBehavior",
      "Transitions": {
        "NextAction": "fba2c05b-c6a2-4795-acaf-200e9f6c39da"
      }
    },
    {
      "Parameters": {
        "WisdomAssistantArn": "arn:aws:wisdom:ap-northeast-1:111111111111:assistant/a1793008-f4de-481b-a9ed-3697ef373ff2"
      },
      "Identifier": "10d508f7-76b0-4ff6-b084-59ed78c9e1a0",
      "Type": "CreateWisdomSession",
      "Transitions": {
        "NextAction": "b3ab6dc2-902c-411a-b211-7c3128e8abfa",
        "Errors": [
          {
            "NextAction": "90537ffa-b186-47ec-938f-cca6dc4286db",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {
        "WisdomSessionArn": "$.Wisdom.SessionArn"
      },
      "Identifier": "b3ab6dc2-902c-411a-b211-7c3128e8abfa",
      "Type": "UpdateContactData",
      "Transitions": {
        "NextAction": "90537ffa-b186-47ec-938f-cca6dc4286db",
        "Errors": [
          {
            "NextAction": "90537ffa-b186-47ec-938f-cca6dc4286db",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {
        "LambdaFunctionARN": "arn:aws:lambda:ap-northeast-1:111111111111:function:cm-hirai-q-in-connect-session-tag-filter-updater",
        "InvocationTimeLimitSeconds": "8",
        "LambdaInvocationAttributes": {
          "tagFilter": {
            "tagCondition": {
              "key": "content_type",
              "value": "address_sapporo"
            }
          }
        },
        "ResponseValidation": {
          "ResponseType": "STRING_MAP"
        }
      },
      "Identifier": "90537ffa-b186-47ec-938f-cca6dc4286db",
      "Type": "InvokeLambdaFunction",
      "Transitions": {
        "NextAction": "d7f03407-1f71-4852-9ca0-1a8e00d17409",
        "Errors": [
          {
            "NextAction": "d7f03407-1f71-4852-9ca0-1a8e00d17409",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {},
      "Identifier": "433d0be0-48cb-44da-a24f-6aa745e3f242",
      "Type": "TransferContactToQueue",
      "Transitions": {
        "NextAction": "8e1110a3-e055-4bf4-9c5a-2b5c2b48f6bc",
        "Errors": [
          {
            "NextAction": "8e1110a3-e055-4bf4-9c5a-2b5c2b48f6bc",
            "ErrorType": "QueueAtCapacity"
          },
          {
            "NextAction": "8e1110a3-e055-4bf4-9c5a-2b5c2b48f6bc",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {
        "QueueId": "arn:aws:connect:ap-northeast-1:111111111111:instance/3ff2093d-af96-43fd-b038-3c07cdd7609c/queue/ba8d05d9-27b3-406e-b089-f5707174697e"
      },
      "Identifier": "d7f03407-1f71-4852-9ca0-1a8e00d17409",
      "Type": "UpdateContactTargetQueue",
      "Transitions": {
        "NextAction": "433d0be0-48cb-44da-a24f-6aa745e3f242",
        "Errors": [
          {
            "NextAction": "8e1110a3-e055-4bf4-9c5a-2b5c2b48f6bc",
            "ErrorType": "NoMatchingError"
          }
        ]
      }
    },
    {
      "Parameters": {},
      "Identifier": "8e1110a3-e055-4bf4-9c5a-2b5c2b48f6bc",
      "Type": "DisconnectParticipant",
      "Transitions": {}
    }
  ]
}

Lambda ブロックの設定

[AWS Lambda 関数を呼び出す] ブロックに、前項で作成した Lambda を指定し、パラメータ tagFilter に次の JSON を渡します。
この設定により、content_type=address_sapporo のタグを持つコンテンツだけが検索対象になります。
(実際に利用する場合は、前段でIVRなどでお客様のお問い合わせ内容に応じて動的にフィルタリングしますが、今回は検証なので、シンプルなフローで検証します。)

関数入力パラメータ
{
	"tagCondition": {
		"key": "content_type",
		"value": "address_sapporo"
	}
}

cm-hirai-screenshot 2025-04-11 11.36.52

UpdateSession API の TagFilter 仕様

UpdateSessiontagFilter は「Tagged Union」形式で、トップレベルには次の 3 つのいずれか 1 つしか指定できません。

  1. andConditions
  2. orConditions
  3. tagCondition(単一条件)

ドキュメントには明確に記載されています。

This is a Tagged Union structure. Only one of the following top level keys can be set: andConditions, orConditions, tagCondition.

つまり、これらの条件を同時に使用することはできません。1つのリクエストで指定できるのは、これらのうちの1つだけです。

ただし、それぞれの条件内で複雑な条件を構築することは可能です。

  • andConditionsでは複数のタグ条件をAND条件で結合できます
  • orConditionsでは複数の条件をOR条件で結合でき、その中にandConditionstagConditionを含めることができます
  • tagConditionは単一のタグキーと値のペアを指定します

これにより、複雑なタグフィルタリング条件を構築できますが、トップレベルでは3つのうち1つだけを選択する必要があります。

以下のように複数条件フィルタリングすることが可能です。

複数の条件をANDで結合する例
{
  "andConditions": [
    {
      "key": "content_type",
      "value": "address_sapporo"
    },
    {
      "key": "category",
      "value": "product_info"
    }
  ]
}
複数の条件をORで結合する例
{
  "orConditions": [
    {
      "tagCondition": {
        "key": "content_type",
        "value": "address_sapporo"
      }
    },
    {
      "tagCondition": {
        "key": "content_type",
        "value": "address_tokyo"
      }
    }
  ]
}
複雑な条件の例
{
  "orConditions": [
    {
      "andConditions": [
        {
          "key": "content_type",
          "value": "address"
        },
        {
          "key": "region",
          "value": "hokkaido"
        }
      ]
    },
    {
      "tagCondition": {
        "key": "priority",
        "value": "high"
      }
    }
  ]
}

https://docs.aws.amazon.com/ja_jp/connect/latest/APIReference/API_amazon-q-connect_TagFilter.html

https://awscli.amazonaws.com/v2/documentation/api/latest/reference/qconnect/update-session.html

動作検証

Amazon Q in Connect コンテンツ検索して、タグフィルタリングの効果を検証します。

まずは、タグフィルタリングのない、通話していないとき、電話対応前で試します。

「オフィス情報を教えて」と質問すると、以下の通り、東京と札幌の2つのオフィス情報が確認できました。

日比谷本社オフィス:
〒105-0003
東京都港区西新橋1-1-1 日比谷フォートタワー26階

札幌オフィス:
〒060-0003
北海道札幌市中央区北3条西1-1-1 札幌ブリックキューブ10階

アクセス:
・JR札幌駅 南口東改札口から徒歩7分
・地下鉄東豊線/南北線 さっぽろ駅 21番出口から徒歩1分
・地下鉄東西線 大通駅から徒歩7分

cm-hirai-screenshot 2025-10-22 11.10.23

対して、チャットで先程のConnectフローでチャットを行い、チャット中に「オフィス情報を教えて」と質問すると、以下の通り、札幌のみのオフィス情報が確認できました。

札幌オフィスは札幌ブリックキューブ10階にあります。住所は〒060-0003 北海道札幌市中央区北3条西1-1-1です。

アクセス方法:
・JR札幌駅 南口東改札口から徒歩7分
・地下鉄東豊線/南北線 さっぽろ駅21番出口から徒歩1分
・地下鉄東西線 大通駅から徒歩7分

コンテンツセグメンテーションが機能していることがわかります。

cm-hirai-screenshot 2025-10-22 11.10.41

最後に

本記事では、Amazon Q in Connectにおける「コンテンツセグメンテーション」機能を使い、ServiceNowをナレッジベースとして、特定タグを持つドキュメントだけをナレッジ検索の対象にする方法を紹介しました。

コンテンツセグメンテーションを活用することで、顧客の問い合わせ内容に応じて関連性の高い情報のみを検索対象とし、回答精度の向上と応答時間の短縮が実現できます。

課題として、現在はナレッジ記事単位でのタグ付けが必要なため、大量のコンテンツを扱う場合は自動化スクリプトの導入が推奨されます。

この記事をシェアする

FacebookHatena blogX

関連記事