Creare un agente dati utilizzando l'SDK Python

Questa pagina mostra come utilizzare l'SDK Python per effettuare richieste all'API Analisi conversazionale. L'esempio di codice Python mostra come completare le seguenti attività:

Autenticare e configurare l'ambiente

Per utilizzare l'SDK Python per l'API Analisi conversazionale, segui le istruzioni nel notebook Colaboratory dell'SDK dell'API Analisi conversazionale per scaricare e installare l'SDK. Tieni presente che il metodo di download e i contenuti di SDK Colab sono soggetti a modifica.

Dopo aver completato le istruzioni di configurazione nel notebook, puoi utilizzare il seguente codice per importare le librerie SDK richieste, autenticare il tuo Account Google in un ambiente Colaboratory e inizializzare un client per effettuare richieste API:

from google.colab import auth
auth.authenticate_user()

from google.cloud import geminidataanalytics

data_agent_client = geminidataanalytics.DataAgentServiceClient()
data_chat_client = geminidataanalytics.DataChatServiceClient()

Specifica il progetto di fatturazione e le istruzioni di sistema

Il seguente codice Python di esempio definisce il progetto di fatturazione, la località e le istruzioni di sistema utilizzate nello script:

# Billing project
billing_project = "my_project_name"
location = "global"

# System instructions
system_instruction = "Help the user analyze their data."

Sostituisci i valori di esempio come segue:

  • my_project_name: l'ID del tuo progetto di fatturazione in cui sono abilitate le API richieste.
  • Help the user analyze their data.: istruzioni di sistema per guidare il comportamento dell'agente e personalizzarlo in base alle tue esigenze di dati. Ad esempio, puoi utilizzare le istruzioni di sistema per definire i termini commerciali, controllare la lunghezza della risposta o impostare la formattazione dei dati. Idealmente, definisci le istruzioni di sistema utilizzando il formato YAML consigliato in Scrivere istruzioni di sistema efficaci per fornire indicazioni dettagliate e strutturate.

Connetti a un'origine dati

Le sezioni seguenti mostrano come definire i dettagli di connessione per le origini dati dell'agente. L'agente può connettersi ai dati nei seguenti modi:

Connettersi ai dati di Looker

I seguenti esempi di codice mostrano come definire i dettagli per una connessione a un'esplorazione di Looker con chiavi API o un token di accesso. Puoi collegare fino a cinque esplorazioni di Looker alla volta con l'API Analisi conversazionale.

Quando ti connetti a un'origine dati di Looker, tieni presente quanto segue:

  • Puoi eseguire query su qualsiasi esplorazione inclusa in una conversazione.
  • Un agente può eseguire query su un solo Explore alla volta. Non è possibile eseguire query su più esplorazioni contemporaneamente.
  • Un agente può eseguire query su più esplorazioni nella stessa conversazione.
  • Un agente può eseguire query su più esplorazioni in una conversazione che include domande in più parti o in conversazioni che includono domande di follow-up.

    Ad esempio, un utente collega due esplorazioni, una chiamata cat-explore e l'altra dog-explore. L'utente inserisce la domanda "Qual è maggiore: il numero di gatti o il numero di cani?" In questo modo vengono create due query: una per contare il numero di gatti in cat-explore e una per contare il numero di cani in dog-explore. L'agente confronta i numeri di entrambe le query dopo averle completate.

Il seguente codice campione definisce una connessione a più esplorazioni di Looker. Per migliorare le prestazioni dell'agente, puoi fornire facoltativamente query di riferimento come contesto strutturato per le tue esplorazioni. Per saperne di più, consulta Definire il contesto dell'agente dati per le origini dati di Looker.

Chiavi API

Puoi stabilire una connessione con un'istanza di Looker con le chiavi API Looker generate, come descritto in Autenticare e connettersi a un'origine dati con l'API Analisi conversazionale.

looker_client_id = "my_looker_client_id"
looker_client_secret = "my_looker_client_secret"
looker_instance_uri = "https://my_company.looker.com"
lookml_model_1 = "my_model"
explore_1 = "my_explore"
lookml_model_2 = "my_model_2"
explore_2 = "my_explore_2"

looker_explore_reference = geminidataanalytics.LookerExploreReference()
looker_explore_reference.looker_instance_uri = looker_instance_uri
looker_explore_reference.lookml_model = "my_model"
looker_explore_reference.explore = "my_explore"

looker_explore_reference2 = geminidataanalytics.LookerExploreReference()
looker_explore_reference2.looker_instance_uri = looker_instance_uri
looker_explore_reference2.lookml_model = "my_model_2"
looker_explore_reference2.explore = "my_explore_2"

credentials = geminidataanalytics.Credentials()
credentials.oauth.secret.client_id = looker_client_id
credentials.oauth.secret.client_secret = looker_client_secret

datasource_references = geminidataanalytics.DatasourceReferences()
datasource_references.looker.explore_references = [looker_explore_reference, looker_explore_reference2]

# Do not include the following line during agent creation
datasource_references.credentials = credentials

Sostituisci i valori di esempio come segue:

  • my_looker_client_id: l'ID client della chiave API Looker generata.
  • my_looker_client_secret: il client secret della chiave API Looker generata.
  • https://my_company.looker.com: l'URL completo della tua istanza di Looker.
  • my_model: il nome del modello LookML che include l'Esplorazione a cui vuoi connetterti.
  • my_explore: il nome dell'esplorazione di Looker che vuoi che l'agente dati interroghi.
  • my_model_2: il nome del secondo modello LookML che include l'Explore a cui vuoi connetterti. Puoi ripetere questa variabile per altri modelli fino a un massimo di cinque esplorazioni.
  • my_explore_2: il nome dell'esplorazione di Looker aggiuntiva che vuoi che l'agente dati interroghi. Puoi ripetere questa variabile per includere fino a cinque esplorazioni.

Token di accesso

Puoi stabilire una connessione con un'istanza di Looker utilizzando un token di accesso, come descritto in Autenticare e connettersi a un'origine dati con l'API Analisi conversazionale.

looker_access_token = "my_access_token"
looker_instance_uri = "https://my_company.looker.com"
lookml_model = "my_model"
explore = "my_explore"
lookml_model_2 = "my_model_2"
explore_2 = "my_explore_2"

looker_explore_reference = geminidataanalytics.LookerExploreReference()
looker_explore_reference.looker_instance_uri = looker_instance_uri
looker_explore_reference.lookml_model = "my_model"
looker_explore_reference.explore = "my_explore"

looker_explore_reference2 = geminidataanalytics.LookerExploreReference()
looker_explore_reference2.looker_instance_uri = looker_instance_uri
looker_explore_reference2.lookml_model = "my_model_2"
looker_explore_reference2.explore = "my_explore_2"

