Skip to content

常驻规划循环(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共享执行上下文,直接复用
AgentHandoffSystemAgent 间交接,复用 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)