Forbedre Amazon Lex med LLM'er og forbedre FAQ-oplevelsen ved hjælp af URL-indtagelse | Amazon Web Services

Forbedre Amazon Lex med LLM'er og forbedre FAQ-oplevelsen ved hjælp af URL-indtagelse | Amazon Web Services

I dagens digitale verden vil de fleste forbrugere hellere finde svar på deres kundeservicespørgsmål på egen hånd frem for at tage sig tid til at nå ud til virksomheder og/eller tjenesteudbydere. Dette blogindlæg udforsker en innovativ løsning til at bygge en spørgsmål og svar chatbot i Amazon Lex der bruger eksisterende ofte stillede spørgsmål fra dit websted. Dette AI-drevne værktøj kan give hurtige, præcise svar på forespørgsler fra den virkelige verden, hvilket giver kunden mulighed for hurtigt og nemt at løse almindelige problemer uafhængigt.

Enkelt URL-indtagelse

Mange virksomheder har et offentliggjort sæt svar på ofte stillede spørgsmål til deres kunder tilgængeligt på deres hjemmeside. I dette tilfælde ønsker vi at tilbyde kunderne en chatbot, der kan besvare deres spørgsmål fra vores offentliggjorte ofte stillede spørgsmål. I blogindlægget med titlen Forbedre Amazon Lex med samtalefunktioner med ofte stillede spørgsmål ved hjælp af LLM'er, viste vi, hvordan du kan bruge en kombination af Amazon Lex og LlamaIndex til at bygge en chatbot drevet af dine eksisterende videnskilder, såsom PDF- eller Word-dokumenter. For at understøtte en simpel FAQ, baseret på et websted med ofte stillede spørgsmål, skal vi oprette en indlæsningsproces, der kan crawle webstedet og oprette indlejringer, der kan bruges af LlamaIndex til at besvare kundespørgsmål. I dette tilfælde vil vi bygge videre på den bot, der er oprettet i forrige blogindlæg, som forespørger på disse indlejringer med en brugers ytring og returnerer svaret fra webstedets ofte stillede spørgsmål.

Følgende diagram viser, hvordan indtagelsesprocessen og Amazon Lex-bot fungerer sammen for vores løsning.

Forbedre Amazon Lex med LLM'er og forbedre FAQ-oplevelsen ved hjælp af URL-indtagelse | Amazon Web Services PlatoBlockchain Data Intelligence. Lodret søgning. Ai.

I løsningens workflow indtages hjemmesiden med FAQs via AWS Lambda. Denne Lambda-funktion gennemgår hjemmesiden og gemmer den resulterende tekst i en Amazon Simple Storage Service (Amazon S3) spand. S3-bøtten udløser derefter en Lambda-funktion, der bruger LlamaIndex til at skabe indlejringer, der er gemt i Amazon S3. Når et spørgsmål fra en slutbruger ankommer, såsom "Hvad er din returpolitik?", bruger Amazon Lex-botten sin Lambda-funktion til at forespørge om indlejringerne ved hjælp af en RAG-baseret tilgang med LlamaIndex. For mere information om denne tilgang og forudsætningerne henvises til blogindlægget, Forbedre Amazon Lex med samtalefunktioner med ofte stillede spørgsmål ved hjælp af LLM'er.

Når forudsætningerne fra den førnævnte blog er fuldført, er det første skridt at indlæse ofte stillede spørgsmål i et dokumentlager, der kan vektoriseres og indekseres af LlamaIndex. Følgende kode viser, hvordan du opnår dette:

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)

I det foregående eksempel tager vi en foruddefineret FAQ-websteds-URL fra Zappos og indtager den ved hjælp af EZWebLoader klasse. Med denne klasse har vi navigeret til URL'en og indlæst alle de spørgsmål, der er på siden, i et indeks. Vi kan nu stille et spørgsmål som "Har Zappos gavekort?" og få svarene direkte fra vores ofte stillede spørgsmål på hjemmesiden. Følgende skærmbillede viser Amazon Lex bot-testkonsollen, der besvarer det spørgsmål fra ofte stillede spørgsmål.

Forbedre Amazon Lex med LLM'er og forbedre FAQ-oplevelsen ved hjælp af URL-indtagelse | Amazon Web Services PlatoBlockchain Data Intelligence. Lodret søgning. Ai.

Vi var i stand til at opnå dette, fordi vi havde crawlet URL'en i det første trin og oprettet indlejringer, som LlamaIndex kunne bruge til at søge efter svaret på vores spørgsmål. Vores bots Lambda-funktion viser, hvordan denne søgning køres, når reservehensigten returneres:

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

Denne løsning fungerer godt, når en enkelt webside har alle svarene. De fleste ofte stillede spørgsmål er dog ikke bygget på en enkelt side. For eksempel, i vores Zappos-eksempel, hvis vi stiller spørgsmålet "Har du en prismatchingpolitik?", så får vi et mindre end tilfredsstillende svar, som vist på det følgende skærmbillede.

Forbedre Amazon Lex med LLM'er og forbedre FAQ-oplevelsen ved hjælp af URL-indtagelse | Amazon Web Services PlatoBlockchain Data Intelligence. Lodret søgning. Ai.

I den foregående interaktion er svaret på prismatch-politikken ikke nyttigt for vores bruger. Dette svar er kort, fordi de ofte stillede spørgsmål, der henvises til, er et link til en specifik side om prismatchningspolitikken, og vores webcrawl var kun for den enkelte side. At opnå bedre svar vil betyde, at man også skal gennemgå disse links. Det næste afsnit viser, hvordan du får svar på spørgsmål, der kræver to eller flere niveauer af sidedybde.

