We have built agents that can think, use tools, scale with middleware, and protect themselves with dynamic security. But in the real world, some actions are simply too dangerous to leave entirely to an AI.
Deploying to production, deleting a database, or sending an invoice to a client — these are high-stakes operations. You don’t want an “autonomous” agent doing these at 3 AM without a human double-checking the parameters.
Today, we’re implementing Human-in-the-Loop (HITL) using LangChain 1.2’s HumanInTheLoopMiddleware. This allows our agent to "pause" right before a dangerous action and wait for a human to say: "Go ahead."
1. Defining “Safe” vs. “Dangerous” Tools
Not every tool needs approval. Checking the cluster health is a “Safe” operation — it’s read-only and harmless. Deploying to production, however, is a “Dangerous” operation.
We signal this to the middleware by categorizing our tools:
@tool
def check_cluster_health():
"""Safe read-only tool. No approval needed."""
return "Cluster Status: HEALTHY"@tool
def deploy_to_production(version: str):
"""🚨 DANGEROUS TOOL 🚨 Requires Human Approval."""
return f"🚀 SUCCESSFULLY DEPLOYED version {version}!"
2. Configuring the Safety Guard
The HumanInTheLoopMiddleware acts as a traffic controller. We tell it exactly which tools should trigger an "Interrupt."
from langchain.agents.middleware import HumanInTheLoopMiddlewareguard = HumanInTheLoopMiddleware(
interrupt_on={
"deploy_to_production": {
# The human can 'approve' or 'reject'
"allowed_decisions": ["approve", "reject"],
},
# Safe tools should act normally (don't pause)
"check_cluster_health": False,
}
)
3. The “Paused” State and Checkpointers
For an agent to wait for a human, it needs a way to “save its progress.” It can’t just stay running in a loop forever. We use an InMemorySaver (or a database in production) to checkpoint the agent’s state.
from langgraph.checkpoint.memory import InMemorySavercheckpointer = InMemorySaver()
agent = create_agent(
model=model,
tools=[check_cluster_health, deploy_to_production],
middleware=[guard],
checkpointer=checkpointer, # Save the "wait" state here
)
4. The Intervention Workflow
Let’s see what happens when the user asks for a deployment:
- AI Decides: “I need to call
- deploy_to_production with
version='v2.0'." - Middleware Intervenes: It sees the tool name, checks the
interrupt_onconfig, and pauses execution before the tool is even called. - App Waits: The
agent.invoke()call finishes, but the history ends with a "Tool Call" that has no result yet. - Human Resumes: The human reviews the request and clicks “Approve.”
- Agent Resumes: We call
agent.invoke()again, sending aCommandwith the decision. The AI then finishes the task.
# Resuming the agent after human approval
final_result = agent.invoke(
Command(resume={"decisions": [{"type": "approve"}]}),
config=config # The unique ID for this conversation
)Conclusion: Trust Through Control
Human-in-the-Loop isn’t about limiting your AI; it’s about building trust. When stakeholders know they have a “kill switch” or an approval button for critical actions, they are much more likely to adopt AI agents in their core business workflows.
With LangChain 1.2, you can build agents that are as autonomous as you want them to be, but as controlled as you need them to be.
💬 What do you think?
Drop your thoughts, questions, or suggestions in the comments below!
Check out my YouTube channel for more exciting content! [YouTube Channel Link — Harsha Selvi]
Disclaimer: This text has been rephrased using AI tools, and some parts are derived from various sources to provide a comprehensive overview.
#AI #LangChain #MachineLearning #CyberSecurity #DevOps #SRE #Python #AIEngineering #Automation #SoftwareSafety