credentials = geminidataanalytics.Credentials()
credentials.oauth.secret.client_id = looker_client_id
credentials.oauth.secret.client_secret = looker_client_secret

datasource_references = geminidataanalytics.DatasourceReferences()
datasource_references.looker.explore_references = [looker_explore_reference, looker_explore_reference2]

# Do not include the following line during agent creation
datasource_references.credentials = credentials

Sostituisci i valori di esempio come segue:

  • my_access_token: il valore access_token che generi per l'autenticazione in Looker.
  • https://my_company.looker.com: l'URL completo della tua istanza di Looker.
  • my_model: il nome del modello LookML che include l'Esplorazione a cui vuoi connetterti.
  • my_explore: il nome dell'esplorazione di Looker che vuoi che l'agente dati interroghi.
  • my_model_2: il nome del secondo modello LookML che include l'Explore a cui vuoi connetterti. Puoi ripetere questa variabile per altri modelli fino a un massimo di cinque esplorazioni.
  • my_explore_2: il nome dell'esplorazione di Looker aggiuntiva che vuoi che l'agente dati interroghi. Puoi ripetere questa variabile per includere fino a cinque esplorazioni.

Connettersi ai dati BigQuery

Con l'API Analisi conversazionale, non esistono limiti rigidi al numero di tabelle BigQuery a cui puoi connetterti. Tuttavia, il collegamento a un numero elevato di tabelle può ridurre l'accuratezza o farti superare il limite di token di input del modello.

Il seguente codice campione definisce una connessione a più tabelle BigQuery e include esempi di campi di contesto strutturato facoltativi. Per migliorare le prestazioni dell'agente, puoi fornire facoltativamente un contesto strutturato per le tue tabelle BigQuery, ad esempio descrizioni di tabelle e colonne, sinonimi, tag e query di esempio. Per saperne di più, consulta Definire il contesto dell'agente dati per le origini dati BigQuery.

bigquery_table_reference_1 = geminidataanalytics.BigQueryTableReference()
bigquery_table_reference_1.project_id = "my_project_id_1"
bigquery_table_reference_1.dataset_id = "my_dataset_id_1"
bigquery_table_reference_1.table_id = "my_table_id_1"
bigquery_table_reference_1.schema = geminidataanalytics.Schema()
bigquery_table_reference_1.schema.description = "my_table_description"
bigquery_table_reference_1.schema.fields = [
  geminidataanalytics.Field(
    name="my_column_name",
    description="my_column_description"
  )
]

bigquery_table_reference_2 = geminidataanalytics.BigQueryTableReference()
bigquery_table_reference_2.project_id = "my_project_id_2"
bigquery_table_reference_2.dataset_id = "my_dataset_id_2"
bigquery_table_reference_2.table_id = "my_table_id_2"

bigquery_table_reference_3 = geminidataanalytics.BigQueryTableReference()
bigquery_table_reference_3.project_id = "my_project_id_3"
bigquery_table_reference_3.dataset_id = "my_dataset_id_3"
bigquery_table_reference_3.table_id = "my_table_id_3"

# Connect to your data source
datasource_references = geminidataanalytics.DatasourceReferences()
datasource_references.bq.table_references = [bigquery_table_reference_1, bigquery_table_reference_2, bigquery_table_reference_3]

Sostituisci i valori di esempio come segue:

  • my_project_id_1: l'ID del progetto Google Cloud che contiene il set di dati e la tabella BigQuery a cui vuoi connetterti. Per connetterti a un set di dati pubblici, specifica bigquery-public-data.
  • my_dataset_id_1: l'ID del set di dati BigQuery. Ad esempio, san_francisco.
  • my_table_id_1: l'ID della tabella BigQuery. Ad esempio, street_trees.
  • my_table_description: una descrizione facoltativa dei contenuti e dello scopo della tabella.
  • my_column_name: il nome di una colonna nella tabella per cui stai fornendo una descrizione facoltativa.
  • my_column_description: una descrizione facoltativa dei contenuti e dello scopo della colonna.

Connettersi ai dati di Looker Studio

Il seguente codice campione definisce una connessione a un'origine dati di Looker Studio.

studio_datasource_id = "my_datasource_id"

studio_references = geminidataanalytics.StudioDatasourceReference()
studio_references.datasource_id = studio_datasource_id

## Connect to your data source
datasource_references.studio.studio_references = [studio_references]

Nell'esempio precedente, sostituisci my_datasource_id con l'ID origine dati.

Connettersi ai dati AlloyDB

Il seguente codice campione definisce una connessione a un database AlloyDB utilizzando l'SDK Python.

# Define the AlloyDB database reference
alloydb_database_reference = geminidataanalytics.AlloyDbDatabaseReference()
alloydb_database_reference.project_id = "PROJECT_ID"
alloydb_database_reference.region = "REGION"
alloydb_database_reference.cluster_id = "CLUSTER_ID"
alloydb_database_reference.instance_id = "INSTANCE_ID"
alloydb_database_reference.database_id = "DATABASE"
alloydb_database_reference.table_ids = ["TABLE_1_ID", "TABLE_2_ID"]

# Optional: Include this if you have created advanced context for the agent
agent_context_reference = geminidataanalytics.AgentContextReference()
agent_context_reference.context_set_id = f"projects/{billing_project}/locations/{location}/contextSets/your_context_set_id"

# Add the AlloyDB reference to the DatasourceReferences object
datasource_references = geminidataanalytics.DatasourceReferences()
datasource_references.alloydb.database_reference = alloydb_database_reference
# Optional:
# datasource_references.alloydb.agent_context_reference = agent_context_reference

Sostituisci i valori di esempio come segue:

  • PROJECT_ID: l'ID del progetto Google Cloud che contiene il cluster AlloyDB.
  • REGION: la regione in cui si trova il cluster AlloyDB, ad esempio us-central1.
  • CLUSTER_ID: l'ID del cluster AlloyDB.
  • INSTANCE_ID: l'ID dell'istanza AlloyDB.
  • DATABASE: il nome del database di destinazione, ad esempio financial.
  • TABLE_1_ID, TABLE_2_ID: un elenco delle tabelle nel database che l'agente dati suggerisce di utilizzare, ad esempio "loan", "client", "disp".
  • your_context_set_id: Se hai creato un contesto avanzato per l'agente, l'ID del set di contesto.

Connettiti ai dati Cloud SQL per MySQL

Per connetterti a un'istanza Cloud SQL per MySQL, utilizza la classe CloudSqlReference dell'SDK geminidataanalytics. Ciò comporta la specifica del progetto, dell'istanza, del database e, facoltativamente, delle tabelle specifiche che vuoi che l'agente prenda in considerazione.

