经典的 plan-and-execute 范式有两种:
双 Agent 模型(LangGraph):Planner 跑一次出 plan → Executor 逐步执行 → 完不成再回 Planner 重规划。优点是职责清晰,缺点是状态在两个 Agent 之间穿梭、prompt 上下文冗长。
单 Agent 自驱模型(ASJ):同一个 ReActAgent 既能调"规划工具"又能调"执行工具"——LLM 自己决定什么时候建计划、什么时候推进、什么时候改、什么时候收尾。框架只做两件事:
- 把"plan 是什么、当前进度、下一步该做什么"以
ReActAgent agent = ReActAgent.builder()
.name("Assistant").model(model).toolkit(toolkit)
.enablePlan()
.build();
复杂一点的话,可以自定义一些配置:
PlanNotebook nb = PlanNotebook.builder()
.planToHint(new DefaultPlanToHint())// 默认;可换成你自己的 prompt 模板
.storage(new InMemoryPlanStorage()) // 默认;生产可换 DB 实现
.maxSubtasks(10) // null=不限
.needUserConfirm(true) / 默认 true:plan 创建后必须等用户说"go"
.keyPrefix("mainPlan") // 同 session 内多个 PlanNotebook 共存
.build();
ReActAgent agent = ReActAgent.builder().planNotebook(nb).build();
build() 的运行,最终会走到 configurePlan(agentToolkit):
private void configurePlan(Toolkit agentToolkit) {
// ① 注册10个工具
agentToolkit.registerTool(planNotebook);
Hook planHintHook = new Hook() {
public <T extends HookEvent> Mono<T> onEvent(T event) {
if (event instanceof PreReasoningEvent e) {
// ② 每轮 reasoning 前生成 hint
return planNotebook.getCurrentHint()
.map(hintMsg -> {
List<Msg> modified = new ArrayList<>(e.getInputMessages());
//追加到 input 末尾
modified.add(hintMsg);
e.setInputMessages(modified);
return (T) e;
})
.defaultIfEmpty(event);
}
return Mono.just(event);
}
};
hooks.add(planHintHook);
}
上面提到10个工具(定义在PlanNoteBook中),按照职责可以分为四组: Plan生命周期组(4 个) | 工具 | 作用 | | --- | --- | | create_plan(name, description,expected_outcome, subtasks) | 创建新 plan | | finish_plan(state, outcome) | 完成或放弃 plan | | view_historical_plans() | 列出历史 plans | | recover_historical_plan(plan_id) | 恢复历史 plan |
结构修改组(2 个) | 工具 | 作用 | | --- | --- | | update_plan_info(name?, description?, expected_outcome?) | 改 plan 元数据,三个字段可选 | | revise_current_plan(subtask_idx, action, subtask) | 通过添加、修改或删除子任务来修订当前计划 |
子任务执行组(3 个) | 工具 | 作用 | | --- | --- | | update_subtask_state(idx, state) | 状态机推进 | | finish_subtask(idx, outcome) | 完成子任务 | | view_subtasks(indexes) | 查看子任务详情 |
状态查询组(1 个) | 工具 | 作用 | | --- | --- | | get_subtask_count() | 返回 total/done/in_progress/todo/abandoned 五元统计 |
工具注册之后,会调用PlanNotebook.getCurrentHint() ,这个方法调 DefaultPlanToHint的generateHint(currentPlan, this)方法生成提示词。DefaultPlanToHint 根据当前 plan 的状态生成五种不同的 prompt:
- NO_PLAN:用户提了个问题,你判断要不要建计划——如果是简单问题直接答,复杂任务才 create_plan。
- AT_THE_BEGINNING:plan 刚建好,所有子任务 TODO——下一步把第 0 个子任务置 IN_PROGRESS 开干。
- WHEN_A_SUBTASK_IN_PROGRESS:有一个子任务正在执行——继续推进、用 finish_subtask 收尾、或必要时改 plan。
- WHEN_NO_SUBTASK_IN_PROGRESS:前几个完成了但当前没活跃——把下一个置 IN_PROGRESS。
- AT_THE_END:全部子任务结束——调 finish_plan 总结收尾。
prompt 末尾还会拼接 RULE_COMMON(语言一致性、用户可能直接改 plan、不要私自改 plan)和 RULE_WAIT_FOR_CONFIRMATION(如果 needUserConfirm=true)等"系统级规则"。
整段 hint 包在
Demo
import
import
import
import
import
import
import
import
import
import
public
files
toolkit
nb
}
输出结果:
>>> Plan changed:
## Calculate and Verify Result
**Description**: Calculate 10*5, save the result to result.txt, then read it back to verify the calculation was correct.
**Expected Outcome**: The file result.txt contains the value '50' and this value is confirmed when read back.
**State**: todo
**Created At**: 2026-06-26 00:34:21
## Subtasks
- [ ] Calculate 10*5
- [ ] Save result to result.txt
- [ ] Read result.txt
>>> Plan changed:
## Calculate and Verify Result
**Description**: Calculate 10*5, save the result to result.txt, then read it back to verify the calculation was correct.
**Expected Outcome**: The file result.txt contains the value '50' and this value is confirmed when read back.
**State**: todo
**Created At**: 2026-06-26 00:34:21
## Subtasks
- [ ] [WIP] Calculate 10*5
- [ ] Save result to result.txt
- [ ] Read result.txt
>>> Plan changed:
## Calculate and Verify Result
**Description**: Calculate 10*5, save the result to result.txt, then read it back to verify the calculation was correct.
**Expected Outcome**: The file result.txt contains the value '50' and this value is confirmed when read back.
**State**: todo
**Created At**: 2026-06-26 00:34:21
## Subtasks
- [x] Calculate 10*5
- [ ] [WIP] Save result to result.txt
- [ ] Read result.txt
>>> Plan changed:
## Calculate and Verify Result
**Description**: Calculate 10*5, save the result to result.txt, then read it back to verify the calculation was correct.
**Expected Outcome**: The file result.txt contains the value '50' and this value is confirmed when read back.
**State**: todo
**Created At**: 2026-06-26 00:34:21
## Subtasks
- [x] Calculate 10*5
- [x] Save result to result.txt
- [ ] [WIP] Read result.txt
>>> Plan changed:
## Calculate and Verify Result
**Description**: Calculate 10*5, save the result to result.txt, then read it back to verify the calculation was correct.
**Expected Outcome**: The file result.txt contains the value '50' and this value is confirmed when read back.
**State**: todo
**Created At**: 2026-06-26 00:34:21
## Subtasks
- [x] Calculate 10*5
- [x] Save result to result.txt
- [x] Read result.txt