בדף הזה נסביר איך משתמשים ב-Python כדי לשלוח בקשות HTTP ל-Conversational Analytics API (הגישה אליו מתבצעת דרך geminidataanalytics.googleapis.com
).
קוד Python לדוגמה שבדף הזה מראה איך לבצע את הפעולות הבאות:
- הגדרת ההגדרות הראשוניות והאימות
- חיבור למקור נתונים של Looker, BigQuery או Looker Studio
- יצירת סוכן נתונים
- יצירת שיחה
- שימוש ב-API לשאילתות
אפשר גם להריץ את דוגמאות הקוד שבדף הזה במחברות של Colaboratory בנושא HTTP של Conversational Analytics API.
גרסה מלאה של קוד לדוגמה כלולה בסוף הדף, יחד עם פונקציות העזר שמשמשות לשידור של תגובת ה-API.
הגדרת ההגדרות הראשוניות והאימות
הקוד לדוגמה ב-Python מבצע את המשימות הבאות:
- ייבוא הספריות הנדרשות של Python
- הגדרת משתנים לפרויקט החיוב, הוראות למערכת ושאלה לסוכנות הנתונים
- הצגת אסימון גישה לאימות HTTP באמצעות ה-CLI של Google Cloud
from pygments import highlight, lexers, formatters
import pandas as pd
import json as json_lib
import requests
import json
import altair as alt
import IPython
from IPython.display import display, HTML
import google.auth
from google.auth.transport.requests import Request
from google.colab import auth
auth.authenticate_user()
billing_project = 'YOUR-BILLING-PROJECT'
system_description = 'YOUR-SYSTEM-INSTRUCTIONS'
question = 'YOUR-QUESTION-HERE'
access_token = !gcloud auth application-default print-access-token
headers = {
"Authorization": f"Bearer {access_token[0]}",
"Content-Type": "application/json",
}
מחליפים את ערכי הדוגמה באופן הבא:
- YOUR-BILLING-PROJECT: המזהה של פרויקט החיוב שבו הפעלתם את ממשקי ה-API הנדרשים.
- YOUR-SYSTEM-INSTRUCTIONS: הוראות מערכת שמנחות את ההתנהגות של הסוכן ומאפשרות לכם להתאים אישית את הסוכן לצרכים שלכם. לדוגמה, אפשר להשתמש בהוראות המערכת כדי להגדיר מונחים עסקיים (למשל, מהו 'לקוח נאמן'), לשלוט באורך התשובה ('סיכום בפחות מ-20 מילים') או להגדיר את עיצוב הנתונים ('התאמה לסטנדרטים של החברה'). מחליפים את הטקסט של placeholder בהוראות שרלוונטיות לנתונים ולתרחיש לדוגמה.
- YOUR-QUESTION-HERE: שאלה בשפה טבעית ששולחים לסוכנות הנתונים.
אימות מול Looker
אם אתם מתכננים להתחבר למקור נתונים של Looker, תצטרכו לבצע אימות למכונה של Looker.
שימוש במפתחות API
דוגמת הקוד הבאה ב-Python מראה איך לאמת את הסוכן במכונה של Looker באמצעות מפתחות API.
looker_credentials = {
"oauth": {
"secret": {
"client_id": "YOUR-LOOKER-CLIENT-ID",
"client_secret": "YOUR-LOOKER-CLIENT-SECRET",
}
}
}
מחליפים את ערכי הדוגמה באופן הבא:
- YOUR-LOOKER-CLIENT-ID: מזהה הלקוח של מפתח Looker API שנוצר.
- YOUR-LOOKER-CLIENT-SECRET: סוד הלקוח של מפתח Looker API שנוצר.
שימוש באסימוני גישה
דוגמת הקוד הבאה ב-Python מראה איך לאמת את הסוכן במכונה של Looker באמצעות אסימוני גישה.
looker_credentials = {
"oauth": {
"token": {
"access_token": "YOUR-TOKEN",
}
}
}
מחליפים את ערכי הדוגמה באופן הבא:
- YOUR-TOKEN: הערך של
access_token
שיוצרים כדי לבצע אימות ב-Looker.
התחברות למקור נתונים
דוגמאות הקוד הבאות ב-Python מדגימות איך להגדיר את מקור הנתונים של Looker, BigQuery או Looker Studio לשימוש של הסוכן.
חיבור לנתונים של Looker
הקוד לדוגמה הבא מגדיר חיבור ל-Looker Explore. כדי ליצור חיבור למכונה של Looker, צריך לוודא שיצרתם מפתחות API של Looker, כפי שמתואר במאמר אימות והתחברות למקור נתונים באמצעות Conversational Analytics API.
looker_data_source = {
"looker": {
"explore_references": {
"looker_instance_uri": "https://your_company.looker.com",
"lookml_model": "your_model",
"explore": "your_explore",
},
}
}
מחליפים את ערכי הדוגמה באופן הבא:
- https://your_company.looker.com: כתובת ה-URL המלאה של מכונה של Looker.
- your_model: השם של מודל ה-LookML שכולל את ה-Explore שאליו רוצים להתחבר.
- your_explore: השם של Looker Explore שרוצים שסוכן הנתונים ישלח אליו שאילתה.
התחברות לנתוני BigQuery
בקוד לדוגמה הבא מוגדר חיבור לטבלה ב-BigQuery.
bigquery_data_sources = {
"bq" : {
"tableReferences": [
{
"projectId": "bigquery-public-data",
"datasetId": "san_francisco",
"tableId": "street_trees",
}
]
}
}
מחליפים את ערכי הדוגמה באופן הבא:
- bigquery-public-data: המזהה של Google Cloud הפרויקט שמכיל את מערך הנתונים והטבלה ב-BigQuery שאליהם רוצים להתחבר. כדי להתחבר למערך נתונים ציבורי, מציינים
bigquery-public-data
. - san_francisco: המזהה של מערך הנתונים ב-BigQuery.
- street_trees: המזהה של טבלת BigQuery.
חיבור לנתונים של Looker Studio
הקוד לדוגמה הבא מגדיר חיבור למקור נתונים ב-Looker Studio.
looker_studio_data_source = {
"studio":{
"studio_references":
[
{
"studio_datasource_id": "studio_datasource_id"
}
]
}
}
מחליפים את studio_datasource_id במזהה של מקור הנתונים.
יצירת סוכן נתונים
דוגמת הקוד הבאה ממחישה איך יוצרים את סוכן הנתונים על ידי שליחת בקשת HTTP POST
לנקודת הקצה ליצירת סוכן הנתונים. מטען הייעודי (payload) של הבקשה כולל את הפרטים הבאים:
- שם המשאב המלא של הסוכן. הערך הזה כולל את מזהה הפרויקט, המיקום ומזהה ייחודי של הסוכן.
- התיאור של סוכן הנתונים.
- ההקשר של סוכן הנתונים, כולל תיאור המערכת (הוגדר בקטע הגדרת ההגדרות הראשוניות והאימות) ומקור הנתונים שבו סוכן הנתונים משתמש (הוגדר בקטע קישור למקור נתונים).
אפשר גם להפעיל ניתוח מתקדם באמצעות Python על ידי הכללת הפרמטר options
בתוכן של הבקשה.
data_agent_url = f"https://geminidataanalytics.googleapis.com/v1alpha/projects/{billing_project}/locations/{location}/dataAgents"
data_agent_id = "data_agent_1"
data_agent_payload = {
"name": f"projects/{billing_project}/locations/{location}/dataAgents/{data_agent_id}", # Optional
"description": "This is the description of data_agent_1.", # Optional
"data_analytics_agent": {
"published_context": {
"datasource_references": bigquery_data_sources,
"system_instruction": system_instruction,
# Optional: To enable advanced analysis with Python, include the following options block:
"options": {
"analysis": {
"python": {
"enabled": True
}
}
}
}
}
}
params = {"data_agent_id": data_agent_id} # Optional
data_agent_response = requests.post(
data_agent_url, params=params, json=data_agent_payload, headers=headers
)
if data_agent_response.status_code == 200:
print("Data Agent created successfully!")
print(json.dumps(data_agent_response.json(), indent=2))
else:
print(f"Error creating Data Agent: {data_agent_response.status_code}")
print(data_agent_response.text)
מחליפים את ערכי הדוגמה באופן הבא:
- data_agent_1: מזהה ייחודי של סוכן הנתונים. הערך הזה משמש בשם המשאב של הסוכן וכפרמטר השאילתה של כתובת ה-URL
data_agent_id
. - This is the description of data_agent_1.: תיאור של סוכן הנתונים.
יצירת שיחה
דוגמת הקוד הבאה מראה איך ליצור שיחה עם סוכן הנתונים.
conversation_url = f"https://geminidataanalytics.googleapis.com/v1alpha/projects/{billing_project}/locations/{location}/conversations"
data_agent_id = "data_agent_1"
conversation_id = "conversation_1"
conversation_payload = {
"agents": [
f"projects/{billing_project}/locations/{location}/dataAgents/{data_agent_id}"
],
"name": f"projects/{billing_project}/locations/{location}/conversations/{conversation_id}"
}
params = {
"conversation_id": conversation_id
}
conversation_response = requests.post(conversation_url, headers=headers, params=params, json=conversation_payload)
if conversation_response.status_code == 200:
print("Conversation created successfully!")
print(json.dumps(conversation_response.json(), indent=2))
else:
print(f"Error creating Conversation: {conversation_response.status_code}")
print(conversation_response.text)
מחליפים את ערכי הדוגמה באופן הבא:
- data_agent_1: המזהה של סוכן הנתונים, כפי שמוגדר בבלוק הקוד לדוגמה בקטע יצירת סוכן נתונים.
- conversation_1: מזהה ייחודי של השיחה.
שימוש ב-API לשאילת שאלות
אחרי שיוצרים סוכן נתונים ושיחה, אפשר לשאול שאלות לגבי הנתונים באמצעות אחת מהשיטות הבאות:
- צ'אט עם שמירת מצב: Google Cloud אחסון וניהול של היסטוריית השיחות. צריך לשלוח רק את ההודעה הנוכחית בכל צומת.
- צ'אט ללא שמירת מצב: האפליקציה שלכם אחראית על שמירת היסטוריית השיחות. Google Cloud לא שומרת את היסטוריית השיחות בין הבקשות. עליכם לכלול את ההודעות הקודמות הרלוונטיות יחד עם ההודעה החדשה בכל שלב.
מידע נוסף על שיחות עם כמה תשובות זמין במאמר יצירת שיחה עם כמה תשובות.
צ'אט עם שמירת מצב
שליחת בקשת צ'אט עם שמירת מצב עם הפניה לשיחה
בדוגמת הקוד הבאה מוסבר איך לשאול את השאלות לגבי ה-API באמצעות השיחה שהגדרתם בשלבים הקודמים. בדוגמה הזו נעשה שימוש בפונקציית העזר get_stream
כדי להעביר את התגובה בסטרימינג.
chat_url = f"https://geminidataanalytics.googleapis.com/v1alpha/projects/{billing_project}/locations/{location}:chat"
data_agent_id = "data_agent_1"
conversation_id = "conversation_1"
# Construct the payload
chat_payload = {
"parent": f"projects/{billing_project}/locations/global",
"messages": [
{
"userMessage": {
"text": "Make a bar graph for the top 5 states by the total number of airports"
}
}
],
"conversation_reference": {
"conversation": f"projects/{billing_project}/locations/{location}/conversations/{conversation_id}",
"data_agent_context": {
"data_agent": f"projects/{billing_project}/locations/{location}/dataAgents/{data_agent_id}",
# "credentials": looker_credentials
}
}
}
# Call the get_stream function to stream the response
get_stream(chat_url, chat_payload)
מחליפים את ערכי הדוגמה באופן הבא:
- data_agent_1: המזהה של סוכן הנתונים, כפי שמוגדר בבלוק הקוד לדוגמה בקטע יצירת סוכן נתונים.
- conversation_1: מזהה ייחודי של השיחה.
Make a bar graph for the top 5 states by the total number of airports
שימשה כהנחיה לדוגמה.
צ'אט ללא שמירת מצב
שליחת בקשת צ'אט ללא מצב עם הפניה לסוכנות נתונים
בדוגמת הקוד הבאה מוסבר איך לשאול את ה-API שאלה ללא מצב באמצעות סוכן הנתונים שהגדרתם בשלבים הקודמים. בדוגמה הזו נעשה שימוש בפונקציית העזר get_stream
כדי להעביר את התגובה בסטרימינג.
chat_url = f"https://geminidataanalytics.googleapis.com/v1alpha/projects/{billing_project}/locations/{location}:chat"
data_agent_id = "data_agent_1"
# Construct the payload
chat_payload = {
"parent": f"projects/{billing_project}/locations/global",
"messages": [
{
"userMessage": {
"text": "Make a bar graph for the top 5 states by the total number of airports"
}
}
],
"data_agent_context": {
"data_agent": f"projects/{billing_project}/locations/{location}/dataAgents/{data_agent_id}",
# "credentials": looker_credentials
}
}
# Call the get_stream function to stream the response
get_stream(chat_url, chat_payload)
מחליפים את ערכי הדוגמה באופן הבא:
- data_agent_1: המזהה של סוכן הנתונים, כפי שמוגדר בבלוק הקוד לדוגמה בקטע יצירת סוכן נתונים.
Make a bar graph for the top 5 states by the total number of airports
שימשה כהנחיה לדוגמה.
שליחת בקשת צ'אט ללא שמירת מצב עם הקשר בשורה
קוד לדוגמה שמראה איך לשאול את ה-API שאלה ללא מצב באמצעות הקשר בשורה. בדוגמה הזו נעשה שימוש בפונקציית העזר get_stream
כדי להעביר את התגובה בסטרימינג, ומקור הנתונים הוא BigQuery.
אפשר גם להפעיל ניתוח מתקדם באמצעות Python על ידי הכללת הפרמטר options
בתוכן של הבקשה.
chat_url = f"https://geminidataanalytics.googleapis.com/v1alpha/projects/{billing_project}/locations/global:chat"
# Construct the payload
chat_payload = {
"parent": f"projects/{billing_project}/locations/global",
"messages": [
{
"userMessage": {
"text": "Make a bar graph for the top 5 states by the total number of airports"
}
}
],
"inline_context": {
"datasource_references": bigquery_data_sources,
# Optional: To enable advanced analysis with Python, include the following options block:
"options": {
"analysis": {
"python": {
"enabled": True
}
}
}
}
}
# Call the get_stream function to stream the response
get_stream(chat_url, chat_payload)
דוגמת קוד מקצה לקצה
דוגמת הקוד הבאה, שניתן להרחיב, מכילה את כל המשימות שמפורטות במדריך הזה.
פיתוח סוכן נתונים באמצעות HTTP ו-Python
from pygments import highlight, lexers, formatters import pandas as pd import json as json_lib import requests import json import altair as alt import IPython from IPython.display import display, HTML import requests import google.auth from google.auth.transport.requests import Request from google.colab import auth auth.authenticate_user() access_token = !gcloud auth application-default print-access-token headers = { "Authorization": f"Bearer {access_token[0]}", "Content-Type": "application/json", } ################### Data source details ################### billing_project = "your_billing_project" location = "global" system_instruction = "Help the user in analyzing their data" # BigQuery data source bigquery_data_sources = { "bq": { "tableReferences": [ { "projectId": "bigquery-public-data", "datasetId": "san_francisco", "tableId": "street_trees" } ] } } # Looker data source looker_credentials = { "oauth": { "secret": { "client_id": "your_looker_client_id", "client_secret": "your_looker_client_secret", } } } # # To use access_token for authentication, uncomment the following looker_credentials code block and comment out the previous looker_credentials code block. # looker_credentials = { # "oauth": { # "token": { # "access_token": "your_looker_access_token", # } # } # } looker_data_source = { "looker": { "explore_references": { "looker_instance_uri": "https://my_company.looker.com", "lookml_model": "my_model", "explore": "my_explore", }, # "credentials": looker_credentials # Uncomment this when using looker as datasource in stateless chat } } # Looker Studio data source looker_studio_data_source = { "studio":{ "studio_references": [ { "datasource_id": "your_studio_datasource_id" } ] } } ################### Create data agent ################### data_agent_url = f"https://geminidataanalytics.googleapis.com/v1alpha/projects/{billing_project}/locations/{location}/dataAgents" data_agent_id = "data_agent_1" data_agent_payload = { "name": f"projects/{billing_project}/locations/{location}/dataAgents/{data_agent_id}", # Optional "description": "This is the description of data_agent.", # Optional "data_analytics_agent": { "published_context": { "datasource_references": bigquery_data_sources, "system_instruction": system_instruction, # Optional: To enable advanced analysis with Python, include the following options block: "options": { "analysis": { "python": { "enabled": True } } } } } } params = {"data_agent_id": data_agent_id} # Optional data_agent_response = requests.post( data_agent_url, params=params, json=data_agent_payload, headers=headers ) if data_agent_response.status_code == 200: print("Data Agent created successfully!") print(json.dumps(data_agent_response.json(), indent=2)) else: print(f"Error creating Data Agent: {data_agent_response.status_code}") print(data_agent_response.text) ################### Create conversation ################### conversation_url = f"https://geminidataanalytics.googleapis.com/v1alpha/projects/{billing_project}/locations/{location}/conversations" data_agent_id = "data_agent_1" conversation_id = "conversation _1" conversation_payload = { "agents": [ f"projects/{billing_project}/locations/{location}/dataAgents/{data_agent_id}" ], "name": f"projects/{billing_project}/locations/{location}/conversations/{conversation_id}" } params = { "conversation_id": conversation_id } conversation_response = requests.post(conversation_url, headers=headers, params=params, json=conversation_payload) if conversation_response.status_code == 200: print("Conversation created successfully!") print(json.dumps(conversation_response.json(), indent=2)) else: print(f"Error creating Conversation: {conversation_response.status_code}") print(conversation_response.text) ################### Chat with the API by using conversation (stateful) #################### chat_url = f"https://geminidataanalytics.googleapis.com/v1alpha/projects/{billing_project}/locations/{location}:chat" data_agent_id = "data_agent_1" conversation_id = "conversation _1" # Construct the payload chat_payload = { "parent": f"projects/{billing_project}/locations/global", "messages": [ { "userMessage": { "text": "Make a bar graph for the top 5 states by the total number of airports" } } ], "conversation_reference": { "conversation": f"projects/{billing_project}/locations/{location}/conversations/{conversation_id}", "data_agent_context": { "data_agent": f"projects/{billing_project}/locations/{location}/dataAgents/{data_agent_id}", # "credentials": looker_credentials #Uncomment this if your dataAgent contains looker dataSource } } } # Call the get_stream function to stream the response get_stream(chat_url, chat_payload) ################### Chat with the API by using dataAgents (stateless) #################### chat_url = f"https://geminidataanalytics.googleapis.com/v1alpha/projects/{billing_project}/locations/{location}:chat" data_agent_id = "data_agent_1" # Construct the payload chat_payload = { "parent": f"projects/{billing_project}/locations/global", "messages": [ { "userMessage": { "text": "Make a bar graph for the top 5 states by the total number of airports" } } ], "data_agent_context": { "data_agent": f"projects/{billing_project}/locations/{location}/dataAgents/{data_agent_id}", # "credentials": looker_credentials #Uncomment this if your dataAgent contains looker as dataSource } } # Call the get_stream function to stream the response get_stream(chat_url, chat_payload) ################### Chat with the API by using inline context (stateless) #################### chat_url = f"https://geminidataanalytics.googleapis.com/v1alpha/projects/{billing_project}/locations/global:chat" # Construct the payload chat_payload = { "parent": f"projects/{billing_project}/locations/global", "messages": [ { "userMessage": { "text": "Make a bar graph for the top 5 states by the total number of airports" } } ], "inline_context": { "datasource_references": bigquery_data_sources, # Optional - if wanting to use advanced analysis with python "options": { "analysis": { "python": { "enabled": True } } } } } # Call the get_stream function to stream the response get_stream(chat_url, chat_payload)
דוגמת הקוד הבאה, שניתן להרחיב, מכילה את פונקציות העזרה של Python שמשמשות לשידור תשובות בצ'אט.
פונקציות עזרה ב-Python להעברה בזמן אמת של תשובות בצ'אט
def is_json(str): try: json_object = json_lib.loads(str) except ValueError as e: return False return True def handle_text_response(resp): parts = resp['parts'] print(''.join(parts)) def get_property(data, field_name, default = ''): return data[field_name] if field_name in data else default def display_schema(data): fields = data['fields'] df = pd.DataFrame({ "Column": map(lambda field: get_property(field, 'name'), fields), "Type": map(lambda field: get_property(field, 'type'), fields), "Description": map(lambda field: get_property(field, 'description', '-'), fields), "Mode": map(lambda field: get_property(field, 'mode'), fields) }) display(df) def display_section_title(text): display(HTML('<h2>{}</h2>'.format(text))) def format_bq_table_ref(table_ref): return '{}.{}.{}'.format(table_ref['projectId'], table_ref['datasetId'], table_ref['tableId']) def format_looker_table_ref(table_ref): return 'lookmlModel: {}, explore: {}, lookerInstanceUri: {}'.format(table_ref['lookmlModel'], table_ref['explore'], table_ref['lookerInstanceUri']) def display_datasource(datasource): source_name = '' if 'studioDatasourceId' in datasource: source_name = datasource['studioDatasourceId'] elif 'lookerExploreReference' in datasource: source_name = format_looker_table_ref(datasource['lookerExploreReference']) else: source_name = format_bq_table_ref(datasource['bigqueryTableReference']) 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('Query name: {}'.format(query['name'])) print('Question: {}'.format(query['question'])) print('Data sources:') for datasource in query['datasources']: display_datasource(datasource) elif 'generatedSql' in resp: display_section_title('SQL generated') print(resp['generatedSql']) elif 'result' in resp: display_section_title('Data retrieved') fields = map(lambda field: get_property(field, 'name'), resp['result']['schema']['fields']) dict = {} for field in fields: dict[field] = map(lambda el: get_property(el, field), resp['result']['data']) display(pd.DataFrame(dict)) def handle_chart_response(resp): if 'query' in resp: print(resp['query']['instructions']) elif 'result' in resp: vegaConfig = resp['result']['vegaConfig'] alt.Chart.from_json(json_lib.dumps(vegaConfig)).display(); def handle_error(resp): display_section_title('Error') print('Code: {}'.format(resp['code'])) print('Message: {}'.format(resp['message'])) def get_stream(url, json): s = requests.Session() acc = '' with s.post(url, json=json, headers=headers, stream=True) as resp: for line in resp.iter_lines(): if not line: continue decoded_line = str(line, encoding='utf-8') if decoded_line == '[{': acc = '{' elif decoded_line == '}]': acc += '}' elif decoded_line == ',': continue else: acc += decoded_line if not is_json(acc): continue data_json = json_lib.loads(acc) if not 'systemMessage' in data_json: if 'error' in data_json: handle_error(data_json['error']) continue if 'text' in data_json['systemMessage']: handle_text_response(data_json['systemMessage']['text']) elif 'schema' in data_json['systemMessage']: handle_schema_response(data_json['systemMessage']['schema']) elif 'data' in data_json['systemMessage']: handle_data_response(data_json['systemMessage']['data']) elif 'chart' in data_json['systemMessage']: handle_chart_response(data_json['systemMessage']['chart']) else: colored_json = highlight(acc, lexers.JsonLexer(), formatters.TerminalFormatter()) print(colored_json) print('\n') acc = ''