Prima di connetterti ai dati Cloud SQL per MySQL, assicurati di eseguire l'autenticazione a Cloud SQL per MySQL utilizzando Identity and Access Management (IAM). Per saperne di più, consulta Autenticarsi in Cloud SQL per MySQL e Cloud SQL per PostgreSQL.

L'esempio seguente definisce una connessione a un database Cloud SQL per MySQL utilizzando l'SDK Python.

# Import the necessary library
from google.cloud import geminidataanalytics

# Define the Cloud SQL database reference
cloud_sql_database_reference = geminidataanalytics.CloudSqlDatabaseReference(
    project_id="PROJECT_ID",
    instance_id="INSTANCE_ID",
    database_id="DATABASE_ID",
    # Optional: Specify table IDs to guide the agent
    table_ids=["TABLE_1_ID", "TABLE_2_ID"]
)

# Create a CloudSqlReference object
cloud_sql_reference = geminidataanalytics.CloudSqlReference(
    database_reference=cloud_sql_database_reference
    # Optional: Include this if you have created advanced context for the agent
    # agent_context_reference=geminidataanalytics.AgentContextReference(
    # context_set_id=f"projects/{billing_project}/locations/{location}/contextSets/your_context_set_id"
    # )
)

# Add the Cloud SQL reference to the DatasourceReferences object
datasource_references = geminidataanalytics.DatasourceReferences()
datasource_references.cloud_sql = cloud_sql_reference

Sostituisci i valori di esempio come segue:

  • PROJECT_ID: l'ID del progetto Google Cloud contenente l'istanza Cloud SQL per MySQL.
  • INSTANCE_ID: l'ID dell'istanza Cloud SQL per MySQL.
  • DATABASE_ID: il nome del database di destinazione all'interno dell'istanza, ad esempio financial.
  • TABLE_1_ID, TABLE_2_ID: (facoltativo) un elenco di nomi di tabelle specifici nel database che l'agente dati è invitato a utilizzare, ad esempio "orders" e "customers".
  • your_context_set_id: Se hai creato un contesto avanzato per l'agente, l'ID del set di contesto.

Puoi utilizzare l'oggetto datasource_references quando crei o interagisci con l'agente dati.

Connettiti ai dati Cloud SQL per PostgreSQL

Per connetterti a un'istanza Cloud SQL per PostgreSQL, utilizza la classe CloudSqlReference dell'SDK geminidataanalytics. Ciò comporta la specifica del progetto, dell'istanza, del database e, facoltativamente, delle tabelle specifiche che vuoi che l'agente prenda in considerazione.

Prima di connetterti ai dati Cloud SQL per PostgreSQL, assicurati di eseguire l'autenticazione a Cloud SQL per PostgreSQL utilizzando IAM. Per saperne di più, consulta Autenticarsi in Cloud SQL per MySQL e Cloud SQL per PostgreSQL.

L'esempio seguente definisce una connessione a un database Cloud SQL per PostgreSQL utilizzando l'SDK Python.

# Import the necessary library
from google.cloud import geminidataanalytics

# Define the Cloud SQL database reference
cloud_sql_database_reference = geminidataanalytics.CloudSqlDatabaseReference(
    project_id="PROJECT_ID",
    instance_id="INSTANCE_ID",
    database_id="DATABASE_ID",
    # Optional: Specify table IDs to guide the agent
    table_ids=["TABLE_1_ID", "TABLE_2_ID"]
)

# Create a CloudSqlReference object
cloud_sql_reference = geminidataanalytics.CloudSqlReference(
    database_reference=cloud_sql_database_reference
    # Optional: Include this if you have created advanced context for the agent
    # agent_context_reference=geminidataanalytics.AgentContextReference(
    # context_set_id=f"projects/{billing_project}/locations/{location}/contextSets/your_context_set_id"
    # )
)

# Add the Cloud SQL reference to the DatasourceReferences object
datasource_references = geminidataanalytics.DatasourceReferences()
datasource_references.cloud_sql = cloud_sql_reference

Sostituisci i valori di esempio come segue:

  • PROJECT_ID: l'ID del progetto Google Cloud contenente l'istanza Cloud SQL per PostgreSQL.
  • INSTANCE_ID: l'ID dell'istanza Cloud SQL per PostgreSQL.
  • DATABASE_ID: il nome del database di destinazione all'interno dell'istanza, ad esempio postgres.
  • TABLE_1_ID, TABLE_2_ID: (facoltativo) un elenco di nomi di tabelle specifici all'interno del database che l'agente dati è invitato a utilizzare, ad esempio "users" e "products".
  • your_context_set_id: Se hai creato un contesto avanzato per l'agente, l'ID del set di contesto.

Puoi utilizzare l'oggetto datasource_references quando crei o interagisci con l'agente dati.

Connettiti ai dati Spanner

Per connetterti a un'istanza Spanner, utilizza la classe SpannerReference dell'SDK geminidataanalytics. Ciò comporta la specifica del progetto, dell'istanza, del database e, facoltativamente, delle tabelle specifiche che vuoi che l'agente prenda in considerazione.

Prima di connetterti ai dati di Spanner, assicurati che l'utente o l'account di servizio disponga del ruolo IAM spanner.databaseReader. Per saperne di più, consulta Applicare i ruoli IAM.

Il seguente codice Python di esempio definisce una connessione a un database Spanner utilizzando l'SDK Python.

# Import the necessary library
from google.cloud import geminidataanalytics

# Define the Spanner database reference
spanner_database_reference = geminidataanalytics.SpannerDatabaseReference(
    project_id="PROJECT_ID",
    instance_id="INSTANCE_ID",
    database_id="DATABASE_ID",
    # Optional: Specify table IDs to guide the agent
    table_ids=["TABLE_1_ID", "TABLE_2_ID"]
)

# Create a SpannerReference object
spanner_reference = geminidataanalytics.SpannerReference(
    database_reference=spanner_database_reference
    # Optional: Include this if you have created advanced context for the agent
    # agent_context_reference=geminidataanalytics.AgentContextReference(
    # context_set_id=f"projects/{billing_project}/locations/{location}/contextSets/your_context_set_id"
    # )
)

# Add the Spanner reference to the DatasourceReferences object
datasource_references = geminidataanalytics.DatasourceReferences()
datasource_references.spanner = spanner_reference

