> For the complete documentation index, see [llms.txt](https://docs.neuron-ai.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.neuron-ai.dev/neuron-v3-zh/gong-zuo-liu/human-in-the-loop.md).

# 中断

### 它是什么

Neuron 的中断模式提供了一个内置的 *人机协同* 机制，允许工作流暂停执行，并在恢复前等待外部输入。

其核心上，中断通过抽象的 `InterruptRequest` 类来实现，这是一种框架原语，开发者可以扩展它以创建针对其应用特定需求的自定义中断体验。该框架包含一个 `ApprovalRequest` 作为内置实现，覆盖最常见的审批操作用例（例如工具调用），但其架构被有意设计得很灵活：任何工作流节点或中间件都可以触发中断，而持久化层会确保状态在暂停/恢复周期中得以保留，因此它适用于任何阶段都需要人工决策点的长时间运行流程。

如果内置的 `ApprovalRequest` 不适合你的用例，你可以创建自定义中断请求，以构建特定的 UI 体验。

工作原理如下：

**中断点**: 你的工作流中的任何节点都可以通过指定要展示给人类的数据来请求中断。这可以是一个简单的是/否决策、警报，或任何结构化数据。

**状态保留**: 当发生中断时，Neuron 会自动保存工作流的完整状态。你的工作流本质上会进入休眠，等待人工输入。

**恢复**: 一旦人类对中断请求作出回应，工作流就会从离开的那个节点原样唤醒。不会丢失任何数据，也不会忘记任何上下文。

**外部反馈集成**: 编辑后的中断请求会注入到被中断的节点中，以便在接收人工反馈后继续执行。

### 视频介绍

我们知道中断流程是一个相当高级的功能。即使下面有完整文档，也可能不容易把握该架构的所有方面。我们很高兴在下面链接一个由我们的社区成员制作的介绍视频 [Amitav Roy](https://www.linkedin.com/in/royamitav/).

它可能会提供一些额外信息，结合文档，可以帮助你理解如何实现你的用例。

{% embed url="<https://www.youtube.com/watch?v=jjEBjTRDLZE>" %}

### 工作原理

当你请求中断时，工作流并不会简单停止，而是会保留其全部状态，并在继续前等待指引。这使你能够创建一个混合智能系统，让 AI 负责计算密集型工作，而人类提供战略监督和决策。

请求中断的最简单方式是调用 `interrupt()` 方法，在节点内提供一个中断请求。下面是一个使用内置 `ApprovalRequest`:

```php
<?php

namespace App\Neuron;

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

class InterruptionNode extends Node
{
    public function __invoke(InputEvent $event, WorkflowState $state): OutputEvent
    {
        // 中断工作流并等待反馈。
        $humanResponse = $this->interrupt(
            new ApprovalRequest(
                message: '我应该继续吗？'
                actions: [
                    new Action('delete_file', '删除文件', '删除 /var/log/old.txt'),
                ],
            )
        );
    
        $action = $humanResponse->getAction('delete_file');
    
        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();
    }
}
```

你最终可以实现自己的自定义中断请求，以传递人机交互所需的信息。之后你可以在工作流之外捕获这些数据，从而向用户征求反馈。

当工作流恢复时，它将从被中断的同一节点重新开始，而 `$feedback` 变量将接收人类的响应数据。

该 `InterruptRequest` 遵循 **请求-响应模式** 其中：

1. **请求阶段**: 工作流节点识别出需要人工批准的操作，并创建一个 `InterruptRequest` 其中包含这些操作的详细信息
2. **暂停阶段**: 工作流抛出一个 `WorkflowInterrupt` 异常，保留整个执行上下文
3. **决策阶段**: 应用将操作展示给用户，由用户批准、拒绝或编辑每个操作
4. **恢复阶段**: 工作流根据用户的决定恢复，并根据反馈继续执行

这种设计确保工作流可以在任意点安全暂停、持久化其状态，并在同一位置精确恢复，即使跨不同会话也是如此。

### 自定义中断请求

该 `InterruptRequest` 是 Neuron 的人机协同（HITL）模式的核心组件，旨在暂停工作流执行并针对特定操作请求人工批准或输入。它为构建交互式 AI 工作流提供了一种结构化、类型安全的方法。

你可以创建自己的实现，并将其传入 interrupt 方法。

```php
class ContentReviewInterrupt extends InterruptRequest
{
    public function __construct(
        protected string $message,
        protected string $content
    ) {
        parent::__construct($message)
    }
    
    public function getContent(): string
    {
        return $this->content;
    }
    
    public function jsonSerialize(): array
    {
        return [
            'message' => $this->message,
            'content' => $this->content,
        ];
    }
    
    public static function fromArray(array $data)
    {
        return new static($data['message'], $data['content']);
    }
}
```

在你的中断用例中使用它：

```php
class InterruptionNode extends Node
{
    public function __invoke(InputEvent $event, WorkflowState $state): OutputEvent
    {
        // 生成文章
        $response = ContentCreatorAgent::make()
            ->chat(new UserMessage($event->prompt))
            ->getMessage();
    
        // 中断工作流并等待反馈。
        $reviewRequest = $this->interrupt(
            new ContentReviewInterrupt(
                message: '这是新文章。请在将内容保存到数据库之前先审阅内容。'
                $response->getContent()
            )
        );
        
        // 保存更新后中断请求的内容
        $state->set('content', $reviewRequest->getContent());
        
        return new InputEvent();
    }
}
```

### 捕获中断

要能够中断并恢复工作流（同样适用于 Agent 和 RAG），你需要在创建 Workflow 实例时提供持久化层：

```php
$workflow = new WorkflowAgent(new FilePersistence(__DIR__));
```

当某个节点请求中断时，Workflow 会触发一种特殊类型的异常，由 **`WorkflowInterrupt`** 类表示。你可以捕获这个异常来管理中断请求。

```php
$workflow = new WorkflowAgent(
    new FilePersistence(__DIR__),
);

try {
    return $workflow->init()->run();
} catch (WorkflowInterrupt $interrupt) {
    $request = $interrupt->getRequest();
    $workflowId = $interrupt->getWorkflowId();
    
    /*
    * 你可以将请求存储为 JSON 对象
    * 并连同恢复令牌一起保存，然后向用户请求反馈。
    */
    $pdo->prepare("INSERT INTO interruption_requests (resume_token, request) VALUES (?, ?)");
    $pdo->execute([
        $workflowId,
        json_encode($request),
    ]);
}
```

使用 `$request` 对象中的信息来引导人类提供反馈。一旦你最终获得用户反馈，就可以将中断请求传给 `init()` 方法来恢复工作流。记得使用相同的 `workflowId` ，即你在中断期间得到的那个。

```php
$workflow = new WorkflowAgent(
    new FilePersistence(__DIR__),
    $workflowId // <- 使用你在中断期间得到的相同 ID
);

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

// 传入处理后的请求作为反馈，恢复工作流
$result = $workflow->init($request)->run();

// 获取最终答案
echo $result->get('content');
```

你可以查看下面的脚本作为此过程的示例：

{% @github-files/github-code-block url="<https://github.com/inspector-apm/neuron-ai/blob/main/examples/workflow/workflow-interrupt.php>" %}

### 检查点

当工作流恢复时，它会从中断的节点重新开始执行。该节点将被完全重新执行，包括中断前存在的代码。

如果你需要的中断不是在节点开头，而是在执行其他操作之后，那么你可以使用检查点来保存前面语句的结果，以便在节点恢复时使用。下面是一个示例：

```php
<?php

namespace App\Neuron;

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

class InterruptionNode extends Node
{
    public function __invoke(InputEvent $event, WorkflowState $state): OutputEvent
    {
        // 这段代码块的结果会在工作流恢复时被保存并返回。
        $sentiment = $this->checkpoint('agent-1', function () {
            return MyAgent::make()->structured(
                new UserMessage(...),
                SentimentResult::class
            );
        });
        
        // 中断工作流并等待反馈。
        if ($sentiment->isNegative()) {
            $feedback = $this->interrupt(
                new ApprovalRequest(
                    message: '我应该继续吗？'
                    actions: [
                        new Action('review_id', '答复审阅', $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();
    }
}
```

checkpoint 方法接受两个参数：

* 该 **检查点** 的名称在节点中必须唯一；
* 一个 **闭包** 来包裹你想保存结果的代码。

当节点执行时，checkpoint 方法会在发生中断时保存闭包的结果。节点在中断后再次执行时，可以以与上一次运行完全相同的状态到达中断点，从而获取外部反馈。

### 消耗中断反馈

你也可以在代码的其他地方消费外部反馈，而不是在调用 `interrupt()` 方法时。

该 `consumeResumeRequest()` 方法允许你获取外部反馈的值；如果节点只是正常运行而未被唤醒，则返回 null：

```php
<?php

namespace App\Neuron;

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

class InterruptionNode extends Node
{
    public function __invoke(InputEvent $event, WorkflowState $state): OutputEvent
    {
        // 请求最终恢复请求
        $feedback = $this->consumeResumeRequest();
    
        // 如果请求还未到达，则跳到中断
        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 ApprovalRequest(
                message: '我应该继续吗？'
                actions: [
                    new Action('review_id', '答复审阅', $state->get('review')),
                ],
            )
        );
        
        $state->set('is_sufficient', false);
        return new InputEvent();
    }
}
```

这使你可以根据给定的反馈，在节点开头应用条件。

### 条件中断

你也可以使用 `interruptIf()` 作为辅助函数来评估条件中断：

```php
<?php

namespace App\Neuron;

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

class InterruptionNode extends Node
{
    public function __invoke(InputEvent $event, WorkflowState $state): OutputEvent
    {
        // 条件中断
        $this->interruptIf(
            $state->get('is_sufficient') == true, 
            new ApprovalRequest(
                message: '我应该继续吗？'
                actions: [
                    new Action('review_id', '答复审阅', $state->get('review')),
                ],
            )
        );
        
        // 或使用回调来评估条件
        $this->interruptIf(
            fn() => $state->get('is_sufficient', false), 
            new ApprovalRequest(
                message: '我应该继续吗？'
                actions: [
                    new Action('review_id', '答复审阅', $state->get('review')),
                ],
            )
        );
        
        return new InputEvent();
    }
}
```

### 监控与调试

你用 Neuron 构建的许多应用都会包含多个步骤，以及对 LLM 调用的多次调用。随着这些应用变得越来越复杂，能够检查你的 agentic 系统内部到底发生了什么就变得至关重要。做到这一点的最佳方式是使用 [Inspector](https://inspector.dev/).

{% embed url="<https://docs.inspector.dev/guides/neuron-ai>" %}
