Potenzia Amazon Lex con LLM e migliora l'esperienza delle domande frequenti utilizzando l'importazione di URL | Servizi Web Amazon

Potenzia Amazon Lex con LLM e migliora l'esperienza delle domande frequenti utilizzando l'importazione di URL | Servizi Web Amazon

Nel mondo digitale odierno, la maggior parte dei consumatori preferisce trovare da sola le risposte alle proprie domande sul servizio clienti piuttosto che dedicare del tempo a contattare aziende e/o fornitori di servizi. Questo post sul blog esplora una soluzione innovativa per creare un chatbot di domande e risposte Amazon-Lex che utilizza le FAQ esistenti dal tuo sito web. Questo strumento basato sull'intelligenza artificiale può fornire risposte rapide e accurate alle richieste del mondo reale, consentendo al cliente di risolvere rapidamente e facilmente problemi comuni in modo indipendente.

Importazione di un singolo URL

Molte aziende hanno pubblicato una serie di risposte alle domande frequenti per i loro clienti disponibili sul loro sito web. In questo caso, vogliamo offrire ai clienti un chatbot in grado di rispondere alle loro domande dalle nostre FAQ pubblicate. Nel post del blog intitolato Potenzia Amazon Lex con funzionalità di domande frequenti conversazionali utilizzando LLM, abbiamo dimostrato come puoi utilizzare una combinazione di Amazon Lex e LlamaIndex per creare un chatbot alimentato dalle tue fonti di conoscenza esistenti, come documenti PDF o Word. Per supportare una semplice FAQ, basata su un sito Web di FAQ, è necessario creare un processo di importazione in grado di eseguire la scansione del sito Web e creare incorporamenti che possono essere utilizzati da LlamaIndex per rispondere alle domande dei clienti. In questo caso, costruiremo sul bot creato nel file precedente post sul blog, che interroga tali incorporamenti con l'espressione di un utente e restituisce la risposta dalle domande frequenti del sito web.

Il diagramma seguente mostra come il processo di importazione e il bot di Amazon Lex interagiscono per la nostra soluzione.

Migliora Amazon Lex con LLM e migliora l'esperienza delle domande frequenti utilizzando l'inserimento di URL | Amazon Web Services PlatoBlockchain Data Intelligence. Ricerca verticale. Ai.

Nel flusso di lavoro della soluzione, il sito Web con le domande frequenti viene importato tramite AWS Lambda. Questa funzione Lambda esegue la scansione del sito Web e memorizza il testo risultante in un file Servizio di archiviazione semplice Amazon (Amazon S3) secchio. Il bucket S3 attiva quindi una funzione Lambda che utilizza LlamaIndex per creare incorporamenti archiviati in Amazon S3. Quando arriva una domanda da un utente finale, ad esempio "Qual è la tua politica di restituzione?", il bot Amazon Lex utilizza la sua funzione Lambda per interrogare gli incorporamenti utilizzando un approccio basato su RAG con LlamaIndex. Per ulteriori informazioni su questo approccio e sui prerequisiti, fare riferimento al post del blog, Potenzia Amazon Lex con funzionalità di domande frequenti conversazionali utilizzando LLM.

Dopo che i prerequisiti del suddetto blog sono stati completati, il primo passo è importare le FAQ in un repository di documenti che può essere vettorializzato e indicizzato da LlamaIndex. Il codice seguente mostra come eseguire questa operazione:

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)

Nell'esempio precedente, prendiamo l'URL di un sito Web FAQ predefinito da Zappos e lo ingeriamo utilizzando il file EZWebLoader classe. Con questa classe, siamo passati all'URL e abbiamo caricato tutte le domande presenti nella pagina in un indice. Ora possiamo porre una domanda del tipo "Zappos ha buoni regalo?" e ottieni le risposte direttamente dalle nostre FAQ sul sito web. Lo screenshot seguente mostra la console di test del bot di Amazon Lex che risponde a questa domanda dalle domande frequenti.

Migliora Amazon Lex con LLM e migliora l'esperienza delle domande frequenti utilizzando l'inserimento di URL | Amazon Web Services PlatoBlockchain Data Intelligence. Ricerca verticale. Ai.

Siamo stati in grado di raggiungere questo obiettivo perché avevamo scansionato l'URL nel primo passaggio e creato incorporamenti che LlamaIndex poteva utilizzare per cercare la risposta alla nostra domanda. La funzione Lambda del nostro bot mostra come viene eseguita questa ricerca ogni volta che viene restituito l'intento di fallback:

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

Questa soluzione funziona bene quando una singola pagina web ha tutte le risposte. Tuttavia, la maggior parte dei siti di FAQ non è costruita su una singola pagina. Ad esempio, nel nostro esempio Zappos, se poniamo la domanda "Hai una politica di abbinamento dei prezzi?", otteniamo una risposta non soddisfacente, come mostrato nello screenshot seguente.

Migliora Amazon Lex con LLM e migliora l'esperienza delle domande frequenti utilizzando l'inserimento di URL | Amazon Web Services PlatoBlockchain Data Intelligence. Ricerca verticale. Ai.

Nell'interazione precedente, la risposta alla politica di corrispondenza dei prezzi non è utile per il nostro utente. Questa risposta è breve perché le domande frequenti a cui si fa riferimento sono un collegamento a una pagina specifica sulla politica di corrispondenza dei prezzi e la nostra scansione del Web riguardava solo la singola pagina. Ottenere risposte migliori significherà anche eseguire la scansione di questi collegamenti. La sezione successiva mostra come ottenere risposte a domande che richiedono due o più livelli di profondità della pagina.

