Îmbunătățiți Amazon Lex cu LLM-uri și îmbunătățiți experiența cu întrebări frecvente folosind ingerarea adreselor URL | Amazon Web Services

Îmbunătățiți Amazon Lex cu LLM-uri și îmbunătățiți experiența cu întrebări frecvente folosind ingerarea adreselor URL | Amazon Web Services

În lumea digitală de astăzi, majoritatea consumatorilor preferă să găsească singuri răspunsuri la întrebările lor legate de serviciile pentru clienți, decât să-și ia timp pentru a contacta companiile și/sau furnizorii de servicii. Această postare de blog explorează o soluție inovatoare pentru a crea un chatbot cu întrebări și răspunsuri Amazon Lex care utilizează întrebări frecvente existente de pe site-ul dvs. Acest instrument alimentat de inteligență artificială poate oferi răspunsuri rapide și precise la întrebările din lumea reală, permițând clientului să rezolve rapid și ușor problemele comune în mod independent.

Ingestie unică de adresă URL

Multe întreprinderi au un set publicat de răspunsuri pentru întrebări frecvente pentru clienții lor, disponibil pe site-ul lor web. În acest caz, dorim să oferim clienților un chatbot care să le poată răspunde întrebărilor din întrebările frecvente publicate. În postarea de blog intitulată Îmbunătățiți Amazon Lex cu funcții de întrebări frecvente conversaționale folosind LLM-uri, am demonstrat cum puteți utiliza o combinație de Amazon Lex și LlamaIndex pentru a construi un chatbot alimentat de sursele dvs. de cunoștințe existente, cum ar fi documentele PDF sau Word. Pentru a sprijini o simplă Întrebări frecvente, bazată pe un site web de Întrebări frecvente, trebuie să creăm un proces de asimilare care să poată accesa cu crawlere site-ul web și să creeze încorporari care să poată fi utilizate de LlamaIndex pentru a răspunde la întrebările clienților. În acest caz, ne vom baza pe botul creat în postare anterioară pe blog, care interogează acele înglobări cu enunțul unui utilizator și returnează răspunsul de la Întrebări frecvente ale site-ului.

Următoarea diagramă arată modul în care procesul de asimilare și botul Amazon Lex lucrează împreună pentru soluția noastră.

Îmbunătățiți Amazon Lex cu LLM-uri și îmbunătățiți experiența cu întrebări frecvente folosind ingerarea adreselor URL | Amazon Web Services PlatoBlockchain Data Intelligence. Căutare verticală. Ai.

În fluxul de lucru al soluției, site-ul web cu întrebări frecvente este ingerat prin AWS Lambdas. Această funcție Lambda accesează cu crawlere site-ul web și stochează textul rezultat într-un Serviciul Amazon de stocare simplă (Amazon S3) găleată. Bucket-ul S3 declanșează apoi o funcție Lambda care utilizează LlamaIndex pentru a crea înglobări care sunt stocate în Amazon S3. Când apare o întrebare de la un utilizator final, cum ar fi „Care este politica dvs. de returnare?”, robotul Amazon Lex își folosește funcția Lambda pentru a interoga înglobările folosind o abordare bazată pe RAG cu LlamaIndex. Pentru mai multe informații despre această abordare și despre cerințele preliminare, consultați postarea de blog, Îmbunătățiți Amazon Lex cu funcții de întrebări frecvente conversaționale folosind LLM-uri.

După ce cerințele preliminare de pe blogul menționat mai sus sunt îndeplinite, primul pas este să ingerați întrebările frecvente într-un depozit de documente care poate fi vectorizat și indexat de LlamaIndex. Următorul cod arată cum să realizați acest lucru:

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)

