1. Panoramica
In diversi settori, la ricerca contestuale è una funzionalità fondamentale che costituisce il cuore delle applicazioni. La generazione di risultati avanzata è un fattore chiave di questa evoluzione tecnologica fondamentale da un po' di tempo, grazie ai suoi meccanismi di recupero basati sull'IA generativa. I modelli generativi, con le loro ampie finestre di contesto e la straordinaria qualità dell'output, stanno trasformando l'IA. La RAG fornisce un modo sistematico per inserire il contesto nelle applicazioni e negli agenti di IA, basandoli su database strutturati o informazioni provenienti da vari media. Questi dati contestuali sono fondamentali per la chiarezza della verità e l'accuratezza dell'output, ma quanto sono precisi questi risultati? La tua attività dipende in gran parte dall'accuratezza di queste corrispondenze contestuali e dalla pertinenza? Allora questo progetto fa al caso tuo.
Il segreto della ricerca di vettori non è solo crearla, ma sapere se le corrispondenze dei vettori sono effettivamente buone. A tutti è capitato di fissare un elenco di risultati chiedendosi: "Ma funziona?" Scopri come valutare la qualità delle corrispondenze di vettori. "Quindi, cosa è cambiato nel RAG?", ti starai chiedendo. Tutto! Per anni, la Retrieval Augmented Generation (RAG) è sembrata un obiettivo promettente, ma sfuggente. Ora, finalmente, abbiamo gli strumenti per creare applicazioni RAG con le prestazioni e l'affidabilità necessarie per le attività di importanza cruciale.
Ora abbiamo già le nozioni di base su tre aspetti:
- Che cosa significa la ricerca contestuale per il tuo agente e come realizzarla utilizzando Vector Search.
- Abbiamo anche approfondito la ricerca di vettori nell'ambito dei tuoi dati, ovvero all'interno del tuo database stesso (se non lo sapevi, tutti i database Google Cloud supportano questa funzionalità).
- Abbiamo fatto un passo avanti rispetto al resto del mondo per spiegarti come realizzare una funzionalità RAG di ricerca vettoriale così leggera con prestazioni e qualità elevate con la funzionalità di ricerca vettoriale di AlloyDB basata sull'indice ScaNN.
Se non hai ancora esaminato questi esperimenti RAG di base, intermedi e leggermente avanzati, ti invitiamo a leggerli qui, qui e qui nell'ordine indicato.
La ricerca di brevetti aiuta l'utente a trovare brevetti pertinenti dal punto di vista del contesto per il testo di ricerca e ne abbiamo già creato una versione in passato. Ora la creeremo con funzionalità RAG nuove e avanzate che consentono una ricerca contestuale controllata per la qualità dell'applicazione. Iniziamo.
L'immagine seguente mostra il flusso generale di ciò che accade in questa applicazione.~
Obiettivo
Consenti a un utente di cercare brevetti in base a una descrizione testuale con prestazioni e qualità migliorate, il tutto mantenendo la possibilità di valutare la qualità delle corrispondenze generate utilizzando le ultime funzionalità RAG di AlloyDB.
Cosa creerai
In questo lab imparerai a:
- Crea un'istanza AlloyDB e carica il set di dati pubblico dei brevetti
- Creare l'indice dei metadati e l'indice ScaNN
- Implementare la ricerca di vettori avanzata in AlloyDB utilizzando il metodo di filtro in linea di ScaNN
- Implementare la funzionalità di valutazione del richiamo
- Valutare la risposta alla query
Requisiti
2. Prima di iniziare
Crea un progetto
- Nella console Google Cloud, nella pagina di selezione del progetto, seleziona o crea un progetto Google Cloud.
- Verifica che la fatturazione sia attivata per il tuo progetto Cloud. Scopri come verificare se la fatturazione è attivata in un progetto .
- Utilizzerai Cloud Shell, un ambiente a riga di comando in esecuzione in Google Cloud. Fai clic su Attiva Cloud Shell nella parte superiore della console Google Cloud.
- Una volta connesso a Cloud Shell, verifica di aver già eseguito l'autenticazione e che il progetto sia impostato sul tuo ID progetto utilizzando il seguente comando:
gcloud auth list
- Esegui il seguente comando in Cloud Shell per verificare che il comando gcloud conosca il tuo progetto.
gcloud config list project
- Se il progetto non è impostato, utilizza il seguente comando per impostarlo:
gcloud config set project <YOUR_PROJECT_ID>
- Abilita le API richieste. Puoi utilizzare un comando gcloud nel terminale Cloud Shell:
gcloud services enable alloydb.googleapis.com compute.googleapis.com cloudresourcemanager.googleapis.com servicenetworking.googleapis.com run.googleapis.com cloudbuild.googleapis.com cloudfunctions.googleapis.com aiplatform.googleapis.com
L'alternativa al comando gcloud è tramite la console cercando ciascun prodotto o utilizzando questo link.
Consulta la documentazione per i comandi e l'utilizzo di gcloud.
3. Configurazione del database
In questo lab utilizzeremo AlloyDB come database per i dati dei brevetti. Utilizza i cluster per contenere tutte le risorse, come database e log. Ogni cluster ha un'istanza principale che fornisce un punto di accesso ai dati. Le tabelle conterranno i dati effettivi.
Creiamo un cluster, un'istanza e una tabella AlloyDB in cui verrà caricato il set di dati dei brevetti.
Crea un cluster e un'istanza
- Esplora la pagina AlloyDB nella console Cloud. Un modo semplice per trovare la maggior parte delle pagine in Cloud Console è cercarle utilizzando la barra di ricerca della console.
- Seleziona CREA CLUSTER in quella pagina:
- Viene visualizzata una schermata come quella riportata di seguito. Crea un cluster e un'istanza con i seguenti valori (assicurati che i valori corrispondano nel caso in cui stia clonando il codice dell'applicazione dal repository):
- cluster id: "
vector-cluster
" - password: "
alloydb
" - PostgreSQL 15 / ultima versione consigliata
- Regione: "
us-central1
" - Networking: "
default
"
- Quando selezioni la rete predefinita, viene visualizzata una schermata come quella riportata di seguito.
Seleziona CONFIGURA CONNESSIONE.
- Da qui, seleziona "Utilizza un intervallo IP allocato automaticamente" e Continua. Dopo aver esaminato le informazioni, seleziona CREA CONNESSIONE.
- Una volta configurata la rete, puoi continuare a creare il cluster. Fai clic su CREA CLUSTER per completare la configurazione del cluster come mostrato di seguito:
Assicurati di modificare l'ID istanza (che puoi trovare al momento della configurazione del cluster / dell'istanza) in
vector-instance
. Se non puoi modificarlo, ricordati di utilizzare l'ID istanza in tutti i riferimenti futuri.
Tieni presente che la creazione del cluster richiede circa 10 minuti. Al termine dell'operazione, dovresti visualizzare una schermata che mostra la panoramica del cluster appena creato.
4. Importazione dati
Ora è il momento di aggiungere una tabella con i dati del negozio. Vai ad AlloyDB, seleziona il cluster principale e poi AlloyDB Studio:
Potresti dover attendere il completamento della creazione dell'istanza. Una volta completata la procedura, accedi ad AlloyDB utilizzando le credenziali che hai creato durante la creazione del cluster. Utilizza i seguenti dati per l'autenticazione in PostgreSQL:
- Nome utente : "
postgres
" - Database : "
postgres
" - Password : "
alloydb
"
Una volta eseguita l'autenticazione in AlloyDB Studio, i comandi SQL vengono inseriti nell'editor. Puoi aggiungere più finestre di Editor utilizzando il segno Più a destra dell'ultima finestra.
Inserisci i comandi per AlloyDB nelle finestre dell'editor, utilizzando le opzioni Esegui, Formatta e Cancella, se necessario.
Attivare le estensioni
Per creare questa app, utilizzeremo le estensioni pgvector
e google_ml_integration
. L'estensione pgvector ti consente di archiviare ed eseguire ricerche di incorporamenti vettoriali. L'estensione google_ml_integration fornisce le funzioni che utilizzi per accedere agli endpoint di previsione di Vertex AI per ottenere le previsioni in SQL. Attiva queste estensioni eseguendo i seguenti DDL:
CREATE EXTENSION IF NOT EXISTS google_ml_integration CASCADE;
CREATE EXTENSION IF NOT EXISTS vector;
Se vuoi controllare le estensioni che sono state attivate nel tuo database, esegui questo comando SQL:
select extname, extversion from pg_extension;
Creare una tabella
Puoi creare una tabella utilizzando l'istruzione DDL riportata di seguito in AlloyDB Studio:
CREATE TABLE patents_data ( id VARCHAR(25), type VARCHAR(25), number VARCHAR(20), country VARCHAR(2), date VARCHAR(20), abstract VARCHAR(300000), title VARCHAR(100000), kind VARCHAR(5), num_claims BIGINT, filename VARCHAR(100), withdrawn BIGINT, abstract_embeddings vector(768)) ;
La colonna abstract_embeddings consente di archiviare i valori vettore del testo.
Concedi autorizzazione
Esegui l'istruzione riportata di seguito per concedere l'esecuzione alla funzione "embedding":
GRANT EXECUTE ON FUNCTION embedding TO postgres;
Concedi il RUOLO Utente Vertex AI all'account di servizio AlloyDB
Nella console IAM di Google Cloud, concedi all'account di servizio AlloyDB (che ha il seguente aspetto: service-<<PROJECT_NUMBER>>@gcp-sa-alloydb.iam.gserviceaccount.com) l'accesso al ruolo "Utente Vertex AI". PROJECT_NUMBER avrà il numero del progetto.
In alternativa, puoi eseguire il comando seguente dal terminale Cloud Shell:
PROJECT_ID=$(gcloud config get-value project)
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:service-$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")@gcp-sa-alloydb.iam.gserviceaccount.com" \
--role="roles/aiplatform.user"
Carica i dati dei brevetti nel database
Come set di dati utilizzeremo i set di dati pubblici di Google Patents su BigQuery. Utilizzeremo AlloyDB Studio per eseguire le query. I dati provengono da questo file insert_scripts.sql
e lo eseguiremo per caricare i dati del brevetto.
- Nella console Google Cloud, apri la pagina AlloyDB.
- Seleziona il cluster appena creato e fai clic sull'istanza.
- Nel menu di navigazione di AlloyDB, fai clic su AlloyDB Studio. Accedi con le tue credenziali.
- Apri una nuova scheda facendo clic sull'icona Nuova scheda a destra.
- Copia l'istruzione di query
insert
dallo scriptinsert_scripts.sql
sopra indicato nell'editor. Puoi copiare 10-50 istruzioni INSERT per una rapida dimostrazione di questo caso d'uso. - Fai clic su Esegui. I risultati della query vengono visualizzati nella tabella Risultati.
Nota:potresti notare che lo script di inserimento contiene molti dati. Questo perché abbiamo incluso gli incorporamenti negli script di inserimento. Fai clic su "Visualizza non elaborati" in caso di problemi di caricamento del file su GitHub. Questo viene fatto per evitare (nei passaggi successivi) di generare più di alcuni embedding (diciamo 20-25 al massimo) nel caso in cui tu stia utilizzando un account di fatturazione con credito di prova per Google Cloud.
5. Creare incorporamenti per i dati dei brevetti
Innanzitutto, testiamo la funzione di embedding eseguendo la seguente query di esempio:
SELECT embedding('text-embedding-005', 'AlloyDB is a managed, cloud-hosted SQL database service.');
Dovrebbe restituire il vettore di incorporamenti, che ha la forma di un array di valori float, per il testo di esempio nella query. Ha il seguente aspetto:
Aggiorna il campo del vettore abstract_embeddings
Esegui il seguente DML per aggiornare gli abstract dei brevetti nella tabella con gli embedding corrispondenti solo se non hai inserito i dati di abstract_embeddings all'interno dello script di inserimento:
UPDATE patents_data set abstract_embeddings = embedding( 'text-embedding-005', abstract);
Potresti riscontrare problemi di generazione di più di alcuni embedding (ad esempio 20-25 al massimo) se utilizzi un account di fatturazione con credito di prova per Google Cloud. Per questo motivo, ho già incluso gli embedding negli script di inserimento e dovresti averli caricati nella tabella se hai completato il passaggio "Carica i dati dei brevetti nel database".
6. Esegui RAG avanzata con le nuove funzionalità di AlloyDB
Ora che la tabella, i dati e gli incorporamenti sono pronti, eseguiamo la ricerca vettoriale in tempo reale per il testo di ricerca dell'utente. Per verificare, esegui la query riportata di seguito:
SELECT id || ' - ' || title as title FROM patents_data ORDER BY abstract_embeddings <=> embedding('text-embedding-005', 'Sentiment Analysis')::vector LIMIT 10;
In questa query,
- Il testo cercato dall'utente è: "Analisi del sentiment".
- Lo stiamo convertendo in embedding nel metodo embedding() utilizzando il modello: text-embedding-005.
- "<=>" indica l'utilizzo del metodo di distanza SIMILARITÀ COSENO.
- Stiamo convertendo il risultato del metodo di embedding in tipo di vettore per renderlo compatibile con i vettori archiviati nel database.
- LIMIT 10 indica che selezioniamo le 10 corrispondenze più strette del testo di ricerca.
AlloyDB porta la ricerca vettoriale RAG a un livello superiore:
Sono state introdotte diverse novità. Due di questi, incentrati sugli sviluppatori, sono:
- Filtro in linea
- Valutatore del richiamo
Filtro in linea
In precedenza, in qualità di sviluppatore, dovevi eseguire la query di ricerca vettoriale e occuparti del filtro e del recupero. Lo strumento di ottimizzazione delle query di AlloyDB effettua delle scelte su come eseguire una query con filtri. Il filtro in linea è una nuova tecnica di ottimizzazione delle query che consente all'ottimizzatore delle query AlloyDB di valutare sia le condizioni di filtro dei metadati sia la ricerca di vettori, sfruttando sia gli indici di vettori sia gli indici delle colonne dei metadati. In questo modo, il rendimento del richiamo è aumentato, consentendo agli sviluppatori di sfruttare ciò che AlloyDB ha da offrire immediatamente.
Il filtro in linea è ideale per i casi con una selettività media. Quando AlloyDB esegue ricerche nell'indice vettoriale, calcola le distanze solo per i vettori che corrispondono alle condizioni di filtro dei metadati (i filtri funzionali in una query di solito gestiti nella clausola WHERE). In questo modo, le prestazioni di queste query migliorano notevolmente, completando i vantaggi del filtro post o pre.
- Installa o aggiorna l'estensione pgvector
CREATE EXTENSION IF NOT EXISTS vector WITH VERSION '0.8.0.google-3';
Se l'estensione pgvector è già installata, esegui l'upgrade all'estensione del vettore alla versione 0.8.0.google-3 o successiva per ottenere le funzionalità di valutazione del richiamo.
ALTER EXTENSION vector UPDATE TO '0.8.0.google-3';
Questo passaggio deve essere eseguito solo se l'estensione di vettore è <0.8.0.google-3>.
Nota importante:se il numero di righe è inferiore a 100, non è necessario creare l'indice ScaNN perché non verrà applicato a un numero inferiore di righe. In questo caso, salta i passaggi che seguono.
- Per creare indici ScaNN, installa l'estensione alloydb_scann.
CREATE EXTENSION IF NOT EXISTS alloydb_scann;
- Innanzitutto, esegui la query di ricerca vettoriale senza l'indice e senza il filtro in linea attivato:
SELECT id || ' - ' || title as title FROM patents_data
WHERE num_claims >= 15
ORDER BY abstract_embeddings <=> embedding('text-embedding-005', 'Sentiment Analysis')::vector LIMIT 10;
Il risultato dovrebbe essere simile al seguente:
- Esegui Explain Analyze (senza indice né filtro in linea):
Il tempo di esecuzione è di 2,4 ms
- Creiamo un indice regolare sul campo num_claims in modo da poter applicare un filtro in base a questo campo:
CREATE INDEX idx_patents_data_num_claims ON patents_data (num_claims);
- Creiamo l'indice ScaNN per la nostra applicazione di ricerca di brevetti. Esegui quanto segue da AlloyDB Studio:
CREATE INDEX patent_index ON patents_data
USING scann (abstract_embeddings cosine)
WITH (num_leaves=32);
Nota importante : (num_leaves=32)
si applica al nostro set di dati totale con più di 1000 righe. Se il numero di righe è inferiore a 100, non è necessario creare un indice in quanto non verrà applicato per un numero inferiore di righe.
- Imposta il filtro in linea attivato nell'indice ScaNN:
SET scann.enable_inline_filtering = on
- Ora eseguiamo la stessa query con il filtro e la ricerca vettoriale:
SELECT id || ' - ' || title as title FROM patents_data
WHERE num_claims >= 15
ORDER BY abstract_embeddings <=> embedding('text-embedding-005', 'Sentiment Analysis')::vector LIMIT 10;
Come puoi vedere, il tempo di esecuzione è notevolmente ridotto per la stessa ricerca di vettori. Il filtro in linea integrato nell'indice ScaNN in Vector Search ha reso tutto ciò possibile.
A questo punto, valutiamo il recupero per questa ricerca vettoriale abilitata a ScaNN.
Valutatore del richiamo
Il richiamo nella ricerca di similarità è la percentuale di istanze pertinenti recuperate da una ricerca, ovvero il numero di veri positivi. Si tratta della metrica più comune utilizzata per misurare la qualità della ricerca. Una fonte di perdita del richiamo deriva dalla differenza tra la ricerca del vicino più prossimo approssimativa (ANN) e la ricerca del vicino più prossimo esatto (KNN). Gli indici vettoriali come ScaNN di AlloyDB implementano algoritmi ANN, consentendo di velocizzare la ricerca vettoriale su set di dati di grandi dimensioni in cambio di un piccolo compromesso in termini di richiamo. Ora AlloyDB ti offre la possibilità di misurare questo compromesso direttamente nel database per le singole query e di assicurarti che sia stabile nel tempo. Puoi aggiornare i parametri di query e indice in base a queste informazioni per ottenere risultati e prestazioni migliori.
Qual è la logica alla base del richiamo dei risultati di ricerca?
Nel contesto della ricerca vettoriale, il richiamo si riferisce alla percentuale di vettori restituiti dall'indice che sono veri vicini più vicini. Ad esempio, se una query del vicino più prossimo per i 20 vicini più prossimi restituisce 19 dei vicini più prossimi del vero valore, il richiamo è 19/20 x 100 = 95%. Il ricordo è la metrica utilizzata per la qualità della ricerca ed è definita come la percentuale di risultati restituiti oggettivamente più vicini ai vettori di query.
Puoi trovare il richiamo per una query vettoriale su un indice vettoriale per una determinata configurazione utilizzando la funzione evaluate_query_recall. Questa funzione ti consente di ottimizzare i parametri per ottenere i risultati di recupero delle query vettoriali che preferisci.
Nota importante:
Se nei passaggi successivi riscontri un errore di autorizzazione negata nell'indice HNSW, per il momento salta l'intera sezione relativa alla valutazione del richiamo. Potrebbe essere dovuto a restrizioni di accesso, dato che è stato appena rilasciato al momento della documentazione di questo codelab.
- Imposta il flag Enable Index Scan sull'indice ScaNN e sull'indice HNSW:
SET scann.enable_indexscan = on
SET hnsw.enable_index_scan = on
- Esegui la seguente query in AlloyDB Studio:
SELECT
*
FROM
evaluate_query_recall($$
SELECT
id || ' - ' || title AS title,
abstract
FROM
patents_data
where num_claims >= 15
ORDER BY
abstract_embeddings <=> embedding('text-embedding-005',
'sentiment analysis')::vector
LIMIT 25 $$,
'{"scann.num_leaves_to_search":1, "scann.pre_reordering_num_neighbors":10}',
ARRAY['scann']);
La funzione evaluate_query_recall riceve la query come parametro e restituisce il relativo richiamo. Sto utilizzando la stessa query utilizzata per controllare il rendimento come query di input della funzione. Ho aggiunto SCaNN come metodo di indicizzazione. Per altre opzioni di parametro, consulta la documentazione.
Il recupero per questa query di ricerca vettoriale che abbiamo utilizzato:
Vedo che il valore RECALL è 70%. Ora posso utilizzare queste informazioni per modificare i parametri dell'indice, i metodi e i parametri di query e migliorare il recupero per questa ricerca vettoriale.
7. Testa con i parametri di query e indice modificati
Ora testiamo la query modificando i parametri di query in base al richiamo ricevuto.
- Ho modificato il numero di righe nel set di risultati impostandolo su 7 (in precedenza 25) e ho notato un miglioramento del RECALL, ovvero dell'86%.
Ciò significa che in tempo reale posso variare il numero di corrispondenze che i miei utenti possono vedere per migliorare la pertinenza delle corrispondenze in base al contesto di ricerca degli utenti.
- Riproviamo modificando i parametri dell'indice:
Per questo test, utilizzerò la funzione di distanza di somiglianza "L2 Distance" anziché "Cosine". Modificherò anche il limite della query su 10 per mostrare se c'è un miglioramento della qualità dei risultati di ricerca anche con un aumento del numero di set di risultati di ricerca.
[PRIMA] Query che utilizza la funzione di distanza di somiglianza di Cosine:
SELECT
*
FROM
evaluate_query_recall($$
SELECT
id || ' - ' || title AS title,
abstract
FROM
patents_data
where num_claims >= 15
ORDER BY
abstract_embeddings <=> embedding('text-embedding-005',
'sentiment analysis')::vector
LIMIT 10 $$,
'{"scann.num_leaves_to_search":1, "scann.pre_reordering_num_neighbors":10}',
ARRAY['scann']);
Nota molto importante: potresti chiederti "Come faccio a sapere che questa query utilizza la somiglianza COSINE?" Puoi identificare la funzione di distanza utilizzando "<=>" per rappresentare la distanza di coseno.
Link ai documenti per le funzioni di distanza di Ricerca vettoriale.
Il risultato della query precedente è:
Come puoi vedere, il RECALL è pari al 70% senza alcuna modifica alla logica dell'indice. Ricordi l'indice ScaNN che abbiamo creato nel passaggio 6 della sezione Filtri in linea, "patent_index
"? Lo stesso indice è ancora valido mentre abbiamo eseguito la query precedente.
Ora creiamo un indice con una query della funzione di distanza diversa: Distanza L2: <->
drop index patent_index;
CREATE INDEX patent_index_L2 ON patents_data
USING scann (abstract_embeddings L2)
WITH (num_leaves=32);
L'istruzione DROP INDEX serve solo per assicurarsi che non esistano indici non necessari nella tabella.
Ora posso eseguire la seguente query per valutare il RECUPERO dopo aver modificato la funzione di distanza della mia funzionalità di ricerca vettoriale.
[DOPO] Query che utilizza la funzione di distanza di somiglianza di Cosine:
SELECT
*
FROM
evaluate_query_recall($$
SELECT
id || ' - ' || title AS title,
abstract
FROM
patents_data
where num_claims >= 15
ORDER BY
abstract_embeddings <-> embedding('text-embedding-005',
'sentiment analysis')::vector
LIMIT 10 $$,
'{"scann.num_leaves_to_search":1, "scann.pre_reordering_num_neighbors":10}',
ARRAY['scann']);
Il risultato della query precedente è:
Che trasformazione nel valore di richiamo, 90%!!!
Esistono altri parametri che puoi modificare nell'indice, ad esempio num_leaves e così via, in base al valore di recupero desiderato e al set di dati utilizzato dalla tua applicazione.
8. Esegui la pulizia
Per evitare che al tuo account Google Cloud vengano addebitati costi relativi alle risorse utilizzate in questo post, segui questi passaggi:
- Nella console Google Cloud, vai alla pagina Gestione risorse.
- Nell'elenco dei progetti, seleziona il progetto che vuoi eliminare, quindi fai clic su Elimina.
- Nella finestra di dialogo, digita l'ID progetto, quindi fai clic su Chiudi per eliminare il progetto.
- In alternativa, puoi semplicemente eliminare il cluster AlloyDB (modifica la posizione in questo link ipertestuale se non hai scelto us-central1 per il cluster al momento della configurazione) che abbiamo appena creato per questo progetto facendo clic sul pulsante ELIMINA CLUSTER.
9. Complimenti
Complimenti! Hai creato correttamente la query di ricerca di brevetti contestuale con la ricerca vettoriale avanzata di AlloyDB per ottenere prestazioni elevate e renderla davvero basata sul significato. Ho creato un'applicazione agente multi-strumento con controllo qualità che utilizza ADK e tutti gli elementi di AlloyDB di cui abbiamo discusso qui per creare un agente di ricerca e analisi di vettori di brevetti di alta qualità e prestazioni elevate che puoi visualizzare qui: https://youtu.be/Y9fvVY0yZTY
Se vuoi scoprire come creare questo agente, consulta questo codelab.