Расширьте возможности Amazon Lex с помощью LLM и улучшите работу с часто задаваемыми вопросами с помощью приема URL | Веб-сервисы Амазонки

Расширьте возможности Amazon Lex с помощью LLM и улучшите работу с часто задаваемыми вопросами с помощью приема URL | Веб-сервисы Амазонки

В современном цифровом мире большинство потребителей предпочитают самостоятельно находить ответы на свои вопросы по обслуживанию клиентов, а не тратить время на обращение к предприятиям и/или поставщикам услуг. В этом сообщении блога рассматривается инновационное решение для создания чат-бота вопросов и ответов в Amazon Lex который использует существующие часто задаваемые вопросы с вашего веб-сайта. Этот инструмент на базе искусственного интеллекта может обеспечить быстрые и точные ответы на реальные запросы, позволяя клиенту быстро и легко решать распространенные проблемы самостоятельно.

Прием одного URL-адреса

Многие предприятия имеют опубликованный набор ответов на часто задаваемые вопросы для своих клиентов, доступный на их веб-сайте. В этом случае мы хотим предложить клиентам чат-бота, который может ответить на их вопросы из наших опубликованных часто задаваемых вопросов. В сообщении блога под названием Расширьте возможности Amazon Lex с помощью диалоговых функций часто задаваемых вопросов с помощью LLM, мы продемонстрировали, как можно использовать комбинацию Amazon Lex и LlamaIndex для создания чат-бота на основе существующих источников знаний, таких как документы PDF или Word. Чтобы поддерживать простой FAQ, основанный на веб-сайте часто задаваемых вопросов, нам необходимо создать процесс загрузки, который может сканировать веб-сайт и создавать вложения, которые LlamaIndex может использовать для ответов на вопросы клиентов. В данном случае мы будем опираться на бота, созданного в Предыдущее сообщение в блоге, который запрашивает эти вложения с высказыванием пользователя и возвращает ответ из часто задаваемых вопросов веб-сайта.

На следующей диаграмме показано, как процесс загрузки и бот Amazon Lex работают вместе для нашего решения.

Расширьте возможности Amazon Lex с помощью программ LLM и улучшите качество часто задаваемых вопросов с помощью приема URL-адресов | Веб-сервисы Amazon PlatoBlockchain Data Intelligence. Вертикальный поиск. Ай.

В рабочем процессе решения веб-сайт с часто задаваемыми вопросами загружается через AWS Lambda. Эта функция Lambda сканирует веб-сайт и сохраняет полученный текст в Простой сервис хранения Amazon (Amazon S3) ведро. Затем корзина S3 запускает функцию Lambda, которая использует LlamaIndex для создания вложений, которые хранятся в Amazon S3. Когда от конечного пользователя поступает вопрос, например «Какова ваша политика возврата?», бот Amazon Lex использует свою функцию Lambda для запроса вложений, используя подход на основе RAG с LlamaIndex. Дополнительные сведения об этом подходе и предварительных требованиях см. в сообщении блога, Расширьте возможности Amazon Lex с помощью диалоговых функций часто задаваемых вопросов с помощью LLM.

После того, как предварительные условия из вышеупомянутого блога выполнены, первым шагом будет добавление часто задаваемых вопросов в репозиторий документов, который может быть векторизован и проиндексирован 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)

В предыдущем примере мы берем предопределенный URL веб-сайта с часто задаваемыми вопросами от Zappos и загружаем его с помощью EZWebLoader сорт. С помощью этого класса мы перешли по URL-адресу и загрузили все вопросы, которые есть на странице, в индекс. Теперь мы можем задать вопрос вроде «Есть ли у Zappos подарочные карты?» и получите ответы прямо из часто задаваемых вопросов на веб-сайте. На следующем снимке экрана показана тестовая консоль бота Amazon Lex, отвечающая на этот вопрос из часто задаваемых вопросов.

Расширьте возможности Amazon Lex с помощью программ LLM и улучшите качество часто задаваемых вопросов с помощью приема URL-адресов | Веб-сервисы Amazon PlatoBlockchain Data Intelligence. Вертикальный поиск. Ай.

Нам удалось добиться этого, потому что мы просканировали 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, если мы зададим вопрос «Есть ли у вас политика соответствия цен?», мы получим менее чем удовлетворительный ответ, как показано на следующем снимке экрана.

Расширьте возможности Amazon Lex с помощью программ LLM и улучшите качество часто задаваемых вопросов с помощью приема URL-адресов | Веб-сервисы Amazon PlatoBlockchain Data Intelligence. Вертикальный поиск. Ай.