În exemplul precedent, luăm o adresă URL predefinită a site-ului cu întrebări frecvente de la Zappos și o ingerăm folosind EZWebLoader clasă. Cu această clasă, am navigat la adresa URL și am încărcat toate întrebările care sunt în pagină într-un index. Acum putem pune o întrebare precum „Zappos are carduri cadou?” și obțineți răspunsurile direct din Întrebările noastre frecvente de pe site. Următoarea captură de ecran arată consola de testare a botului Amazon Lex care răspunde la această întrebare din întrebările frecvente.

Îmbunătățiți Amazon Lex cu LLM-uri și îmbunătățiți experiența cu întrebări frecvente folosind ingerarea adreselor URL | Amazon Web Services PlatoBlockchain Data Intelligence. Căutare verticală. Ai.

Am reușit să reușim acest lucru deoarece am accesat cu crawlere adresa URL în primul pas și am creat încorporații pe care LlamaIndex le-ar putea folosi pentru a căuta răspunsul la întrebarea noastră. Funcția Lambda a botului nostru arată cum se execută această căutare ori de câte ori este returnată intenția de rezervă:

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

Această soluție funcționează bine atunci când o singură pagină web are toate răspunsurile. Cu toate acestea, majoritatea site-urilor cu întrebări frecvente nu sunt construite pe o singură pagină. De exemplu, în exemplul nostru Zappos, dacă punem întrebarea „Aveți o politică de potrivire a prețurilor?”, atunci obținem un răspuns mai puțin decât satisfăcător, așa cum se arată în următoarea captură de ecran.

Îmbunătățiți Amazon Lex cu LLM-uri și îmbunătățiți experiența cu întrebări frecvente folosind ingerarea adreselor URL | Amazon Web Services PlatoBlockchain Data Intelligence. Căutare verticală. Ai.

În interacțiunea anterioară, răspunsul privind politica de potrivire a prețurilor nu este util pentru utilizatorul nostru. Acest răspuns este scurt, deoarece întrebările frecvente la care se face referire este un link către o anumită pagină despre politica de potrivire a prețurilor, iar accesarea cu crawlere web a fost doar pentru o singură pagină. Obținerea unor răspunsuri mai bune va însemna accesarea cu crawlere și a acestor link-uri. Următoarea secțiune arată cum să obțineți răspunsuri la întrebări care necesită două sau mai multe niveluri de adâncime a paginii.

Crawling la nivel N

Când accesăm cu crawlere o pagină web pentru cunoașterea întrebărilor frecvente, informațiile pe care le dorim pot fi conținute în paginile legate. De exemplu, în exemplul nostru Zappos, punem întrebarea „Aveți o politică de potrivire a prețurilor?” iar răspunsul este „Da, vă rugăm să vizitați pentru a afla mai multe.” Dacă cineva întreabă „Care este politica ta de potrivire a prețurilor?” atunci vrem să dăm un răspuns complet cu politica. Atingerea acestui lucru înseamnă că avem nevoie să traversăm link-uri pentru a obține informațiile reale pentru utilizatorul nostru final. În timpul procesului de asimilare, putem folosi încărcătorul nostru web pentru a găsi link-urile de ancorare către alte pagini HTML și apoi le traversăm. Următoarea modificare a codului la crawler-ul nostru web ne permite să găsim link-uri în paginile pe care le accesăm cu crawlere. Include, de asemenea, o logică suplimentară pentru a evita accesarea cu crawlere circulară și pentru a permite un filtru după un 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)

În codul precedent, introducem posibilitatea de a accesa cu crawlere N niveluri profund și dăm un prefix care ne permite să restricționăm accesarea cu crawlere doar la lucrurile care încep cu un anumit model de adresă URL. În exemplul nostru Zappos, toate paginile de servicii pentru clienți sunt înrădăcinate zappos.com/c, așa că îl includem ca prefix pentru a ne limita accesările cu crawlere la un subset mai mic și mai relevant. Codul arată cum putem ingera până la două niveluri adânci. Logica Lambda a botului nostru rămâne aceeași, deoarece nimic nu s-a schimbat, cu excepția că crawler-ul ingerează mai multe documente.

