|
| 1 | +# ADK HITL Approval Dashboard |
| 2 | + |
| 3 | +A drop-in **production-ready Human-in-the-Loop (HITL) approval middleware** for Google Agent Development Kit (ADK) agents — complete with an API backend and a demo Streamlit dashboard UI. |
| 4 | + |
| 5 | +## The Problem Solved |
| 6 | + |
| 7 | +ADK 1.x ships with an experimental `require_confirmation=True` feature that handles pausing the LLM loop for human verification. However, it is fundamentally built for local debugging and introduces major blockers to an enterprise environment: |
| 8 | + |
| 9 | +1. **Incompatible with Persistent Sessions:** Native confirmations intentionally do not serialize well and will completely fail to resume your agent if you use `DatabaseSessionService`, `SpannerSessionService`, or `VertexAiSessionService` (the mandatory session backends for production deployments). |
| 10 | +2. **Single-Agent Limitations:** They silently break across `AgentTool` nested bounds and true multi-agent (A2A) topologies, causing missing events or infinitely looping models. |
| 11 | +3. **No Resilient Audit Log:** The native confirmation tool leaves no easily queryable paper trail linking the human supervisor to a precise LLM request. |
| 12 | + |
| 13 | +*This project is the production implementation of the HITL pattern covered in the [ADK Multi-Agent Patterns Guide (Advent of Agents Day 13)](https://medium.com/@garythomasgeorge/why-google-adks-human-in-the-loop-story-has-a-production-gap-and-one-way-it-could-be-fixed-66aabef33a32).* |
| 14 | + |
| 15 | +## What This Library Provides |
| 16 | + |
| 17 | +This project solves the production gaps by explicitly decoupling the human approval payload from ADK's internal session memory. It introduces a session-agnostic REST API layer using an Adapter pattern. |
| 18 | + |
| 19 | +### The 3-Layer Architecture |
| 20 | + |
| 21 | +``` |
| 22 | +┌─────────────────────────────────────────┐ |
| 23 | +│ Dashboard UI (Streamlit) │ Layer 3: Demo/reference UI |
| 24 | +│ Approval inbox, audit log viewer │ (Easily replaced by Zendesk/etc.) |
| 25 | +└──────────────────┬──────────────────────┘ |
| 26 | + │ |
| 27 | +┌──────────────────▼──────────────────────┐ |
| 28 | +│ ApprovalRequest Model (Pydantic) │ Layer 2: Normalised Contract API |
| 29 | +│ FastAPI backend + SQLite store │ Session-agnostic persistence |
| 30 | +└────────────────┬────────────────────────┘ |
| 31 | + │ |
| 32 | + ┌──────────┴───────────┐ |
| 33 | +┌─────▼──────┐ ┌──────────▼──────┐ |
| 34 | +│ ADK 1.x │ │ ADK 2.0 │ Layer 1: Adapters |
| 35 | +│ Adapter │ │ Adapter │ Only this changes between versions |
| 36 | +└────────────┘ └─────────────────┘ |
| 37 | +``` |
| 38 | + |
| 39 | +By retaining HITL state inside an independent FastAPI engine and SQLite database, an active agent can pause safely. When a human supervisor hits "Approve" inside a centralized web portal hours later, the middleware simply posts the decision back into the agent's `/run_sse` stream seamlessly. |
| 40 | + |
| 41 | +## Configuration |
| 42 | + |
| 43 | +| Environment Variable | Default | Description | |
| 44 | +|---|---|---| |
| 45 | +| `ADK_HITL_API_URL` | `http://localhost:8000` | URL of the HITL approval FastAPI backend. Override for Cloud Run or any remote deployment. | |
| 46 | +| `ADK_HITL_POLL_INTERVAL_S` | `2.0` | Base polling interval in seconds. Up to 1s of random jitter is added automatically to reduce backend traffic under concurrent load. | |
| 47 | + |
| 48 | +Set these before starting the gateway: |
| 49 | + |
| 50 | +```bash |
| 51 | +export ADK_HITL_API_URL="https://your-hitl-service.run.app" |
| 52 | +export ADK_HITL_POLL_INTERVAL_S="3.0" |
| 53 | +``` |
| 54 | + |
| 55 | +## Quick Start (Local Sandbox) |
| 56 | + |
| 57 | +We have provided a demo customer service agent (`credit_agent`) alongside a launch script to test the interaction end-to-end. |
| 58 | + |
| 59 | +1. Create your Python virtual environment and sync dependencies using `uv` (requires Python 3.11+): |
| 60 | + |
| 61 | +```bash |
| 62 | +uv venv --python "python3.11" ".venv" |
| 63 | +source .venv/bin/activate |
| 64 | +uv sync --all-extras |
| 65 | +``` |
| 66 | + |
| 67 | +2. Start the FastAPI backend, Streamlit dashboard, and ADK Live Chat agent all at once: |
| 68 | + |
| 69 | +```bash |
| 70 | +./start_servers.sh |
| 71 | +``` |
| 72 | + |
| 73 | +3. Open `http://localhost:8080` to chat with the agent and ask for a $75 account credit. |
| 74 | +4. When the agent pauses and asks for a supervisor, open `http://localhost:8501` to approve or reject the request. |
| 75 | + |
| 76 | +## How to Use in Your Own ADK Application |
| 77 | + |
| 78 | +Wrapping an ADK agent with a formal enterprise HITL checkpoint takes under 5 lines of code: |
| 79 | + |
| 80 | +1. Import the `hitl_tool` gateway wrapper. |
| 81 | +2. Decorate your function tool. |
| 82 | +3. Attach it to your ADK Agent initialization using a standard `FunctionTool`. |
| 83 | + |
| 84 | +```python |
| 85 | +from google.adk.tools import FunctionTool |
| 86 | +from google.adk_community.tools.hitl.gateway import hitl_tool |
| 87 | + |
| 88 | +# 1. Wrap your function with the decorator |
| 89 | +@hitl_tool(agent_name="my_billing_agent") |
| 90 | +async def issue_refund(user_id: str, amount: float): |
| 91 | + # This block won't execute until explicitly approved in the dashboard |
| 92 | + return {"status": "success", "amount_refunded": amount} |
| 93 | + |
| 94 | +# 2. Attach to ADK Agent |
| 95 | +root_agent = Agent( |
| 96 | + name="my_billing_agent", |
| 97 | + tools=[FunctionTool(issue_refund)] |
| 98 | +) |
| 99 | +``` |
| 100 | + |
| 101 | +## Production Integration Strategies |
| 102 | + |
| 103 | +This repository acts as the production baseline for a contact center or enterprise orchestration grid. Once deployed to staging, consider swapping out: |
| 104 | + |
| 105 | +- **Storage Layer:** Replace the local `SQLite` engine in `app/api/store.py` with `PostgreSQL` or `Cloud Spanner`. |
| 106 | +- **Proactive Notification:** Hook the FastAPI `POST /approvals/` route into Slack, PagerDuty, or Microsoft Teams to actively ping channels when a high-risk request pops up. |
| 107 | +- **Remove Streamlit:** Bypass the Streamlit frontend completely and point your existing support portal interface (like Salesforce Service Cloud) directly to `GET /approvals/pending` and `POST /approvals/{id}/decide`. |
| 108 | + |
| 109 | +## ADK 2.0 Compatibility |
| 110 | + |
| 111 | +This project currently uses ADK 1.x conventions and event triggers. Because it strictly implements an `adapters` layer, all the Pydantic API schemas and Streamlit logic are completely forward-compatible with ADK 2.0 `RequestInput` workflow yielding. You'll simply need to switch the adapter layer translation once ADK 2.0 exits Alpha. The `ADK_HITL_API_URL` and `ADK_HITL_POLL_INTERVAL_S` environment variables remain valid across both adapter versions. |
0 commit comments