Crea un bot di supporto clienti in 20 minuti con Tanuki + GPT4

Crea un bot di supporto clienti in soli 20 minuti con Tanuki + GPT4

Un Tanuki programmatore - creato con DALL·E 3

TLDR: Questo flusso di lavoro risponde ai messaggi dei clienti e li analizza per creare ticket di supporto prioritizzati utilizzando GPT4 + Tanuki (open-source).

A chi è utile? A chiunque sia interessato a creare applicazioni che integrino le capacità di GPT4 in flussi di lavoro complessi con poco o nessuno sforzo.

Quanto è avanzato questo post? È richiesta una conoscenza di base di Python, ma è tutto qui. Tanuki si occupa del resto.

Cosa non fa questo post. Non raccogliamo tweet (vedi la loro API) né pubblichiamo sul servizio di supporto clienti (che dipende fortemente dalla tecnologia utilizzata).

Introduzione

GPT4 ha conquistato il mondo. I casi d’uso potenziali vanno dai chatbot ai classificatori, all’analisi dei dati e ora anche alla visione.

Una sfida ancora presente è creare applicazioni che richiedono output strutturati, poiché i modelli linguistici restituiscono output in testo naturale.

Inoltre, i LLM con zero-shot sono noti per essere fuorviati (i modelli linguistici possono capire il compito in modo diverso) e generare allucinazioni. Per contrastare questo problema, abbiamo creato Tanuki, un modo semplice per creare applicazioni basate su LLM allineate e garantire output strutturati e di tipo.

In questo articolo, mostrerò come utilizzare Tanuki in 20 minuti per creare:

  1. Un chatbot di supporto clienti
  2. Un classificatore di supporto clienti, e
  3. Oggetti strutturati affidabili per l’accesso ai database.

Caso d’uso in pochissime parole

In questo esempio di caso d’uso, creeremo un flusso di lavoro che funge sia da chatbot di supporto clienti che crea possibili problemi in un sistema interno. In particolare, assumiamo che un tweet inviato all’account di supporto di un’azienda sia l’input. Il flusso di lavoro creerà una risposta empatica al tweet e lo classificherà per determinare se richiede un’azione aggiuntiva da parte del team di supporto dell’azienda. Se il tweet richiede un’azione aggiuntiva, verrà creato un ticket di supporto che potrai salvare nel tuo database interno per un’azione successiva.

Tanuki verrà utilizzato in questo esempio per creare la risposta, classificare il tweet e creare il ticket di supporto se necessario. Siamo riusciti a creare questa app Python in 20 minuti. Ora vogliamo mostrarti come fare lo stesso. Se sei impaziente, puoi trovare il repository e il caso d’uso ai seguenti link:

  • Repository di Tanuki su Github
  • Codice per questo particolare caso d’uso qui o in un Google Colab notebook

Che cos’è Tanuki e perché è utile per questo caso d’uso?

Tanuki è una libreria open-source per la creazione facile e senza soluzione di continuità di app basate su LLM con poca o nessuna conoscenza richiesta di LLM o di come fare delle richieste. Le caratteristiche utili di Tanuki, in particolare per questo caso d’uso, sono:

  • Consapevolezza del tipo: Gli output seguiranno sempre la struttura degli hint di tipo specificata dall’utente (un incubo da gestire con i LLM, poiché gli output sono testo libero). Gli output possono essere specificati come tipi di base di Python (interi, liste, dizionari) o tipi più complessi come classi Pydantic o letterali.
  • Comportamento del modello linguistico facilmente allineabile attraverso esempi dimostrativi di simulazione (segue l’idea di In-Context Learning). L’aggiunta di questi statement di allineamento non è obbligatoria, ma di solito migliora significativamente le prestazioni del modello. L’aggiunta di esempi di simulazione è semplice come scriverli su un blocco note.
  • Tanuki esegue automaticamente la distillazione automatica del modello, ovvero il modello di insegnamento (GPT-4) viene automaticamente distillato in un modello più piccolo e affinato, con una riduzione dei costi e della latenza fino a 15 volte senza sacrificare le prestazioni.