Avem acum toate documentele indexate și putem pune o întrebare mai detaliată. În următoarea captură de ecran, botul nostru oferă răspunsul corect la întrebarea „Aveți o politică de potrivire a prețurilor?”

Îmbunătățiți Amazon Lex cu LLM-uri și îmbunătățiți experiența cu întrebări frecvente folosind ingerarea adreselor URL | Amazon Web Services PlatoBlockchain Data Intelligence. Căutare verticală. Ai.

Acum avem un răspuns complet la întrebarea noastră despre potrivirea prețurilor. În loc să ni se spună pur și simplu „Da, vezi politica noastră”, ne oferă detaliile din accesarea cu crawlere de nivel al doilea.

A curăța

Pentru a evita cheltuielile viitoare, continuați cu ștergerea tuturor resurselor care au fost implementate ca parte a acestui exercițiu. Am furnizat un script pentru a închide cu grație punctul final Sagemaker. Detaliile de utilizare sunt în README. În plus, pentru a elimina toate celelalte resurse pe care le puteți rula cdk destroy în același director cu celelalte comenzi cdk pentru a deproviziona toate resursele din stiva dumneavoastră.

Concluzie

Capacitatea de a ingera un set de întrebări frecvente într-un chatbot permite clienților tăi să găsească răspunsurile la întrebările lor cu interogări simple, în limbaj natural. Combinând suportul încorporat în Amazon Lex pentru gestionarea alternativă cu o soluție RAG, cum ar fi LlamaIndex, putem oferi clienților noștri o cale rapidă pentru a obține răspunsuri satisfăcătoare, organizate și aprobate la întrebările frecvente. Aplicând accesarea cu crawlere la nivel N în soluția noastră, putem permite răspunsuri care ar putea cuprinde mai multe link-uri de întrebări frecvente și să ofere răspunsuri mai profunde la întrebările clienților noștri. Urmând acești pași, puteți încorpora fără probleme capabilități Q și A puternice bazate pe LLM și ingerare eficientă de adrese URL în chatbot-ul dvs. Amazon Lex. Acest lucru are ca rezultat interacțiuni mai precise, cuprinzătoare și conștiente de context cu utilizatorii.


Despre autori

Îmbunătățiți Amazon Lex cu LLM-uri și îmbunătățiți experiența cu întrebări frecvente folosind ingerarea adreselor URL | Amazon Web Services PlatoBlockchain Data Intelligence. Căutare verticală. Ai.Max Henkel-Wallace este inginer de dezvoltare software la AWS Lex. Îi place să lucreze folosind tehnologia pentru a maximiza succesul clienților. În afara serviciului, este pasionat de gătit, de petrecerea timpului cu prietenii și de drumeții în spate.

Îmbunătățiți Amazon Lex cu LLM-uri și îmbunătățiți experiența cu întrebări frecvente folosind ingerarea adreselor URL | Amazon Web Services PlatoBlockchain Data Intelligence. Căutare verticală. Ai.Song Feng este cercetător senior aplicat la AWS AI Labs, specializat în procesarea limbajului natural și inteligența artificială. Cercetarea ei explorează diverse aspecte ale acestor domenii, inclusiv modelarea dialogului bazat pe documente, raționamentul pentru dialoguri orientate spre sarcini și generarea de text interactiv folosind date multimodale.

Îmbunătățiți Amazon Lex cu LLM-uri și îmbunătățiți experiența cu întrebări frecvente folosind ingerarea adreselor URL | Amazon Web Services PlatoBlockchain Data Intelligence. Căutare verticală. Ai.John Baker este SDE principal la AWS, unde lucrează la procesarea limbajului natural, modelele de limbaj mari și alte proiecte legate de ML/AI. A fost cu Amazon de peste 9 ani și a lucrat pe AWS, Alexa și Amazon.com. În timpul liber, lui John îi place schiul și alte activități în aer liber în nord-vestul Pacificului.

Timestamp-ul:

Mai mult de la Învățare automată AWS