В предыдущем диалоге ответ о правилах сопоставления цен бесполезен для нашего пользователя. Этот ответ короткий, потому что ссылка на часто задаваемые вопросы является ссылкой на конкретную страницу о политике сопоставления цен, а наше веб-сканирование было только для одной страницы. Чтобы получить более качественные ответы, необходимо также сканировать эти ссылки. В следующем разделе показано, как получить ответы на вопросы, требующие двух или более уровней глубины страницы.

Сканирование N-уровня

Когда мы сканируем веб-страницу в поисках часто задаваемых вопросов, нужная нам информация может содержаться на связанных страницах. Например, в нашем примере с Zappos мы задаем вопрос «Есть ли у вас политика соответствия цен?» и ответ: «Да, пожалуйста, посетите Узнать больше." Если кто-то спросит: «Какова ваша политика соответствия цен?» тогда мы хотим дать полный ответ с политикой. Достижение этого означает, что нам необходимо пройти по ссылкам, чтобы получить актуальную информацию для нашего конечного пользователя. В процессе загрузки мы можем использовать наш веб-загрузчик, чтобы найти якорные ссылки на другие 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 нашего бота осталась прежней, потому что ничего не изменилось, за исключением того, что краулер загружает больше документов.

Теперь у нас есть все документы, проиндексированные, и мы можем задать более подробный вопрос. На следующем снимке экрана наш бот дает правильный ответ на вопрос «Есть ли у вас политика сопоставления цен?»

Расширьте возможности Amazon Lex с помощью программ LLM и улучшите качество часто задаваемых вопросов с помощью приема URL-адресов | Веб-сервисы Amazon PlatoBlockchain Data Intelligence. Вертикальный поиск. Ай.

Теперь у нас есть полный ответ на наш вопрос о сопоставлении цен. Вместо того, чтобы просто сказать «Да, смотрите нашу политику», он предоставляет нам детали сканирования второго уровня.

Убирать

Чтобы избежать будущих расходов, продолжите удаление всех ресурсов, которые были развернуты в рамках этого упражнения. Мы предоставили сценарий для корректного закрытия конечной точки Sagemaker. Подробности использования в README. Кроме того, чтобы удалить все другие ресурсы, которые вы можете запустить cdk destroy в том же каталоге, что и другие команды cdk, чтобы отменить все ресурсы в вашем стеке.

Заключение

Возможность загрузить набор часто задаваемых вопросов в чат-бот позволяет вашим клиентам находить ответы на свои вопросы с помощью простых запросов на естественном языке. Объединив встроенную в Amazon Lex поддержку резервной обработки с решением RAG, таким как LlamaIndex, мы можем предоставить нашим клиентам быстрый способ получить удовлетворительные, проверенные и утвержденные ответы на часто задаваемые вопросы. Применяя сканирование N-уровня в нашем решении, мы можем разрешить ответы, которые могут охватывать несколько ссылок на часто задаваемые вопросы, и предоставить более подробные ответы на запросы наших клиентов. Следуя этим шагам, вы сможете беспрепятственно интегрировать мощные возможности вопросов и ответов на основе LLM и эффективный прием URL-адресов в чат-бота Amazon Lex. Это приводит к более точному, всестороннему и контекстно-зависимому взаимодействию с пользователями.


Об авторах

Расширьте возможности Amazon Lex с помощью программ LLM и улучшите качество часто задаваемых вопросов с помощью приема URL-адресов | Веб-сервисы Amazon PlatoBlockchain Data Intelligence. Вертикальный поиск. Ай.Макс Хенкель-Уоллес работает инженером по разработке программного обеспечения в AWS Lex. Ему нравится работать, используя технологии для максимального успеха клиентов. Вне работы он страстно любит готовить, проводить время с друзьями и ходить в походы.

Расширьте возможности Amazon Lex с помощью программ LLM и улучшите качество часто задаваемых вопросов с помощью приема URL-адресов | Веб-сервисы Amazon PlatoBlockchain Data Intelligence. Вертикальный поиск. Ай.Сун Фэн — старший научный сотрудник AWS AI Labs, специализирующийся на обработке естественного языка и искусственном интеллекте. В ее исследованиях рассматриваются различные аспекты этих областей, включая моделирование диалогов на основе документов, обоснование диалогов, ориентированных на выполнение задач, и создание интерактивного текста с использованием мультимодальных данных.

Расширьте возможности Amazon Lex с помощью программ LLM и улучшите качество часто задаваемых вопросов с помощью приема URL-адресов | Веб-сервисы Amazon PlatoBlockchain Data Intelligence. Вертикальный поиск. Ай.Джон Бейкер является руководителем SDE в AWS, где он работает над обработкой естественного языка, большими языковыми моделями и другими проектами, связанными с ML/AI. Он работает в Amazon более 9 лет и работал с AWS, Alexa и Amazon.com. В свободное время Джон любит кататься на лыжах и заниматься другими видами активного отдыха на северо-западе Тихого океана.

Отметка времени:

Больше от Машинное обучение AWS