Queste caratteristiche sono rilevanti per questo caso d’uso perché:

  1. Gli output del LLM saranno utilizzati da altri sistemi. Questo richiede che gli output siano sempre tipizzati per garantire che nulla si rompa e non ci siano bug a tempo di esecuzione causati dai dati. Questo è cruciale, poiché il flusso di lavoro crea oggetti di ticket strutturati che devono essere registrati nei database che si aspettano i dati in uno schema specifico.
  2. Cosa dovrebbe (e non dovrebbe) essere considerato è soggettivo e non ovvio. Inoltre, è di grande importanza come il modello di linguaggio risponde al feedback dei clienti (il tono e il messaggio devono essere corretti per non irritare i clienti che stanno già avendo problemi). Pertanto, l’allineamento è cruciale, in quanto si desidera assicurarsi che i LLM comprendano cosa è una risposta appropriata e che qualsiasi altra azione (non si vuole perdere una richiesta del cliente) sia allineata a come un dipendente affronterebbe questi problemi. Allineare il modello di linguaggio a queste problematiche è l’unico modo per garantire prestazioni adatte all’uso in produzione.
  3. La quantità di ticket di supporto potenziali e feedback in ambienti di produzione è colossale. Pertanto, ridurre i costi di un fattore di 15 può rappresentare un grande risparmio e una motivazione all’uso a lungo termine, soprattutto perché le prestazioni rimarranno costanti e il flusso di lavoro non sarà influenzato da eventuali future modifiche di versione a GPT4.

Per definire il progetto, utilizzeremo i seguenti requisiti

Prima di tutto, assumiamo il seguente flusso di lavoro generale:

  • Un messaggio di feedback dell’utente riguardo a un prodotto o servizio viene inviato all’account Twitter.
  • Il LLM analizza il feedback e risponde in modo empatico (o per quanto possibile), ossia l’aspetto del chatbot.
  • Dato il feedback del cliente, il LLM classificherà il feedback come “richiede azione” o meno, ossia l’aspetto del classificatore.
  • Se richiede azione, quindi il chatbot creerà un oggetto ticket del cliente da utilizzare successivamente in applicazioni downstream.

Utilizzeremo GPT4 di OpenAI per questo caso d’uso. Per iniziare, dovremo impostare alcune variabili di ambiente con la tua chiave API di OpenAI. A tal fine, dovremmo creare un file .env e aggiungerlo alla directory. Successivamente, il file .env verrà letto e le variabili di ambiente verranno correttamente interpretate.

OPENAI_API_KEY=sk-XXX

E questo è tutto ciò di cui hai bisogno per configurare Tanuki! Quindi, vediamo ora come realizzare il caso d’uso successivo.

Realizzazione del flusso di lavoro

Come accennato prima, potresti utilizzare GPT4 con prompt se il costo non fosse un fattore, anche se ottenere output tipizzati richiederebbe ulteriore lavoro. Se avessi qualche settimana, potresti persino addestrare in modo più specifico un LLM open source per gestire questo compito. Invece, useremo Tanuki per farlo in 20 minuti.

Prima linea di lavoro: dobbiamo installare Tanuki

pip install tanuki.py

Poi prepariamo le basi. Possiamo assumere che gli oggetti tweet in input saranno i seguenti:

from pydantic import BaseModelclass Tweet(BaseModel):    """    Oggetto tweet    Il nome è l'account dell'utente    Il testo è il tweet che hanno inviato    id è un classificatore univoco    """    name: str    text: str    id: str

Successivamente, creeremo un oggetto response Pydantic che verrà dato a questo tweet per rispondere all’utente e, se è necessario, verrà creato anche un oggetto SupportTicket da salvare nel database per l’intervento umano dato il messaggio di input

from typing import Literal, Optionalclass Response(BaseModel):    """    Oggetto di risposta, in cui l'attributo di risposta è la risposta inviata al cliente    requires_ticket è un booleano che indica se il tweet in ingresso era una domanda o un problema    diretto che richiederebbe l'intervento e l'azione umana    """    requires_ticket: bool     response: str class SupportTicket(BaseModel):    """    Oggetto ticket di supporto, in cui    l'issue è una breve descrizione del problema che il cliente ha    urgenza indica quanto urgentemente il team dovrebbe rispondere al problema    """    issue: str    urgency: Literal["low", "VoAGI", "high"]

Ora, avendo preparato le basi, possiamo iniziare a creare le funzioni effettive che faranno tutto il lavoro difficile per noi.

Prima di tutto, creiamo la funzione che crea l’oggetto Response dal tweet in ingresso. Specifichiamo il tweet come input e il tipo di outputhint come Response. Specificare gli hint di tipo è cruciale, poiché dirà al modello di linguaggio che esegue la funzione cosa creare come output finale. Tanuki si assicurerà sempre che gli output siano conformi agli hint di tipo, quindi possiamo dire con sicurezza che nulla si romperà a causa di oggetti incorretti o output non affidabili.

