Human in the Loop and Approvals

Purpose and Scope

Human-in-the-loop workflows let an agent pause before taking a sensitive action, ask a person to review the proposed action, and then continue only after an explicit decision has been supplied. In this repository, the modern approval primitive is centered on the LangChain v1 agent middleware contract. That contract models a review as a sequence of proposed actions, a set of review policies for those actions, and a response containing decisions such as approve, edit, reject, or respond. The design is deliberately operational: it governs tool execution rather than merely adding another user utterance to a chat transcript. Sources: libs/langchain_v1/langchain/agents/middleware/human_in_the_loop.py

A useful mental model is that normal conversation and approval control are two different channels of intent. A human message is the user's conversational content passed to a model, serialized with the human message type, and optionally represented as content blocks. An approval decision, by contrast, is structured runtime control data that decides whether a model-requested action may proceed. Keeping those ideas separate avoids ambiguous application behavior: the reviewer can reject a dangerous deletion without that rejection being treated as a normal prompt, and a user can still send ordinary conversation messages without implicitly approving tools. Sources: libs/core/langchain_core/messages/human.py, libs/langchain_v1/langchain/agents/middleware/human_in_the_loop.py

Relevant Source Files

  • libs/langchain_v1/langchain/agents/middleware/human_in_the_loop.py — Defines the typed human-in-the-loop request, review configuration, decision, and response payloads used by agent middleware.
  • libs/core/langchain_core/messages/human.py — Defines the conversation-level human message and chunk classes used when user content is passed into chat models.
  • libs/langchain/langchain_classic/callbacks/human.py — Preserves classic callback import names for human approval handlers by dynamically forwarding them to community implementations.
  • libs/langchain/langchain_classic/chat_models/human.py — Preserves the classic human input chat model import surface through a dynamic deprecated-import lookup.
  • libs/langchain/langchain_classic/llms/human.py — Preserves the classic human input LLM import surface through a dynamic deprecated-import lookup.
  • libs/langchain/langchain_classic/document_loaders/parsers/language/javascript.py — Shows the same classic dynamic import pattern for a deprecated JavaScript segmenter, useful for understanding compatibility shims even though it is not an approval primitive.

Core Primitives

The core request shape begins with an action. An action has a name and arguments, while an action request adds an optional reviewer-facing description. A review configuration then states which action name is governed, which decision types are allowed, and, when editing is supported, what argument schema can be used to validate revised inputs. This means the policy is attached to the action boundary rather than hidden in a UI convention. A file deletion tool, an email tool, and a read-only lookup tool can each receive distinct review options without changing the model's basic tool-calling interface. Sources: libs/langchain_v1/langchain/agents/middleware/human_in_the_loop.py

The decision union is intentionally small but expressive. Approve allows the proposed action to continue. Edit replaces the action with a reviewer-supplied name and arguments, which is useful when the model chose the right operation but proposed unsafe or incorrect parameters. Reject prevents execution and may include an explanatory message; the contract also defines default behavior when no message is supplied, telling the model not to retry the same call unless the user asks. Respond skips tool execution and returns human-provided content as a successful synthetic tool result, which fits ask-user tools and manual lookup workflows. Sources: libs/langchain_v1/langchain/agents/middleware/human_in_the_loop.py

ComponentRoleKey fields or behavior
ActionMinimal executable action descriptionname and args
ActionRequestReviewable action proposalname, args, optional description
ReviewConfigPolicy for a reviewable actionaction_name, allowed_decisions, optional args_schema
HITLRequestPayload sent for reviewaction_requests and review_configs
DecisionHuman response unionapprove, edit, reject, or respond
HITLResponsePayload used to resumedecisions
HumanMessageUser conversation messagecontent or typed content blocks, serialized as human

Execution Flow

A typical approval flow starts when an agent is about to execute a tool whose name matches a configured review policy. The middleware can package the tool call into an action request, include the review configuration that says which decisions are allowed, and interrupt the run so an external reviewer interface can display the proposed action. The source imports LangGraph configuration, tool runtime support, and the interrupt function, which places this primitive in a resumable execution model rather than a simple terminal input prompt. Official Deep Agents docs describe the same idea through tool-level interrupt configuration and a required checkpointer for interrupted runs. Sources: libs/langchain_v1/langchain/agents/middleware/human_in_the_loop.py

