Human-in-the-Loop (HITL) Systems
Design human-in-the-loop AI systems in Java: approval flows for agent actions, escalation patterns, confidence thresholds and feedback loops — how to deploy autonomy without accepting unbounded risk.
On this page
Full autonomy is rarely the goal; useful autonomy with human judgement at the right points usually is. Human-in-the-loop design is how you deploy agents that do real work without accepting the risk of letting them act unsupervised on consequential things. This tutorial covers the patterns.
Key Takeaways
- The agent proposes; a human disposes on consequential actions.
- Gate by the cost of being wrong, not the difficulty of the task.
- Confidence thresholds route uncertain decisions to humans, automate confident ones.
- Good HITL still automates the work — the human only makes the final call.
The propose-dispose pattern
The foundational HITL design: the agent does everything up to the consequential action, then waits.
public Proposal proposeAction(String task) {
// The agent investigates and drafts the action — all the work.
Investigation investigation = agent.investigate(task);
ProposedAction action = agent.draft(investigation);
// But it cannot execute. It creates a proposal for human review.
return proposals.create(Proposal.pending(action, investigation.reasoning()));
}
// Executed only from an authenticated human approval, never by the agent.
public Result approve(String proposalId, String approverId) {
Proposal proposal = proposals.require(proposalId);
validateApproval(proposal, approverId); // status, staleness, authority
return executor.execute(proposal.action());
}The agent's reasoning travels with the proposal so the human can approve in seconds rather than re-investigating. The DevAgentic project builds this gate in full.
What needs a human
Gate on the cost of being wrong, not the difficulty:
| Action | Needs approval? |
|---|---|
| Search, look up, calculate | No — read-only |
| Draft an email | No — reversible (it is not sent) |
| Send an email | Yes — external and irreversible |
| Move money, issue a refund | Yes — irreversible, high impact |
| Delete data | Yes — irreversible |
| Deploy code | Yes — high impact |
Confidence thresholds
Route by confidence: automate the confident cases, escalate the uncertain ones.
public Outcome handle(Request request) {
Decision decision = agent.decide(request);
if (decision.confidence() >= AUTO_THRESHOLD && decision.isReversible()) {
return executor.execute(decision); // confident + safe → automatic
}
if (decision.confidence() >= REVIEW_THRESHOLD) {
return humanQueue.enqueueForApproval(decision); // uncertain → review
}
return humanQueue.enqueueForHandling(request); // low → human handles fully
}Confidence can come from the model's own estimate, from agreement across multiple attempts (self-consistency), or from business rules ("refunds over £500 always go to a human").
Escalation patterns
When the agent cannot or should not proceed, escalate with context:
public Handling route(Request request) {
AgentAttempt attempt = agent.attempt(request);
return switch (attempt.status()) {
case SOLVED -> Handling.resolved(attempt.result());
// Escalate WITH the work done, so the human continues rather than restarts.
case NEEDS_APPROVAL -> Handling.escalate(attempt.proposal(), attempt.reasoning());
case OUT_OF_SCOPE -> Handling.escalate(request, attempt.whyStuck());
case LOW_CONFIDENCE -> Handling.escalate(request, attempt.alternatives());
};
}The escalation must carry what the agent found. A handoff that dumps the raw request back on a human, discarding the investigation, wastes the automation entirely.
Feedback loops
Human decisions are training signal. Capture them:
public void recordDecision(String proposalId, ApprovalDecision decision) {
// When a human overrides the agent, that is a labelled example of where the
// agent was wrong. Feed it into your evaluation set and, over time, into
// tuning the confidence thresholds and prompts.
feedbackStore.record(proposalId, decision);
if (decision.wasOverride()) {
evaluationSet.addCase(proposalId, decision.correctOutcome());
}
}Over time, the pattern of overrides tells you where the agent is weak and where you can safely raise the automation threshold. See agent evaluation & testing.
Designing the human's experience
HITL fails if the human's job is tedious. A good approval interface shows: the proposed action, the agent's reasoning, the evidence, and one-click approve/reject/modify. If reviewing a proposal takes as long as doing the task, you have automated nothing. The agent's job is to make the human's decision fast and well-informed.
Next
- Productionizing agentic systems
- Ethical AI & responsible agent design
- DevAgentic project — HITL in a real system
Frequently Asked Questions
What is a human-in-the-loop AI system?
Which agent actions need human approval?
How do confidence thresholds work in HITL?
Does human-in-the-loop defeat the point of automation?
Related tutorials
- Agentic RAG — Advanced PatternsAdvanced RAG where the model controls retrieval: self-RAG, corrective RAG, adaptive retrieval and query planning — when to let an agent decide whether and what to retrieve, in Java.
- Productionizing Agentic SystemsTake agents to production: per-run budgets and step caps, guardrails, durable execution, scaling on the JVM, cost control and the operational patterns that keep agents from causing incidents.
- Agent Evaluation & TestingHow to evaluate and test AI agents: trajectory analysis, benchmarking, hallucination detection, outcome verification and human-in-the-loop evaluation — with Java patterns.
- Ethical AI & Responsible Agent DesignBuild responsible AI agents: managing bias, ensuring transparency and accountability, designing for contestability, and the engineering practices that make agents safe and fair.