N-niveau kravling

Når vi gennemgår en webside for at få viden om ofte stillede spørgsmål, kan den information, vi ønsker, være indeholdt på linkede sider. For eksempel stiller vi i vores Zappos-eksempel spørgsmålet "Har du en prismatchingpolitik?" og svaret er "Ja, besøg venligst at lære mere." Hvis nogen spørger "Hvad er din prismatchingpolitik?" så vil vi give et fuldstændigt svar med politikken. At opnå dette betyder, at vi har behov for at krydse links for at få den faktiske information til vores slutbruger. Under indtagelsesprocessen kan vi bruge vores webindlæser til at finde ankerlinkene til andre HTML-sider og derefter krydse dem. Følgende kodeændring til vores webcrawler giver os mulighed for at finde links på de sider, vi gennemgår. Det indeholder også noget ekstra logik for at undgå cirkulær gennemgang og tillade et filter efter et præfiks.

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)

I den foregående kode introducerer vi muligheden for at crawle N niveauer dybt, og vi giver et præfiks, der giver os mulighed for at begrænse crawl til kun ting, der begynder med et bestemt URL-mønster. I vores Zappos-eksempel er alle kundeservicesiderne forankret fra zappos.com/c, så vi inkluderer det som et præfiks for at begrænse vores gennemgange til en mindre og mere relevant undergruppe. Koden viser, hvordan vi kan indtage op til to niveauer dybt. Vores bots Lambda-logik forbliver den samme, fordi intet er ændret, undtagen at crawleren indtager flere dokumenter.

Vi har nu alle dokumenterne indekseret, og vi kan stille et mere detaljeret spørgsmål. I det følgende skærmbillede giver vores bot det korrekte svar på spørgsmålet "Har du en prismatchingpolitik?"

Forbedre Amazon Lex med LLM'er og forbedre FAQ-oplevelsen ved hjælp af URL-indtagelse | Amazon Web Services PlatoBlockchain Data Intelligence. Lodret søgning. Ai.

Vi har nu et komplet svar på vores spørgsmål om prismatching. I stedet for blot at få at vide "Ja, se vores politik", giver det os detaljerne fra gennemgangen på andet niveau.

Ryd op

For at undgå fremtidige udgifter skal du fortsætte med at slette alle de ressourcer, der blev implementeret som en del af denne øvelse. Vi har leveret et script til at lukke Sagemaker-endepunktet på en yndefuld måde. Brugsoplysninger findes i README. Derudover for at fjerne alle de andre ressourcer, du kan køre cdk destroy i samme mappe som de andre cdk-kommandoer for at deprovisionere alle ressourcerne i din stak.

Konklusion

Evnen til at indsætte et sæt ofte stillede spørgsmål i en chatbot gør det muligt for dine kunder at finde svarene på deres spørgsmål med enkle, naturlige sprogforespørgsler. Ved at kombinere den indbyggede support i Amazon Lex til fallback-håndtering med en RAG-løsning, såsom et LlamaIndex, kan vi give vores kunder en hurtig vej til at få tilfredsstillende, kuraterede og godkendte svar på ofte stillede spørgsmål. Ved at anvende N-niveau crawling i vores løsning, kan vi tillade svar, der muligvis kan spænde over flere FAQ-links og give dybere svar på vores kunders forespørgsler. Ved at følge disse trin kan du problemfrit inkorporere kraftfulde LLM-baserede Q og A-funktioner og effektiv URL-indtagelse i din Amazon Lex chatbot. Dette resulterer i mere præcise, omfattende og kontekstuelt bevidste interaktioner med brugere.


Om forfatterne

Forbedre Amazon Lex med LLM'er og forbedre FAQ-oplevelsen ved hjælp af URL-indtagelse | Amazon Web Services PlatoBlockchain Data Intelligence. Lodret søgning. Ai.Max Henkel-Wallace er softwareudviklingsingeniør hos AWS Lex. Han nyder at arbejde ved at udnytte teknologi til at maksimere kundernes succes. Uden for arbejdet brænder han for at lave mad, tilbringe tid med venner og rygsæk.

Forbedre Amazon Lex med LLM'er og forbedre FAQ-oplevelsen ved hjælp af URL-indtagelse | Amazon Web Services PlatoBlockchain Data Intelligence. Lodret søgning. Ai.Sangen Feng er Senior Applied Scientist ved AWS AI Labs, med speciale i Natural Language Processing og Artificial Intelligence. Hendes forskning udforsker forskellige aspekter af disse felter, herunder dokumentbaseret dialogmodellering, ræsonnement for opgaveorienterede dialoger og interaktiv tekstgenerering ved hjælp af multimodale data.

Forbedre Amazon Lex med LLM'er og forbedre FAQ-oplevelsen ved hjælp af URL-indtagelse | Amazon Web Services PlatoBlockchain Data Intelligence. Lodret søgning. Ai.John bager er Principal SDE hos AWS, hvor han arbejder med Natural Language Processing, Large Language Models og andre ML/AI-relaterede projekter. Han har været hos Amazon i mere end 9 år og har arbejdet på tværs af AWS, Alexa og Amazon.com. I sin fritid nyder John at stå på ski og andre udendørsaktiviteter i hele Pacific Northwest.

Tidsstempel:

Mere fra AWS maskinindlæring