Human-in-the-loop for LangChain
Human-in-the-loop for LangChain agents, on a phone.
Wrap decisions.ask() in a LangChain @tool and give it to your agent. When the model reaches a step a person should sign off on, the question is delivered to that person's phone as a push notification, the call blocks until they respond, and the answer comes back fail-closed. Run pip install pushary, or use @pushary/server in TypeScript. There is no @pushary/langchain package and you do not need one. The external_id is your own id for the end-user, connected once with a keyless one-tap link.
import os
from langchain_core.tools import tool
from pushary import PusharyServer
px = PusharyServer(api_key=os.environ["PUSHARY_API_KEY"])
# Connect the user's phone once, keyed to your own id.
px.enroll("user_123")
@tool
def ask_human(question: str) -> str:
"""Ask a real person to approve before acting."""
d = px.decisions.ask(question=question, external_id="user_123", type="confirm")
return "approved" if d["approved"] else f"not approved ({d['status']})"
# agent = create_agent(model, tools=[ask_human])Why you need it
LangChain pauses the agent. Pushary reaches the human.
A LangChain tool runs wherever the agent runs, and a print or an input() only works while a developer is watching the process. decisions.ask() reaches the person who should decide on the device they carry, waits durably, and records the response in an audit trail. The same call works inside a LangGraph node, because a node is a plain function.
How it works
From ask to resume
Connect the phone once, then ask a human from the tool or step that guards the action. The waiting, the delivery, and the record are handled for you.
- 01
Install the SDK
Run pip install pushary for Python, or npm i @pushary/server for TypeScript, and set PUSHARY_API_KEY.
- 02
Connect the phone
Call px.enroll(external_id) once per end-user to connect their phone with a keyless one-tap link.
- 03
Wrap decisions.ask in an @tool
Define an @tool function whose body calls px.decisions.ask() and pass it in your agent's tools list. The docstring tells the model when to use it.
- 04
Act on the decision
The tool returns the human's response, fail-closed. A declined, expired, or unanswered confirm means the agent does not take the guarded action.
A decision is a yes or no confirm, a multiple-choice select, or a free-text input. Every one is stored with its outcome and kept in the audit log.
For your users
Your agent asks your users, not just you
Ship the agent to real users and let each of them approve their own decisions from their phone, under your brand.
Your users answer on their own phones
The externalId is your own id for the end-user. Connect their phone once with a keyless one-tap link, and every approval lands with the right person. No account, no app, no key on their side.
Durable, fail-closed waits
The agent stays open on a durable wait backed by a decisions ledger. If nobody answers in the window you set, the decision comes back denied, so a timeout or a restart never turns into an unapproved action.
Phone-first delivery
Pushary reaches the person on the device they carry: native app push first, then PWA push notifications, then Slack if you route it there. You do not build a delivery pipeline.
Signed webhooks and an audit trail
Every decision is written to a durable ledger you can query and export, and resolved decisions post a signed webhook to your callback URL. You have the record of who approved what and when.
FAQ
Questions, answered
Human-in-the-loop for other frameworks
Let your LangChain agent ask a human.
Connect a user with one tap, ask a human with one call, and act on a fail-closed decision. The durable store and the audit log come with it.
Partner plan, $99 a month. Your users never see a Pushary login or bill.