Function Calling Agent

Source notebook

Repo path: gemini/agents/genai-experience-concierge/agent-design-patterns/function-calling.ipynb · Open on GitHub · advanced

Builds a Gemini function-calling retail assistant over Cymbal Retail data in BigQuery.

Summary

The notebook teaches how to build a function-calling agent that searches products, stores, and inventory for a fictional Cymbal Retail dataset. It wires Gemini to declared tools, executes BigQuery queries through controlled handlers, and streams tool calls and responses through LangGraph-managed conversation state. Product search can use BigQuery ML embedding support for semantic similarity ranking.

Key code patterns

Agent config schema

class AgentConfig(pydantic.BaseModel):
    project: str
    region: str
    chat_model_name: str
    cymbal_dataset_location: str
    cymbal_products_table_uri: str
    cymbal_stores_table_uri: str
    cymbal_inventory_table_uri: str
    cymbal_embedding_model_uri: str

Keeps model, project, region, and BigQuery resources explicit and typed.

Stream function calls

response = await client.aio.models.generate_content_stream(
    model=model,
    contents=contents,
    config=config,
)
async for chunk in response:
    yield chunk.candidates[0].content
    if chunk.function_calls:
        tasks.append(asyncio.create_task(run_function_async(func, kwargs)))

Streams Gemini output while detecting tool calls and executing them asynchronously.

Controlled tool dispatch

if function_call.name not in fn_map:
    raise RuntimeError(f"Function not provided in fn_map: {function_call.name}")
func = fn_map[function_call.name]
kwargs = function_call.args or {}
tasks.append(asyncio.create_task(run_function_async(func, kwargs)))

Restricts execution to registered functions instead of arbitrary generated code.

query_job_config = bigquery.QueryJobConfig()
query_job_config.query_parameters = [
    bigquery.ScalarQueryParameter("store_id", "INTEGER", store_id),
    bigquery.ScalarQueryParameter("product_id", "STRING", product_id),
]
query_job = bq_client.query(query=query, job_config=query_job_config)

Uses BigQuery query parameters for structured, safer database access.

Function declaration

find_products_fd = genai_types.FunctionDeclaration(
    response=None,
    description="Search for products with optional semantic search queries and filters.",
    name="find_products",
    parameters=genai_types.Schema(type=genai_types.Type.OBJECT),
)

Defines the schema Gemini uses to call the product search tool.

Models & APIs used

  • Models: gemini-3.5-flash
  • APIs / services: Vertex AI, BigQuery
  • SDKs / libraries: google-genai, google-cloud-bigquery, google-cloud-bigquery-storage, langgraph, langgraph-checkpoint, langchain_core, pydantic, thefuzz, db-dtypes

When to use this

Use this pattern when an assistant must answer natural-language retail search questions by safely calling constrained backend data tools.

Gotchas & caveats

  • The Cymbal Retail dataset, product table, store table, inventory table, and remote embedding model must already be created.
  • Colab users must authenticate with google.colab.auth.authenticate_user().
  • The notebook installs dependencies and says the Jupyter runtime must be restarted afterward.
  • PROJECT_ID defaults from GOOGLE_CLOUD_PROJECT if the placeholder is not changed.
  • Dataset location is configured separately as CYMBAL_DATASET_LOCATION = “US” while REGION is “us-central1”.
  • find_stores asserts latitude and longitude must both be defined or both omitted.
  • Radius store search raises an error when user location is unknown.
  • The notebook notes google-genai does not properly handle floats for radius, so radius_km is typed as an integer.

Best practices

  • Use function declarations to constrain database access instead of generating and executing arbitrary SQL.
  • Use BigQuery query parameters for filters such as price, store IDs, radius, product ID, and store ID.
  • Cap requested result counts with MAX_PRODUCT_RESULTS and MAX_STORE_RESULTS.
  • Use semantic search only when product_search_query is provided, otherwise use standard SQL filtering.
  • Execute multiple function calls asynchronously and feed function responses back to the model.
  • Validate BigQuery rows into pydantic models before returning tool results.