After the reviewer answers, the application resumes with a response containing one decision per reviewed action. If the decision is approve, downstream execution can use the original call. If it is edit, execution should use the edited action supplied by the reviewer. If it is reject, the model receives feedback that the tool did not run and can adapt its next step. If it is respond, the tool itself is bypassed and the model receives a successful tool message containing the human's answer. This gives applications a uniform resume path while preserving the semantic difference between execution, correction, denial, and manual completion. Sources: libs/langchain_v1/langchain/agents/middleware/human_in_the_loop.py

For production applications, treat the interrupt as a durable boundary. The official docs call out checkpointing because a reviewer may respond later, from another process, or after a UI refresh. The source contract reinforces that durability requirement by making the review payload fully serializable: action requests are names, argument dictionaries, descriptions, allowed decision lists, schemas, and decision objects. A review queue or approval screen can store and render those fields without importing the tool implementation itself. That separation lets the agent runtime own execution while the application UI owns human review, audit context, and approval ergonomics. Sources: libs/langchain_v1/langchain/agents/middleware/human_in_the_loop.py

Implementation Guidance

Design approvals as policy before building the reviewer UI. Start by inventorying tools that can cause side effects, disclose sensitive information, contact external users, modify records, or spend money. For each tool, decide whether default approval choices are too broad. A destructive operation may allow only approve and reject. A notification operation may allow edit so the reviewer can correct recipients, subject lines, or body text. A human-answer tool may allow respond because the human is effectively the data source. The middleware types support this per-action policy shape through review configurations and explicit allowed decision lists. Sources: libs/langchain_v1/langchain/agents/middleware/human_in_the_loop.py

When implementing the reviewer surface, render the action name, arguments, and optional description from the action request, then restrict controls to the decisions allowed by the matching review configuration. If editing is enabled and an argument schema is present, validate revised arguments before resuming the run. If the reviewer rejects without a custom message, preserve the contract's default meaning that the action was not executed and should not simply be retried. If the reviewer responds on behalf of a tool, make that distinction visible in audit logs because no external tool implementation actually ran. Sources: libs/langchain_v1/langchain/agents/middleware/human_in_the_loop.py

Classic Compatibility Surfaces

The repository also contains classic human-facing import paths, but those files are compatibility surfaces rather than the modern middleware design. The classic callback module exposes human approval callback handler names and a human rejection exception through a dynamic importer backed by a deprecated lookup table. The classic chat model and LLM modules follow the same pattern for human input model classes. This matters for migrations: older applications may still import those names, but new agent approval behavior should be designed around review requests, review configurations, and structured decisions rather than callback-only prompts. Sources: libs/langchain/langchain_classic/callbacks/human.py, libs/langchain/langchain_classic/chat_models/human.py, libs/langchain/langchain_classic/llms/human.py

The JavaScript parser shim is not related to approval, but it demonstrates the same deprecation and dynamic import mechanism used by the classic human modules. A lookup dictionary maps an exported name to a community package, a generated importer resolves attributes at access time, and the module declares the forwarded public name. When reading older LangChain examples, this pattern is a signal that an import path may remain available while implementation ownership has moved. For human-in-the-loop work, that distinction helps separate legacy compatibility from the current agent middleware contract. Sources: libs/langchain/langchain_classic/document_loaders/parsers/language/javascript.py, libs/langchain/langchain_classic/callbacks/human.py

Next Steps

Use this page with the middleware and tools documentation when deciding where to place approval checks in an agent lifecycle. Pair it with event streaming if the UI needs to show pending review, resumed execution, and final tool results as separate events. Pair it with sessions and chat history if interrupted runs must survive user navigation or long review delays. For observability, log the action request, allowed decisions, reviewer decision, and whether a real tool or synthetic response completed the step, while keeping sensitive arguments protected according to your application's data policy.