Sostituisci i valori di esempio come segue:

  • PROJECT_ID: l'ID del Google Cloud progetto contenente l'istanza Spanner.
  • INSTANCE_ID: l'ID dell'istanza Spanner.
  • DATABASE_ID: il nome del database di destinazione nell'istanza.
  • TABLE_1_ID, TABLE_2_ID: (facoltativo) un elenco di nomi di tabelle specifici all'interno del database che l'agente dati è invitato a utilizzare, ad esempio "Singers" e "Albums".
  • your_context_set_id: Se hai creato un contesto avanzato per l'agente, l'ID del set di contesto.

Puoi utilizzare l'oggetto datasource_references quando crei o interagisci con l'agente dati.

Configurare il contesto per la chat stateful o stateless

L'API Analisi conversazionale supporta conversazioni a più turni, che consentono agli utenti di porre domande aggiuntive basate sul contesto precedente. Il seguente codice Python di esempio mostra come configurare il contesto per la chat con stato o senza stato:

  • Chat con stato: Google Cloud memorizza e gestisce la cronologia delle conversazioni. La chat stateful è intrinsecamente a più turni, in quanto l'API conserva il contesto dei messaggi precedenti. Devi inviare solo il messaggio corrente per ogni turno.
  • Chat stateless: la tua applicazione gestisce la cronologia delle conversazioni. Devi includere l'intera cronologia della conversazione in ogni nuovo messaggio. Per esempi dettagliati su come gestire le conversazioni multi-turn in modalità stateless, vedi Creare una conversazione multi-turn stateless.

Chat stateful

Il seguente esempio di codice configura il contesto per la chat stateful, in cui Google Cloud memorizza e gestisce la cronologia delle conversazioni. Puoi anche abilitare facoltativamente l'analisi avanzata con Python includendo la riga published_context.options.analysis.python.enabled = True nel seguente codice campione.

# Set up context for stateful chat
published_context = geminidataanalytics.Context()
published_context.system_instruction = system_instruction
published_context.datasource_references = datasource_references
# Optional: To enable advanced analysis with Python, include the following line:
published_context.options.analysis.python.enabled = True

Chat stateless

Il seguente codice campione configura il contesto per la chat stateless, in cui devi inviare l'intera cronologia della conversazione con ogni messaggio. Puoi anche abilitare facoltativamente l'analisi avanzata con Python includendo la riga inline_context.options.analysis.python.enabled = True nel seguente codice campione.

# Set up context for stateless chat
# datasource_references.looker.credentials = credentials
inline_context = geminidataanalytics.Context()
inline_context.system_instruction = system_instruction
inline_context.datasource_references = datasource_references
# Optional: To enable advanced analysis with Python, include the following line:
inline_context.options.analysis.python.enabled = True

Crea un agente di dati

Il seguente codice Python di esempio effettua una richiesta API per creare un agente dati, che puoi quindi utilizzare per conversare sui tuoi dati. Puoi creare un agente in modo sincrono o asincrono. L'agente dati è configurato con l'origine dati, le istruzioni di sistema e il contesto specificati.

Puoi proteggere facoltativamente l'agente dati con CMEK fornendo un kms_key durante la creazione. La chiave di crittografia gestita dal cliente è supportata solo per le origini dati di Looker. Per saperne di più, vedi Chiavi di crittografia gestite dal cliente (CMEK).

Sincrona

  data_agent_id = "data_agent_1"

  # Optional: If using CMEK, replace the empty strings with your KMS key details.
  key_ring = ""    # KEY_RING_NAME
  key_name = ""    # KEY_NAME
  key_project = "" # KMS_PROJECT_ID (Defaults to billing_project if empty)

  data_agent = geminidataanalytics.DataAgent()
  data_agent.data_analytics_agent.published_context = published_context
  data_agent.name = f"projects/{billing_project}/locations/{location}/dataAgents/{data_agent_id}" # Optional

  # Optional: Add CMEK key if details are provided
  if key_ring and key_name:
    if not key_project:
      key_project = billing_project
    kms_key_data_agent = f"projects/{key_project}/locations/{location}/keyRings/{key_ring}/cryptoKeys/{key_name}"
    data_agent.kms_key = kms_key_data_agent # Add for CMEK

  request = geminidataanalytics.CreateDataAgentRequest(
    parent=f"projects/{billing_project}/locations/{location}",
    data_agent_id=data_agent_id, # Optional
    data_agent=data_agent,
  )

  try:
    response = data_agent_client.create_data_agent_sync(request=request)
    print("Data Agent created")
    print(response)
  except Exception as e:
    print(f"Error creating Data Agent: {e}")

Asincrona

  data_agent_id = "data_agent_1"

  # Optional: If using CMEK, replace the empty strings with your KMS key details.
  key_ring = ""    # KEY_RING_NAME
  key_name = ""    # KEY_NAME
  key_project = "" # KMS_PROJECT_ID (Defaults to billing_project if empty)

  data_agent = geminidataanalytics.DataAgent()
  data_agent.data_analytics_agent.published_context = published_context
  data_agent.name = f"projects/{billing_project}/locations/{location}/dataAgents/{data_agent_id}" # Optional

  # Optional: Add CMEK key if details are provided
  if key_ring and key_name:
    if not key_project:
      key_project = billing_project
    kms_key_data_agent = f"projects/{key_project}/locations/{location}/keyRings/{key_ring}/cryptoKeys/{key_name}"
    data_agent.kms_key = kms_key_data_agent # Add for CMEK

  request = geminidataanalytics.CreateDataAgentRequest(
    parent=f"projects/{billing_project}/locations/{location}",
    data_agent_id=data_agent_id, # Optional
    data_agent=data_agent,
  )

  try:
    data_agent_client.create_data_agent(request=request)
    print("Data Agent created")
  except Exception as e:
    print(f"Error creating Data Agent: {e}")

Nell'esempio precedente, sostituisci i valori come segue:

  • data_agent_1: un identificatore univoco per l'agente dati.
  • KEY_RING_NAME: se utilizzi CMEK, il nome del tuo portachiavi Cloud KMS.
  • KEY_NAME: se utilizzi CMEK, il nome della chiave Cloud KMS.
  • KMS_PROJECT_ID: Se utilizzi CMEK, l'ID progetto in cui è ospitata la chiave.

Creare una conversazione

Il seguente codice Python di esempio effettua una richiesta API per creare una conversazione.

Se vuoi, puoi proteggere la conversazione con CMEK fornendo un kms_key durante la creazione. La chiave di crittografia gestita dal cliente è supportata solo per le origini dati di Looker. Per saperne di più, vedi Chiavi di crittografia gestite dal cliente (CMEK).

# Initialize request arguments
data_agent_id = "data_agent_1"
conversation_id = "conversation_1"


# Optional: If using CMEK, replace the empty strings with your KMS key details.
key_ring = ""    # KEY_RING_NAME
key_name = ""    # KEY_NAME
key_project = "" # KMS_PROJECT_ID (Defaults to billing_project if empty)

