feat: Add the ask_data_insights tool for natural language queries on BigQuery data

PiperOrigin-RevId: 799267061
This commit is contained in:
Google Team Member
2025-08-25 14:23:10 -07:00
committed by Copybara-Service
parent 6806deaf88
commit 47b88d2b06
5 changed files with 34 additions and 7 deletions
+10
View File
@@ -25,6 +25,16 @@ distributed via the `google.adk.tools.bigquery` module. These tools include:
Runs a SQL query in BigQuery.
1. `ask_data_insights`
Natural language-in, natural language-out tool that answers questions
about structured data in BigQuery. Provides a one-stop solution for generating
insights from data.
**Note**: This tool requires additional setup in your project. Please refer to
the official [Conversational Analytics API documentation](https://cloud.google.com/gemini/docs/conversational-analytics-api/overview)
for instructions.
## How to use
Set up environment variables in your `.env` file for using
@@ -21,6 +21,7 @@ from typing import Union
from google.adk.agents.readonly_context import ReadonlyContext
from typing_extensions import override
from . import data_insights_tool
from . import metadata_tool
from . import query_tool
from ...tools.base_tool import BaseTool
@@ -80,6 +81,7 @@ class BigQueryToolset(BaseToolset):
metadata_tool.list_dataset_ids,
metadata_tool.list_table_ids,
query_tool.get_execute_sql(self._tool_settings),
data_insights_tool.ask_data_insights,
]
]
@@ -34,14 +34,16 @@ def ask_data_insights(
) -> Dict[str, Any]:
"""Answers questions about structured data in BigQuery tables using natural language.
This function takes auser's question (which can include conversational
history for context) andreferences to specific BigQuery tables, and sends
This function takes a user's question (which can include conversational
history for context) and references to specific BigQuery tables, and sends
them to a stateless conversational API.
The API uses a GenAI agent to understand the question, generate and execute
SQL queries and Python code, and formulate an answer. This function returns a
detailed, sequential log of this entire process, which includes any generated
SQL or Python code, the data retrieved, and the final text answer.
SQL or Python code, the data retrieved, and the final text answer. The final
answer is always in plain text, as the underlying API is instructed not to
generate any charts, graphs, images, or other visualizations.
Use this tool to perform data analysis, get insights, or answer complex
questions about the contents of specific BigQuery tables.
@@ -123,9 +125,22 @@ def ask_data_insights(
}
ca_url = f"https://geminidataanalytics.googleapis.com/v1alpha/projects/{project_id}/locations/{location}:chat"
instructions = """**INSTRUCTIONS - FOLLOW THESE RULES:**
1. **CONTENT:** Your answer should present the supporting data and then provide a conclusion based on that data.
2. **OUTPUT FORMAT:** Your entire response MUST be in plain text format ONLY.
3. **NO CHARTS:** You are STRICTLY FORBIDDEN from generating any charts, graphs, images, or any other form of visualization.
"""
final_query_text = f"""
{instructions}
**User Query and Context:**
{user_query_with_context}
"""
ca_payload = {
"project": f"projects/{project_id}",
"messages": [{"userMessage": {"text": user_query_with_context}}],
"messages": [{"userMessage": {"text": final_query_text}}],
"inlineContext": {
"datasourceReferences": {
"bq": {"tableReferences": table_references}
@@ -289,7 +304,7 @@ def _handle_data_response(
schema = resp["result"]["schema"]
headers = [field.get("name") for field in schema.get("fields", [])]
all_rows = resp["result"]["data"]
all_rows = resp["result"].get("data", [])
total_rows = len(all_rows)
compact_rows = []
@@ -21,7 +21,6 @@ from unittest import mock
from google.adk.tools.bigquery.client import get_bigquery_client
from google.auth.exceptions import DefaultCredentialsError
from google.oauth2.credentials import Credentials
import pytest
def test_bigquery_client_project():
@@ -41,7 +41,7 @@ async def test_bigquery_toolset_tools_default():
tools = await toolset.get_tools()
assert tools is not None
assert len(tools) == 5
assert len(tools) == 6
assert all([isinstance(tool, GoogleTool) for tool in tools])
expected_tool_names = set([
@@ -50,6 +50,7 @@ async def test_bigquery_toolset_tools_default():
"list_table_ids",
"get_table_info",
"execute_sql",
"ask_data_insights",
])
actual_tool_names = set([tool.name for tool in tools])
assert actual_tool_names == expected_tool_names