LLM で Amazon Lex を強化し、URL 取り込みを使用して FAQ エクスペリエンスを改善する | アマゾン ウェブ サービス

LLM で Amazon Lex を強化し、URL 取り込みを使用して FAQ エクスペリエンスを改善する | アマゾン ウェブ サービス

今日のデジタル世界では、ほとんどの消費者は、時間をかけて企業やサービスプロバイダーに連絡するよりも、顧客サービスの質問に対する答えを自分で見つけることを望んでいます。 このブログ投稿では、質問と回答のチャットボットを構築するための革新的なソリューションを検討します。 Amazon Lex Web サイトの既存の FAQ を使用します。 この AI を活用したツールは、現実世界の問い合わせに対して迅速かつ正確に応答できるため、顧客は一般的な問題を独自に迅速かつ簡単に解決できます。

単一 URL の取り込み

多くの企業は、顧客向けの FAQ に対する一連の回答を Web サイトで公開しており、公開しています。 この場合、公開されている FAQ からの質問に回答できるチャットボットを顧客に提供したいと考えています。 というタイトルのブログ投稿で LLM を使用した会話型 FAQ 機能で Amazon Lex を強化するでは、Amazon Lex と LlamaIndex を組み合わせて、PDF や Word ドキュメントなどの既存のナレッジソースを活用したチャットボットを構築する方法を説明しました。 FAQ の Web サイトに基づいて簡単な FAQ をサポートするには、Web サイトをクロールし、LlamaIndex が顧客の質問に答えるために使用できる埋め込みを作成できる取り込みプロセスを作成する必要があります。 この場合、で作成したボットをベースに構築します。 前のブログ投稿、ユーザーの発話でこれらの埋め込みをクエリし、Web サイトの FAQ から回答を返します。

次の図は、ソリューションのために取り込みプロセスと Amazon Lex ボットがどのように連携するかを示しています。

LLM で Amazon Lex を強化し、URL 取り込みを使用して FAQ エクスペリエンスを改善する |アマゾン ウェブ サービス PlatoBlockchain データ インテリジェンス。垂直検索。あい。

ソリューション ワークフローでは、FAQ を含む Web サイトが次の方法で取り込まれます。 AWSラムダ。 この Lambda 関数は Web サイトをクロールし、結果のテキストを Amazon シンプル ストレージ サービス (Amazon S3) バケット。 次に、S3 バケットは、LlamaIndex を使用して Amazon S3 に保存されるエンベディングを作成する Lambda 関数をトリガーします。 「返品ポリシーは何ですか?」などのエンドユーザーからの質問が届くと、Amazon Lex ボットは Lambda 関数を使用して、LlamaIndex による RAG ベースのアプローチを使用してエンベディングをクエリします。 このアプローチと前提条件の詳細については、ブログ投稿を参照してください。 LLM を使用した会話型 FAQ 機能で Amazon Lex を強化する.

前述のブログの前提条件が完了したら、最初のステップとして、LlamaIndex によってベクトル化およびインデックス付けできるドキュメント リポジトリに FAQ を取り込みます。 次のコードは、これを実現する方法を示しています。

import logging
import sys
import requests
import html2text
from llama_index.readers.schema.base import Document
from llama_index import GPTVectorStoreIndex
from typing import List logging.basicConfig(stream=sys.stdout, level=logging.INFO)
logging.getLogger().addHandler(logging.StreamHandler(stream=sys.stdout)) class EZWebLoader: def __init__(self, default_header: str = None): self._html_to_text_parser = html2text() if default_header is None: self._default_header = {"User-agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.80 Safari/537.36"} else: self._default_header = default_header def load_data(self, urls: List[str], headers: str = None) -> List[Document]: if headers is None: headers = self._default_header documents = [] for url in urls: response = requests.get(url, headers=headers).text response = self._html2text.html2text(response) documents.append(Document(response)) return documents url = "http://www.zappos.com/general-questions"
loader = EZWebLoader()
documents = loader.load_data([url])
index = GPTVectorStoreIndex.from_documents(documents)

前の例では、Zappos から事前定義された FAQ Web サイトの URL を取得し、 EZWebLoader クラス。 このクラスでは、URL に移動し、ページ内のすべての質問をインデックスにロードしました。 「Zappos にはギフトカードがありますか?」のような質問ができるようになりました。 ウェブサイトのよくある質問から直接答えを入手してください。 次のスクリーンショットは、よくある質問からの質問に答える Amazon Lex ボット テスト コンソールを示しています。

LLM で Amazon Lex を強化し、URL 取り込みを使用して FAQ エクスペリエンスを改善する |アマゾン ウェブ サービス PlatoBlockchain データ インテリジェンス。垂直検索。あい。

これを達成できたのは、最初のステップで URL をクロールし、LlamaIndex が質問に対する答えを検索するために使用できる埋め込みを作成したからです。 ボットの Lambda 関数は、フォールバック インテントが返されるたびにこの検索がどのように実行されるかを示します。