Scansione di livello N

Quando eseguiamo la scansione di una pagina Web per la conoscenza delle FAQ, le informazioni desiderate possono essere contenute nelle pagine collegate. Ad esempio, nel nostro esempio Zappos, poniamo la domanda "Hai una politica di corrispondenza dei prezzi?" e la risposta è “Sì, per favore visita per saperne di più." Se qualcuno chiede "Qual è la tua politica di corrispondenza dei prezzi?" poi vogliamo dare una risposta completa con la policy. Raggiungere questo significa che abbiamo la necessità di attraversare i collegamenti per ottenere le informazioni effettive per il nostro utente finale. Durante il processo di importazione, possiamo utilizzare il nostro caricatore Web per trovare i collegamenti di ancoraggio ad altre pagine HTML e quindi attraversarli. La seguente modifica al codice del nostro web crawler ci consente di trovare link nelle pagine di cui eseguiamo la scansione. Include anche una logica aggiuntiva per evitare la scansione circolare e consentire un filtro tramite un prefisso.

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)

Nel codice precedente, introduciamo la possibilità di eseguire la scansione in profondità di N livelli e forniamo un prefisso che ci consente di limitare la scansione solo alle cose che iniziano con un determinato pattern URL. Nel nostro esempio Zappos, tutte le pagine del servizio clienti sono radicate zappos.com/c, quindi lo includiamo come prefisso per limitare le nostre ricerche per indicizzazione a un sottoinsieme più piccolo e pertinente. Il codice mostra come possiamo ingerire fino a due livelli di profondità. La logica Lambda del nostro bot rimane la stessa perché non è cambiato nulla, tranne che il crawler ingerisce più documenti.

Ora abbiamo tutti i documenti indicizzati e possiamo porre una domanda più dettagliata. Nello screenshot seguente, il nostro bot fornisce la risposta corretta alla domanda "Hai una politica di corrispondenza dei prezzi?"

Migliora Amazon Lex con LLM e migliora l'esperienza delle domande frequenti utilizzando l'inserimento di URL | Amazon Web Services PlatoBlockchain Data Intelligence. Ricerca verticale. Ai.

Ora abbiamo una risposta completa alla nostra domanda sulla corrispondenza dei prezzi. Invece di sentirsi semplicemente dire "Sì, vedi la nostra politica", ci fornisce i dettagli della scansione di secondo livello.

ripulire

Per evitare di incorrere in spese future, procedi con l'eliminazione di tutte le risorse che sono state impiegate come parte di questo esercizio. Abbiamo fornito uno script per arrestare correttamente l'endpoint Sagemaker. I dettagli sull'utilizzo sono nel README. Inoltre, per rimuovere tutte le altre risorse che puoi eseguire cdk destroy nella stessa directory degli altri comandi cdk per eseguire il deprovisioning di tutte le risorse nel tuo stack.

Conclusione

La possibilità di importare una serie di FAQ in un chatbot consente ai tuoi clienti di trovare le risposte alle loro domande con domande semplici e in linguaggio naturale. Combinando il supporto integrato in Amazon Lex per la gestione del fallback con una soluzione RAG come LlamaIndex, possiamo fornire ai nostri clienti un percorso rapido per ottenere risposte soddisfacenti, curate e approvate alle domande frequenti. Applicando la scansione di livello N nella nostra soluzione, possiamo consentire risposte che potrebbero estendersi su più collegamenti FAQ e fornire risposte più approfondite alle domande dei nostri clienti. Seguendo questi passaggi, puoi incorporare senza problemi potenti funzionalità di domande e risposte basate su LLM e un'efficiente importazione di URL nel tuo chatbot di Amazon Lex. Ciò si traduce in interazioni più accurate, complete e contestuali con gli utenti.


Circa gli autori

Migliora Amazon Lex con LLM e migliora l'esperienza delle domande frequenti utilizzando l'inserimento di URL | Amazon Web Services PlatoBlockchain Data Intelligence. Ricerca verticale. Ai.Max Henkel Wallace è un ingegnere di sviluppo software presso AWS Lex. Gli piace lavorare sfruttando la tecnologia per massimizzare il successo dei clienti. Al di fuori del lavoro è appassionato di cucina, passa il tempo con gli amici e lo zaino in spalla.

Migliora Amazon Lex con LLM e migliora l'esperienza delle domande frequenti utilizzando l'inserimento di URL | Amazon Web Services PlatoBlockchain Data Intelligence. Ricerca verticale. Ai.Canzone Feng è Senior Applied Scientist presso AWS AI Labs, specializzato in elaborazione del linguaggio naturale e intelligenza artificiale. La sua ricerca esplora vari aspetti di questi campi, tra cui la modellazione di dialoghi basati su documenti, il ragionamento per dialoghi orientati alle attività e la generazione di testi interattivi utilizzando dati multimodali.

Migliora Amazon Lex con LLM e migliora l'esperienza delle domande frequenti utilizzando l'inserimento di URL | Amazon Web Services PlatoBlockchain Data Intelligence. Ricerca verticale. Ai.John Baker è Principal SDE presso AWS, dove lavora su Natural Language Processing, Large Language Models e altri progetti relativi a ML/AI. Lavora con Amazon da più di 9 anni e ha lavorato su AWS, Alexa e Amazon.com. Nel suo tempo libero, John ama sciare e altre attività all'aria aperta in tutto il nord-ovest del Pacifico.

Timestamp:

Di più da Apprendimento automatico di AWS