Successivamente, aggiungiamo un riepilogo nella docstring della funzione su cosa deve fare il LLM e aggiungiamo il decorator @tanuki.patch. Questo garantisce che gli output di classify_and_respond siano ben tipizzati per essere analizzati a valle.

import [email protected] classify_and_respond(tweet: Tweet) -> Response:    """Rispondi al testo del tweet di supporto ai clienti in modo empatico e gentile.     Trasmettere che ti preoccupi del problema e se il problema era un problema diretto che il team di supporto dovrebbe risolvere o una domanda, il team risponderà.     """

Per garantire prestazioni affidabili e aggiungere gli statement di allineamento per orientare le prestazioni dell’LLM su cosa generare in output all’utente, creeremo un’altra funzione chiamata align_respond con il decorator @tanuki.align.

In align_respond allineeremo gli output di LLM mostrando esempi di input e output validi. Questo allineamento:

  1. Mostra come rispondere alla richiesta del cliente
  2. Mostra quali sono le richieste del cliente che devono essere registrate (e deve essere creato un ticket interno).

Questo allineamento verrà simulato come input e output desiderati utilizzando le istruzioni di asserzione Python. Di seguito sono riportati alcuni esempi per gli statement di allineamento per gli oggetti di output del chatbot:

@tanuki.aligndef align_respond():    input_tweet_1 = Tweet(nome = "Laia Johnson",                          testo = "Mi piace molto la nuova pala, ma l'impugnatura si è rotta dopo 2 giorni di utilizzo. Posso avere un ricambio?",                          id = "123")    assert classify_and_respond(input_tweet_1) == Response(                                                            richiede_ticket=True,                                                             risposta="Ciao, ci dispiace sentirlo. Ti risponderemo con un ricambio il prima possibile, puoi inviarci il tuo numero d'ordine?"                                                            )    input_tweet_2 = Tweet(nome = "Keira Townsend",                          testo = "Odio il nuovo design dell'iPhone. È così brutto. Sto passando a Samsung",                          id = "10pa")    assert classify_and_respond(input_tweet_2) == Response(                                                            richiede_ticket=False,                                                             risposta="Ciao, ci dispiace sentirlo. Prenderemo in considerazione il tuo feedback e lo comunicheremo al team di prodotto"                                                            )    input_tweet_3 = Tweet(nome = "Thomas Bell",                          testo = "@Amazonsupport. Ho una domanda sull'ordinazione, consegnate in Finlandia?",                          id = "test")    assert classify_and_respond(input_tweet_3) == Response(                                                            richiede_ticket=True,                                                             risposta="Ciao, grazie per il tuo contatto. La tua domanda verrà inviata al nostro team di supporto e ti risponderanno il prima possibile"                                                            )    input_tweet_4 = Tweet(nome = "Jillian Murphy",                          testo = "Ho appena acquistato il nuovo goodybox e finora mi piace molto!",                          id = "009")    assert classify_and_respond(input_tweet_4) == Response(                                                            richiede_ticket=False,                                                             risposta="Ciao, grazie per il tuo contatto. Siamo felici di sentire che stai apprezzando il prodotto"                                                            )

Gli asserzioni come quelle sopra riducono notevolmente la probabilità di allucinazioni e guasti imprevisti allineando l’LLM al comportamento desiderato. Mi piace pensare a questo come “allineamento guidato dai test” (sviluppo guidato dai test per LLM).

Esattamente la stessa cosa deve essere fatta anche per la seconda parte, ossia la creazione del ticket di supporto per il registro. Seguendo la stessa struttura di funzioni di patch e allineamento, abbiamo quanto segue:

@tanuki.patchdef create_support_ticket(testo_tweet: str) -> SupportTicket:    """    Utilizzando il testo del tweet, crea un ticket di supporto da salvare nel database interno    Crea un breve riassunto dell'azione da intraprendere e dell'urgenza del problema    """@tanuki.aligndef align_supportticket():    input_tweet_1 = "Mi piace molto la nuova pala, ma l'impugnatura si è rotta dopo 2 giorni di utilizzo. Posso avere un ricambio?"    assert create_support_ticket(input_tweet_1) == SupportTicket(                                                                problema="Necessita di un prodotto di sostituzione perché l'impugnatura si è rotta",                                                                 urgenza="alta"                                                                )    input_tweet_2 = "@Amazonsupport. Ho una domanda sull'ordinazione, consegnate in Finlandia?"    assert create_support_ticket(input_tweet_2) == SupportTicket(                                                                problema="Scoprire e rispondere se attualmente consegniamo in Finlandia",                                                                urgenza="bassa"                                                                )    input_tweet_3 = "Ho appena acquistato il nuovo goodybox e finora mi piace molto! Il pacchetto della crema era leggermente danneggiato, però vorrei che fosse sostituito"    assert create_support_ticket(input_tweet_3) == SupportTicket(                                                                problema="Necessita di una nuova crema poiché il pacchetto era leggermente danneggiato",                                                                 urgenza="VoAGI"                                                                )