import time
import json
import os
import logging
import boto3
from llama_index import StorageContext, load_index_from_storage logger = logging.getLogger()
logger.setLevel(logging.DEBUG) def download_docstore(): # Create an S3 client s3 = boto3.client('s3') # List all objects in the S3 bucket and download each one
try: bucket_name = 'faq-bot-storage-001' s3_response = s3.list_objects_v2(Bucket=bucket_name) if 'Contents' in s3_response: for item in s3_response['Contents']: file_name = item['Key'] logger.debug("Downloading to /tmp/" + file_name) s3.download_file(bucket_name, file_name, '/tmp/' + file_name) logger.debug('All files downloaded from S3 and written to local filesystem.') except Exception as e: logger.error(e)
raise e #download the doc store locally
download_docstore() storage_context = StorageContext.from_defaults(persist_dir="/tmp/")
# load index
index = load_index_from_storage(storage_context)
query_engine = index.as_query_engine() def lambda_handler(event, context): """
Route the incoming request based on intent.
The JSON body of the request is provided in the event slot. """ # By default, treat the user request as coming from the America/New_York time zone. os.environ['TZ'] = 'America/New_York' time.tzset() logger.debug("===== START LEX FULFILLMENT ====") logger.debug(event) slots = {} if "currentIntent" in event and "slots" in event["currentIntent"]: slots = event["currentIntent"]["slots"] intent = event["sessionState"]["intent"] dialogaction = {"type": "Delegate"} message = [] if str.lower(intent["name"]) == "fallbackintent": #execute query from the input given by the user response = str.strip(query_engine.query(event["inputTranscript"]).response) dialogaction["type"] = "Close" message.append({'content': f'{response}', 'contentType': 'PlainText'}) final_response = { "sessionState": { "dialogAction": dialogaction, "intent": intent }, "messages": message } logger.debug(json.dumps(final_response, indent=1)) logger.debug("===== END LEX FULFILLMENT ====") return final_response

このソリューションは、XNUMX つの Web ページにすべての答えがある場合にうまく機能します。 ただし、ほとんどの FAQ サイトは単一のページで構築されていません。 たとえば、Zappos の例では、「価格一致ポリシーはありますか?」という質問をすると、次のスクリーンショットに示すように、満足のいく答えとは言えません。

LLM で Amazon Lex を強化し、URL 取り込みを使用して FAQ エクスペリエンスを改善する |アマゾン ウェブ サービス PlatoBlockchain データ インテリジェンス。垂直検索。あい。

前述の対話では、価格一致ポリシーの回答はユーザーにとって役に立ちません。 参照されている FAQ は価格一致ポリシーに関する特定のページへのリンクであり、Web クロールは XNUMX つのページのみを対象としたものであるため、この回答は短いです。 より良い答えを得るには、これらのリンクもクロールする必要があります。 次のセクションでは、XNUMX レベル以上のページの深さを必要とする質問への回答を得る方法を示します。

Nレベルのクローリング

FAQ の知識を求めて Web ページをクロールすると、リンクされたページに必要な情報が含まれていることがあります。 たとえば、Zappos の例では、「価格一致ポリシーはありますか?」という質問をします。 答えは「はい、訪問してください」です。 詳しく知ることができ。" 「あなたの価格一致ポリシーは何ですか?」と誰かが尋ねたら、 次に、ポリシーで完全な答えを出したいと思います。 これを達成するには、リンクをたどってエンドユーザーに実際の情報を取得する必要があることを意味します。 取り込みプロセス中に、Web ローダーを使用して他の HTML ページへのアンカー リンクを見つけ、それらのページを横断することができます。 Web クローラーに対する次のコード変更により、クロールするページ内のリンクを見つけることができるようになります。 また、循環クロールを回避し、プレフィックスによるフィルターを許可するための追加ロジックも含まれています。

import logging
import requests
import html2text
from llama_index.readers.schema.base import Document
from typing import List
import re def find_http_urls_in_parentheses(s: str, prefix: str = None): pattern = r'((https?://[^)]+))' urls = re.findall(pattern, s) matched = [] if prefix is not None: for url in urls: if str(url).startswith(prefix): matched.append(url) else: matched = urls return list(set(matched)) # remove duplicates by converting to set, then convert back to list class EZWebLoader: def __init__(self, default_header: str = None): self._html_to_text_parser = html2text if default_header is None: self._default_header = {"User-agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.80 Safari/537.36"} else: self._default_header = default_header def load_data(self, urls: List[str], num_levels: int = 0, level_prefix: str = None, headers: str = None) -> List[Document]: logging.info(f"Number of urls: {len(urls)}.") if headers is None: headers = self._default_header documents = [] visited = {} for url in urls: q = [url] depth = num_levels for page in q: if page not in visited: #prevent cycles by checking to see if we already crawled a link logging.info(f"Crawling {page}") visited[page] = True #add entry to visited to prevent re-crawling pages response = requests.get(page, headers=headers).text response = self._html_to_text_parser.html2text(response) #reduce html to text documents.append(Document(response)) if depth > 0: #crawl linked pages ingest_urls = find_http_urls_in_parentheses(response, level_prefix) logging.info(f"Found {len(ingest_urls)} pages to crawl.") q.extend(ingest_urls) depth -= 1 #reduce the depth counter so we go only num_levels deep in our crawl else: logging.info(f"Skipping {page} as it has already been crawled") logging.info(f"Number of documents: {len(documents)}.") return documents url = "http://www.zappos.com/general-questions"
loader = EZWebLoader()
#crawl the site with 1 level depth and prefix of "/c/" for customer service root
documents = loader.load_data([url], num_levels=1, level_prefix="https://www.zappos.com/c/")
index = GPTVectorStoreIndex.from_documents(documents)

