Interruption

The key breakthrough is that interruption isn't a bug – it's a feature.

Human in the loop

Neuron Workflow supports a robust human-in-the-loop pattern, enabling human intervention at any point in an automated process. This is especially useful in large language model (LLM)-driven applications where model output may require validation, correction, or additional context to complete the task.

Here's how it works technically:

Interruption Points: Any node in your Workflow can request an interruption by specifying the data it want to present to the human. This could be a simple yes/no decision, a content review, data validation, or structured data.

State Preservation: When an interruption happens, Neuron automatically saves the complete state of your Workflow. Your Workflow essentially goes to sleep, waiting for human input.

Resume: Once a human provides the requested input, the Workflow wakes up exactly from the node it left off. No data is lost, no context is forgotten.

External Feedback Integration: The human input is injected into the interrupted node to be continue its execution.

How it works

When you call for an interruption, the Workflow doesn't simply stop, it preserves its entire state, and waits for guidance before proceeding. This allows yuu to creates a hybrid intelligence system where AI handles the computational heavy lifting while humans contribute to strategic oversight, and decision-making.

The simplest way to ask for an interruption is calling the interrupt() method inside a node, providing an interrupt request:

<?php

namespace App\Neuron;

use NeuronAI\Workflow\Events\Event;
use NeuronAI\Workflow\Interrupt\Action;
use NeuronAI\Workflow\Interrupt\InterruptRequest;
use NeuronAI\Workflow\Node;
use NeuronAI\Workflow\WorkflowState;

class InterruptionNode extends Node
{
    public function __invoke(InputEvent $event, WorkflowState $state): OutputEvent
    {
        // Interrupt the workflow and wait for the feedback.
        $feedbackRequest = $this->interrupt(
            new InterruptRequest(
                message: 'Should I continue?'
                actions: [
                    new Action('delete_id', 'Delete File', 'Delete /var/log/old.txt'),
                ],
            )
        );
    
        $action = $feedbackRequest->getAction('delete_id');
    
        if ($action->isApproved()) {
            $state->set('is_sufficient', true);
            $state->set('user_feedback', $action->feedback);
            return new OutputEvent();
        }
        
        $state->set('is_sufficient', false);
        return new InputEvent();
    }
}

Calling the interrupt() method you can pass the information you need to interact with the human. You will be able to catch this data later, outside of the workflow so you can inform the user with relevant information from inside the Workflow to ask for feedback.

When the Workflow will be resumed it will restart from the same node it was interrupted, and the $feedback variable will receive the human's response data.

InterruptionRequest

The InterruptRequest is the central component of Neuron's human-in-the-loop (HITL) pattern, designed to pause workflow execution and request human approval or input for specific actions. It provides a structured, type-safe approach to building interactive AI workflows that require human oversight.

The InterruptRequest follows a request-response pattern where:

  1. Request Phase: A workflow node or middleware identifies actions requiring human approval and creates an InterruptRequest containing those actions

  2. Pause Phase: The workflow throws a WorkflowInterrupt exception, preserving the entire execution context

  3. Decision Phase: The application presents actions to users, who approve, reject, or edit each action

  4. Resume Phase: The workflow resumes with user decisions, continuing execution based on the feedback

This design ensures workflows can safely pause at any point, persist their state, and resume exactly where they left off, even across different processes or servers.

Checkpointing

When the Workflow is resumed it restarts the execution from the node where it was interrupted. The node will be re-executed entirely including the code present before the interruption.

If you need to call for an interruption not at the beginning of the node, but after performing other operations, you can use checkpoints to save the result of previous statements to be used when the node is resumed. Here is an example:

<?php

namespace App\Neuron;

use NeuronAI\Workflow\Node;
use NeuronAI\Workflow\WorkflowState;

class InterruptionNode extends Node
{
    public function __invoke(InputEvent $event, WorkflowState $state): OutputEvent
    {
        // The result of this code block is saved and returned when the workflow is resumed.
        $sentiment = $this->checkpoint('agent-1', function () {
            return MyAgent::make()->structured(
                new UserMessage(...),
                SentimentResult::class
            );
        });
        
        // Interrupt the workflow and wait for the feedback.
        if ($sentiment->isNegative()) {
            $feedback = $this->interrupt(
                new InterruptRequest(
                    message: 'Should I continue?'
                    actions: [
                        new Action('review_id', 'Answer review', $sentiment->content),
                    ],
                )
            );
            
            if ($feedback->getAction('review_id')->isApproved()) {
                $state->set('is_sufficient', true);
                $state->set('user_feedback', $feedback->getAction('review_id')->feedback);
                return new OutputEvent();
            }
        }
        
        $state->set('is_sufficient', false);
        return new InputEvent();
    }
}

