Förbättra Amazon Lex med LLM och förbättra FAQ-upplevelsen med hjälp av URL-inmatning | Amazon webbtjänster

Förbättra Amazon Lex med LLM och förbättra FAQ-upplevelsen med hjälp av URL-inmatning | Amazon webbtjänster

I dagens digitala värld vill de flesta konsumenter hellre hitta svar på sina kundtjänstfrågor på egen hand snarare än att ta sig tid att nå ut till företag och/eller tjänsteleverantörer. Det här blogginlägget utforskar en innovativ lösning för att bygga en chatbot för frågor och svar Amazon Lex som använder befintliga vanliga frågor från din webbplats. Detta AI-drivna verktyg kan ge snabba, exakta svar på verkliga förfrågningar, vilket gör att kunden snabbt och enkelt kan lösa vanliga problem självständigt.

Enstaka URL-inmatning

Många företag har en publicerad uppsättning svar på vanliga frågor för sina kunder tillgängliga på deras webbplats. I det här fallet vill vi erbjuda kunderna en chatbot som kan svara på deras frågor från våra publicerade vanliga frågor. I blogginlägget med titeln Förbättra Amazon Lex med vanliga vanliga samtalsfunktioner med hjälp av LLM, visade vi hur du kan använda en kombination av Amazon Lex och LlamaIndex för att bygga en chatbot som drivs av dina befintliga kunskapskällor, som PDF- eller Word-dokument. För att stödja en enkel FAQ, baserad på en webbplats med vanliga frågor, måste vi skapa en inmatningsprocess som kan genomsöka webbplatsen och skapa inbäddningar som kan användas av LlamaIndex för att svara på kundfrågor. I det här fallet kommer vi att bygga på boten som skapats i föregående blogginlägg, som frågar efter dessa inbäddningar med en användares yttrande och returnerar svaret från webbplatsens vanliga frågor.

Följande diagram visar hur intagsprocessen och Amazon Lex-boten fungerar tillsammans för vår lösning.

Förbättra Amazon Lex med LLM och förbättra FAQ-upplevelsen med hjälp av URL-inmatning | Amazon Web Services PlatoBlockchain Data Intelligence. Vertikal sökning. Ai.

I lösningens arbetsflöde intas webbplatsen med FAQs via AWS Lambda. Denna Lambda-funktion genomsöker webbplatsen och lagrar den resulterande texten i en Amazon enkel lagringstjänst (Amazon S3) hink. S3-skopan utlöser sedan en Lambda-funktion som använder LlamaIndex för att skapa inbäddningar som lagras i Amazon S3. När en fråga från en slutanvändare kommer, till exempel "Vad är din returpolicy?", använder Amazon Lex-boten sin Lambda-funktion för att fråga inbäddningarna med en RAG-baserad metod med LlamaIndex. För mer information om detta tillvägagångssätt och förutsättningarna, se blogginlägget, Förbättra Amazon Lex med vanliga vanliga samtalsfunktioner med hjälp av LLM.

Efter att förutsättningarna från ovannämnda blogg är klara är det första steget att mata in de vanliga frågorna i ett dokumentarkiv som kan vektoriseras och indexeras av LlamaIndex. Följande kod visar hur du gör detta:

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 föregående exemplet tar vi en fördefinierad FAQ-webbadress från Zappos och matar in den med hjälp av EZWebLoader klass. Med den här klassen har vi navigerat till URL:en och laddat alla frågor som finns på sidan till ett index. Vi kan nu ställa en fråga som "Har Zappos presentkort?" och få svaren direkt från våra vanliga frågor på webbplatsen. Följande skärmdump visar Amazon Lex-bottestkonsolen som svarar på den frågan från vanliga frågor.

Förbättra Amazon Lex med LLM och förbättra FAQ-upplevelsen med hjälp av URL-inmatning | Amazon Web Services PlatoBlockchain Data Intelligence. Vertikal sökning. Ai.

Vi kunde uppnå detta eftersom vi hade genomsökt webbadressen i det första steget och skapat inbäddningar som LlamaIndex kunde använda för att söka efter svaret på vår fråga. Vår bots Lambda-funktion visar hur den här sökningen körs när reservavsikten returneras:

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

Denna lösning fungerar bra när en enda webbsida har alla svar. De flesta FAQ-webbplatser är dock inte byggda på en enda sida. Till exempel, i vårt Zappos-exempel, om vi ställer frågan "Har du en prismatchningspolicy?", får vi ett mindre än tillfredsställande svar, som visas i följande skärmdump.

Förbättra Amazon Lex med LLM och förbättra FAQ-upplevelsen med hjälp av URL-inmatning | Amazon Web Services PlatoBlockchain Data Intelligence. Vertikal sökning. Ai.

I föregående interaktion är svaret på prismatchningspolicyn inte till hjälp för vår användare. Det här svaret är kort eftersom de vanliga frågorna som hänvisas till är en länk till en specifik sida om prismatchningspolicyn och vår webbgenomsökning gällde endast den enskilda sidan. Att uppnå bättre svar kommer att innebära att man genomsöker dessa länkar också. Nästa avsnitt visar hur du får svar på frågor som kräver två eller flera nivåer av siddjup.

