Put the user in the loop
Some steps should not happen without a human saying yes. Human-in-the-loop pauses the agent mid-run, renders an interactive component in the frontend, and waits. The user makes a choice; the agent resumes with that choice and continues. The mechanism is a tool the frontend registers. When the model calls it, the run halts at that tool call until the user responds. Nothing happens automatically: the agent stays parked untilrespond() hands control back.
In the example below, the agent proposes a list of task steps. The user enables or disables each step and confirms. The agent then continues, respecting exactly what the user approved.
This pattern works with Flows. It relies on the Flow’s chat loop re-entering after
respond(): the returned value comes back as a tool result, and the agent’s next turn acts on it.Build it
1
Bind the frontend actions into the model's tools
In your Flow, add the frontend-registered actions to the model’s tool list with
*self.state.copilotkit.actions. Those actions are the tools your frontend registered (via useHumanInTheLoop). Binding them lets the model call them; the run pauses at that tool call until the user responds.CopilotKitState carries the frontend-registered actions on self.state.copilotkit.actions. When the model calls one, the run pauses there. After the user responds, the returned value lands in self.state.messages as the tool result, and the Flow loops back through chat so the model can act on the decision.2
Serve the Flow over AG-UI
Expose the Flow from your FastAPI server with
add_crewai_flow_fastapi_endpoint, the same way as every other agent. See Frontend Overview for the full server, runtime, and provider setup.3
Register the interactive tool on the frontend
useHumanInTheLoop registers the tool the agent pauses on and gives you a render function to draw the interactive UI. When the agent calls the tool, your component appears; when the user acts, you call respond() to resume the agent.render function receives:args— the tool arguments the model produced (here, the proposedsteps). These stream in as the model generates them.status— the tool call’s lifecycle. While it is"executing", the agent is paused and waiting on the human.respond(value)— resumes the agent with the user’s decision. The agent’s next turn sees the returned value and acts on it.
4
Let the user decide, then respond
Your component reads Once the user clicks Confirm,
args.steps, lets the user toggle each one, and calls respond() with the final selection. That value is what the agent continues with.respond() fires, the run resumes, and the Flow’s chat step runs again with the user’s choices in the message history.Related
Frontend Actions
Let the agent call functions that run in the browser.
Shared State
Keep agent state and your app UI in two-way sync.
Agentic Generative UI
Render live agent state as custom components.