前述のコードでは、N レベルを深くクロールする機能を導入し、特定の URL パターンで始まるもののみにクロールを制限できるプレフィックスを与えています。 Zappos の例では、カスタマー サービス ページはすべて zappos.com/c, そのため、これをプレフィックスとして含めて、クロールをより小さく、より関連性の高いサブセットに制限します。 このコードは、最大 XNUMX レベルの深さまで取り込む方法を示しています。 クローラーがより多くのドキュメントを取り込むこと以外は何も変わっていないため、ボットの Lambda ロジックは同じままです。

すべての文書のインデックスが作成されたので、より詳細な質問をすることができます。 次のスクリーンショットでは、ボットは「価格一致ポリシーはありますか?」という質問に対して正しい答えを提供しています。

LLM で Amazon Lex を強化し、URL 取り込みを使用して FAQ エクスペリエンスを改善する |アマゾン ウェブ サービス PlatoBlockchain データ インテリジェンス。垂直検索。あい。

価格の一致に関する質問に対する完全な回答が得られました。 「はい、ポリシーをご覧ください」と単に言われるのではなく、第 XNUMX レベルのクロールから詳細が得られます。

クリーンアップ

将来の出費を避けるために、この演習の一部としてデプロイされたすべてのリソースの削除に進みます。 Sagemaker エンドポイントを正常にシャットダウンするためのスクリプトが提供されています。 使用方法の詳細は README に記載されています。 さらに、他のすべてのリソースを削除するには、次のコマンドを実行します。 cdk destroy スタック内のすべてのリソースをプロビジョニング解除するには、他の cdk コマンドと同じディレクトリにあります。

まとめ

一連の FAQ をチャットボットに取り込む機能により、顧客は簡単な自然言語のクエリで質問に対する答えを見つけることができます。 Amazon Lex に組み込まれたフォールバック処理のサポートと LlamaIndex などの RAG ソリューションを組み合わせることで、顧客が満足のいく、精選され、承認された FAQ の回答を迅速に得ることができます。 N レベルのクロールをソリューションに適用することで、複数の FAQ リンクにまたがる可能性のある回答を可能にし、顧客の質問に対してより深い回答を提供できるようになります。 これらの手順に従うことで、強力な LLM ベースの Q&A 機能と効率的な URL 取り込みを Amazon Lex チャットボットにシームレスに組み込むことができます。 これにより、より正確かつ包括的で、コンテキストを認識したユーザーとの対話が可能になります。


著者について

LLM で Amazon Lex を強化し、URL 取り込みを使用して FAQ エクスペリエンスを改善する |アマゾン ウェブ サービス PlatoBlockchain データ インテリジェンス。垂直検索。あい。マックス・ヘンケル=ウォレス は AWS Lex のソフトウェア開発エンジニアです。 彼はテクノロジーを活用して顧客の成功を最大化することに楽しんで取り組んでいます。 仕事以外では、料理をしたり、友人と時間を過ごしたり、バックパッキングに情熱を注いでいます。

LLM で Amazon Lex を強化し、URL 取り込みを使用して FAQ エクスペリエンスを改善する |アマゾン ウェブ サービス PlatoBlockchain データ インテリジェンス。垂直検索。あい。ソン・フェン AWS AI Labs の上級応用科学者であり、自然言語処理と人工知能を専門としています。 彼女の研究では、ドキュメントに基づいた対話モデリング、タスク指向の対話の推論、マルチモーダル データを使用した対話型テキスト生成など、これらの分野のさまざまな側面を調査しています。

LLM で Amazon Lex を強化し、URL 取り込みを使用して FAQ エクスペリエンスを改善する |アマゾン ウェブ サービス PlatoBlockchain データ インテリジェンス。垂直検索。あい。ジョン·ベイカー 彼は AWS のプリンシパル SDE であり、自然言語処理、大規模言語モデル、その他の ML/AI 関連プロジェクトに取り組んでいます。 彼は Amazon に 9 年以上勤務しており、AWS、Alexa、Amazon.com で働いてきました。 余暇には、ジョンは太平洋岸北西部全域でスキーやその他のアウトドア アクティビティを楽しんでいます。

タイムスタンプ:

より多くの AWS機械学習