Forbedre Amazon Lex med LLM-er og forbedre FAQ-opplevelsen ved å bruke URL-inntak | Amazon Web Services

Forbedre Amazon Lex med LLM-er og forbedre FAQ-opplevelsen ved å bruke URL-inntak | Amazon Web Services

I dagens digitale verden vil de fleste forbrukere heller finne svar på kundeservicespørsmålene sine på egen hånd i stedet for å ta seg tid til å nå ut til bedrifter og/eller tjenesteleverandører. Dette blogginnlegget utforsker en innovativ løsning for å bygge en spørsmål og svar chatbot i Amazon Lex som bruker eksisterende vanlige spørsmål fra nettstedet ditt. Dette AI-drevne verktøyet kan gi raske, nøyaktige svar på forespørsler fra den virkelige verden, slik at kunden raskt og enkelt kan løse vanlige problemer uavhengig.

Enkel URL-inntak

Mange bedrifter har et publisert sett med svar på vanlige spørsmål for sine kunder tilgjengelig på nettsiden deres. I dette tilfellet ønsker vi å tilby kundene en chatbot som kan svare på spørsmålene deres fra våre publiserte vanlige spørsmål. I blogginnlegget med tittelen Forbedre Amazon Lex med samtalefunksjoner for vanlige spørsmål ved å bruke LLM-er, viste vi hvordan du kan bruke en kombinasjon av Amazon Lex og LlamaIndex til å bygge en chatbot drevet av dine eksisterende kunnskapskilder, for eksempel PDF- eller Word-dokumenter. For å støtte en enkel FAQ, basert på et nettsted med vanlige spørsmål, må vi lage en inntaksprosess som kan gjennomsøke nettsiden og lage innbygginger som kan brukes av LlamaIndex for å svare på kundespørsmål. I dette tilfellet vil vi bygge på boten som er opprettet i forrige blogginnlegg, som spør etter disse innebyggingene med en brukers ytring og returnerer svaret fra vanlige spørsmål på nettstedet.

Følgende diagram viser hvordan inntaksprosessen og Amazon Lex-boten fungerer sammen for løsningen vår.

Enhance Amazon Lex with LLMs and improve the FAQ experience using URL ingestion | Amazon Web Services PlatoBlockchain Data Intelligence. Vertical Search. Ai.

I løsningsarbeidsflyten inntas nettsiden med FAQs via AWS Lambda. Denne Lambda-funksjonen gjennomsøker nettstedet og lagrer den resulterende teksten i en Amazon enkel lagringstjeneste (Amazon S3) bøtte. S3-bøtten utløser deretter en Lambda-funksjon som bruker LlamaIndex til å lage innbygginger som er lagret i Amazon S3. Når et spørsmål fra en sluttbruker kommer, for eksempel "Hva er returpolitikken din?", bruker Amazon Lex-boten sin Lambda-funksjon til å spørre om innebyggingene ved hjelp av en RAG-basert tilnærming med LlamaIndex. For mer informasjon om denne tilnærmingen og forutsetningene, se blogginnlegget, Forbedre Amazon Lex med samtalefunksjoner for vanlige spørsmål ved å bruke LLM-er.

Etter at forutsetningene fra den nevnte bloggen er fullført, er det første trinnet å legge inn de vanlige spørsmålene i et dokumentlager som kan vektoriseres og indekseres av LlamaIndex. Følgende kode viser hvordan du oppnå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 eksemplet tar vi en forhåndsdefinert webadresse for vanlige spørsmål fra Zappos og bruker den ved å bruke EZWebLoader klasse. Med denne klassen har vi navigert til URL-en og lastet alle spørsmålene som er på siden inn i en indeks. Vi kan nå stille et spørsmål som "Har Zappos gavekort?" og få svarene direkte fra våre vanlige spørsmål på nettstedet. Følgende skjermbilde viser Amazon Lex-bottestkonsollen som svarer på det spørsmålet fra vanlige spørsmål.

Enhance Amazon Lex with LLMs and improve the FAQ experience using URL ingestion | Amazon Web Services PlatoBlockchain Data Intelligence. Vertical Search. Ai.

Vi var i stand til å oppnå dette fordi vi hadde gjennomsøkt URL-en i det første trinnet og laget embedddings som LlamaIndex kunne bruke for å søke etter svaret på spørsmålet vårt. Vår robots Lambda-funksjon viser hvordan dette søket kjøres når reservehensikten 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øsningen fungerer bra når en enkelt nettside har alle svarene. De fleste FAQ-nettsteder er imidlertid ikke bygget på én enkelt side. For eksempel, i vårt Zappos-eksempel, hvis vi stiller spørsmålet "Har du en pristilpasningspolicy?", får vi et mindre enn tilfredsstillende svar, som vist i følgende skjermbilde.

Enhance Amazon Lex with LLMs and improve the FAQ experience using URL ingestion | Amazon Web Services PlatoBlockchain Data Intelligence. Vertical Search. Ai.

I den forrige interaksjonen er ikke svaret på retningslinjene for prissamsvar nyttig for brukeren vår. Dette svaret er kort fordi de vanlige spørsmålene det refereres til er en lenke til en spesifikk side om retningslinjene for prissamsvar, og nettgjennomgangen vår var kun for den enkelte siden. Å oppnå bedre svar vil bety å gjennomsøke disse koblingene også. Den neste delen viser hvordan du får svar på spørsmål som krever to eller flere nivåer av sidedybde.

