Build Your First ADK Agent with Vertex AI
Wrap a WCAG scanner as an ADK tool and build an AI triage agent on Vertex AI.
Introduction
30 Second Summary
Most websites have accessibility issues that affect real people every day. Automated scanners can find violations, but the real challenge is knowing which ones to fix first.
In this project, you will turn the accessibility scanner you built in Project 1 into an AI-powered triage agent using Google's Agent Development Kit (ADK) and Vertex AI. Give it a URL, and it autonomously scans the page, groups violations by severity, cites the exact WCAG criteria, and tells you what to fix first.
What You'll Build
Imagine typing a URL into a chat interface and watching an AI agent launch a headless browser, run an accessibility scan, then hand you a prioritized triage report with WCAG citations and fix suggestions.
By the end of this project, you'll have:
- A working ADK agent connected to Vertex AI that autonomously runs your scanner tool when you give it a URL, returning real violations from axe-core instead of hallucinated results.
- A structured triage report with violations grouped by priority (P0-P3), WCAG success criteria citations, and actionable fix suggestions for the top issues.
- Agent output stored in session state via output_key, ready for the Fix-Generation agent you will build in Project 3.
- Secret Mission: A regression detection tool that queries your existing PostgreSQL scan history to flag which violations are new vs. recurring.
Are there any prerequisites?
This is Project 2 in the AccessAgent series. You need to have completed Project 1 (Containerize a Python Scanner with Docker), which gives you the accessagent-scanner/ directory with a working scanner, Docker containers, and a Python virtual environment already set up.
You should also be familiar with Python functions, type hints, and docstrings.
Before We Start
Before writing any agent code, take a moment to think about what you are building and why it connects to the larger AccessAgent platform.
Set Up GCP and Install ADK
Your ADK agent needs a backend LLM to reason about scan results. Vertex AI provides access to Gemini 2.5 Flash, and ADK (Agent Development Kit) gives you the framework to wire Python tools to that LLM.
Without a GCP project and credentials, nothing can authenticate. In this step, you'll create a GCP project, set up authentication, and install ADK into your existing scanner environment from Project 1.
In this step, get ready to:
- Create a GCP project with the Vertex AI API enabled.
- Authenticate with Application Default Credentials.
- Install Google ADK into your existing virtual environment.
Create a GCP project and enable Vertex AI
ADK connects to Gemini through Vertex AI, which requires a GCP project with the API enabled.
- Create a new GCP project by running this command:
gcloud projects create [[GCP_PROJECT="accessagent"]] --name="AccessAgent"
You should see a message confirming the project creation operation completed.
Seeing a project ID already exists error?
GCP project IDs must be globally unique. If accessagent is taken, try a variation like accessagent-001 or add your initials as a prefix.
Help me resolve a GCP project ID conflict.
- Set this as your active project by running:
gcloud config set project [[GCP_PROJECT="accessagent"]]
- Enable the Vertex AI API by running:
gcloud services enable aiplatform.googleapis.com
You should see a message that the operation finished successfully.
What do these commands do?
- gcloud config set project tells all future gcloud commands to target this project by default.
- gcloud services enable aiplatform.googleapis.com turns on the Vertex AI API so ADK can call Gemini models.
Next, link a billing account so Vertex AI can use your existing credits.
- Open the Google Cloud Console at https://console.cloud.google.com.
- Select your new project from the project dropdown at the top of the page.
- Open the Navigation menu (hamburger icon in the top-left).
- Select Billing.
- Click Link a billing account and choose your existing billing account from the dropdown.
- Click Set account to confirm.
You should now see the Billing Overview page showing your linked account.
Why does billing need to be linked?
Vertex AI API calls are charged against a billing account. Your existing Vertex AI credits are tied to your billing account, so linking it to this project lets ADK use those credits for Gemini API calls.
If you only have one billing account, GCP may link it automatically. In that case you'll see the Billing Overview page directly instead of the link prompt.
Authenticate with Application Default Credentials
ADK uses Application Default Credentials (ADC) to authenticate with Vertex AI. ADC is a local credential file that lets Google Cloud client libraries make API calls on your behalf without embedding secrets in code.
- Set up ADC by running this command:
gcloud auth application-default login
A browser window opens asking you to sign in with your Google account. Sign in with the account that owns your GCP project and grant the requested permissions.
After signing in, the terminal prints a confirmation that credentials have been saved.
What is ADC?
Application Default Credentials is a standard way for Google Cloud libraries to find credentials on your machine. ADK uses ADC automatically when GOOGLE_GENAI_USE_ENTERPRISE=True is set in its environment file (you'll create that file in the next step).
Browser didn't open?
If the browser does not open automatically, re-run the command with the --no-launch-browser flag and follow the URL printed in your terminal to complete authorization manually.
Help me set up Application Default Credentials.
Install ADK into your existing project
ADK provides the agent framework including tool calling, session state, and the adk web dev UI. You'll install it into the same virtual environment you built in Project 1.
- Navigate to your project directory and activate the virtual environment by running:
macOS
cd ~/Desktop/accessagent-scanner
source .venv/bin/activate
Windows
cd ~\Desktop\accessagent-scanner
.venv\Scripts\activate
You should see (.venv) appear at the start of your terminal prompt.
- Install ADK by running:
pip install google-adk
You'll see pip downloading and installing the google-adk package along with its dependencies. This takes a minute or two.
Why install into the existing environment?
Your .venv already has Playwright and axe-playwright-python from Project 1. The ADK agent will call these libraries as tools, so everything needs to live in the same environment.
Before you run the verification, what do you expect adk --version to print if the install succeeded?
- Verify ADK is installed by running:
adk --version
You should see a version number printed (such as 2.4.0 or higher). This confirms ADK is installed and available on your PATH.
- Confirm ADC is working by running:
gcloud auth application-default print-access-token
You should see a long access token string printed. This proves your local credentials can reach Google Cloud APIs.
Command not found or token error?
- If adk --version says command not found, make sure your virtual environment is active. You should see (.venv) in your terminal prompt.
- If the token command fails, re-run gcloud auth application-default login and complete the browser sign-in flow.
Help me troubleshoot ADK installation or ADC authentication.
Your GCP project is live, credentials are configured, and ADK is installed. Next up, you'll create your first agent and chat with it through ADK's web interface.
Create a Bare Agent and Chat via ADK Web
Your GCP project is live, credentials are configured, and ADK is installed. Now it's time to build something with it.
Before wiring in the scanner, you need to confirm the path from your machine to Gemini actually works. A minimal "bare" agent with no tools proves the authentication pipeline is live and shows you the ADK project structure.
In this step, get ready to:
- Create the ADK agent directory structure inside your project.
- Define a minimal agent connected to Vertex AI.
- Chat with your agent through the ADK web interface.
Create the ADK agent directory inside your existing project
ADK expects a specific directory layout. Each agent lives in its own folder containing an __init__.py, an agent.py, and a .env file for configuration.
- In your terminal (still inside accessagent-scanner/ with your virtual environment active), create the agent directory and its files by running:
mkdir triage_agent
touch triage_agent/__init__.py triage_agent/agent.py triage_agent/.env
- Verify the files were created by running:
ls triage_agent
You should see __init__.py, agent.py, and .env listed in the output.
What does each file do?
- __init__.py tells ADK which module contains the agent definition.
- agent.py defines the agent itself (model, name, instructions, tools).
- .env stores Vertex AI connection settings so ADK knows which GCP project and region to use.
Your project structure now looks like this:
accessagent-scanner/
models.py # from Project 1
scanner.py # from Project 1
persist.py # from Project 1
Dockerfile # from Project 1
docker-compose.yml # from Project 1
requirements.txt # from Project 1
triage_agent/ # NEW - ADK agent
__init__.py
agent.py
.env
- Open triage_agent/__init__.py in VS Code and paste this single line:
from . import agent
Why this import?
This line tells ADK to load the agent module when it discovers the triage_agent/ directory. ADK looks for this import to find your agent definition.
- Open triage_agent/.env and paste the following configuration:
GOOGLE_GENAI_USE_ENTERPRISE=True
GOOGLE_CLOUD_PROJECT=[[GCP_PROJECT_ID="your GCP project ID (e.g. accessagent)"]]
GOOGLE_CLOUD_LOCATION=us-central1
What do these variables do?
- GOOGLE_GENAI_USE_ENTERPRISE=True tells ADK to route requests through Vertex AI instead of the consumer Gemini API.
- GOOGLE_CLOUD_PROJECT is the GCP project ID you created in Step 1.
- GOOGLE_CLOUD_LOCATION sets the Vertex AI region. us-central1 is the default with broad model availability.
- Save both __init__.py and .env.
Not sure what your project ID is?
Run gcloud config get-value project in your terminal. It prints the active project ID you set in Step 1.
help me find my GCP project ID
Define a minimal agent in agent.py
The agent definition is where you tell ADK which model to use and how the agent should behave. For now, you will create a bare agent with no tools that can only chat using Gemini's general knowledge.
- Open triage_agent/agent.py in VS Code and paste the following code:
from google.adk import Agent
root_agent = Agent(
model="gemini-2.5-flash",
name="triage_agent",
description="An accessibility triage agent that scans websites and prioritizes WCAG violations.",
instruction="""You are an accessibility expert specializing in WCAG 2.1/2.2 AA compliance. Answer questions about web accessibility and WCAG guidelines.""",
)
What does this code do?
- from google.adk import Agent imports the ADK Agent class.
- model="gemini-2.5-flash" connects the agent to Gemini 2.5 Flash on Vertex AI.
- name is how ADK identifies this agent. It must match the directory name.
- instruction is the system prompt that shapes the agent's personality and behavior.
- root_agent is the variable name ADK looks for when loading the agent module.
- Save agent.py.
Seeing a red underline on the import?
VS Code may show a linting error if it is not using the correct Python interpreter. Press Cmd+Shift+P (macOS) or Ctrl+Shift+P (Windows), type Python: Select Interpreter, and choose the .venv interpreter inside accessagent-scanner/.
help me fix a red underline on my google.adk import in VS Code
✔️ Awesome, I've got everything!
Great. Double check you have saved all three files in triage_agent/.
ⓧ I'd like to double check the full code
Here is the content of each file. Confirm yours matches exactly.
from . import agent
GOOGLE_GENAI_USE_ENTERPRISE=True
GOOGLE_CLOUD_PROJECT=your-project-id-here
GOOGLE_CLOUD_LOCATION=us-central1
from google.adk import Agent
root_agent = Agent(
model="gemini-2.5-flash",
name="triage_agent",
description="An accessibility triage agent that scans websites and prioritizes WCAG violations.",
instruction="""You are an accessibility expert specializing in WCAG 2.1/2.2 AA compliance. Answer questions about web accessibility and WCAG guidelines.""",
)
Launch adk web and chat with the agent
ADK includes a built-in web interface for testing agents interactively. It auto-discovers agent folders in the directory you point it to.
- Start the ADK dev server by running this command from accessagent-scanner/ (the parent of triage_agent/):
adk web .
What does this command do?
adk web . starts a local web server on port 8000. The . tells ADK to scan the current directory for agent subdirectories. It finds triage_agent/ and loads it.
You should see output in the terminal indicating the server is running.
- Open your browser and go to http://localhost:8000.
- In the top-left dropdown, select triage_agent.
Before you type anything, think about this: what do you expect the agent to say if you just say "hello"?
- Type hello in the chat box and press Enter.
The agent responds with a greeting and offers to help with accessibility questions. This confirms your Vertex AI authentication is working end-to-end.
Agent not responding or seeing an error?
- Check that your .env file has the correct project ID. Run gcloud config get-value project to confirm.
- Confirm ADC is valid by running gcloud auth application-default print-access-token in a new terminal tab. If it fails, re-run gcloud auth application-default login.
- Make sure you selected triage_agent from the dropdown, not a different agent.
help me debug why my ADK agent is not responding in the web UI
Now try something more ambitious. What do you think will happen if you ask the agent to scan a URL for accessibility issues?
- Type something like Scan https://example.com for accessibility violations and press Enter.
The agent either politely refuses ("I don't have the ability to scan websites") or fabricates results from its general training data. Either way, it cannot actually run a scan. It has no tools.
Why can't it scan?
Right now the agent is just an LLM with a system prompt. It can discuss accessibility concepts, but it has no way to execute real code or open a browser.
In the next step, you will wrap your existing Playwright scanner as an ADK tool. The LLM will then be able to call it autonomously when a user asks for a scan.
Your bare agent is live and chatting through Vertex AI. Next up, you will give it real scanning power by wrapping your Playwright scanner as an ADK tool.
Wrap the Scanner as an ADK Tool
In the previous step, you asked the agent to scan a URL and it either refused or invented results. Without a real scanning tool, the LLM has no choice but to hallucinate accessibility data.
ADK tools are plain Python functions with type hints and docstrings. The LLM reads the function signature to decide when and how to call it. In this step, you'll wrap the axe-core scanner from Project 1 as a tool the agent can invoke autonomously.
In this step, get ready to:
- Create tools.py with a function that runs axe-core scans.
- Wire the tool into the agent so the LLM can invoke it.
- Test the agent with a real accessibility scan.
Create tools.py with the scanner function
ADK discovers tools through the function's type hints and docstring. The LLM reads the docstring to understand what the tool does and when to use it. This means your docstring needs to be thorough and precise.
- Create a new file called tools.py inside the triage_agent/ directory.
- Add the imports and the first part of the scanner function by copying this code into it:
from axe_playwright_python.sync_playwright import Axe
from playwright.sync_api import sync_playwright
def scan_url_for_violations(url: str) -> dict:
"""Scans a URL for WCAG accessibility violations using the axe-core engine.
This tool opens the given URL in a headless browser, runs the axe-core
accessibility engine, and returns all WCAG violations found including
rule IDs, severity levels, descriptions, and affected elements.
Args:
url: The full URL to scan including the protocol (e.g. https://example.com).
Returns:
A dictionary containing the URL scanned, total violation count,
and a list of violations with rule_id, impact, description,
help_url, wcag_tags, and affected_elements count.
"""
axe = Axe()
with sync_playwright() as pw:
browser = pw.chromium.launch(headless=True)
page = browser.new_page()
page.goto(url, wait_until="networkidle")
results = axe.run(page)
browser.close()
What does this code do?
The function signature scan_url_for_violations(url: str) -> dict tells the LLM exactly what input the tool needs (a URL string) and what it returns (a dictionary).
The long docstring is not just documentation for humans. The LLM reads it to decide when this tool is relevant to a user's request. A vague docstring means the LLM won't know when to call the tool.
The scanning logic uses the same Playwright and axe-playwright-python libraries you already have from Project 1. It opens a headless browser, navigates to the URL, runs the axe-core engine, and returns raw results.
- Now add the violations processing below the browser.close() line. Copy this code at the bottom of the function:
violations = []
for v in results.response.get("violations", []):
wcag_tags = [t for t in v.get("tags", []) if t.startswith("wcag")]
violations.append({
"rule_id": v["id"],
"impact": v.get("impact", "unknown"),
"description": v.get("description", ""),
"help_url": v.get("helpUrl", ""),
"wcag_tags": wcag_tags,
"affected_elements": len(v.get("nodes", [])),
})
return {
"url": url,
"total_violations": len(violations),
"violations": violations,
}
What does the violations processing do?
- results.response is the raw axe-core output dictionary. The loop extracts each violation and reshapes it into a clean structure the LLM can reason about.
- The wcag_tags filter keeps only WCAG-related tags (like wcag2a or wcag21aa) so the agent can cite specific success criteria.
- The return dictionary gives the LLM a total count and the full list of violations with all the metadata it needs for triage.
- Save triage_agent/tools.py.
- Verify the function is importable by running this command from your accessagent-scanner/ directory:
python -c "from triage_agent.tools import scan_url_for_violations; print(scan_url_for_violations)"
You should see something like <function scan_url_for_violations at 0x...> printed. This confirms Python can find and import your tool function.
Seeing a ModuleNotFoundError?
- Make sure you are running the command from the accessagent-scanner/ directory (the parent of triage_agent/), not from inside triage_agent/ itself.
- Check that your virtual environment is active. You should see (.venv) in your terminal prompt.
- Confirm the file is named exactly tools.py (not tool.py) and lives inside triage_agent/.
Help me debug my triage_agent/tools.py import error.
✔️ Awesome, I've got everything!
Great. Make sure you've saved tools.py before moving on.
ⓧ I'd like to double check the full code
Your triage_agent/tools.py should contain these sections in order:
- Two imports at the top: Axe from axe_playwright_python and sync_playwright from playwright.
- The scan_url_for_violations function definition with the full docstring (Args, Returns).
- The browser scanning block: creates an Axe() instance, launches Chromium, navigates to the URL, runs axe.run(page), and closes the browser.
- The violations processing loop: iterates results.response.get("violations", []), extracts WCAG tags, and builds a clean list of dicts.
- The return statement: a dict with url, total_violations, and violations.
Scroll up and confirm each section is present in your file, in order.
Wire the tool into agent.py
The tool function exists, but the agent doesn't know about it yet. You need to import it and add it to the agent's tools list.
- Open triage_agent/agent.py and replace the entire contents with this updated version:
from google.adk import Agent
from .tools import scan_url_for_violations
root_agent = Agent(
model="gemini-2.5-flash",
name="triage_agent",
description="An accessibility triage agent that scans websites and prioritizes WCAG violations.",
instruction="""You are an accessibility expert specializing in WCAG 2.1/2.2 AA compliance.
When the user provides a URL to scan, use the scan_url_for_violations tool to scan the page and report the findings.""",
tools=[scan_url_for_violations],
)
What changed in agent.py?
- from .tools import scan_url_for_violations imports the function from your new tools.py file.
- tools=[scan_url_for_violations] registers the function as a tool the LLM can call. ADK reads the function's docstring and type hints to generate the tool schema automatically.
- The updated instruction explicitly tells the agent to use the tool when a user provides a URL. This guides the LLM's decision-making.
- Save triage_agent/agent.py.
✔️ Awesome, I've got everything!
Great. Double check you've saved the file before restarting the dev server.
ⓧ I'd like to double check the full code
Here is the complete triage_agent/agent.py:
from google.adk import Agent
from .tools import scan_url_for_violations
root_agent = Agent(
model="gemini-2.5-flash",
name="triage_agent",
description="An accessibility triage agent that scans websites and prioritizes WCAG violations.",
instruction="""You are an accessibility expert specializing in WCAG 2.1/2.2 AA compliance.
When the user provides a URL to scan, use the scan_url_for_violations tool to scan the page and report the findings.""",
tools=[scan_url_for_violations],
)
Test the agent with a real scan
- If adk web is still running from the previous step, stop it by pressing Ctrl+C in the terminal.
- Restart the ADK dev server by running this command from the accessagent-scanner/ directory:
adk web .
- Open http://localhost:8000 in your browser.
- Select triage_agent from the agent dropdown.
Before you send a message, think about what you expect to happen. Last time you asked the agent to scan a URL, it hallucinated results. Now it has a real tool. What do you think will be different?
- Type this message into the chat:
Scan https://example.com for accessibility violations
- Click the Events tab in the ADK web UI to see what happened under the hood.
In the Events tab, you should see the agent make a tool call to scan_url_for_violations with the URL you provided. The agent then receives real axe-core results and reports back its findings.
What should I see?
The agent's response contains real accessibility data from axe-core. For https://example.com, the page is simple enough that you might see zero violations or just a few minor ones. The key proof is in the Events tab: you can see the tool call, the URL argument, and the returned data.
The response is real, but notice it's verbose and unstructured. The agent dumps results without clear prioritization. That limitation is what the next step addresses.
Agent not calling the tool?
- Check that you restarted adk web . after saving your changes. The server does not hot-reload.
- Confirm agent.py has tools=[scan_url_for_violations] in the Agent constructor. A missing tools parameter means the LLM has nothing to call.
- Check your terminal for import errors. If tools.py has a syntax error, the server will show it at startup.
Help me debug why my ADK agent isn't calling the scan_url_for_violations tool.
Your agent now has real capabilities. It autonomously decides when to scan and returns actual axe-core data. Next up, you'll refine the agent's instructions to produce a structured, prioritized triage report instead of a raw data dump.
Produce a Prioritized Triage Report
Your agent can now run a real accessibility scan and return results. The problem is that raw output dumped by the LLM is not actionable.
Downstream agents in your pipeline (like the Fix-Generation agent you will build in Project 3) need a consistently structured triage with severity grouping and WCAG citations. In this step, you will refine the agent's instruction and wire up session state so the triage output is ready for the next agent to consume.
In this step, get ready to:
- Rewrite the agent instruction to produce structured, prioritized triage reports.
- Add output_key to store the triage result in session state for downstream agents.
- Test the improved agent on a complex site and verify session state.
Rewrite the agent instruction for structured triage
An LLM's output quality is directly shaped by the instruction you give it. A vague instruction produces vague output. A detailed instruction with a clear format specification produces consistent, structured results every time.
- In triage_agent/agent.py, find the current instruction parameter:
instruction="""You are an accessibility expert specializing in WCAG 2.1/2.2 AA compliance.
When the user provides a URL to scan, use the scan_url_for_violations tool to scan the page and report the findings.""",
- Replace that entire instruction value with the following detailed triage prompt:
instruction="""You are an accessibility expert specializing in WCAG 2.1/2.2 AA compliance.
When the user provides a URL to scan:
1. Use the scan_url_for_violations tool to scan the page.
2. Group the violations by impact level: critical, serious, moderate, minor.
3. For each violation, cite the specific WCAG success criterion from the wcag_tags field.
4. Assign a priority: P0 for critical, P1 for serious, P2 for moderate, P3 for minor.
5. For the top 3 highest-priority violations, provide a brief actionable fix suggestion.
Format your response as a triage report with:
- A 1-2 sentence summary of the site's accessibility health
- Violation counts by severity
- A prioritized list of violations with WCAG citations and fix suggestions
Be concise and actionable. Focus on what to fix first and why.""",
What does this instruction do?
The instruction gives the LLM a step-by-step procedure and an explicit output format. This is the pattern that turns a general-purpose model into a specialist.
- Steps 1-5 tell the agent exactly what to do with the scan results: group, cite standards, assign priorities, suggest fixes.
- The format section constrains the response shape so downstream agents always know what to parse.
- Priority labels (P0-P3) map directly to axe-core's impact levels: critical, serious, moderate, minor.
Add output_key to store results in session state
In Project 3, the Fix-Generation agent will need to read the triage report this agent produces. ADK's output_key parameter automatically stores the agent's final text response in session state under the key you specify.
- In agent.py, find the closing of the Agent constructor. Locate this line:
tools=[scan_url_for_violations],
)
- Add output_key="triage_report" so the last two lines of the constructor look like this:
tools=[scan_url_for_violations],
output_key="triage_report",
)
What does output_key do?
When the agent finishes responding, ADK automatically stores the full text response in session state under the key you provide. Other agents in a pipeline can then read from that key without needing a direct connection to this agent.
- Save agent.py.
✔️ Awesome, I've got everything!
Great. Make sure you have saved your file before continuing.
ⓧ I'd like to double check the full code
Here is the complete agent.py file. Confirm yours matches:
from google.adk import Agent
from .tools import scan_url_for_violations
root_agent = Agent(
model="gemini-2.5-flash",
name="triage_agent",
description="An accessibility triage agent that scans websites and prioritizes WCAG violations.",
instruction="""You are an accessibility expert specializing in WCAG 2.1/2.2 AA compliance.
When the user provides a URL to scan:
1. Use the scan_url_for_violations tool to scan the page.
2. Group the violations by impact level: critical, serious, moderate, minor.
3. For each violation, cite the specific WCAG success criterion from the wcag_tags field.
4. Assign a priority: P0 for critical, P1 for serious, P2 for moderate, P3 for minor.
5. For the top 3 highest-priority violations, provide a brief actionable fix suggestion.
Format your response as a triage report with:
- A 1-2 sentence summary of the site's accessibility health
- Violation counts by severity
- A prioritized list of violations with WCAG citations and fix suggestions
Be concise and actionable. Focus on what to fix first and why.""",
tools=[scan_url_for_violations],
output_key="triage_report",
)
Test with a complex site and verify session state
Time to see the difference the new instruction makes. You need to restart the ADK dev server to pick up your changes.
- In your terminal, press Ctrl+C to stop the running adk web process.
- Restart the dev server by running:
adk web .
- Open http://localhost:8000 in your browser.
- Select triage_agent from the dropdown.
Before you send your message, think about what you expect the response to look like now compared to the raw dump from Step 3. Do you expect priorities, WCAG citations, and a structured format?
- Ask the agent to scan a site with known accessibility issues. Try something like:
Scan https://www.example.com for accessibility violations
The agent responds with a structured triage report. You should see violations grouped by severity (P0-P3), WCAG criterion citations from the wcag_tags field, and actionable fix suggestions for the top violations.
- Click the Events tab in the ADK web UI to inspect session state.
- Confirm you see the triage_report key populated with the agent's full output text.
What should I see in the Events tab?
The session state section shows key-value pairs that persist across turns. The triage_report key contains the full text of the agent's triage response. This is the data the Fix-Generation agent in Project 3 will read to decide what code changes to suggest.
Agent not producing structured output?
- Make sure you restarted adk web after saving. The server does not hot-reload agent code.
- Check that the instruction parameter uses triple quotes (""") and that the closing triple quote has a comma after it.
- Verify output_key="triage_report" appears as its own line inside the Agent constructor, with a trailing comma.
Still not working? Help me debug my ADK agent instruction
Your agent now produces clean, prioritized triage reports and stores them in session state. In the Secret Mission, you will give the agent a second tool that checks your Postgres database for regressions, letting it flag violations as new or recurring.
Secret mission
Add a Regression Detection Tool
Your triage agent scans a site and groups violations by severity. But without historical context, you can't tell whether a violation is brand new or has been lurking for weeks. In this mission, you'll connect the agent to your Postgres database from Project 1 and teach it to flag regressions.
Clean Up Your Resources
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project uses Vertex AI API calls charged against your existing credits, with no idle charges when you are not making requests.
Resources you used:
- GCP Project (accessagent or your chosen ID) with the Vertex AI API enabled.
- ADK agent directory (triage_agent/) added inside your existing accessagent-scanner/ project.
Keep everything running
No action needed. Choose this if you are still actively building the AccessAgent platform. The GCP project and local files stay available for Projects 3-8.
- Your GCP project incurs no charges when you are not sending API requests.
- If you resumed Postgres for the Secret Mission, stop it to free memory by running docker compose stop from the accessagent-scanner/ directory.
- The google-adk package remains installed in your .venv virtual environment alongside your Project 1 dependencies.
Pause - I'll come back to this later
Shut down running processes to free up memory. Your files and GCP project remain intact for when you return.
- Close the terminal running adk web (if still open). The ADK dev server stops immediately.
- Stop the Postgres container by running this from the accessagent-scanner/ directory:
docker compose stop
- The GCP project incurs no charges when idle. No further action needed.
Delete - I don't want to use this again
Remove all project resources and start fresh. You will need to recreate the GCP project for future projects in this series.
- Delete the GCP project by running:
gcloud projects delete [[GCP_PROJECT_ID="your GCP project ID e.g. accessagent"]]
- Remove the triage_agent/ directory while keeping the rest of your Project 1 work intact.
- On macOS or Linux, run:
cd ~/Desktop/accessagent-scanner
rm -rf triage_agent/
- On Windows, run:
cd ~\Desktop\accessagent-scanner
rmdir /s /q triage_agent
- If you resumed Postgres for the Secret Mission, stop it by running docker compose stop from the accessagent-scanner/ directory. This preserves your Project 1 scan history.
Nice Work!
Nice Work!
Nice work! You just evolved a standalone CLI scanner into an AI-powered triage agent that autonomously decides when to scan, analyzes the results, and delivers actionable fix priorities.
You've learned how to:
- Build an ADK agent connected to Vertex AI using Application Default Credentials, giving your Python code access to Gemini 2.5 Flash for reasoning.
- Wrap a real scanning tool as an ADK tool function that the LLM invokes autonomously based on user requests, reusing the same Playwright and axe-core stack from Project 1.
- Craft agent instructions that produce prioritized triage reports with WCAG citations, and store triage output in session state via output_key for multi-agent orchestration in Project 3.
- Secret Mission: Added a regression detection tool that queries your existing Postgres scan history to identify new vs. recurring violations.
Ready to quiz yourself?