N-nivå krypning

När vi genomsöker en webbsida för kunskap om vanliga frågor kan den information vi vill ha finnas på länkade sidor. Till exempel, i vårt Zappos-exempel ställer vi frågan "Har du en prismatchningspolicy?" och svaret är "Ja snälla besök att lära sig mer." Om någon frågar "Vad är din prismatchningspolicy?" då vill vi ge ett fullständigt svar med policyn. Att uppnå detta innebär att vi måste gå igenom länkar för att få den faktiska informationen för vår slutanvändare. Under inmatningsprocessen kan vi använda vår webbläsare för att hitta ankarlänkarna till andra HTML-sidor och sedan gå igenom dem. Följande kodändring av vår sökrobot gör att vi kan hitta länkar på sidorna vi genomsöker. Den innehåller också ytterligare logik för att undvika cirkulär genomsökning och tillåta ett filter med ett prefix.

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 föregående koden introducerar vi möjligheten att genomsöka N nivåer djupt, och vi ger ett prefix som gör att vi kan begränsa genomsökningen till bara saker som börjar med ett visst webbadressmönster. I vårt Zappos-exempel är alla kundtjänstsidor rotade från zappos.com/c, så vi inkluderar det som ett prefix för att begränsa våra genomsökningar till en mindre och mer relevant delmängd. Koden visar hur vi kan få i oss upp till två nivåer djupa. Vår bots Lambda-logik förblir densamma eftersom ingenting har förändrats förutom att sökroboten matar in fler dokument.

Vi har nu alla dokument indexerade och vi kan ställa en mer detaljerad fråga. I följande skärmdump ger vår bot det korrekta svaret på frågan "Har du en prismatchningspolicy?"

Förbättra Amazon Lex med LLM och förbättra FAQ-upplevelsen med hjälp av URL-inmatning | Amazon Web Services PlatoBlockchain Data Intelligence. Vertikal sökning. Ai.

Vi har nu ett komplett svar på vår fråga om prismatchning. Istället för att bara få höra "Ja, se vår policy", ger den oss detaljerna från genomsökningen på andra nivån.

Städa upp

För att undvika framtida utgifter, fortsätt med att ta bort alla resurser som distribuerades som en del av denna övning. Vi har tillhandahållit ett skript för att stänga av Sagemaker-slutpunkten på ett elegant sätt. Användningsinformation finns i README. Dessutom, för att ta bort alla andra resurser du kan köra cdk destroy i samma katalog som de andra cdk-kommandona för att ta bort alla resurser i din stack.

Slutsats

Möjligheten att lägga in en uppsättning vanliga frågor i en chatbot gör det möjligt för dina kunder att hitta svaren på sina frågor med enkla, naturliga språkfrågor. Genom att kombinera det inbyggda stödet i Amazon Lex för reservhantering med en RAG-lösning som ett LlamaIndex, kan vi tillhandahålla en snabb väg för våra kunder att få tillfredsställande, utvalda och godkända svar på vanliga frågor. Genom att tillämpa N-nivå genomsökning i vår lösning kan vi tillåta svar som eventuellt kan sträcka sig över flera FAQ-länkar och ge djupare svar på våra kunders frågor. Genom att följa dessa steg kan du sömlöst integrera kraftfulla LLM-baserade Q och A-funktioner och effektiv URL-inmatning i din Amazon Lex chatbot. Detta resulterar i mer exakta, heltäckande och kontextmedvetna interaktioner med användare.


Om författarna

Förbättra Amazon Lex med LLM och förbättra FAQ-upplevelsen med hjälp av URL-inmatning | Amazon Web Services PlatoBlockchain Data Intelligence. Vertikal sökning. Ai.Max Henkel-Wallace är mjukvaruutvecklingsingenjör på AWS Lex. Han tycker om att arbeta med teknik för att maximera kundernas framgång. Utanför jobbet brinner han för att laga mat, umgås med vänner och backpacka.

Förbättra Amazon Lex med LLM och förbättra FAQ-upplevelsen med hjälp av URL-inmatning | Amazon Web Services PlatoBlockchain Data Intelligence. Vertikal sökning. Ai.Låten Feng är senior tillämpad forskare vid AWS AI Labs, specialiserad på naturlig språkbehandling och artificiell intelligens. Hennes forskning utforskar olika aspekter av dessa områden, inklusive dokumentbaserad dialogmodellering, resonemang för uppgiftsorienterade dialoger och interaktiv textgenerering med hjälp av multimodala data.

Förbättra Amazon Lex med LLM och förbättra FAQ-upplevelsen med hjälp av URL-inmatning | Amazon Web Services PlatoBlockchain Data Intelligence. Vertikal sökning. Ai.John Baker är en Principal SDE på AWS där han arbetar med Natural Language Processing, Large Language Models och andra ML/AI-relaterade projekt. Han har varit hos Amazon i 9+ år och har arbetat med AWS, Alexa och Amazon.com. På sin fritid tycker John om att åka skidor och andra utomhusaktiviteter i hela Pacific Northwest.

Tidsstämpel:

Mer från AWS maskininlärning