Skip to main content

Human In The Loop

note

Introduced in v1.79.0.

Overview

Human-in-the-Loop (HITL) is a critical design pattern in agent development that integrates human judgment and expertise into the workflow of AI systems. In Arcanna's Agentic Workflows, HITL enables agents to collaborate with humans, ensuring reliability, accuracy, and accountability — particularly for sensitive or complex tasks.

While AI agents are designed to perform tasks autonomously, HITL provides mechanisms for human intervention at key decision points. This is essential for scenarios where AI confidence is low, ethical considerations are present, or real-world nuances require human interpretation.

Arcanna exposes two complementary HITL primitives on top of Google ADK. They look similar from the outside — both pause the workflow until a human responds — but they serve fundamentally different roles:

  • request_human_input tool — lets an agent ask the human a question and stay in its own context. The answer comes back to the same agent, which then continues reasoning with that new information. Use it when the agent itself needs missing input, clarification, or guidance to keep working.
  • HITLAgent — is not part of any agent's reasoning. It's a standalone step you place between two agents in an orchestrator. Its only job is to ask: "should the next agent run, yes or no?" Approve → the next sub-agent executes; reject → the workflow stops.

Both pause the run via AWAITING_DECISION, but the tool resumes the same agent with new info, while the HITL agent gates the next agent.

request_human_input — TOOL
Agent stays in context, resumes with the answer
Agent
│ calls tool
AWAITING_DECISION
operator submits free-form text
Same agent resumes
HITLAgent — GATE
Standalone step between two agents in the pipeline
Sub-agent A
HITLAgent
AWAITING_DECISION
✓ approve
Sub-agent B
✗ reject
REJECTED

The Two Primitives

1. request_human_input tool — mid-agent question (stays in context)

A function tool an agent can call when it needs free-form human input mid-execution. The agent keeps its conversation, its state, and its goal; it just waits for the human's text answer and then continues.

ArgTypePurpose
ask_userstrClear, concise message to the operator.
suggestions_for_userList[str]Concrete options / recommended courses of action.

Flow: The agent calls the tool → the workflow pauses and the operator sees a prompt in Arcanna's UI → once they submit their answer, the same agent picks up where it left off with the answer available.

2. HITLAgent — gate between agents (approve / reject only)

A standalone step you place between two agents in the pipeline. It does not participate in any agent's reasoning — its only job is to decide whether the next agent is allowed to run.

  • It summarizes what the previous agent produced and waits for the operator to decide.
  • The operator submits approve or reject via Arcanna's UI:
    • approve → the next agent runs.
    • reject → the workflow stops.

Choosing Between the Two

Use casePick
An agent needs missing info / clarification and must continue working with the answertool
You want a hard approve/reject checkpoint between two pipeline stagesagent
The human's answer should feed back into the agent's reasoningtool
The human only decides "should the next agent run?"agent

How They Reach User Code

Both request_human_input and HITLAgent are available in your workflow code without any imports — Arcanna provides them automatically.

Example Workflow

This example shows both HITL primitives in a pipeline that runs end-to-end without any external integration, so it's a good starting point for trying HITL out before you wire up MCP servers or other tools. The drafter produces a stakeholder notification for an alert, but the alert doesn't say who owns the affected host — context the LLM cannot infer. So the agent calls request_human_input to ask the operator, then drafts. The HITLAgent then gates the draft before the publisher formats it for delivery.

from google.adk.agents import LlmAgent, SequentialAgent
from google.adk.models.lite_llm import LiteLlm

MODEL = LiteLlm(model="bedrock/us.anthropic.claude-opus-4-6-v1")

# Drafts a notification, but must ask the operator for org-specific
# context that the LLM cannot know from the alert alone.
drafter = LlmAgent(
name="AlertNotificationDrafter",
description="Drafts a stakeholder notification for a security alert.",
instruction=(
"You draft a notification for the alert in the workflow input. "
"You need to know the asset owner / on-call team for the affected "
"host before you can address the message. Call request_human_input "
"to ask the operator for it, with concrete suggestions if helpful. "
"Once you have the answer, draft a concise notification (max 6 lines)."
),
model=MODEL,
output_key="notification_draft",
tools=[internal_tools.request_human_input],
)

publisher = LlmAgent(
name="NotificationPublisher",
description="Formats the approved draft for delivery.",
instruction=(
"Take notification_draft and output the final message formatted "
"for Slack (markdown, with a one-line subject)."
),
model=MODEL,
output_key="final_notification",
)

root_agent = SequentialAgent(
name="AlertNotificationWorkflow",
sub_agents=[
drafter,
internal_agents.HITLAgent(model=MODEL), # approve before "send"
publisher,
],
)

Sample input — an alert that triggers request_human_input because it doesn't say who owns the affected host:

{
"alert_id": "a-9821",
"rule": "Suspicious sudo escalation on production host",
"host": "prod-db-07",
"user": "amartinez",
"timestamp": "2026-05-07T14:22:11Z",
"details": "User executed `sudo -i` followed by 4 failed `psql` connection attempts to a non-standard port within 90 seconds."
}

What to expect at runtime:

  1. drafter runs, sees it has the what but not the who-do-we-tell, and calls request_human_input to ask for the asset owner — the workflow pauses on AWAITING_DECISION and the operator answers in Arcanna's UI.
  2. With the answer in hand, drafter produces notification_draft.
  3. HITLAgent pauses again so the operator can approve or reject the draft.
  4. On approve, publisher outputs the final formatted message; on reject, the workflow stops.
Example pipeline
drafter
request_human_input
operator → text → resume
HITLAgent
(standalone gate)
✓ approve
✗ reject
publisher
(reject → workflow stops)

Runtime Lifecycle

  1. Run starts. The workflow executes normally.
  2. HITL detected: either a request_human_input tool call, or a HITLAgent finishing its turn.
  3. Workflow pauses and the operator sees a prompt in Arcanna's UI.
  4. Operator decides via Arcanna's UI or the API.
  5. Resume. The workflow continues from where it stopped.