MIKAEELS

Explore the blog

Get Started with ADK: Manage Sessions and State in Your Agent

In Part 3, each status request supplied a service name. A more convenient conversation looks like this:

User: Select payments. User: What is the status of my selected service? User: Switch to catalog. User: Check my selected service again.

We will store the selection under selected_service and make a tool read it explicitly. That gives our Python code a concrete value to inspect instead of relying on the model to reconstruct the selection from conversation history.

Prerequisites and file changes

Complete Parts 2 and 3 first. Work from adk-starter with .venv activated. Stop the running ADK process before editing.

Replace both ops_assistant/tools.py and ops_assistant/agent.py below. Keep the existing .env, __init__.py, and dependency environment.

Step 1: Choose the state scope

An ADK session represents one conversation and includes its events and state. Storage is handled by a session service; an in-memory implementation does not survive process restarts. See Google's session documentation.

For this example, use the unprefixed key selected_service, which belongs to the current session. ADK also supports user: for state shared across a user's sessions, app: for state shared throughout an application, and temp: for a single invocation. Our selection needs session scope. The state guide explains these scopes.

The expected behavior is simple: a fresh conversation has no selection, a valid selection replaces the old one, and an invalid selection leaves the existing value alone.

Step 2: Replace the complete tools module

Replace adk-starter/ops_assistant/tools.py with:

from google.adk.tools import ToolContext DEMO_SERVICES = { "payments": { "health": "degraded", "detail": "Elevated checkout latency in the demo dataset.", }, "catalog": { "health": "operational", "detail": "No active incident in the demo dataset.", }, } def list_services() -> dict: """List the service names supported by this simulated status demo.""" return { "status": "success", "source": "simulated_demo_data", "services": sorted(DEMO_SERVICES), } def get_service_status(service_name: str) -> dict: """Look up simulated health without changing the selected service. Args: service_name: Service name, such as payments or catalog. """ name = service_name.strip().lower() record = DEMO_SERVICES.get(name) if record is None: return { "status": "error", "source": "simulated_demo_data", "error_message": f"Unknown service: {name!r}.", "supported_services": sorted(DEMO_SERVICES), } return { "status": "success", "source": "simulated_demo_data", "service": name, "health": record["health"], "detail": record["detail"], } def select_service(service_name: str, tool_context: ToolContext) -> dict: """Remember a supported service for this conversation. Args: service_name: The service the user wants to select. """ result = get_service_status(service_name) if result["status"] == "error": return result name = result["service"] tool_context.state["selected_service"] = name return {"status": "success", "selected_service": name} def get_selected_service_status(tool_context: ToolContext) -> dict: """Return simulated health for the service selected in this conversation.""" name = tool_context.state.get("selected_service") if not name: return { "status": "error", "error_message": "No service selected. Ask the user to select one.", "supported_services": sorted(DEMO_SERVICES), } return get_service_status(name)

ADK supplies ToolContext when invoking these tools; the model does not provide that argument. Updates through tool_context.state are tracked in the tool event and processed by the session service. This follows Google's context injection and state update documentation.

The validation happens before the assignment. Selecting billing therefore cannot overwrite a valid selection with an unsupported name.

Step 3: Replace the complete agent

Replace adk-starter/ops_assistant/agent.py with:

from google.adk.agents import Agent from .tools import ( get_selected_service_status, get_service_status, list_services, select_service, ) root_agent = Agent( name="ops_assistant", model="gemini-flash-latest", description="Explains simulated service health and remembers a selection.", instruction=( "You are an operations learning assistant. " "Use list_services when asked which services are supported. " "When the user asks to select, switch to, or remember a service, " "call select_service with that name. " "For a status question naming a service, use get_service_status. " "A direct status lookup does not change the selection. " "For a status question about the selected service or a status " "question without a name, use get_selected_service_status. " "If no service is selected, ask the user to select one. " "Answer general operations questions without a status lookup. " "Use tool results for all selection and service-health claims. " "Always label health results as simulated demo data. " "Explain tool errors without inventing a successful result. " "You cannot restart, deploy, or modify services." ), tools=[ list_services, get_service_status, select_service, get_selected_service_status, ], )

Step 4: Test a complete conversation

Start the application:

adk web --port 8000

Open http://localhost:8000, select ops_assistant, and create a fresh session. Send these messages in order:

MessageExpected result
What is the status of my selected service?A request to select a service
Select payments.Confirms payments after a successful selection tool call
What is the status of my selected service?Simulated degraded payments status
Switch to catalog.Confirms catalog
Check my selected service again.Simulated operational catalog status
Select billing.Unsupported-service error
Check my selected service again.Still reports catalog
What is the status of payments?Reports simulated degraded payments status without changing the selection
Check my selected service again.Still reports catalog

After selecting payments, the selection tool's response should be:

{ "status": "success", "selected_service": "payments" }

Inspect the tool event and session state in the development interface. The state should contain selected_service with the same name. A verbal confirmation without the state update does not pass this check.

Step 5: Verify conversation isolation

Keep the server running and create a second, new session in the interface. Ask:

What is the status of my selected service?

The assistant should ask you to choose a service. It should not inherit catalog from the first session. Return to the original session and ask again; its selection should still be catalog.

If both conversations share a selection, check that you actually created a new session and that the code uses selected_service, rather than user:selected_service or app:selected_service.

Step 6: Review the example's limits

Our Python logic validates service names and controls selection updates. The model still decides which tool to call, so inspect tool events when evaluating behavior. Instructions alone do not guarantee that every conversation will follow the expected path.

Persistence is a separate choice from scope. Do not infer restart persistence from a successful conversation test; configure and test the session backend explicitly before relying on stored selections across deployments.

A real application must also associate sessions with authenticated users and enforce service access in its backend. The string selected_service is a preference, not evidence of permission.

The four-part project is now complete: you have a locally runnable agent, explicit tool contracts, and session-aware behavior. To extend it, replace the demo lookup with a monitoring integration and repeat the same success, unknown-service, failure, and isolation checks. You can return to the series introduction for the full learning path.

Quick Recap

How does an ADK tool update session state?

Accept an injected ToolContext and assign values through tool_context.state. ADK tracks these changes as part of the tool's event.

Does session state automatically survive a server restart?

No. Persistence depends on the configured session service. An in-memory session service loses its data when the process exits.

Does remembering a service authorize access to it?

No. The selected service is conversation data. A real application must enforce authorization separately in its backend or tool implementation.

Comments

Share your thoughts and questions below