---
title: "agent - 会话挂起（Session Pending）"
---


在 Agent 的执行过程中，有时需要中断当前的计算流。例如：触发 HITL（Human-In-The-Loop，人机交互）等待人工审批，或是在特定条件下进行外部强行中断。

注意：

- 当前 `ReActAgent` 和 `TeamAgent` 在准备新的请求时会清除挂起标记，因此使用同一个 Session 发起恢复请求时通常可以继续执行；自定义 Session 或执行流程不应假定一定存在此行为。
- 挂起控制计算图后续推进，但不等同于 Reactor cancel，也不保证正在执行的阻塞模型调用立即停止。

### 1、会话的挂起接口

通过 AgentSession 对象，可以对当前会话的运行状态进行精细化控制。一旦进入挂起状态，Agent 内部的计算图（Execution Graph）将停止推进。

| 方法 | 描述 |
|------|------|
| `AgentSession::pending(pending, reason)` | 设置挂起状态（true 为挂起，并需提供挂起原因；false 为恢复执行，此时 reason 传 null 会清除挂起原因） |
| `AgentSession::isPending()` | 判断当前会话是否处于挂起状态 |
| `AgentSession::getPendingReason()` | 获取挂起原因（通常用于前端展示或后续逻辑判断） |

#### 内部机制说明

`pending()` 方法的默认实现（在 AgentSession 接口中）的工作流程如下：

```java
default void pending(boolean pending, String reason) {
    FlowContextInternal contextInternal = ((FlowContextInternal) getContext());

    if (pending == true) {
        // 1. 调用 contextInternal.stop() 停止执行流
        // 2. 设置 stopped(true) 标记，后续计算图检测到此标记后停止推进
        contextInternal.stop();
        contextInternal.stopped(true);
    } else {
        // 清除 stopped 标记，允许继续执行
        contextInternal.stopped(false);
    }

    if (reason == null) {
        // 清除挂起原因
        getContext().remove(Agent.KEY_PENDING_REASON);
    } else {
        // 记录挂起原因（可用于前端展示）
        getContext().put(Agent.KEY_PENDING_REASON, reason);
    }
}
```

### 2、挂起应用场景

会话挂起主要分为 内部逻辑拦截 和 外部异步中断 两种模式。

#### 通过拦截器挂起（适用于：同步、异步、流式响应）

这是最常用的场景，通常用于在工具执行（Action）前进行合规检查或人工授权。

```java
public void useInterceptor() throws Throwable {
    ReActAgent agent = ReActAgent.of(null)
        .defaultInterceptorAdd(new ReActInterceptor() {
            @Override
            public void onToolCallStart(ReActTrace trace, ToolExchanger toolExchanger) {
                // 模拟 HITL 场景：根据业务逻辑决定是否挂起
                if ("delete_user".equals(toolExchanger.getToolName())) {
                    trace.getSession().pending(true, "敏感操作，等待人工审批");
                }
            }
        })
        .build();

    AgentSession session = InMemoryAgentSession.of();
    // 发起同步调用
    ReActResponse resp = agent.prompt("删除 ID 为 1001 的用户").session(session).call();

    // 检查挂起状态
    if (session.isPending()) {
        System.out.println("执行已挂起，原因：" + session.getPendingReason());
    }
}

```

> **注意**：旧拦截器方法 `onAction` 已在 4.0.4 标记为 `@Deprecated`，推荐使用 `onToolCallStart`。

#### 外部主动中断（适用于：异步调用、流式响应）

在异步或流式任务执行过程中，外部根据用户指令或其他监控事件强行挂起会话。

- 示例 A：异步调用中断

```java
public void asyncInterrupt() throws Throwable {
    ReActAgent agent = ReActAgent.of(null).build();
    AgentSession session = InMemoryAgentSession.of();

    // 发起异步调用
    agent.prompt("生成一份万字长文报告").session(session).callAsync()
        .whenComplete((resp, err) -> {
            // 处理最终结果
        });

    // 模拟外部干预：立即强行挂起
    session.pending(true, "用户点击了停止生成");
}

```

- 示例 B：流式调用中断

```java
public void streamInterrupt() {
    ReActAgent agent = ReActAgent.of(null).build();
    AgentSession session = InMemoryAgentSession.of();

    // 保存 Disposable，主动停止流时调用 dispose()
    Disposable disposable = agent.prompt("查一下杭州今天的天气").session(session).stream()
        .subscribe(
            event -> {
                // 处理流事件内容
            },
            error -> handleError(error));

    // session.pending(...) 记录并控制会话挂起；它不等同于取消当前订阅
    session.pending(true, "资源配额不足，自动挂起");

    // 如果业务要求立即停止向订阅方传输事件：
    disposable.dispose();
}

```

### 3、恢复执行

挂起的会话可以通过两种方式恢复：

**方式一：重新发起调用时自动恢复**

当重新对同一个 Session 调用 `agent.prompt("新的指令").session(session).call()` 时，Trace 的 `prepare()` 方法会自动调用 `session.pending(false, null)`，清除挂起状态使计算图继续推进。

**方式二：手动恢复**

```java
// 恢复执行
session.pending(false, null);
// 调用 agent.prompt().session(session).call() 继续推进
```

### 4、HITL 场景的完整配合

在标准 HITL 路径中，事件分属两次请求：

- 首次请求：`ReasonEndEvent -> HITLPendingEvent -> RunEndEvent(isAbnormal=true) -> onComplete`。此时 Session 仍为 pending，且不会产生 Action 或 ToolCall 的开始/结束事件。
- 外部提交决策后，使用同一个 Session 和空 Prompt 发起恢复请求；恢复流中产生 `HITLDecidedEvent`，再按批准、拒绝或跳过结果继续。
- 普通代码直接调用 `session.pending(true, reason)` 时，不能假定系统一定会生成 `HITLPendingEvent`。

主动取消属于 Reactor cancel，不会补发 `RunEndEvent`；未处理异常通过 `onError` 结束，也不会补发顶层结束事件。

详细请参考 HITL 相关专题文章。
