概述
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— 模型推理的每个 chunkActingChunkEvent— 工具执行的每个 chunkSummaryChunkEvent— 总结阶段的每个 chunk
这些由 notifyReasoningChunk、notifyActingChunk、notifySummaryChunk 方法触发。
builder 构造
Builder(第 1229 行)的默认值:
| 配置项 | 默认值 | 说明 |
|---|---|---|
| memory | new InMemoryMemory() | 对话历史 |
| maxIters | 10 | 最大迭代次数 |
| toolkit | new Toolkit() | 空工具集 |
| checkRunning | true | 并发检查 |
| structuredOutputReminder | TOOL_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();
关键设计要点
- 纯 Reactor 异步 — 所有方法返回
Mono<T>或Flux<T>,call()最终也通过.block()阻塞等待 - 错误隔离 — 工具执行失败不会抛到上层,而是生成
[ERROR] ToolResultBlock让模型继续 - pending tool 追踪 — 通过
getPendingToolUseIds()从 memory 中比对 tool call ID 和 tool result ID,找哪些工具还没执行 - HITL 支持 — PostReasoningEvent 和 PostActingEvent 都可以
stopRequested()打断循环 - Summarizing 兜底 — maxIters 到了还有未执行工具时,生成 error result 后再调 LLM 总结