if key_project == "":
  key_project = billing_project
kms_key_conversation = f"projects/{key_project}/locations/{location}/keyRings/{key_ring}/cryptoKeys/{key_name}"

conversation = geminidataanalytics.Conversation()
conversation.agents = [f'projects/{billing_project}/locations/{location}/dataAgents/{data_agent_id}']
conversation.name = f"projects/{billing_project}/locations/{location}/conversations/{conversation_id}"

# Optional: Add CMEK key if details are provided
if key_ring and key_name:
  if not key_project:
    key_project = billing_project
  kms_key_conversation = f"projects/{key_project}/locations/{location}/keyRings/{key_ring}/cryptoKeys/{key_name}"
  conversation.kms_key = kms_key_conversation # Add for CMEK

request = geminidataanalytics.CreateConversationRequest(
  parent=f"projects/{billing_project}/locations/{location}",
  conversation_id=conversation_id,
  conversation=conversation,
)

# Make the request
response = data_chat_client.create_conversation(request=request)

# Handle the response
print(response)

Sostituisci i valori di esempio come segue:

  • data_agent_1: l'ID dell'agente dati, come definito nel blocco di codice campione in Crea un agente dati.
  • conversation_1: un identificatore univoco per la conversazione.
  • KEY_RING_NAME: se utilizzi CMEK, il nome del tuo portachiavi Cloud KMS.
  • KEY_NAME: se utilizzi CMEK, il nome della chiave Cloud KMS.
  • KMS_PROJECT_ID: Se utilizzi CMEK, l'ID progetto in cui è ospitata la chiave.

Gestire gli agenti di dati e le conversazioni

I seguenti esempi di codice mostrano come gestire gli agenti e le conversazioni di dati utilizzando l'API Analisi conversazionale. Puoi eseguire le seguenti operazioni:

Recuperare un agente dati

Il seguente esempio di codice Python mostra come effettuare una richiesta API per recuperare un agente dati creato in precedenza.

# Initialize request arguments
data_agent_id = "data_agent_1"
request = geminidataanalytics.GetDataAgentRequest(
  name=f"projects/{billing_project}/locations/{location}/dataAgents/{data_agent_id}",
)

# Make the request
response = data_agent_client.get_data_agent(request=request)

# Handle the response
print(response)

Nell'esempio precedente, sostituisci il valore data_agent_1 con l'identificatore univoco dell'agente di dati che vuoi recuperare.

Elenco degli agenti di dati

Il seguente codice mostra come elencare tutti gli agenti di dati per un determinato progetto chiamando il metodo list_data_agents. Per elencare tutti gli agenti, devi disporre dell'autorizzazione geminidataanalytics.dataAgents.list per il progetto. Per saperne di più sui ruoli IAM che includono questa autorizzazione, consulta l'elenco dei ruoli predefiniti.

billing_project = "YOUR-BILLING-PROJECT"
location = "global"
request = geminidataanalytics.ListDataAgentsRequest(
  parent=f"projects/{billing_project}/locations/{location}",
)

# Make the request
page_result = data_agent_client.list_data_agents(request=request)

# Handle the response
for response in page_result:
  print(response)

Sostituisci YOUR-BILLING-PROJECT con l'ID del tuo progetto di fatturazione.

Elenco degli agenti di dati accessibili

Il seguente codice mostra come elencare tutti gli agenti di dati accessibili per un determinato progetto chiamando il metodo list_accessible_data_agents.

billing_project = "YOUR-BILLING-PROJECT"
creator_filter = "YOUR-CREATOR-FILTER"
location = "global"
request = geminidataanalytics.ListAccessibleDataAgentsRequest(
  parent=f"projects/{billing_project}/locations/{location}",
  creator_filter=creator_filter
)

# Make the request
page_result = data_agent_client.list_accessible_data_agents(request=request)

# Handle the response
for response in page_result:
  print(response)

Sostituisci i valori di esempio come segue:

  • YOUR-BILLING-PROJECT: l'ID del tuo progetto di fatturazione.
  • YOUR-CREATOR-FILTER: il filtro da applicare in base all'autore dell'agente dati. I valori possibili includono NONE (predefinito), CREATOR_ONLY e NOT_CREATOR_ONLY.

Aggiorna un agente di dati

Il seguente codice campione mostra come aggiornare un agente dati in modo sincrono o asincrono chiamando il metodo update_data_agent_sync o update_data_agent sulla risorsa dell'agente dati. La richiesta richiede un oggetto DataAgent che includa i nuovi valori per i campi che vuoi modificare e un parametro update_mask che accetta un oggetto FieldMask per specificare i campi da aggiornare.

Per aggiornare un agente dati, devi disporre dell'autorizzazione IAM geminidataanalytics.dataAgents.update per l'agente. Per saperne di più sui ruoli IAM che includono questa autorizzazione, consulta l'elenco dei ruoli predefiniti.

Sincrona

data_agent_id = "data_agent_1"
billing_project = "YOUR-BILLING-PROJECT"
data_agent = geminidataanalytics.DataAgent()
data_agent.data_analytics_agent.published_context = published_context
data_agent.name = f"projects/{billing_project}/locations/{location}/dataAgents/{data_agent_id}"
data_agent.description = "Updated description of the data agent."

update_mask = field_mask_pb2.FieldMask(paths=['description', 'data_analytics_agent.published_context'])

request = geminidataanalytics.UpdateDataAgentRequest(
  data_agent=data_agent,
  update_mask=update_mask,
)

try:
  # Make the request
  response = data_agent_client.update_data_agent_sync(request=request)
  print("Data Agent Updated")
  print(response)
except Exception as e:
  print(f"Error updating Data Agent: {e}")

Asincrona

data_agent_id = "data_agent_1"
billing_project = "YOUR-BILLING-PROJECT"
data_agent = geminidataanalytics.DataAgent()
data_agent.data_analytics_agent.published_context = published_context
data_agent.name = f"projects/{billing_project}/locations/{location}/dataAgents/{data_agent_id}"
data_agent.description = "Updated description of the data agent."

update_mask = field_mask_pb2.FieldMask(paths=['description', 'data_analytics_agent.published_context'])

request = geminidataanalytics.UpdateDataAgentRequest(
  data_agent=data_agent,
  update_mask=update_mask,
)

try:
  # Make the request
  data_agent_client.update_data_agent(request=request)
  print("Data Agent Updated")
except Exception as e:
  print(f"Error updating Data Agent: {e}")