N-nivå gjennomgang

Når vi gjennomsøker en nettside for kunnskap om vanlige spørsmål, kan informasjonen vi ønsker finnes på lenkede sider. For eksempel, i vårt Zappos-eksempel, stiller vi spørsmålet "Har du en policy for prismatching?" og svaret er "Ja, vennligst besøk å lære mer." Hvis noen spør "Hva er retningslinjene dine for prismatching?" da ønsker vi å gi et fullstendig svar med politikken. Å oppnå dette betyr at vi har behov for å krysse lenker for å få den faktiske informasjonen for sluttbrukeren vår. Under inntaksprosessen kan vi bruke nettlasteren vår til å finne ankerlenkene til andre HTML-sider og deretter gå gjennom dem. Den følgende kodeendringen til vår webcrawler lar oss finne koblinger på sidene vi gjennomsøker. Det inkluderer også litt ekstra logikk for å unngå sirkulær gjennomgang og tillate et filter etter et prefiks.

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 koden introduserer vi muligheten til å gjennomsøke N nivåer dypt, og vi gir et prefiks som lar oss begrense gjennomsøking til kun ting som begynner med et bestemt URL-mønster. I vårt Zappos-eksempel er alle kundeservicesidene forankret fra zappos.com/c, så vi inkluderer det som et prefiks for å begrense gjennomsøkingene våre til et mindre og mer relevant delsett. Koden viser hvordan vi kan innta opptil to nivåer dypt. Vår robots Lambda-logikk forblir den samme fordi ingenting har endret seg bortsett fra at robotsøkeprogrammet tar inn flere dokumenter.

Vi har nå alle dokumentene indeksert og vi kan stille et mer detaljert spørsmål. I det følgende skjermbildet gir boten vår det riktige svaret på spørsmålet "Har du en policy for prismatching?"

Enhance Amazon Lex with LLMs and improve the FAQ experience using URL ingestion | Amazon Web Services PlatoBlockchain Data Intelligence. Vertical Search. Ai.

Vi har nå et fullstendig svar på spørsmålet vårt om prismatching. I stedet for bare å bli fortalt «Ja, se retningslinjene våre», gir den oss detaljene fra gjennomgangen på andre nivå.

Rydd opp

For å unngå å pådra seg fremtidige utgifter, fortsett med å slette alle ressursene som ble distribuert som en del av denne øvelsen. Vi har gitt et skript for å stenge Sagemaker-endepunktet på en elegant måte. Bruksdetaljer er i README. I tillegg, for å fjerne alle de andre ressursene du kan kjøre cdk destroy i samme katalog som de andre cdk-kommandoene for å deprovisionere alle ressursene i stabelen din.

konklusjonen

Muligheten til å legge inn et sett med vanlige spørsmål i en chatbot gjør det mulig for kundene dine å finne svar på spørsmålene deres med enkle, naturlig språkspørsmål. Ved å kombinere den innebygde støtten i Amazon Lex for reservehåndtering med en RAG-løsning som en LlamaIndex, kan vi tilby en rask vei for våre kunder for å få tilfredsstillende, kuraterte og godkjente svar på vanlige spørsmål. Ved å bruke N-nivå gjennomgang i løsningen vår, kan vi tillate svar som muligens kan spenne over flere FAQ-koblinger og gi dypere svar på kundenes spørsmål. Ved å følge disse trinnene kan du sømløst integrere kraftige LLM-baserte Q- og A-funksjoner og effektiv URL-inntak i Amazon Lex-chatboten. Dette resulterer i mer nøyaktige, omfattende og kontekstuelt bevisste interaksjoner med brukere.


Om forfatterne

Enhance Amazon Lex with LLMs and improve the FAQ experience using URL ingestion | Amazon Web Services PlatoBlockchain Data Intelligence. Vertical Search. Ai.Max Henkel-Wallace er programvareutviklingsingeniør ved AWS Lex. Han liker å jobbe med å utnytte teknologi for å maksimere kundesuksess. Utenom jobben er han lidenskapelig opptatt av matlaging, tilbringe tid med venner og backpacking.

Enhance Amazon Lex with LLMs and improve the FAQ experience using URL ingestion | Amazon Web Services PlatoBlockchain Data Intelligence. Vertical Search. Ai.Sang Feng er Senior Applied Scientist ved AWS AI Labs, med spesialisering i naturlig språkbehandling og kunstig intelligens. Forskningen hennes utforsker ulike aspekter ved disse feltene, inkludert dokumentbasert dialogmodellering, resonnement for oppgaveorienterte dialoger og interaktiv tekstgenerering ved bruk av multimodale data.

Enhance Amazon Lex with LLMs and improve the FAQ experience using URL ingestion | Amazon Web Services PlatoBlockchain Data Intelligence. Vertical Search. Ai.John Baker er en rektor SDE ved AWS hvor han jobber med naturlig språkbehandling, store språkmodeller og andre ML/AI-relaterte prosjekter. Han har vært hos Amazon i over 9 år og har jobbet på tvers av AWS, Alexa og Amazon.com. På fritiden liker John å gå på ski og andre utendørsaktiviteter i hele Pacific Northwest.

Tidstempel:

Mer fra AWS maskinlæring