Aprimore o Amazon Lex com LLMs e melhore a experiência de perguntas frequentes usando a ingestão de URL | Amazon Web Services

Aprimore o Amazon Lex com LLMs e melhore a experiência de perguntas frequentes usando a ingestão de URL | Amazon Web Services

No mundo digital de hoje, a maioria dos consumidores prefere encontrar respostas para suas perguntas de atendimento ao cliente por conta própria, em vez de perder tempo entrando em contato com empresas e/ou provedores de serviços. Esta postagem do blog explora uma solução inovadora para criar um chatbot de perguntas e respostas em Amazon-Lex que usa as perguntas frequentes existentes em seu site. Essa ferramenta com tecnologia de IA pode fornecer respostas rápidas e precisas para consultas do mundo real, permitindo que o cliente resolva problemas comuns com rapidez e facilidade de forma independente.

Processamento de URL único

Muitas empresas têm um conjunto publicado de respostas para perguntas frequentes para seus clientes disponíveis em seu site. Nesse caso, queremos oferecer aos clientes um chatbot que possa responder às suas perguntas de nossas perguntas frequentes publicadas. Na postagem do blog intitulada Aprimore o Amazon Lex com recursos de perguntas frequentes conversacionais usando LLMs, demonstramos como você pode usar uma combinação de Amazon Lex e LlamaIndex para criar um chatbot baseado em suas fontes de conhecimento existentes, como PDF ou documentos do Word. Para oferecer suporte a um FAQ simples, baseado em um site de FAQs, precisamos criar um processo de ingestão que possa rastrear o site e criar incorporações que possam ser usadas pelo LlamaIndex para responder às perguntas dos clientes. Neste caso, vamos construir no bot criado no Postagem de blog anterior, que consulta essas incorporações com o enunciado de um usuário e retorna a resposta das perguntas frequentes do site.

O diagrama a seguir mostra como o processo de ingestão e o bot do Amazon Lex funcionam juntos para nossa solução.

Aprimore o Amazon Lex com LLMs e melhore a experiência de perguntas frequentes usando ingestão de URL | Inteligência de dados PlatoBlockchain da Amazon Web Services. Pesquisa vertical. Ai.

No fluxo de trabalho da solução, o site com perguntas frequentes é ingerido por meio de AWS Lambda. Essa função do Lambda rastreia o site e armazena o texto resultante em um Serviço de armazenamento simples da Amazon (Amazon S3). O bucket S3 aciona uma função Lambda que usa LlamaIndex para criar embeddings que são armazenados no Amazon S3. Quando chega uma pergunta de um usuário final, como “Qual é a sua política de devolução?”, o bot do Amazon Lex usa sua função Lambda para consultar as incorporações usando uma abordagem baseada em RAG com LlamaIndex. Para obter mais informações sobre essa abordagem e os pré-requisitos, consulte a postagem do blog, Aprimore o Amazon Lex com recursos de perguntas frequentes conversacionais usando LLMs.

Depois de concluídos os pré-requisitos do blog mencionado acima, o primeiro passo é inserir as perguntas frequentes em um repositório de documentos que pode ser vetorizado e indexado pelo LlamaIndex. O código a seguir mostra como fazer isso:

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)

No exemplo anterior, pegamos um URL de site de perguntas frequentes predefinido da Zappos e o ingerimos usando o EZWebLoader aula. Com esta classe, navegamos até a URL e carregamos todas as perguntas que estão na página em um índice. Agora podemos fazer uma pergunta como “A Zappos tem vales-presente?” e obtenha as respostas diretamente de nossas perguntas frequentes no site. A captura de tela a seguir mostra o console de teste de bot do Amazon Lex respondendo a essa pergunta das perguntas frequentes.

Aprimore o Amazon Lex com LLMs e melhore a experiência de perguntas frequentes usando ingestão de URL | Inteligência de dados PlatoBlockchain da Amazon Web Services. Pesquisa vertical. Ai.

Conseguimos isso porque rastreamos a URL na primeira etapa e criamos embeddings que o LlamaIndex poderia usar para procurar a resposta para nossa pergunta. A função Lambda do nosso bot mostra como essa pesquisa é executada sempre que a intenção de fallback é retornada:

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

Essa solução funciona bem quando uma única página da Web contém todas as respostas. No entanto, a maioria dos sites de FAQ não são construídos em uma única página. Por exemplo, em nosso exemplo da Zappos, se fizermos a pergunta “Você tem uma política de equiparação de preços?”, obteremos uma resposta insatisfatória, conforme mostrado na captura de tela a seguir.

Aprimore o Amazon Lex com LLMs e melhore a experiência de perguntas frequentes usando ingestão de URL | Inteligência de dados PlatoBlockchain da Amazon Web Services. Pesquisa vertical. Ai.

Na interação anterior, a resposta da política de equiparação de preços não é útil para nosso usuário. Esta resposta é curta porque o FAQ referenciado é um link para uma página específica sobre a política de equiparação de preços e nosso rastreamento da web foi apenas para uma única página. Obter melhores respostas significa rastrear esses links também. A próxima seção mostra como obter respostas para perguntas que exigem dois ou mais níveis de profundidade de página.

