HITL Overview

Allow your agent and users to collaborate on complex tasks.

Open your coding agent in your project's folder, or in an empty folder for a new app.This runs in a coding agent on your computer.

What is this?#

Human-in-the-loop (HITL) lets an agent pause mid-run to collect input, confirmation, or a choice from the user, then resume with that answer folded back into its reasoning. It's what turns an autonomous workflow into a collaborative one: the agent keeps its context, the user keeps the steering wheel.

When should I use this?#

Use HITL when you need:

  • Quality control — a human gate at high-stakes decision points
  • Edge cases — graceful fallbacks when the agent's confidence is low
  • Expert input — lean on the user for domain knowledge the model lacks
  • Reliability — a more robust loop for real-world, production traffic

Two patterns for HITL in CopilotKit#

CopilotKit ships two complementary ways to pause an agent turn and ask the human something. They look similar from the outside (the chat pauses, a custom component appears, the user answers, the run resumes), but they're wired differently on the backend, and each has its own niche.

PatternWho decides to pause?Backend surface
useHumanInTheLoopThe LLM, by calling a registered client-side toolA frontend-only tool description (Zod schema + render)
useInterruptThe graph, by calling interrupt(...) during a nodeA server-side interrupt() call in your agent

Pick useHumanInTheLoop when the pause is an agent-initiated decision — the model chose to ask the user — and you want the picker UI inlined into the normal tool-call flow.

Pick useInterrupt when the pause is a graph-enforced checkpoint — the code path deterministically requires a human answer — and you want the runtime interrupt as the server-side contract.

Pattern 1 — useHumanInTheLoop (tool-based)#

The app registers a HITL tool on the client with useHumanInTheLoop. When the LLM calls that tool, CopilotKit renders your Vue component and gives it a respond callback during the executing phase. The agent sees the answer as the tool result and continues from there.

src/components/MeetingApproval.vue
<script setup lang="ts">
import { z } from "zod";
import { useHumanInTheLoop } from "@copilotkit/vue/v2";
import TimePickerCard from "./TimePickerCard.vue";

useHumanInTheLoop({
  name: "schedule_meeting",
  description: "Ask the user to select a meeting time.",
  parameters: z.object({
    topic: z.string(),
    attendee: z.string(),
  }),
  render: TimePickerCard,
});
</script>

<template><p>Meeting approval tool registered.</p></template>

The picker UI can use static candidate slots, live availability, a calendar API, or any other application data. Mount the registration component beneath CopilotKitProvider so the composable can reach the runtime.

Pattern 2 — useInterrupt (graph-paused)#

With a runtime interrupt, the pause is enforced by the graph itself: a node interrupts with a serializable payload, the run suspends, the client receives the payload, renders a UI, and resumes the run with the user's answer. CopilotKit's useInterrupt composable and CopilotChat's #interrupt slot form the Vue render contract.

See the useInterrupt deep dive for the full walkthrough, including the backend contract and Vue slot wiring.

Going headless#

Both patterns above can drive custom UI. For runtime interrupts, set renderInChat: false and use the reactive state returned by useInterrupt; for a frontend-tool pause, let the Promise-returning HITL tool drive application state. See the headless interrupts guide for the interrupt path.

Choose your AI backend

See Integrations for all available frameworks (human-in-the-loop).