概述

ReActAgent 是 AgentScope Java 的核心推理引擎,位于 agentscope-core 模块。它实现了 ReAct (Reasoning and Acting) 设计模式——在推理(想下一步做什么)和执行(调工具)之间交替循环。

源码位置:agentscope-core/src/main/java/io/agentscope/core/ReActAgent.java(1925 行)

核心依赖

public class ReActAgent extends StructuredOutputCapableAgent {
    private final Memory memory;         // 对话历史(InMemoryMemory)
    private final String sysPrompt;      // 系统提示词
    private final Model model;           // LLM 模型
    private final int maxIters;          // 最大迭代次数(默认 10)
    private final Toolkit toolkit;       // 可用工具集
    private final PlanNotebook planNotebook;  // 计划本
    private final StatePersistence statePersistence;  // 状态持久化策略
}

核心循环:reason → act → reason → act → …

整个 ReAct 循环的入口:

private Mono<Msg> executeIteration(int iter) {
    return reasoning(iter, false);  // 先推理
}

reasoning 阶段(第 560-650 行)

① 检查 maxIters → 超限则走 summarizing()
② notifyPreReasoningEvent()  ← Hook 注入点:CompactionHook、SubagentsHook、WorkspaceContextHook
③ model.stream(messages + tool schemas)  ← 调 LLM
④ 逐 chunk 处理 + notifyReasoningChunk()  ← 流式输出
⑤ notifyPostReasoning()  ← Hook 注入点
⑥ 判断:
    - stopRequested → 返回(HITL 打断)
    - gotoReasoning → 继续推理(结构化输出重试)
    - isFinished(msg) → 没有 tool call → 返回结果
    - 有 tool call → going to acting(iter)

关键判断 isFinished(第 969 行):

private boolean isFinished(Msg msg) {
    List<ToolUseBlock> toolCalls = msg.getContentBlocks(ToolUseBlock.class);
    return toolCalls.isEmpty();  // 没有 tool call 就算结束
}

acting 阶段(第 669-731 行)

① extractPendingToolCalls() → 找本轮未执行的 tool call
② 没有 pending tool → executeIteration(iter+1)(继续下一轮)
③ notifyPreActingHooks()  ← Hook 注入点
④ toolkit.callTools()  ← 真正执行工具
⑤ notifyPostActingHook()  ← Hook 注入点,结果写回 Memory
⑥ 判断:
    - stopRequested → 返回
    - 有 pending(suspended)→ 返回等待用户
    - 正常 → executeIteration(iter+1)

完整的流程图

call(msgs)
  │
  ├── ① beforeAgentExecution → bindRuntimeContext
  │
  └── doCall(msgs)
       │
       ├── [无 pending tool] → addToMemory → executeIteration(0)
       ├── [有 pending,无输入] → acting(0)
       └── [有 pending + 输入] → validate → acting / executeIteration
            │
            ▼
       executeIteration(iter)
            │
            ▼
       reasoning(iter, ignoreMaxIters)
            │
            ├── iter >= maxIters → summarizing() ← 生成总结
            │
            ├── notifyPreReasoningEvent  ← Hook
            │     (CompactionHook 10, SubagentsHook 80, WorkspaceContextHook 900)
            │
            ├── model.stream(messages)  ← 调 LLM,流式返回 chunks
            │     └── 每 chunk → notifyReasoningChunk
            │
            ├── notifyPostReasoning  ← Hook
            │
            ├── stopRequested? → 返回
            ├── gotoReasoning?   → reasoning(iter+1, true)
            ├── isFinished? → 返回结果
            │
            └── acting(iter)  ← 有 tool call
                  │
                  ├── extractPendingToolCalls
                  ├── notifyPreActingHooks  ← Hook
                  │
                  ├── toolkit.callTools()  ← 执行工具
                  │     └── 错误处理:给所有失败 tool 生成 [ERROR] 结果
                  │
                  ├── notifyPostActingHook(每个结果)  ← Hook,结果写入 Memory
                  │     (ToolResultEvictionHook 50 在此截断大结果)
                  │
                  ├── stopRequested? → 返回
                  ├── suspended? → 返回等待用户
                  │
                  └── executeIteration(iter+1)  ← 继续下一轮

Hook 通知机制(第 1031 行)

private <T extends HookEvent> Mono<T> notifyHooks(T event) {
    Mono<T> result = Mono.just(event);
    for (Hook hook : getSortedHooks()) {
        result = result.flatMap(hook::onEvent);  // 链式调用,每个 hook 可以修改 event
    }
    return result;
}

所有 hook 按 priority 排序后串成链,前一个 hook 的输出作为后一个的输入。所以 WorkspaceContextHook(900) 能看到前面所有 hook 修改后的结果。

流式输出(Event 机制)

stream() 模式下,ReAct 循环中产生的每个 chunk 都通过 Event 推给调用方:

  • ReasoningChunkEvent — 模型推理的每个 chunk
  • ActingChunkEvent — 工具执行的每个 chunk
  • SummaryChunkEvent — 总结阶段的每个 chunk

这些由 notifyReasoningChunknotifyActingChunknotifySummaryChunk 方法触发。

builder 构造

Builder(第 1229 行)的默认值:

配置项默认值说明
memorynew InMemoryMemory()对话历史
maxIters10最大迭代次数
toolkitnew Toolkit()空工具集
checkRunningtrue并发检查
structuredOutputReminderTOOL_CHOICE结构化输出模式

完整构造示例:

ReActAgent agent = ReActAgent.builder()
    .name("Assistant")
    .sysPrompt("You are a helpful assistant.")
    .model(model)
    .toolkit(toolkit)
    .memory(new InMemoryMemory())
    .maxIters(10)
    .hook(new MyCustomHook())
    .build();

关键设计要点

  1. 纯 Reactor 异步 — 所有方法返回 Mono<T>Flux<T>call() 最终也通过 .block() 阻塞等待
  2. 错误隔离 — 工具执行失败不会抛到上层,而是生成 [ERROR] ToolResultBlock 让模型继续
  3. pending tool 追踪 — 通过 getPendingToolUseIds() 从 memory 中比对 tool call ID 和 tool result ID,找哪些工具还没执行
  4. HITL 支持 — PostReasoningEvent 和 PostActingEvent 都可以 stopRequested() 打断循环
  5. Summarizing 兜底 — maxIters 到了还有未执行工具时,生成 error result 后再调 LLM 总结