The checkpoint method accepts two arguments:

  • The name of the checkpoint must be unique in the node;

  • A Closure to wrap the code whose result you want to save.

When the node is executed, the checkpoint method saves the result of the Closure in case of an interruption. When the node is executed again after the interruption, it can reach the interruption point with the exact same state of the previous run to get the external feedback.

Consume The Feedback

You can also consume the external feedback somewhere in your code other than where you call the interrupt() method.

The consumeInterruptFeedabck() method allows you get the value of the external feedback or null if the node is simply running and not awakening:

<?php

namespace App\Neuron;

use NeuronAI\Workflow\Node;
use NeuronAI\Workflow\WorkflowState;

class InterruptionNode extends Node
{
    public function __invoke(InputEvent $event, WorkflowState $state): OutputEvent
    {
        // Interrupt the workflow and wait for the feedback.
        $feedback = $this->consumeInterruptFeedback();
    
        if ($feedback !== null && $feedback->getAction('review_id')->isApproved()) {
            $state->set('is_sufficient', true);
            $state->set('user_feedback', $feedback->getAction('review_id')->feedback);
            return new OutputEvent();
        }
        
        $this->interrupt(
            new InterruptRequest(
                message: 'Should I continue?'
                actions: [
                    new Action('review_id', 'Answer review', $state->get('review')),
                ],
            )
        );
        
        $state->set('is_sufficient', false);
        return new InputEvent();
    }
}

This allows you to apply condition at the beginning of the node based on the given feedback.

Conditional Interruption

You can also use interruptIf() as an helper to evaluate a conditional interruption:

<?php

namespace App\Neuron;

use NeuronAI\Workflow\Node;
use NeuronAI\Workflow\WorkflowState;

class InterruptionNode extends Node
{
    public function __invoke(InputEvent $event, WorkflowState $state): OutputEvent
    {
        // Conditional interruption
        $this->interruptIf(
            $state->get('is_sufficient') == true, 
            new InterruptRequest(
                message: 'Should I continue?'
                actions: [
                    new Action('review_id', 'Answer review', $state->get('review')),
                ],
            )
        );
        
        // Or use a callback to evaluate the condition
        $this->interruptIf(
            fn() => $state->get('is_sufficient', false), 
            new InterruptRequest(
                message: 'Should I continue?'
                actions: [
                    new Action('review_id', 'Answer review', $state->get('review')),
                ],
            )
        );
        
        return new InputEvent();
    }
}

Catch the Interruption & Resume

To be able to interrupt and resume a Workflow you need to provide a persistence layer and a workflow ID when creating the Workflow instance:

$workflow = new WorkflowAgent(
    new FilePersistence(__DIR__),
    'CUSTOM_ID'
);

The ID is the reference to save and load the state of a specific Workflow during the interruption and resume the execution later in time. When a node call for an interruption it fires a special type of exception represented by the WorkflowInterrupt class. You can catch this exception to manage the interruption request.

try {
    $result = $workflow->start()->getResult();
} catch (WorkflowInterrupt $interrupt) {
    $request = $interrupt->getRequest();
    
    /*
     * You can store the request as a json with $request->jsonSerialize() along with the Workflow-ID,
     * and alert the user to provide a feedback.
     */
}

Use the information in the $request object to guide the human in providing a feedback. Once you finally have the user's feedback you can resume the workflow. Remeber to use the same ID of the interrupted execution.

$workflow = new WorkflowAgent(
    new FilePersistence(__DIR__),
    'CUSTOM_ID' // <- Use the same ID of the interrupted workflow
);

$request = InterruptRequest::fromArray($data);

// Resume the Workflow passing the processed request as the feedback
$result = $workflow->start($request)->getResult();

// Get the final answer
echo $result->get('answer');

You can take a look at the script below as an example of this process:

Unexpected error with integration github-files: Integration is not installed on this space

Monitoring & Debugging

Before moving into the Workflow creation process, we recommend having the monitoring system in place. It could make the learning curve of how Workflow works much more easier. The best way to monitoring Workflow is with Inspector.

After you sign up at the link above, make sure to set the INSPECTOR_INGESTION_KEY variable in the application environment file to monitoring Workflow execution:

.env
INSPECTOR_INGESTION_KEY=nwse877auxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Last updated