Rastreamento em nível N

Quando rastreamos uma página da Web para obter conhecimento de perguntas frequentes, as informações que desejamos podem estar contidas em páginas vinculadas. Por exemplo, em nosso exemplo da Zappos, fazemos a pergunta “Você tem uma política de equiparação de preços?” e a resposta é “Sim, visite aprender mais." Se alguém perguntar "Qual é a sua política de equiparação de preços?" então queremos dar uma resposta completa com a política. Conseguir isso significa que temos a necessidade de percorrer os links para obter as informações reais para o nosso usuário final. Durante o processo de ingestão, podemos usar nosso carregador da Web para localizar os links de âncora para outras páginas HTML e atravessá-los. A alteração de código a seguir em nosso rastreador da web nos permite encontrar links nas páginas que rastreamos. Ele também inclui alguma lógica adicional para evitar rastreamento circular e permitir um filtro por um prefixo.

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)

No código anterior, apresentamos a capacidade de rastrear N níveis de profundidade e fornecemos um prefixo que nos permite restringir o rastreamento apenas a coisas que começam com um determinado padrão de URL. Em nosso exemplo da Zappos, todas as páginas de atendimento ao cliente são originadas de zappos.com/c, portanto, incluímos isso como um prefixo para limitar nossos rastreamentos a um subconjunto menor e mais relevante. O código mostra como podemos ingerir até dois níveis de profundidade. A lógica do Lambda do nosso bot permanece a mesma porque nada mudou, exceto que o rastreador ingere mais documentos.

Agora temos todos os documentos indexados e podemos fazer uma pergunta mais detalhada. Na captura de tela a seguir, nosso bot fornece a resposta correta para a pergunta “Você tem uma política de equiparação de preços?”

Aprimore o Amazon Lex com LLMs e melhore a experiência de perguntas frequentes usando ingestão de URL | Inteligência de dados PlatoBlockchain da Amazon Web Services. Pesquisa vertical. Ai.

Agora temos uma resposta completa para nossa pergunta sobre correspondência de preços. Em vez de simplesmente ouvir “Sim, veja nossa política”, ele nos fornece os detalhes do rastreamento de segundo nível.

limpar

Para evitar despesas futuras, prossiga com a exclusão de todos os recursos que foram implantados como parte deste exercício. Fornecemos um script para encerrar o endpoint do Sagemaker normalmente. Os detalhes de uso estão no README. Além disso, para remover todos os outros recursos, você pode executar cdk destroy no mesmo diretório que os outros comandos cdk para desprovisionar todos os recursos em sua pilha.

Conclusão

A capacidade de inserir um conjunto de perguntas frequentes em um chatbot permite que seus clientes encontrem as respostas para suas perguntas com consultas diretas e em linguagem natural. Ao combinar o suporte integrado no Amazon Lex para tratamento de fallback com uma solução RAG, como um LlamaIndex, podemos fornecer um caminho rápido para nossos clientes obterem respostas satisfatórias, selecionadas e aprovadas para perguntas frequentes. Ao aplicar o rastreamento de nível N em nossa solução, podemos permitir respostas que podem abranger vários links de perguntas frequentes e fornecer respostas mais profundas às consultas de nossos clientes. Seguindo essas etapas, você pode incorporar facilmente recursos avançados de perguntas e respostas baseados em LLM e processamento eficiente de URLs em seu chatbot do Amazon Lex. Isso resulta em interações mais precisas, abrangentes e contextualmente conscientes com os usuários.


Sobre os autores

Aprimore o Amazon Lex com LLMs e melhore a experiência de perguntas frequentes usando ingestão de URL | Inteligência de dados PlatoBlockchain da Amazon Web Services. Pesquisa vertical. Ai.Max Henkel Wallace é um engenheiro de desenvolvimento de software na AWS Lex. Ele gosta de trabalhar aproveitando a tecnologia para maximizar o sucesso do cliente. Fora do trabalho, ele adora cozinhar, passar tempo com os amigos e fazer mochilas.

Aprimore o Amazon Lex com LLMs e melhore a experiência de perguntas frequentes usando ingestão de URL | Inteligência de dados PlatoBlockchain da Amazon Web Services. Pesquisa vertical. Ai.Canção Feng é um cientista aplicado sênior no AWS AI Labs, especializado em processamento de linguagem natural e inteligência artificial. Sua pesquisa explora vários aspectos desses campos, incluindo modelagem de diálogo baseada em documentos, raciocínio para diálogos orientados a tarefas e geração de texto interativo usando dados multimodais.

Aprimore o Amazon Lex com LLMs e melhore a experiência de perguntas frequentes usando ingestão de URL | Inteligência de dados PlatoBlockchain da Amazon Web Services. Pesquisa vertical. Ai.John Baker é SDE principal na AWS, onde trabalha em processamento de linguagem natural, modelos de linguagem grandes e outros projetos relacionados a ML/IA. Ele está na Amazon há mais de 9 anos e trabalhou na AWS, Alexa e Amazon.com. Em seu tempo livre, John gosta de esquiar e outras atividades ao ar livre em todo o noroeste do Pacífico.

Carimbo de hora:

Mais de Aprendizado de máquina da AWS