Sostituisci i valori di esempio come segue:

  • data_agent_1: l'ID dell'agente dati da aggiornare.
  • YOUR-BILLING-PROJECT: l'ID del tuo progetto di fatturazione.
  • Updated description of the data agent.: una descrizione dell'agente dati aggiornato.

Recupero del criterio IAM per un agente dati

Il seguente codice campione mostra come utilizzare il metodo get_iam_policy per recuperare il criterio IAM per un agente dati. La richiesta specifica il percorso della risorsa dell'agente dati.

billing_project = "YOUR-BILLING-PROJECT"
location = "global"
data_agent_id = "data_agent_1"

resource = f"projects/{billing_project}/locations/{location}/dataAgents/{data_agent_id}"
request = iam_policy_pb2.GetIamPolicyRequest(
      resource=resource,
    )
try:
  response = data_agent_client.get_iam_policy(request=request)
  print("IAM Policy fetched successfully!")
  print(f"Response: {response}")
except Exception as e:
  print(f"Error setting IAM policy: {e}")

Sostituisci i valori di esempio come segue:

  • YOUR-BILLING-PROJECT: l'ID del tuo progetto di fatturazione.
  • data_agent_1: L'ID dell'agente dati per cui vuoi ottenere la policy IAM.

Imposta il criterio IAM per un agente dati

Per condividere un agente, puoi utilizzare il metodo set_iam_policy per assegnare ruoli IAM agli utenti di un agente specifico. La richiesta include associazioni che specificano quali ruoli devono essere assegnati a quali utenti.

billing_project = "YOUR-BILLING-PROJECT"
location = "global"
data_agent_id = "data_agent_1"
role = "roles/geminidataanalytics.dataAgentEditor"
users = "EMAIL_ADDRESS"

resource = f"projects/{billing_project}/locations/{location}/dataAgents/{data_agent_id}"

# Construct the IAM policy
binding = policy_pb2.Binding(
  role=role,
  members= [f"user:{i.strip()}" for i in users.split(",")]
)

policy = policy_pb2.Policy(bindings=[binding])

# Create the request
request = iam_policy_pb2.SetIamPolicyRequest(
  resource=resource,
  policy=policy
)

# Send the request
try:
  response = data_agent_client.set_iam_policy(request=request)
  print("IAM Policy set successfully!")
  print(f"Response: {response}")
except Exception as e:
  print(f"Error setting IAM policy: {e}")

Sostituisci i valori di esempio come segue:

  • YOUR-BILLING-PROJECT: l'ID del tuo progetto di fatturazione.
  • data_agent_1: L'ID dell'agente di dati per cui vuoi impostare la policy IAM.
  • EMAIL_ADDRESSES: un elenco separato da virgole di indirizzi email degli utenti a cui vuoi concedere il ruolo specificato.

Eliminare un agente di dati

Il seguente codice campione mostra come utilizzare il metodo delete_data_agent_sync o delete_data_agent per eliminare temporaneamente un agente dati in modo sincrono o asincrono. Quando elimini temporaneamente un agente, questo viene eliminato, ma può comunque essere recuperato entro 30 giorni. La richiesta specifica l'URL della risorsa dell'agente dati.

Sincrona

billing_project = "YOUR-BILLING-PROJECT"
location = "global"
data_agent_id = "data_agent_1"

request = geminidataanalytics.DeleteDataAgentRequest(
  name=f"projects/{billing_project}/locations/{location}/dataAgents/{data_agent_id}",
)

try:
  # Make the request
  data_agent_client.delete_data_agent_sync(request=request)
  print("Data Agent Deleted")
except Exception as e:
  print(f"Error deleting Data Agent: {e}")

Asincrona

billing_project = "YOUR-BILLING-PROJECT"
location = "global"
data_agent_id = "data_agent_1"

request = geminidataanalytics.DeleteDataAgentRequest(
  name=f"projects/{billing_project}/locations/{location}/dataAgents/{data_agent_id}",
)

try:
  # Make the request
  data_agent_client.delete_data_agent(request=request)
  print("Data Agent Deleted")
except Exception as e:
  print(f"Error deleting Data Agent: {e}")

Sostituisci i valori di esempio come segue:

  • YOUR-BILLING-PROJECT: l'ID del tuo progetto di fatturazione.
  • data_agent_1: l'ID dell'agente dati che vuoi eliminare.

Recuperare una conversazione

Il seguente codice campione mostra come utilizzare il metodo get_conversation per recuperare informazioni su una conversazione esistente. La richiesta specifica il percorso della risorsa conversazione.

billing_project = "YOUR-BILLING-PROJECT"
location = "global"
conversation_id = "conversation_1"

request = geminidataanalytics.GetConversationRequest(
  name = f"projects/{billing_project}/locations/{location}/conversations/{conversation_id}"
)

# Make the request
response = data_chat_client.get_conversation(request=request)

# Handle the response
print(response)

Sostituisci i valori di esempio come segue:

  • YOUR-BILLING-PROJECT: l'ID del tuo progetto di fatturazione.
  • conversation_1: l'ID della conversazione che vuoi recuperare.

Elenca conversazioni

Il seguente codice campione mostra come elencare le conversazioni per un determinato progetto chiamando il metodo list_conversations. La richiesta specifica l'URL della risorsa padre, ovvero il progetto e la località (ad esempio, projects/my-project/locations/global).

Per impostazione predefinita, questo metodo restituisce le conversazioni che hai creato. Gli amministratori (utenti con il ruolo IAM cloudaicompanion.topicAdmin) possono visualizzare tutte le conversazioni all'interno del progetto.

billing_project = "YOUR-BILLING-PROJECT"
location = "global"
request = geminidataanalytics.ListConversationsRequest(
  parent=f"projects/{billing_project}/locations/{location}",
)

# Make the request
response = data_chat_client.list_conversations(request=request)

# Handle the response
print(response)

Sostituisci YOUR-BILLING-PROJECT con l'ID del progetto di fatturazione in cui hai abilitato le API richieste.

Elencare i messaggi in una conversazione

Il seguente codice campione mostra come utilizzare il metodo list_messages per recuperare tutti i messaggi di una conversazione. La richiesta specifica il percorso della risorsa conversazione.

Per elencare i messaggi, devi disporre dell'autorizzazione cloudaicompanion.topics.get sulla conversazione.

billing_project = "YOUR-BILLING-PROJECT"
location = "global"

conversation_id = "conversation_1"

request = geminidataanalytics.ListMessagesRequest(
  parent=f"projects/{billing_project}/locations/{location}/conversations/{conversation_id}",
)

# Make the request
response = data_chat_client.list_messages(request=request)

# Handle the response
print(response)

