DEV Community

Cover image for One Advisory: a fleet of agents that acts after the water warning, with nobody at the keyboard
Ujwal Vanjare
Ujwal Vanjare

Posted on

One Advisory: a fleet of agents that acts after the water warning, with nobody at the keyboard

I created this piece of content for the purposes of entering the All Things Agentic Hackathon. #AllThingsAgenticHackathon

A boil-water advisory reaches a dialysis clinic, a school and a long-term-care home in the same second. The message is identical. The work is not. Dialysis has to verify treatment water and a continuity plan. The school has to secure drinking and hygiene water and hold food service. The care home has to protect resident care and environmental services.

And today, nobody verifies that any of it happened. Existing tools define zones, send the alert, log the approval, and stop. An alert is treated as a response.

One Advisory is the fleet that starts after the warning. It's my entry for the Fortified Enterprise Fleet track, built on Gemini, Google ADK and Google Cloud, and the whole point of it is that it works when no human is watching.

Everything in the sandbox is fictional: facilities, people, advisories, contacts. It proves architecture and execution, not public-health outcomes.

What "works without a human" actually means here

I set myself one rule for the demo: the operator gets zero "continue" clicks. The system had to advance on real events and real time, and stop only at decisions a human is legally supposed to make. Here's the loop that runs for one incident:

  1. An authorized advisory arrives (an image or pasted text).
  2. Model Armor screens the text inline (sanitizeUserPrompt). A prompt-injection or PII match returns HTTP 422 with the receipt and nothing is stored.
  3. Gemini 3.5 Flash extracts only fields it can quote verbatim from its own transcription. Gemini Embedding 001 orders the facility playbooks. Agent Engine Memory Bank recalls how each facility behaved last time.
  4. Gemini synthesizes facility-specific tasks from the advisory wording, the facility's capacity note and its remembered history. A verifier keeps only tasks that cite a registered public-health source (CDC toolkit, CDC dialysis guidance, published incident studies). Anything else is dropped; a facility with fewer than two survivors keeps its standing playbook, and the fallback is recorded.
  5. Four ADK role agents on Vertex AI Agent Engine (Facility Fleet, Policy Gateway, Resource Coordinator, Recovery Verifier), each with its own Agent Identity behind one governed Agent Gateway, authorize each typed command. More on that below.
  6. Standing playbooks are delivered and three durable acknowledgement wakes are written to Firestore.
  7. Facilities answer asynchronously: from the UI, a keyed /v1 API, or a Pub/Sub push subscription. Gemma 4 triages what they said and flags replies that don't match the declared event. A facility can attach a photo; Gemini describes what is visible and says whether it supports the claim. An unsupported photo never becomes evidence.
  8. Cloud Scheduler fires the wakes on wall-clock time. A facility that answered is confirmed. A facility that stayed silent is escalated by the wake itself, a recheck is scheduled, and the governed fleet resumes.
  9. Two assistance requests become a resource conflict with options. The AI chooses nobody. A named incident commander allocates.
  10. After an authorized rescission the fleet verifies recovery per facility, writes each facility's outcome to Memory Bank, drafts the after-action briefing from the audit trace, and schedules a 42-day follow-up wake so a closed incident resumes weeks later.

Every step produces a receipt. GET /api/incidents/{id}/autonomy-proof classifies the whole trace: automatic actions, human-authority events, external events, durable wakes, managed commands, and the operator continue-click count, which is zero.

The part I'm proudest of: agents that propose, schemas that decide

The first version of the managed runtimes was a lookup table: a role/state/command matrix. It was safe, auditable, and honestly a bit embarrassing as an "agent". The final version keeps the table but moves it behind the model:

class OneAdvisoryRuntimeAgent:
    def query(self, payload):
        guard = self.armor.screen_request(payload)          # structural screen
        command, status = payload["expected_command"], payload["status"]
        schema_allowed = status in COMMAND_STATES[self.role].get(command, set())
        model = self.reasoner.propose(payload["incident_id"], status, command)  # ADK LlmAgent
        model_allowed = model["allowed"] and model["proposed_command"] == command
        allowed = schema_allowed and model_allowed          # both must agree
        ...
Enter fullscreen mode Exit fullscreen mode

The ADK LlmAgent (Gemini 3.5 Flash, a pydantic output_schema, low thinking) reads the bounded incident state and proposes a command with a one-sentence rationale. The registered schema then checks it. A hallucinated close_every_facility is simply not in the table; a model outage fails closed; an out-of-role request is refused with the reason. You get real reasoning and a provable ceiling on what it can do, and the rationale rides along in every receipt.

The part that surprised me: making the background worker matter

Halfway through I audited my own project and found the worst kind of gap: the Cloud Scheduler job fired every minute, the Firestore wakes were claimed transactionally, retries and dead letters all worked, and the handler appended one log row and did nothing. Perfect plumbing, no water.

The fix changed how I think about "autonomous". The wake now reads the facility's state, escalates silence with trigger: durable_wake, schedules a recheck, and calls the same advance_safe_automation the API surfaces call. The proof endpoint has a check called silent facility is escalated by the wake itself, and I verified it live: created an incident, answered for one facility, walked away, and came back to two escalations and a stage that had verified itself.

Lesson: background autonomy is only credible when the background worker changes state. The wake that escalates silence is worth more than any dashboard.

Things that cost me hours (so they don't cost you)

  • Agent Engine reserves GOOGLE_CLOUD_PROJECT / GOOGLE_CLOUD_LOCATION as env var names. Gemini 3.5 Flash is served from the global endpoint, not the runtime's region, so I set my own variable and point the ADK client at global at import time.
  • Agent Engine calls query() inside a running event loop. asyncio.run() on an ADK turn raises RuntimeError; run it on its own loop in a worker thread.
  • ADK wants LlmAgent(output_schema=PydanticModel), not response_schema in the generate config. And Gemini 3.5 spends tokens thinking: a 200-token cap returned "Here is the" and nothing else. thinking_level="LOW" cut a 5 s call to ~1.6 s.
  • Cloud Run has two URLs and Scheduler and Pub/Sub may sign OIDC tokens for different ones. Accept both audiences or your worker returns 401 after a redeploy.
  • Cloud Run traffic can be pinned to a named revision. My new revisions were "ready" and serving nothing. update-traffic --to-latest after every deploy.
  • Model Armor's NO_MATCH_FOUND contains the substring MATCH_FOUND. Parse the nested matchState fields, don't grep.
  • Gemma 4 on the Gemini API (gemma-4-26b-a4b-it) returns thinking parts by default; pass ThinkingConfig(include_thoughts=False) and a system instruction.

What it is not

It cannot issue or rescind an advisory, close a facility, or allocate a scarce resource. Three fictional facility classes cannot establish general coverage. No emergency manager, dialysis expert or accessibility specialist has validated it. No health, speed or compliance outcome is claimed. The public sandbox accepts fictional data only.

What it does prove: routing, governance, memory, guardrails and failure behaviour on a real Google Cloud fleet, and that "the agent handled it while you were away" can be a receipt, not a promise.

Try it: https://one-advisory-109051079423.us-central1.run.app ยท Code: https://github.com/usv240/one-advisory

Top comments (0)