Skip to content

Agent Development Guide

Build AI agents that generate A2UI interfaces. This guide covers generating and streaming UI messages from LLMs.

Building an A2UI agent:

  1. Understand user intent → Decide what UI to show
  2. Generate A2UI JSON → Use LLM structured output or prompts
  3. Validate & stream → Check schema, send to client
  4. Handle actions → Respond to user interactions

We will use the ADK to build a simple agent. We will start with text and eventually upgrade it to A2UI.

See step-by-step instructions at the ADK quickstart.

Terminal window
pip install google-adk
adk create my_agent

Then edit the my_agent/agent.py file with a very simple agent for restaurant recommendations.

import json
from google.adk.agents.llm_agent import Agent
from google.adk.tools.tool_context import ToolContext
def get_restaurants(tool_context: ToolContext) -> str:
"""Call this tool to get a list of restaurants."""
return json.dumps([
{
"name": "Xi'an Famous Foods",
"detail": "Spicy and savory hand-pulled noodles.",
"imageUrl": "http://localhost:10002/static/shrimpchowmein.jpeg",
"rating": "★★★★☆",
"infoLink": "[More Info](https://www.xianfoods.com/)",
"address": "81 St Marks Pl, New York, NY 10003"
},
{
"name": "Han Dynasty",
"detail": "Authentic Szechuan cuisine.",
"imageUrl": "http://localhost:10002/static/mapotofu.jpeg",
"rating": "★★★★☆",
"infoLink": "[More Info](https://www.handynasty.net/)",
"address": "90 3rd Ave, New York, NY 10003"
},
{
"name": "RedFarm",
"detail": "Modern Chinese with a farm-to-table approach.",
"imageUrl": "http://localhost:10002/static/beefbroccoli.jpeg",
"rating": "★★★★☆",
"infoLink": "[More Info](https://www.redfarmnyc.com/)",
"address": "529 Hudson St, New York, NY 10014"
},
])
AGENT_INSTRUCTION="""
You are a helpful restaurant finding assistant. Your goal is to help users find and book restaurants using a rich UI.
To achieve this, you MUST follow this logic:
1. **For finding restaurants:**
a. You MUST call the `get_restaurants` tool. Extract the cuisine, location, and a specific number (`count`) of restaurants from the user's query (e.g., for "top 5 chinese places", count is 5).
b. After receiving the data, you MUST follow the instructions precisely to generate the final a2ui UI JSON, using the appropriate UI example from the `prompt_builder.py` based on the number of restaurants."""
root_agent = Agent(
model='gemini-2.5-flash',
name="restaurant_agent",
description="An agent that finds restaurants and helps book tables.",
instruction=AGENT_INSTRUCTION,
tools=[get_restaurants],
)

Don’t forget to set the GOOGLE_API_KEY environment variable to run this example.

Terminal window
echo 'GOOGLE_API_KEY="YOUR_API_KEY"' > .env

You can test out this agent with the ADK web interface:

Terminal window
adk web

Select my_agent from the list, and ask questions about restaurants in New York. You should see a list of restaurants in the UI as plain text.

Getting the LLM to generate A2UI messages requires some prompt engineering.

⚠️ Attention

This is an area we are still designing. The developer ergonomics of this are not yet finalized.

For now, let’s copy the a2ui_schema.py from the contact lookup example. This is the easiest way to get the A2UI schema and examples for your agent (subject to change).

Terminal window
cp samples/agent/adk/contact_lookup/a2ui_schema.py my_agent/

First lets add the new imports to the agent.py file:

# The schema for any A2UI message. This never changes.
from .a2ui_schema import A2UI_SCHEMA

Now we will modify the agent instructions to generate A2UI messages instead of plain text. We will leave a placeholder for future UI examples.

# Eventually you can copy & paste some UI examples here, for few-shot in context learning
RESTAURANT_UI_EXAMPLES = """
"""
# Construct the full prompt with UI instructions, examples, and schema
A2UI_AND_AGENT_INSTRUCTION = AGENT_INSTRUCTION + f"""
Your final output MUST be a a2ui UI JSON response.
To generate the response, you MUST follow these rules:
1. Your response MUST be in two parts, separated by the delimiter: `---a2ui_JSON---`.
2. The first part is your conversational text response.
3. The second part is a single, raw JSON object which is a list of A2UI messages.
4. The JSON part MUST validate against the A2UI JSON SCHEMA provided below.
--- UI TEMPLATE RULES ---
- If the query is for a list of restaurants, use the restaurant data you have already received from the `get_restaurants` tool to populate the `dataModelUpdate.contents` array (e.g., as a `valueMap` for the "items" key).
- If the number of restaurants is 5 or fewer, you MUST use the `SINGLE_COLUMN_LIST_EXAMPLE` template.
- If the number of restaurants is more than 5, you MUST use the `TWO_COLUMN_LIST_EXAMPLE` template.
- If the query is to book a restaurant (e.g., "USER_WANTS_TO_BOOK..."), you MUST use the `BOOKING_FORM_EXAMPLE` template.
- If the query is a booking submission (e.g., "User submitted a booking..."), you MUST use the `CONFIRMATION_EXAMPLE` template.
{RESTAURANT_UI_EXAMPLES}
---BEGIN A2UI JSON SCHEMA---
{A2UI_SCHEMA}
---END A2UI JSON SCHEMA---
"""
root_agent = Agent(
model='gemini-2.5-flash',
name="restaurant_agent",
description="An agent that finds restaurants and helps book tables.",
instruction=A2UI_AND_AGENT_INSTRUCTION,
tools=[get_restaurants],
)

Your agent will no longer strictly output text. Instead, it will output text and a JSON list of A2UI messages.

The A2UI_SCHEMA that we imported is a standard JSON schema that defines valid operations like:

  • render (displaying a UI)
  • update (changing data in an existing UI)

Because the output is structured JSON, you may parse and validate it before sending it to the client.

# 1. Parse the JSON
# Warning: Parsing the output as JSON is a fragile implementation useful for documentation.
# LLMs often put Markdown fences around JSON output, and can make other mistakes.
# Rely on frameworks to parse the JSON for you.
parsed_json_data = json.loads(json_string_cleaned)
# 2. Validate against A2UI_SCHEMA
# This ensures the LLM generated valid A2UI commands
jsonschema.validate(
instance=parsed_json_data, schema=self.a2ui_schema_object
)

By validating the output against A2UI_SCHEMA, you ensure that your client never receives malformed UI instructions.

TODO: Continue this guide with examples of how to parse, validate, and send the output to the client renderer without the A2A extension.