Examples

Ask a human

The smallest integration - an agent asks a question, a human answers in the inbox, and the answer becomes the tool result.

Sometimes the agent does not need permission - it needs an answer. This example has a single tool, ask_human, that never executes: the only decision a reviewer can take is respond, and whatever they type is returned to the model as the tool's result. Source: Vigilator/examples/ask-a-human.

What you'll see

  • Inbox - an interrupt whose action request is the question itself, with a single Respond box.
  • Live View - a policy-helper session before the question and another after it.

Run it

Install and configure

git clone https://github.com/Vigilator/examples
cd examples/ask-a-human
uv sync
cp .env.example .env   # VIGILATOR_API_KEY and ANTHROPIC_API_KEY

Run with polling

uv run main.py

The agent asks what the returns window is and prints the interrupt id. Answer in the inbox; the script resumes and the agent writes the reply using your answer verbatim.

Or resume by webhook

uv run fastapi dev webhook.py
svix listen http://localhost:8000/webhooks/vigilator

Register the relay's URL for interrupt.answered, put the signing secret in .env, and POST /runs.

How it works

Three lines do the pausing:

agent.py
@tool
def ask_human(question: str) -> str:
    """Ask a human colleague a question and wait for their answer."""
    raise RuntimeError("ask_human is answered by a human, not executed")

INTERRUPT_ON = {"ask_human": {"allowed_decisions": ["respond"]}}
middleware = [HumanInTheLoopMiddleware(interrupt_on=INTERRUPT_ON)]

When the model calls ask_human, the middleware raises an interrupt instead of running it. The bridge sees a request whose only allowed decision is respond and follows Vigilator's convention for questions - an action request named after the question with allowedDecisions=[respond]:

vigilator_bridge.py
ActionRequest(
    name=args["question"],                       # the question is the action request
    description="Asked by the agent through its `ask_human` tool.",
    allowedDecisions=[AllowedDecision.respond],
)

The reviewer's responseText comes back as a respond decision, and the middleware hands it to the model as the tool's result:

vigilator_bridge.py
Command(resume={"decisions": [{"type": "respond", "message": action.response_text}]})

Adapting it

  • Keep the tool's docstring specific about when to ask; the model decides to call it.
  • Several questions in one turn become several action requests on one interrupt; the reviewer answers each and the bridge maps them back in order.
  • To mix questions with approvals, see LangChain; for the same pattern in TypeScript, the ask_human tool in LangGraph.js or ask-human in Mastra.

On this page