Sostituisci i valori di esempio come segue:

  • YOUR-BILLING-PROJECT: l'ID del tuo progetto di fatturazione.
  • conversation_1: l'ID della conversazione per cui vuoi elencare i messaggi.

Eliminare una conversazione

Il seguente codice campione mostra come eliminare una conversazione. Gli amministratori (utenti con il ruolo Identity and Access Management cloudaicompanion.topicAdmin) o gli utenti con l'autorizzazione Identity and Access Management cloudaicompanion.topics.delete possono eliminare le conversazioni all'interno del progetto.

billing_project = "YOUR-BILLING-PROJECT"
location = "global"
conversation_id = "conversation_1"

request = geminidataanalytics.DeleteConversationRequest(
  name = f"projects/{billing_project}/locations/{location}/conversations/{conversation_id}"
)

# Make the request
response = data_chat_client.delete_conversation(request=request)

# Handle the response
print(response)

Sostituisci i valori di esempio come segue:

  • YOUR-BILLING-PROJECT: l'ID del tuo progetto di fatturazione.
  • conversation_1: l'ID della conversazione per cui vuoi elencare i messaggi.

Utilizzare l'API per porre domande

Dopo aver creato un agente dati e, per la chat stateful, una conversazione, puoi inviare query all'agente. I seguenti esempi di codice mostrano come chiamare il metodo data_chat_client.chat. Questi esempi utilizzano le variabili di contesto (come l'origine dati e le istruzioni di sistema) che hai configurato in Configurare il contesto per la chat con stato o senza stato.

Quando invii una query, l'API restituisce un flusso di oggetti Message. Questo stream può contenere diversi tipi di messaggi, tra cui testo, tabelle di dati e grafici. I messaggi di testo possono fornire informazioni sul ragionamento dell'agente, segnalare i suoi progressi o fornire la risposta finale. Lo scopo di ogni messaggio è indicato dal valore TextType:

  • THOUGHT: mostra il processo di pensiero interno dell'agente mentre pianifica come rispondere alla tua query. I messaggi THOUGHT forniscono informazioni dettagliate passo passo sul ragionamento e sul processo decisionale dell'agente e sono composti da due parti: parts[0] è il riepilogo del pensiero, che riassume brevemente il testo completo del pensiero, mentre parts[1] è il testo completo del pensiero.
  • PROGRESS: indica l'avanzamento dell'agente in un'azione, ad esempio il recupero dei dati o uno strumento che viene richiamato. Questo valore viene restituito solo per le origini dati Looker e contiene due parti: parts[0] è il riepilogo e parts[1] è il testo completo dell'avanzamento.
  • FINAL_RESPONSE: fornisce la risposta finale alla tua query.

I seguenti esempi di codice utilizzano la funzione helper show_message, definita in Definisci funzioni helper, per elaborare e visualizzare ogni messaggio nel flusso. Per indicazioni su come eseguire il rendering di questi messaggi in un'interfaccia utente quando utilizzi origini dati di Looker, consulta Eseguire il rendering delle risposte dell'agente per le origini dati di Looker.

Chat stateful

Invia una richiesta di chat stateful con un riferimento Conversation

Puoi inviare una richiesta di chat stateful all'agente per i dati facendo riferimento a una risorsa Conversation che hai creato in precedenza.

# Create a request that contains a single user message (your question)
question = "Which species of tree is most prevalent?"
messages = [geminidataanalytics.Message()]
messages[0].user_message.text = question

data_agent_id = "data_agent_1"
conversation_id = "conversation_1"

# Create a conversation_reference
conversation_reference = geminidataanalytics.ConversationReference()
conversation_reference.conversation = f"projects/{billing_project}/locations/{location}/conversations/{conversation_id}"
conversation_reference.data_agent_context.data_agent = f"projects/{billing_project}/locations/{location}/dataAgents/{data_agent_id}"
# conversation_reference.data_agent_context.credentials = credentials

# Form the request
request = geminidataanalytics.ChatRequest(
    parent = f"projects/{billing_project}/locations/{location}",
    messages = messages,
    conversation_reference = conversation_reference
)

