通过 LLM 增强 Amazon Lex 并使用 URL 提取改善常见问题解答体验 | 亚马逊网络服务

通过 LLM 增强 Amazon Lex 并使用 URL 提取改善常见问题解答体验 | 亚马逊网络服务

在当今的数字世界中,大多数消费者宁愿自己寻找客户服务问题的答案,也不愿花时间与企业和/或服务提供商联系。 这篇博文探讨了一种创新的解决方案,用于构建问答聊天机器人 亚马逊Lex 使用您网站上的现有常见问题解答。 这种人工智能驱动的工具可以对现实世界的询问提供快速、准确的响应,使客户能够快速、轻松地独立解决常见问题。

单个 URL 摄取

许多企业在其网站上为客户提供了一组已发布的常见问题解答。 在这种情况下,我们希望为客户提供一个聊天机器人,可以回答我们发布的常见问题解答中的问题。 在标题为 使用 LLM 增强 Amazon Lex 的对话式常见问题解答功能中,我们演示了如何结合使用 Amazon Lex 和 LlamaIndex 来构建由现有知识源(例如 PDF 或 Word 文档)提供支持的聊天机器人。 为了支持简单的常见问题解答,基于常见问题解答网站,我们需要创建一个摄取流程,可以抓取网站并创建 LlamaIndex 可以使用的嵌入来回答客户问题。 在这种情况下,我们将在创建的机器人的基础上进行构建 以前的博文,它用用户的话语查询这些嵌入,并从网站常见问题解答中返回答案。

下图显示了提取过程和 Amazon Lex 机器人如何协同工作以实现我们的解决方案。

通过 LLM 增强 Amazon Lex 并使用 URL 提取改善常见问题解答体验 |亚马逊网络服务柏拉图区块链数据智能。垂直搜索。人工智能。

在解决方案工作流程中,包含常见问题解答的网站通过 AWS Lambda。 此 Lambda 函数抓取网站并将结果文本存储在 亚马逊简单存储服务 (亚马逊 S3)存储桶。 然后,S3 存储桶会触发 Lambda 函数,该函数使用 LlamaIndex 创建存储在 Amazon S3 中的嵌入。 当最终用户提出问题(例如“您的退货政策是什么?”)时,Amazon Lex 机器人会使用其 Lambda 函数,通过基于 RAG 的方法和 LlamaIndex 来查询嵌入。 有关此方法和先决条件的更多信息,请参阅博客文章: 使用 LLM 增强 Amazon Lex 的对话式常见问题解答功能.

完成上述博客的先决条件后,第一步是将常见问题解答提取到可以由 LlamaIndex 进行矢量化和索引的文档存储库中。 以下代码显示了如何完成此操作:

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 获取预定义的常见问题解答网站 URL,并使用 EZWebLoader 班级。 通过这个类,我们导航到 URL 并将页面中的所有问题加载到索引中。 我们现在可以问“Zappos 有礼品卡吗?”之类的问题。 并直接从我们网站上的常见问题解答中获取答案。 以下屏幕截图显示了 Amazon Lex 机器人测试控制台回答常见问题解答中的该问题。

通过 LLM 增强 Amazon Lex 并使用 URL 提取改善常见问题解答体验 |亚马逊网络服务柏拉图区块链数据智能。垂直搜索。人工智能。

我们能够实现这一目标是因为我们在第一步中抓取了 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

当单个网页包含所有答案时,此解决方案效果很好。 然而,大多数常见问题解答网站都不是构建在单个页面上的。 例如,在我们的 Zappos 示例中,如果我们提出问题“你们有价格匹配政策吗?”,那么我们会得到一个不太令人满意的答案,如下面的屏幕截图所示。

通过 LLM 增强 Amazon Lex 并使用 URL 提取改善常见问题解答体验 |亚马逊网络服务柏拉图区块链数据智能。垂直搜索。人工智能。

在前面的交互中,价格匹配策略答案对我们的用户没有帮助。 这个答案很短,因为引用的常见问题解答是指向有关价格匹配政策的特定页面的链接,而我们的网络爬行仅针对单个页面。 获得更好的答案也意味着抓取这些链接。 下一节将介绍如何获得需要两级或更多级别页面深度的问题的答案。

