androidengineers.Book a session

Ground the Gemini agent

Ground the Gemini agent

articleSelf-paced

Learning outcome

Separate trusted instructions, public event data and provider credentials.

The companion server fetches the DevEarth discovery feed and normalizes event fields before asking Gemini for a UI. Event descriptions, prior assistant text and user prompts remain untrusted data. The system instruction constrains the component vocabulary and tells the model to use supplied facts; validation still has to reject invalid output.

Android and server data boundaries with Gemini credentials

Android and server data boundaries with Gemini credentials.

Agent request envelope sent by Android

{
  "prompt":"Update the preparation advice using my current checklist selections.",
  "eventId":"published-event-id",
  "surfaceId":"new-surface-id",
  "history":[{"role":"user","text":"Help me prepare"}],
  "action":{"name":"refine","surfaceId":"old-surface-id",
    "componentId":"update-button","context":{"eventId":"published-event-id"}},
  "surfaceData":{"old-surface-id":{"ready0":true}},
  "previousComponents":[{"surfaceId":"old-surface-id","components":[
    {"id":"pack","component":"CheckBox","label":"Pack a charger","value":{"path":"/ready0"}}
  ]}]
}

This is an explanatory shape, not a live request: substitute an actual published event ID and complete earlier surface when exercising the server. The app sends at most 12 text turns, 3 previous component trees and snapshots of 6 surfaces. Counts and string limits bound the request, but they are not a tokenizer or a complete privacy policy. Changing endpoints requires a new conversation so prior context is not accidentally forwarded elsewhere.

Response ownership: conceptual server sequence

// Pseudocode showing boundaries; inspect server.mjs for executable code.
const events = normalizeDiscovery(await fetchPublishedListings());
const generated = await requestGemini({ instructions, events, context });
const composition = JSON.parse(generated); // { text, components }
const reply = validateSurface(composition, surfaceId, eventIds, priorChecklist);
// reply contains { text, messages }, with server-created A2UI envelopes.

Gemini returns assistant text and components, not a trusted executable program. protocol.mjs wraps the validated composition into three A2UI messages. Response JSON mode helps parseability but does not establish correct field values, factual grounding or a safe action. The server checks action event IDs against current listings. It does not prove every sentence is true.

Both Android and the server fetch listings in this preview: Android needs official links for local actions; the server needs current facts for generation. A disappearing event can make an action fail. A production session design could share a versioned snapshot instead of assuming two requests see identical data. Keep missing pricing or venue fields visibly unknown rather than replacing them with plausible guesses.

Practice and checkpoint

Trace where the Gemini key enters an HTTP header and show that it never appears in AgentRequest. Ask for an unsupported action such as buying a ticket: the app must not claim a purchase. Compare event title, date, venue and sponsored label against the feed; record mismatches instead of treating protocol validity as factual accuracy.

Source and next steps

Back to roadmap · Practice this unit in the codelab

Files to inspect

YOUR LEARNING JOURNEY

0 of 12 available lessons completed

Progress saved in this browser. No account needed.
Ground the Gemini agent | A2UI on Android with PocketCommunity | Android Engineers