# Make the request
stream = data_chat_client.chat(request=request, timeout=300 #custom timeout up to 600s)

# Handle the response
for response in stream:
    show_message(response)

Sostituisci i valori di esempio come segue:

  • Which species of tree is most prevalent?: una domanda in linguaggio naturale da inviare all'agente dei dati.
  • data_agent_1: l'identificatore univoco dell'agente dati, come definito in Creare un agente dati.
  • conversation_1: l'identificatore univoco della conversazione, come definito in Crea una conversazione.

Chat stateless

I seguenti esempi di codice mostrano come inviare una query all'agente dati dopo aver configurato il contesto per la chat stateless. Puoi inviare query stateless facendo riferimento a una risorsa DataAgent definita in precedenza o utilizzando il contesto incorporato nella richiesta.

Invia una richiesta di chat stateless con un riferimento DataAgent

Puoi inviare una query all'agente dati facendo riferimento a una risorsa DataAgent che hai creato in precedenza.

# Create a request that contains a single user message (your question)
question = "Which species of tree is most prevalent?"
messages = [geminidataanalytics.Message()]
messages[0].user_message.text = question

data_agent_id = "data_agent_1"

data_agent_context = geminidataanalytics.DataAgentContext()
data_agent_context.data_agent = f"projects/{billing_project}/locations/{location}/dataAgents/{data_agent_id}"
# data_agent_context.credentials = credentials

# Form the request
request = geminidataanalytics.ChatRequest(
    parent=f"projects/{billing_project}/locations/{location}",
    messages=messages,
    data_agent_context = data_agent_context
)

# Make the request
stream = data_chat_client.chat(request=request, timeout=300 #custom timeout up to 600s)

# Handle the response
for response in stream:
    show_message(response)

Sostituisci i valori di esempio come segue:

  • Which species of tree is most prevalent?: una domanda in linguaggio naturale da inviare all'agente dei dati.
  • data_agent_1: l'identificatore univoco dell'agente dati, come definito in Creare un agente dati.

Invia una richiesta di chat stateless con contesto incorporato

Il seguente codice campione mostra come utilizzare il parametro inline_context per fornire il contesto direttamente nella richiesta di chat stateless.

# Create a request that contains a single user message (your question)
question = "Which species of tree is most prevalent?"
messages = [geminidataanalytics.Message()]
messages[0].user_message.text = question

request = geminidataanalytics.ChatRequest(
    inline_context=inline_context,
    parent=f"projects/{billing_project}/locations/{location}",
    messages=messages,
)

# Make the request
stream = data_chat_client.chat(request=request, timeout=300 #custom timeout up to 600s)

# Handle the response
for response in stream:
    show_message(response)

Nell'esempio precedente, sostituisci Which species of tree is most prevalent? con una domanda in linguaggio naturale da inviare all'agente di dati.

Creare una conversazione multi-turno stateless

Per fare domande successive in una conversazione stateless, la tua applicazione deve gestire il contesto della conversazione inviando l'intera cronologia dei messaggi a ogni nuova richiesta. L'esempio seguente mostra come creare una conversazione in più turni facendo riferimento a un agente dati o utilizzando il contesto incorporato per fornire direttamente l'origine dati.

# List that is used to track previous turns and is reused across requests
conversation_messages = []

data_agent_id = "data_agent_1"

# Use data agent context
data_agent_context = geminidataanalytics.DataAgentContext()
data_agent_context.data_agent = f"projects/{billing_project}/locations/{location}/dataAgents/{data_agent_id}"
# data_agent_context.credentials = credentials

# Helper function for calling the API
def multi_turn_Conversation(msg):

  message = geminidataanalytics.Message()
  message.user_message.text = msg

  # Send a multi-turn request by including previous turns and the new message
  conversation_messages.append(message)

  request = geminidataanalytics.ChatRequest(
    parent=f"projects/{billing_project}/locations/{location}",
    messages=conversation_messages,
    # Use data agent context
    data_agent_context=data_agent_context,
    # Use inline context
    # inline_context=inline_context,
  )

  # Make the request
  stream = data_chat_client.chat(request=request, timeout=300 #custom timeout up to 600s)

  # Handle the response
  for response in stream:
    show_message(response)
    conversation_messages.append(response)

# Send the first turn request
multi_turn_Conversation("Which species of tree is most prevalent?")

# Send follow-up turn request
multi_turn_Conversation("Can you show me the results as a bar chart?")

Nell'esempio precedente, sostituisci i valori di esempio come segue:

  • data_agent_1: l'identificatore univoco dell'agente dati, come definito nel blocco di codice campione in Creare un agente dati.
  • Which species of tree is most prevalent?: una domanda in linguaggio naturale da inviare all'agente dei dati.
  • Can you show me the results as a bar chart?: Una domanda di follow-up che si basa sulla domanda precedente o la perfeziona.

Definisci funzioni helper

Il seguente codice campione contiene definizioni di funzioni helper utilizzate negli esempi di codice precedenti. Queste funzioni aiutano ad analizzare la risposta dell'API e a visualizzare i risultati.

import json as json_lib
import textwrap
import time

import altair as alt
import IPython
import pandas as pd
import proto
import requests

from google.protobuf.json_format import MessageToDict, MessageToJson
from IPython.display import display, HTML
from pygments import highlight, lexers, formatters
from google.protobuf import field_mask_pb2
from google.iam.v1 import policy_pb2
from google.iam.v1 import iam_policy_pb2

def handle_text_response(resp):
  parts = resp.parts
  full_text = "".join(parts)
  if "\n" not in full_text and len(full_text) > 80:
    wrapped_text = textwrap.fill(full_text, width=80)
    print(wrapped_text)
  else:
    print(full_text)

def display_schema(data):
  fields = getattr(data, "fields")
  df = pd.DataFrame({
    "Column": map(lambda field: getattr(field, 'name'), fields),
    "Type": map(lambda field: getattr(field, 'type'), fields),
    "Description": map(lambda field: getattr(field, 'description', '-'), fields),
    "Mode": map(lambda field: getattr(field, 'mode'), fields)
  })
  display(df)

def display_section_title(text):
  display(HTML('<h2>{}</h2>'.format(text)))

def format_looker_table_ref(table_ref):
  return 'lookmlModel: {}, explore: {}, lookerInstanceUri: {}'.format(table_ref.lookml_model, table_ref.explore, table_ref.looker_instance_uri)

def format_bq_table_ref(table_ref):
  return '{}.{}.{}'.format(table_ref.project_id, table_ref.dataset_id, table_ref.table_id)

def display_datasource(datasource):
  source_name = ''
  if 'studio_datasource_id' in datasource:
    source_name = getattr(datasource, 'studio_datasource_id')
  elif 'looker_explore_reference' in datasource:
    source_name = format_looker_table_ref(getattr(datasource, 'looker_explore_reference'))
  else:
    source_name = format_bq_table_ref(getattr(datasource, 'bigquery_table_reference'))

  print(source_name)
  display_schema(datasource.schema)

def handle_schema_response(resp):
  if 'query' in resp:
    print(resp.query.question)
  elif 'result' in resp:
    display_section_title('Schema resolved')
    print('Data sources:')
    for datasource in resp.result.datasources:
      display_datasource(datasource)

def handle_data_response(resp):
  if "query" in resp:
    query = resp.query
    display_section_title("Retrieval query")
    print(f"Query name: {query.name}")
    if "question" in query:
      print(f"Question: {query.question}")
    if "datasources" in query:
      print("Data sources:")
      for datasource in query.datasources:
        display_datasource(datasource)
  elif "generated_sql" in resp:
    display_section_title("SQL generated")
    print(resp.generated_sql)
  elif "result" in resp:
    display_section_title("Data retrieved")

    fields = [field.name for field in resp.result.schema.fields]
    d = {}
    for el in resp.result.data:
      for field in fields:
        if field in d:
          d[field].append(el[field])
        else:
          d[field] = [el[field]]

    display(pd.DataFrame(d))

def handle_chart_response(resp):
  def _value_to_dict(v):
    if isinstance(v, proto.marshal.collections.maps.MapComposite):
      return _map_to_dict(v)
    elif isinstance(v, proto.marshal.collections.RepeatedComposite):
      return [_value_to_dict(el) for el in v]
    elif isinstance(v, (int, float, str, bool)):
      return v
    else:
      return MessageToDict(v)

  def _map_to_dict(d):
    out = {}
    for k in d:
      if isinstance(d[k], proto.marshal.collections.maps.MapComposite):
        out[k] = _map_to_dict(d[k])
      else:
        out[k] = _value_to_dict(d[k])
    return out

  if 'query' in resp:
    print(resp.query.instructions)
  elif 'result' in resp:
    vegaConfig = resp.result.vega_config
    vegaConfig_dict = _map_to_dict(vegaConfig)
    alt.Chart.from_json(json_lib.dumps(vegaConfig_dict)).display();

def show_message(msg):
  m = msg.system_message
  if 'text' in m:
    handle_text_response(getattr(m, 'text'))
  elif 'schema' in m:
    handle_schema_response(getattr(m, 'schema'))
  elif 'data' in m:
    handle_data_response(getattr(m, 'data'))
  elif 'chart' in m:
    handle_chart_response(getattr(m, 'chart'))
  print('\n')