Appearance
常驻规划循环(PlanningLoop)实现方案
文档日期:2026-03-13 模块:muotai-ai
背景与目标
问题
当前 AutoPlanningAgent 和 OrchestrationAgent 的执行是单次线性的——规划一次、执行完毕、结束。对于复杂大任务被拆成多个子任务后,缺少一个"常驻监督者"在执行过程中感知上下文变化、动态决定是否继续/重新规划。
对标参考
Claude Code 的 TaskCreate → TaskUpdate → 遇阻 → 重新思考 → 重排任务 模式:当任务失败时,系统不是盲目重试,而是进行 LLM 推理后决定下一步行动(继续 / 局部重规划 / 全量重规划 / 中止)。
目标
在 muotai-ai 框架中引入 PlanningLoop(规划循环) 组件,作为大任务生命周期的常驻监督者:
- 感知子任务执行中断、失败、结果异常等上下文变化
- 通过 LLM 决策是否继续/局部重规划/全量重规划/中止
- 支持多次 replan,保留已完成任务的上下文
现有能力盘点(可复用)
| 已有组件 | 复用点 |
|---|---|
TaskPlan / TaskStep (chatbot/dto) | 已有 replanCount、status=replanning,直接复用 |
StreamHandler | 已有 onReplanRequired(reason, count) 钩子,直接复用 |
AutoPlanningAgent | 现有 agentic_loop LangGraph 图,在此基础上扩展 |
OrchestrationAgent | 执行阶段/步骤,增加事件发布出口 |
AgentRunContext | 共享执行上下文,直接复用 |
AgentHandoffSystem | Agent 间交接,复用 HandoffTrigger 触发机制 |
TaskManager | 任务取消/状态查询,复用 cancelTask() |
TaskExecution | 扩展新增 INTERRUPTED 状态 |
整体架构
数据流
用户输入
↓
AutoPlanningAgent.autonomousPlanning()
↓ 创建 PlanningLoopSession
↓ 调用 TaskPlanningAgent.plan() → TaskPlan(含 TaskSteps)
↓ 注册到 PlanningLoopRegistry
↓
OrchestrationAgent.executePlan(plan, sessionId) [异步]
├── 步骤成功 → 更新 session.completedTasks
└── 步骤失败/中断 → publishContextChange(ContextChangeEvent)
↓
PlanningLoop 监控协程(常驻)
├── 收到 ContextChangeEvent
├── 判断触发条件(CRITICAL立即 / 积累N个 / 阶段里程碑)
└── 调用 ReactiveDecisionAgent.decide(situationReport)
↓
┌───────────┬─────────────────┬──────────────┐
CONTINUE REPLAN_REMAINING REPLAN_FULL ABORT
│ │ │ │
继续执行 取消pending任务 取消全部任务 取消全部
│ TaskPlanningAgent 全量重规划 SessionStatus
│ .replan(...) 重新执行 → ABORTED
│ 新计划→重新执行
└──────────────────────────────────────────────→ 通知用户LangGraph 状态机
PlanningLoopState:
GOAL / CURRENT_PLAN / PENDING_TASKS / COMPLETED_TASKS
CONTEXT_CHANGES / LOOP_STATUS / REPLAN_COUNT / LAST_DECISION
节点:
planNode → 调用 TaskPlanningAgent 生成/重建计划
executeNode → 分发给 OrchestrationAgent 异步执行(非阻塞)
monitorNode → 等待 events 或完成信号(阻塞等待)
reactNode → 调用 ReactiveDecisionAgent 做 LLM 决策
replanNode → 整理"已完成+上下文变更",准备重规划入参
条件边:
planNode → executeNode
executeNode → monitorNode
monitorNode → reactNode (有待处理事件 or 里程碑达到)
monitorNode → END (全部任务完成)
reactNode → monitorNode (CONTINUE)
reactNode → replanNode (REPLAN_REMAINING / REPLAN_FULL)
reactNode → END (ABORT)
replanNode → planNode新建类(7个)
1. context/ContextChangeEvent.java
java
public class ContextChangeEvent {
String sessionId; // PlanningLoopSession ID
String taskId; // 触发变更的任务ID
ContextChangeType type; // 变更类型
Severity severity; // INFO / WARNING / CRITICAL
String description;
Map<String, Object> payload;
LocalDateTime occurredAt;
}
enum ContextChangeType {
TASK_FAILED, // 子任务执行失败
TASK_INTERRUPTED, // 子任务被中断
TASK_UNEXPECTED_RESULT, // 结果与预期不符
NEW_CONSTRAINT, // 发现新的约束条件
DEPENDENCY_BROKEN, // 依赖关系断裂
EXTERNAL_SIGNAL, // 外部信号(用户输入等)
MILESTONE_REACHED // 阶段完成(触发主动评估)
}2. context/PlanningLoopSession.java
java
public class PlanningLoopSession {
String sessionId;
String originalGoal;
TaskPlan currentPlan;
List<TaskStep> completedTasks;
BlockingQueue<ContextChangeEvent> pendingEvents;
List<ReplanRecord> replanHistory;
PlanningSessionStatus status; // PLANNING/EXECUTING/MONITORING/REACTING/REPLANNING/COMPLETED/ABORTED
int replanCount;
StreamHandler streamHandler;
AgentRunContext runContext;
LocalDateTime startTime;
// 触发配置
int maxReplans = 3;
Severity reactThreshold = Severity.WARNING;
int batchReactSize = 3; // 积累N个事件后批量react
}3. context/ReplanDecision.java
java
public class ReplanDecision {
ReplanAction action; // CONTINUE / REPLAN_REMAINING / REPLAN_FULL / ABORT
String reasoning; // LLM 推理过程(透明化)
List<Integer> stepsToCancel; // 需要取消的步骤索引
String newContextHints; // 给新规划的补充上下文
float confidence; // 决策置信度 0-1
}4. core/PlanningLoop.java ⭐ 核心组件(Spring @Component)
java
@Component
public class PlanningLoop {
// 注入
@Autowired TaskPlanningAgent taskPlanningAgent;
@Autowired OrchestrationAgent orchestrationAgent;
@Autowired ReactiveDecisionAgent reactiveDecisionAgent;
@Autowired TaskManager taskManager;
@Autowired PlanningLoopRegistry registry;
// 活跃会话
ScheduledExecutorService monitorExecutor;
// 对外API
PlanningLoopSession start(Intent intent, StreamHandler handler);
void publishEvent(String sessionId, ContextChangeEvent event);
CompletableFuture<ReplanDecision> react(String sessionId);
void executeReplan(PlanningLoopSession session, ReplanDecision decision);
void stop(String sessionId);
PlanningLoopSession getSession(String sessionId);
// 内部
String buildSituationReport(PlanningLoopSession session);
}5. core/ReactiveDecisionAgent.java(继承 BaseAgent)
专用系统提示词:给定原始目标、已完成任务摘要、上下文变更列表、剩余任务,输出结构化决策。
java
public class ReactiveDecisionAgent extends BaseAgent {
CompletableFuture<ReplanDecision> decide(String situationReport);
}LLM 提示词结构:
[原始目标]
[已完成的任务及结果摘要]
[发生了什么变化(ContextChangeEvent 列表)]
[剩余待执行的任务]
[约束:最多还能 replan N 次]
请输出 JSON:
{
"action": "CONTINUE|REPLAN_REMAINING|REPLAN_FULL|ABORT",
"reasoning": "...",
"stepsToCancel": [],
"newContextHints": "...",
"confidence": 0.0
}6. core/PlanningLoopRegistry.java(Spring @Component)
java
@Component
public class PlanningLoopRegistry {
Map<String, PlanningLoopSession> sessions = new ConcurrentHashMap<>();
void register(PlanningLoopSession session);
PlanningLoopSession get(String sessionId);
void remove(String sessionId);
List<PlanningLoopSession> getActiveSessions();
}7. langgraph/PlanningLoopState.java
java
public class PlanningLoopState extends AgentState {
static final String GOAL = "goal";
static final String CURRENT_PLAN = "currentPlan";
static final String PENDING_TASKS = "pendingTasks";
static final String COMPLETED_TASKS = "completedTasks";
static final String CONTEXT_CHANGES = "contextChanges";
static final String LOOP_STATUS = "loopStatus";
static final String REPLAN_COUNT = "replanCount";
static final String LAST_DECISION = "lastDecision";
}修改已有类(4个)
1. context/TaskExecution.java
java
// 新增枚举值
INTERRUPTED, // 任务被中断,携带中断上下文
// 新增字段
Map<String, Object> interruptionContext;
// 新增方法
void interrupt(String reason, Map<String, Object> ctx) {
this.status = TaskStatus.INTERRUPTED;
this.interruptionContext = ctx;
this.interruptionContext.put("reason", reason);
}2. core/OrchestrationAgent.java
java
// 新增字段
String planningLoopSessionId; // 可空,关联 PlanningLoopSession
// 步骤失败分支(在 executeStepsInPhase 中)发布事件:
planningLoop.publishEvent(sessionId, ContextChangeEvent.builder()
.type(TASK_FAILED)
.severity(step.isSkippable() ? WARNING : CRITICAL)
.taskId(step.getStepId())
.description("步骤[" + step.getStepName() + "]执行失败: " + errorMsg)
.build());
// 阶段完成时发布里程碑事件:
planningLoop.publishEvent(sessionId, ContextChangeEvent.builder()
.type(MILESTONE_REACHED)
.severity(INFO)
.description("阶段[" + phase.getPhaseName() + "]完成")
.build());3. core/TaskPlanningAgent.java
java
// 新增方法:局部重规划
CompletableFuture<TaskPlan> replan(
String originalGoal,
List<TaskStep> completedSteps,
List<ContextChangeEvent> contextChanges,
String additionalHints
);
// 说明:生成的 TaskPlan 只包含剩余步骤,已完成步骤作为背景信息注入提示词4. chatbot/AutoPlanningAgent.java
java
// autonomousPlanning() 中:
// 1. 创建 PlanningLoopSession 并注册
PlanningLoopSession session = planningLoop.start(intent, streamHandler);
// 2. 将 sessionId 传给 OrchestrationAgent
orchestrationAgent.setPlanningLoopSessionId(session.getSessionId());
// 3. 执行完毕后清理
session.onComplete(() -> planningLoop.stop(session.getSessionId()));React 触发策略
立即触发(CRITICAL):
- 步骤失败且 skippable=false
- 核心依赖断裂(DEPENDENCY_BROKEN)
积累触发(WARNING,默认 batchReactSize=3):
- 多个非关键步骤失败或结果异常
里程碑触发(INFO):
- 每个 ExecutionPhase 完成后主动评估
- 防止长时间无监控运行
上限保护:
- replanCount >= maxReplans(3) 时,强制 ABORT文件路径汇总
新建(相对于 muotai-ai-biz/src/main/java/com/pig4cloud/pigx/ai/agent/):
context/ContextChangeEvent.java
context/PlanningLoopSession.java
context/ReplanDecision.java
core/PlanningLoop.java ⭐
core/ReactiveDecisionAgent.java
core/PlanningLoopRegistry.java
langgraph/PlanningLoopState.java
修改:
context/TaskExecution.java + INTERRUPTED 状态 + interrupt()
core/OrchestrationAgent.java + 事件发布出口
core/TaskPlanningAgent.java + replan() 方法
chatbot/AutoPlanningAgent.java + 创建/绑定 PlanningLoopSession验证方案
| 场景 | 验证方式 |
|---|---|
| 单步失败 → REPLAN_REMAINING | 构造3步任务,第2步刻意失败,验证第3步被替换为新规划步骤 |
| 正常完成 | 3步全成功,验证 session.status = COMPLETED |
| 达到 replan 上限 | 触发4次失败,验证第4次直接 ABORT |
| 流式通知 | SSE 连接,验证 onReplanRequired(reason, count) 被正确推送 |
| 并发安全 | 多个 session 并发运行,互不干扰 |
| 局部重规划保留历史 | Replan 后已完成步骤不被重复执行,结果进入新计划上下文 |
实现优先级
P0(核心骨架):
ContextChangeEvent + PlanningLoopSession + ReplanDecision
PlanningLoop 基础实现(start/publishEvent/stop)
OrchestrationAgent 事件发布接入
P1(决策能力):
ReactiveDecisionAgent(LLM决策)
PlanningLoop react() + executeReplan()
TaskPlanningAgent.replan() 方法
StreamHandler.onReplanRequired 接入
P2(完善):
PlanningLoopRegistry(多会话管理)
LangGraph PlanningLoopState 状态机
AutoPlanningAgent 集成
配置化触发策略(maxReplans/reactThreshold)