Per mettere tutto insieme, creiamo una funzione analyse_and_respond() per creare la risposta e il ticket di supporto, se necessario, e abbiamo finito!

def analyse_and_respond(tweet: Tweet) -> tuple[Optional[SupportTicket], Response]:    # ottieni la risposta    response = classify_and_respond(tweet)    # se la risposta richiede un ticket, crea un ticket    if response.requires_ticket:        support_ticket = create_support_ticket(tweet.text)        return response, support_ticket    return response, None

Il codice completo e finale di tutto ciò dovrebbe apparire così:

from dotenv import load_dotenvload_dotenv()from pydantic import BaseModelfrom typing import Literal, Optionalimport tanukiclass Tweet(BaseModel):    """    Oggetto Tweet    Il nome è il nome dell'utente    L'issue è il messaggio che hanno inviato    """    name: str    text: str    id: strclass Response(BaseModel):    """    Oggetto di risposta, dove l'attributo di risposta è la risposta inviata al cliente     requires_ticket è un booleano che indica se il tweet in arrivo era una domanda o un problema diretto     che richiederebbe un intervento e un'azione umana    """    requires_ticket: bool    response: str class SupportTicket(BaseModel):    """    Oggetto ticket di supporto, dove     issue è una breve descrizione del problema che l'utente ha avuto     urgency indica quanto urgentemente il team dovrebbe rispondere al problema    """    issue: str    urgency: Literal["low", "VoAGI", "high"]# creazione della [email protected] classify_and_respond(tweet: Tweet) -> Response:    """    Rispondi al tweet di supporto del cliente in modo empatico e gentile.     Fai capire che ti preoccupi del problema e che se il problema era un problema diretto che il team di supporto dovrebbe risolvere o una domanda, il team risponderà.     """@tanuki.aligndef align_respond():    input_tweet_1 = Tweet(name = "Laia Johnson",                          text = "Mi piace davvero la nuova pala, ma l'impugnatura si è rotta dopo 2 giorni di utilizzo. Posso avere un sostituto?",                          id = "123")    assert classify_and_respond(input_tweet_1) == Response(                                                            requires_ticket=True,                                                             response="Ciao, ci dispiace sentire ciò. Ti risponderemo al più presto con un sostituto, puoi inviarci il tuo numero di ordine?"                                                            )    input_tweet_2 = Tweet(name = "Keira Townsend",                          text = "Odio il nuovo design dell'iPhone. È così brutto. Passo a Samsung",                          id = "10pa")    assert classify_and_respond(input_tweet_2) == Response(                                                            requires_ticket=False,                                                             response="Ciao, ci dispiace sentire ciò. Prenderemo in considerazione il tuo feedback e lo comunicheremo al team di prodotto"                                                            )    input_tweet_3 = Tweet(name = "Thomas Bell",                          text = "@Amazonsupport. Ho una domanda sull'ordine, consegnate in Finlandia?",                          id = "test")    assert classify_and_respond(input_tweet_3) == Response(                                                            requires_ticket=True,                                                             response="Ciao, grazie per il contatto. La tua domanda verrà inviata al nostro team di supporto e ti risponderanno al più presto"                                                            )    input_tweet_4 = Tweet(name = "Jillian Murphy",                          text = "Ho appena comprato la nuova goodybox e finora mi piace molto!",                          id = "009")    assert classify_and_respond(input_tweet_4) == Response(                                                            requires_ticket=False,                                                             response="Ciao, grazie per il contatto. Siamo felici di sapere che ti sta piacendo il prodotto"                                                                        )# creazione del ticket di [email protected] create_support_ticket(tweet_text: str) -> SupportTicket:    """    Utilizzando il testo del tweet, crea un ticket di supporto da salvare nel database interno    Crea un breve riassunto dell'azione da intraprendere e dell'urgenza del problema    """@tanuki.aligndef align_supportticket():    input_tweet_1 = "Mi piace davvero la nuova pala, ma l'impugnatura si è rotta dopo 2 giorni di utilizzo. Posso avere un sostituto?"    assert create_support_ticket(input_tweet_1) == SupportTicket(                                                                issue="Serve un prodotto di sostituzione perché l'impugnatura si è rotta",                                                                 urgency="high"                                                                )    input_tweet_2 = "@Amazonsupport. Ho una domanda sull'ordine, consegnate in Finlandia?"    assert create_support_ticket(input_tweet_2) == SupportTicket(                                                                issue="Scopri e rispondi se attualmente consegniamo in Finlandia",                                                                urgency="low"                                                                )    input_tweet_3 = "Ho appena comprato la nuova goodybox e finora mi piace molto! L'imballaggio della crema era leggermente danneggiato, però, vorrei che fosse sostituito"    assert create_support_ticket(input_tweet_3) == SupportTicket(                                                                issue="Serve una nuova crema poiché l'imballaggio era leggermente danneggiato",                                                                 urgency="VoAGI"                                                                )    # funzione finale per il flusso di lavorodef analyse_and_respond(tweet: Tweet) -> tuple[Optional[SupportTicket], Response]:    # ottieni la risposta    response = classify_and_respond(tweet)    # se la risposta richiede un ticket, crea un ticket    if response.requires_ticket:        support_ticket = create_support_ticket(tweet.text)        return response, support_ticket    return response, None

