How Six AI Frameworks Handle Human-in-the-Loop Workflows

How Six AI Frameworks Handle Human-in-the-Loop Workflows

Vijay Raina is a seasoned expert in the architecture of enterprise SaaS technology, with a particular focus on how software design must evolve to accommodate the growing complexity of AI agents. In his years of providing thought leadership in software design, he has witnessed the shift from simple automation to agentic workflows that require nuanced human intervention. He argues that while the demos of AI agents often look seamless—a simple pause, a human click, and a continuation—the underlying architecture of “Human-in-the-Loop” (HITL) varies wildly across frameworks, leading to significant production consequences. In our discussion, Raina breaks down the fundamental divide between durable graph interrupts, message-loop injections, and blocking gates, offering a deep dive into the six major agent frameworks and how they manage the delicate balance between autonomy and human oversight.

The conversation explores the structural differences in how agents pause and resume execution, highlighting why some frameworks can survive a system restart while others lose all context. Raina details the specific implementations of HITL within deepagents, AutoGen, Agno, OpenAI Agents SDK, CrewAI, and Pydantic AI, providing a roadmap for architects to choose the right tool based on the granularity of intervention required. He emphasizes that the decision between a tool-call interrupt and a task-output review is not just a parameter change but a fundamental architectural choice that defines how an agent interacts with its environment and its human handlers.

When we look at agentic demos, the interaction often seems trivial, yet you argue that the design space for Human-in-the-Loop is actually quite fragmented. How do the three fundamental patterns—durable graph interrupt, message-loop injection, and blocking gates—differ in terms of how they actually handle the flow of execution?

The reality is that “Human-in-the-Loop” isn’t a single feature; it is a spectrum of design choices that dictate how an agent survives the “waiting” period. In a durable graph interrupt, which we see in frameworks like deepagents using LangGraph, the system serializes the entire state of the execution graph and suspends at a specific node. This is a robust approach because if the server crashes or the process exits, the run can be resumed hours later from an external checkpointer without losing a single bit of context. It feels like hitting “save” on a video game right before a big boss fight.

Contrast that with message-loop injection, where there is no real suspension at all. In this pattern, common in AutoGen, the human is simply treated as another agent or a “UserProxy” in a multi-agent conversation. The loop keeps spinning, and the human response is just another turn in the chat. It’s conversational and fluid, but if the process shuts down mid-discussion, that ephemeral state is often gone forever. Finally, the blocking gate or run-termination pattern, used by Pydantic AI and the OpenAI Agents SDK, effectively ends the run and hands the “approval pending” object back to the caller. Here, the responsibility for resuming the execution shifts entirely to the developer or the human, which can be cleaner for certain async web architectures but requires more manual wiring.

Designing a system where execution pauses for a tool-call approval is quite different from reviewing a finished task. What are the practical implications of choosing a framework that interrupts at the tool-call level versus one that only allows review after a task is complete?

This is where many teams get tripped up because the granularity of the intervention changes everything about the user experience. If you use deepagents, you can intercept a model the moment it generates a tool call; you can approve it, reject it, or even edit the arguments before the tool ever executes. This level of control is vital for high-stakes actions like making a $1,000 purchase or deleting a database record. You are acting as a literal gatekeeper for the agent’s intent, ensuring the execution matches your expectations before the side effect occurs.

On the other hand, a framework like CrewAI often relies on task-level review, specifically through its human_input=True setting. In this scenario, the agent has already finished its work, and you are reviewing the final output. The tools have already run, and the actions are already taken; you are simply providing feedback on the result. This is fine for creative writing or research tasks where you might want to tell the agent to “try again” with a different tone. However, it’s a dangerous architectural choice if your primary goal is to prevent the agent from making a catastrophic technical error during the execution phase itself.

You mentioned that “true suspension” is a rare feature among these six frameworks. Why is the ability to resume after a process restart such a high bar, and how does LangGraph’s checkpointer address this specifically?

Achieving true suspension is incredibly difficult because it requires a framework to capture the entire “memory” of the execution—not just the message history, but the internal state of the logic flow. Deepagents, leveraging LangGraph’s interrupt() primitive, is currently the leader here because it uses a serialized state stored in a checkpointer. This means the agent can batch two tools into a single request, hit an interrupt, and the system can literally go offline. When the human reviewer eventually logs in and provides an “edit” or “approve” decision, the system reloads the state and picks up exactly where it left off.

