Human in the loop

휴먼 인 더 루프

A card that stops an agent before an irreversible action and requires explicit human approval or denial.

Also known as: Approval cardConfirm before actionAgent approval gate
···
html
<div class="card" id="card">
  <div class="head"><i class="warn">!</i><strong>승인이 필요해요</strong></div>
  <p>임시 파일 <b>12개</b>를 삭제합니다. 이 작업은 되돌릴 수 없어요.</p>
  <div class="row">
    <button class="deny" id="deny">거부</button>
    <button class="approve" id="ok">승인</button>
  </div>
</div>
css
.card{width:min(280px,94%);border:1px solid var(--line);border-radius:13px;background:var(--surface);padding:14px;display:grid;gap:9px;transition:opacity .2s}
.head{display:flex;align-items:center;gap:8px;font-size:12.5px}
.warn{width:18px;height:18px;border-radius:50%;background:#f5a524;color:#fff;font-style:normal;font-weight:800;font-size:11px;display:grid;place-items:center;flex-shrink:0}
p{margin:0;font-size:11.5px;color:var(--muted);line-height:1.55}
.row{display:flex;gap:8px}
.deny,.approve{flex:1;border-radius:9px;padding:8px 0;font-size:12px;font-weight:700;cursor:pointer;border:1px solid var(--line)}
.deny{background:var(--bg);color:var(--muted)}
.approve{background:var(--accent);color:#fff;border-color:var(--accent)}
.card[data-approved] .row{display:none}
.status{display:none;align-items:center;gap:6px;font-size:12px;font-weight:700;color:#0f9d78}
.card[data-approved] .status{display:flex}
.status i{width:16px;height:16px;border-radius:50%;background:var(--accent-3);display:inline-grid;place-items:center}
.status i::after{content:"";width:4px;height:7px;border:solid #fff;border-width:0 1.4px 1.4px 0;transform:rotate(45deg) translate(-1px,-1px)}
js
const card = document.getElementById('card'), ok = document.getElementById('ok');
const row = document.querySelector('.row');
const status = document.createElement('div');
status.className = 'status';
status.innerHTML = '<i></i><span>승인됨 — 삭제를 실행합니다</span>';
card.appendChild(status);
function approve() {
  card.setAttribute('data-approved', '');
  setTimeout(reset, 2000);
}
function reset() { card.removeAttribute('data-approved'); setTimeout(approve, 1600); }
setTimeout(approve, 1600);
ok.addEventListener('click', approve);

Right before an agent takes an irreversible action — deleting files, making a payment, sending an email — this stops and asks explicitly: "should this run?" The card needs to spell out the action, its target, and its consequence ("12 files, cannot be undone") concretely enough that the person has something real to judge.

Approve and deny need visually distinct colors and positions — in particular, don't put deny/cancel where it's easy to hit by accident, since the dangerous side is execution, not cancellation. The moment approval is given, the card flips to an "approved" state and execution proceeds.

Trust in this pattern hinges entirely on whether it actually blocks anything. A card that runs regardless of approval is just decoration, and once that's discovered, every future approval prompt gets clicked through without being read.

When to use

Use it whenever an action is hard to reverse or has real cost — deletion, payment, external sends. Requiring approval for side-effect-free actions like a read-only lookup just adds friction and trains people to click through without reading.