Human-in-the-Loop Example
AgentOS with tools requiring user confirmation
Implement Human-in-the-Loop (HITL) flows in AgentOS. When an agent needs to execute a tool that requires confirmation, the run pauses and waits for user approval before proceeding.
Prerequisites
- Python 3.9 or higher
- PostgreSQL (setup instructions below)
- OpenAI API key
Code
These two tools simulate deletion and notification by returning strings; they do not modify records or send messages.
from agno.agent import Agent
from agno.db.postgres import PostgresDb
from agno.models.openai import OpenAIResponses
from agno.os import AgentOS
from agno.tools import tool
# Database connection
db_url = "postgresql+psycopg://ai:ai@localhost:5532/ai"
db = PostgresDb(db_url=db_url)
@tool(requires_confirmation=True)
def delete_records(table_name: str, count: int) -> str:
"""Delete records from a database table.
Args:
table_name: Name of the table
count: Number of records to delete
Returns:
str: Confirmation message
"""
return f"Deleted {count} records from {table_name}"
@tool(requires_confirmation=True)
def send_notification(recipient: str, message: str) -> str:
"""Send a notification to a user.
Args:
recipient: Email or username of the recipient
message: Notification message
Returns:
str: Confirmation message
"""
return f"Sent notification to {recipient}: {message}"
# Create agent with HITL tools
agent = Agent(
name="Data Manager",
id="data_manager",
model=OpenAIResponses(id="gpt-5.2"),
tools=[delete_records, send_notification],
instructions=["You help users manage data operations"],
db=db,
markdown=True,
)
# Create AgentOS
agent_os = AgentOS(
id="agentos-hitl",
agents=[agent],
)
app = agent_os.get_app()
if __name__ == "__main__":
agent_os.serve(app="hitl_confirmation:app", port=7777)Testing the Example
Once the server is running, test the HITL flow:
# 1. Send a request that requires confirmation
curl -X POST http://localhost:7777/agents/data_manager/runs \
-F "message=Delete 50 old records from the users table" \
-F "user_id=test_user" \
-F "session_id=test_session" \
-F "stream=false"
# The response will have status: "PAUSED" with tools awaiting confirmation
# 2. Continue with approval (copy run_id, tool_call_id, name and arguments from the response)
curl -X POST http://localhost:7777/agents/data_manager/runs/{run_id}/continue \
-F "tools=[{\"tool_call_id\": \"{tool_call_id}\", \"tool_name\": \"delete_records\", \"tool_args\": {\"table_name\": \"users\", \"count\": 50}, \"requires_confirmation\": true, \"confirmed\": true}]" \
-F "session_id=test_session" \
-F "user_id=test_user" \
-F "stream=false"Preserve the pending tool execution fields returned by the paused run and change confirmed to true. The payload above shows the fields for the requested delete_records call; requires_confirmation must remain true. Use the actual returned arguments, which may differ from the requested values.
Usage
Set up your virtual environment
uv venv --python 3.12
source .venv/bin/activateSet Environment Variables
export OPENAI_API_KEY=your_openai_api_keyInstall dependencies
uv pip install -U agno fastapi uvicorn python-multipart sqlalchemy pgvector "psycopg[binary]" openaiSetup PostgreSQL Database
docker run -d \
--name agno-postgres \
-e POSTGRES_DB=ai \
-e POSTGRES_USER=ai \
-e POSTGRES_PASSWORD=ai \
-p 5532:5432 \
pgvector/pgvector:pg17Run Example
python hitl_confirmation.py