Most other frameworks struggle with this because they rely on in-memory states or blocking threads. For example, Agno has a very powerful HumanReview config that supports retry loops and step-level gates, but it lacks a workflow-level checkpoint equivalent. If your FastAPI server restarts while an Agno workflow is waiting for a human, that workflow state is typically lost unless you’ve manually wired up external session storage. The complexity of persisting a running execution graph is what makes the deepagents/LangGraph approach so distinct in the current landscape.

AutoGen takes a very different approach by modeling the human as a peer participant. What are the trade-offs of this conversational “UserProxy” model when compared to more structured approval gates?

The AutoGen model is brilliant for collaborative co-pilots where the agent and human are having a back-and-forth dialogue to reach a goal. By treating the human as a first-class participant in a multi-agent conversation, you get a very natural interaction where the human can steer the agent’s replies just like any other agent would. However, the limitation is that the conversation loop never truly suspends in a structural way. There is no built-in checkpointing, so for asynchronous web interfaces, you are often forced to manage background threads and asyncio queues just to bridge the gap between the human’s input and the agent’s loop.

This “no-suspension” model means that the developer has to do a lot of heavy lifting to ensure reliability. If you are building a real-time chat interface, AutoGen feels great because it’s designed for that constant pulse. But if you need an agent that performs a task, pauses for three hours while a manager reviews a proposed plan, and then resumes, the message-loop pattern starts to feel brittle. You lose the structured “approve/edit/reject” schema that you get with more formal interrupt-based frameworks.

Pydantic AI introduces a concept called “Deferred Tools” for handling human intervention. How does this termination-and-resume flow change the way developers build asynchronous applications?

Pydantic AI’s approach is a very elegant solution for developers who don’t want to manage a complex graph runtime but still need structured tool-call approval. When you mark a tool with requires_approval=True, the agent run actually terminates when it hits that tool, returning a DeferredToolRequests object. This is a clean break. The caller—the human or the application—then has the responsibility to resolve those approvals and start a new run with the original message history.

It is a “blocking gate” pattern that favors the developer’s control over the process lifecycle. You don’t have to worry about a long-running process sitting idle in memory; you just save the message history and the pending tool call IDs to your database and wait. The catch, of course, is that there is no durable state serialization provided out of the box. The human must persist result.all_messages() externally. If you forget to do that and the process exits, you cannot recover the run. It’s a great middle-ground for “approve or reject” logic, though it lacks the “edit” decision type that deepagents provides.

If a developer is looking for the simplest possible path to implement a basic “approve or reject” workflow for specific tools, which framework offers the least friction, and what are its limitations?

For the simplest possible implementation where you control the process lifecycle and just want a quick gate, the OpenAI Agents SDK is probably your best bet. It uses a needs_approval parameter directly on the function_tool. When the model triggers that tool, the run loop pauses and surfaces a ToolApprovalItem. It is very straightforward: the caller either approves or rejects it via the RunState. It feels very native to the OpenAI ecosystem and doesn’t require learning complex graph theory or multi-agent messaging protocols.

However, the simplicity comes with major trade-offs. The OpenAI SDK is strictly “approve-or-reject.” There is no built-in way for a human to modify the tool arguments within the SDK’s approval mechanism. If the model wants to book a flight for the wrong date, you can’t just fix the date in the approval UI; you have to reject it and hope the model learns from the rejection. Additionally, while it has some partial resumability via state.to_string(), the burden of persisting and restoring that state is still entirely on the human. It is a lightweight solution for low-complexity gates, but it won’t scale to sophisticated enterprise workflows where editing and long-term durability are non-negotiable.

What is your forecast for AI agent orchestration?

I expect we will see a rapid consolidation toward durable execution patterns as more teams move agents out of the “demo” phase and into production where reliability is the primary metric. Currently, we have 6 or more major frameworks all trying different HITL strategies, but the “durable graph interrupt” pattern is the only one that truly solves the problem of long-running, asynchronous human oversight. In the next 18 to 24 months, I forecast that frameworks like Pydantic AI and Agno will be forced to adopt more robust checkpointing and serialization primitives to compete with the LangGraph ecosystem. We will likely move away from “chatting” with agents toward “managing” agents through structured state-machine interfaces, where the ability to edit an agent’s intent mid-flight becomes a standard requirement for any enterprise-grade AI system.

Subscribe to our weekly news digest.

Join now and become a part of our fast-growing community.

Invalid Email Address
Thanks for Subscribing!
We'll be sending you our best soon!
Something went wrong, please try again later