Ecco fatto! Ora possiamo testare il flusso di lavoro con un paio di esempi.

def main():    """    Questa funzione analizza il tweet in ingresso e restituisce una risposta e, se necessario, un ticket    """    # inizia chiamando gli allineamenti per registrare le dichiarazioni di allineamento    align_respond()    align_supportticket()    input_tweet_1 = Tweet(nome = "Jack Bell",                          testo = "Bro @Argos perché non è arrivato il mio ordine? L'ho ordinato 2 settimane fa. Servizio orribile",                          id = "1")    risposta, ticket = analizza_e_rispondi(input_tweet_1)        print(risposta)    # requires_ticket=True     # response="Ciao Jack, siamo davvero spiacenti di sentire ciò. Lo verificheremo subito e ti risponderemo al più presto."        print(ticket)    # issue="L'ordine del cliente non è arrivato dopo 2 settimane"    # urgency='alta'    input_tweet_2 = Tweet(nome = "Casey Montgomery",                          testo = "@Argos Il tempo di consegna sarebbe stato di 3 settimane ma mi è stato promesso 1. Non mi piace.",                          id = "12")    risposta, ticket = analizza_e_rispondi(input_tweet_2)        print(risposta)    # requires_ticket=True     # response="Ciao Casey, siamo davvero spiacenti per il ritardo nella consegna. Verificheremo questo problema e ti risponderemo al più presto."        print(ticket)    # issue='Il tempo di consegna è stato più lungo di quanto promesso'    # urgency='VoAGI'    input_tweet_3 = Tweet(nome = "Jacks Parrow",                          testo = "@Argos Il nuovo logo sembra piuttosto brutto, chissà perché l'hanno cambiato",                          id = "1123")    risposta, ticket = analizza_e_rispondi(input_tweet_3)    print(risposta)    # requires_ticket=False     # response="Ciao Jacks Parrow, ci dispiace che tu non apprezzi il nuovo logo. Faremo arrivare il tuo feedback al team competente. Grazie per avercelo fatto sapere."        print(ticket)    # None

Sembra che funzioni come previsto! Le risposte sembrano buone, seguendo il tono che abbiamo allineato, e i ticket vengono creati quando necessario con l’urgenza appropriata.

E tutto ciò ci ha richiesto meno di mezz’ora per essere realizzato.

Quale Sarà il Prossimo Passo

Sebbene questo fosse solo un piccolo esempio, dimostra quanto facilmente gli sviluppatori possono creare funzioni e app supportate da LLM utilizzando Tanuki.

Se ti sembra interessante e desideri saperne di più (o, ancora meglio, partecipare), ti preghiamo di unirti al nostro Discord. Questo è stato solo un caso d’uso che abbiamo creato, puoi trovarne altri qui (ad esempio, creare dati strutturati da web scraping, creare un’app di lista “To-Do” a partire da descrizioni di testo naturale, bloccare linguaggio profondo e molto altro).

Se hai delle domande, fammelo sapere qui nei commenti o su Discord. Parleremo presto!