N级爬取

当我们抓取网页获取FAQ知识时,我们想要的信息就可以包含在链接的页面中。 例如,在我们的 Zappos 示例中,我们提出问题“你们有价格匹配政策吗?” 答案是“是的,请访问了解更多。” 如果有人问“你们的价格匹配政策是什么?” 那么我们想用政策给出一个完整的答案。 实现这一目标意味着我们需要遍历链接来为最终用户获取实际信息。 在摄取过程中,我们可以使用 Web 加载器查找其他 HTML 页面的锚链接,然后遍历它们。 对我们的网络爬虫进行以下代码更改使我们能够在爬行的页面中查找链接。 它还包括一些额外的逻辑,以避免循环爬行并允许按前缀过滤。

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,因此我们将其作为前缀,以将我们的爬网限制为更小且更相关的子集。 该代码展示了我们如何摄取最多两层的深度。 我们的机器人的 Lambda 逻辑保持不变,因为除了爬虫摄取更多文档之外没有任何变化。

现在我们已经对所有文档建立了索引,我们可以提出更详细的问题。 在下面的屏幕截图中,我们的机器人提供了“你们有价格匹配政策吗?”问题的正确答案。

通过 LLM 增强 Amazon Lex 并使用 URL 提取改善常见问题解答体验 |亚马逊网络服务柏拉图区块链数据智能。垂直搜索。人工智能。

现在我们对价格匹配的问题有了完整的答案。 它不是简单地告诉我们“是的,请参阅我们的政策”,而是为我们提供第二级爬网的详细信息。

清理

为了避免产生未来费用,请继续删除在此练习中部署的所有资源。 我们提供了一个脚本来正常关闭 Sagemaker 端点。 使用详细信息位于自述文件中。 此外,要删除您可以运行的所有其他资源 cdk destroy 与其他 cdk 命令位于同一目录中,以取消配置堆栈中的所有资源。

结论

将一组常见问题解答引入聊天机器人的功能使您的客户能够通过简单的自然语言查询找到问题的答案。 通过将 Amazon Lex 中对回退处理的内置支持与 LlamaIndex 等 RAG 解决方案相结合,我们可以为客户提供一条快速路径,以获得令人满意的、精心策划且经过批准的常见问题解答。 通过将 N 级爬行应用到我们的解决方案中,我们可以提供可能跨越多个常见问题解答链接的答案,并为客户的查询提供更深入的答案。 通过执行这些步骤,您可以将强大的基于 LLM 的问答功能和高效的 URL 提取无缝集成到您的 Amazon Lex 聊天机器人中。 这使得与用户的交互更加准确、全面且具有情境意识。


关于作者

通过 LLM 增强 Amazon Lex 并使用 URL 提取改善常见问题解答体验 |亚马逊网络服务柏拉图区块链数据智能。垂直搜索。人工智能。马克斯·汉克尔-华莱士 是 AWS Lex 的软件开发工程师。 他喜欢利用技术来最大限度地提高客户的成功。 工作之余,他热衷于烹饪、与朋友共度时光以及背包旅行。

通过 LLM 增强 Amazon Lex 并使用 URL 提取改善常见问题解答体验 |亚马逊网络服务柏拉图区块链数据智能。垂直搜索。人工智能。宋峰 是 AWS AI 实验室的高级应用科学家,专门研究自然语言处理和人工智能。 她的研究探索了这些领域的各个方面,包括基于文档的对话建模、面向任务的对话推理以及使用多模式数据的交互式文本生成。

通过 LLM 增强 Amazon Lex 并使用 URL 提取改善常见问题解答体验 |亚马逊网络服务柏拉图区块链数据智能。垂直搜索。人工智能。约翰贝克 是 AWS 的首席 SDE,负责自然语言处理、大型语言模型和其他 ML/AI 相关项目。 他已在 Amazon 工作 9 年多,曾在 AWS、Alexa 和 Amazon.com 工作过。 在业余时间,约翰喜欢在太平洋西北地区滑雪和进行其他户外活动。

时间戳记:

更多来自 AWS机器学习