Plan 系统

wescode 中的 Plan 系统——LLM 驱动的线性任务清单,用于管理多步骤任务。


Plan 概念

Plan 生命周期状态机

Plan 是 LLM 驱动的线性任务清单(不是 DAG):

Plan: "重构 UserService"
  1. [done] 分析现有 UserService 接口
  2. [in_progress] 拆分 CRUD 和业务逻辑
  3. [pending] 更新调用方
  4. [pending] 添加测试
  5. [pending] 更新文档

核心原则


Plan 工具

模型通过 plan 工具管理 Plan:

plan(create)

{
  "tool": "plan",
  "action": "create",
  "title": "重构 UserService",
  "steps": [
    "分析现有 UserService 接口",
    "拆分 CRUD 和业务逻辑",
    "更新调用方",
    "添加测试",
    "更新文档"
  ]
}

plan(update)

{
  "tool": "plan",
  "action": "update",
  "task_id": 0,
  "status": "done",
  "result": "UserService 有 12 个公开方法,其中 5 个是纯 CRUD"
}

plan(complete)

{
  "tool": "plan",
  "action": "complete"
}

complete 会收口所有剩余 open 步骤为 done。


PlanStatus 枚举

状态含义允许的迁移
planning初始/草拟→ active
active正在执行→ completed / terminated
completed模型声明完成→ terminated(中断时)
terminated被终止→ completed(恢复后完成)

状态迁移函数

ApplyCompletion(plan)     // → completed(合法迁移)
ApplyTermination(plan, reason)  // → terminated(合法迁移)

TerminationReason

原因触发方式
run_interrupted用户中断 / 异常终止
nudge_exhaustedNudge 次数耗尽
validation_failed验证失败

注意:terminated → completed 是合法的 resume 迁移(恢复后重新完成)。


Plan 步骤状态

状态含义
pending未开始
in_progress正在执行
done已完成
skipped已跳过

实质化验证(INV-PE-15)

步骤标记为 done 需要实质化验证——必须有真实工作发生过:

PlanStep.Substantiated:该步骤在之前的轮次中有非 plan 的工具调用
PlanWorkThisTurn:本轮有非 plan 的工具调用

done 与工作发生过同构:
  → Session ledger 记录每步的 Substantiated 状态
  → execute 开始给全部 open 步骤盖章
  → 盖章对象是全部 open 步骤(禁止 first-pending-only)
  → 记账可以是下一轮

为什么实质化很重要

场景:模型读了几个文件,然后批量 plan(update, done) × 5
  → 如果不验证:模型可以声称完成但实际没做
  → 实质化验证:检查是否真的有非 plan 的工具调用发生过

Plan Nudge 机制

当 Run 准备结束(end_turn)但 Plan 仍有 pending 步骤时,引擎会 nudge 模型继续工作:

Nudge 规则

end_turn + pending 步骤存在 + Plan 未终态
  → 注入提醒消息:继续执行 + 完成即 update 补标
  → 最多 nudge 3 次(maxPlanPendingNudges)
  → 3 次后正常终止,步骤留在 pending

Nudge 内容

nudge 消息包含:
  1. pending 步骤列表(带 0-based task_id 索引)
  2. 引导"继续执行 + 完成即 plan(update, done)"
  3. 显式警告"不要调 plan(complete),它使 pending 无法解决"

Nudge 耗尽

3 次 nudge 用完
  → Run 正常终止
  → 步骤留在 pending(真实进度)
  → 发出 EventPlanNudge(提醒非错误)

Plan 分类

在 CycleDetector 中,plan 工具被分类为 categoryExploration(探索类):

// plan 在 toolCategoryMap 内置分类为 categoryExploration
// 意味着:探索预算 + 进度减半
// 合法的多步更新不会触发 action streak

应用层不得重复声明 ExtraCategories/progressAwareTools(冗余声明 = 同源尾巴)。


Plan 与 CognitiveSettlement

Plan 是 SessionState 的子字段(INV-CTX-46):

Run 结束
  → CognitiveSettlement
    → SessionState 持久化
      ├── 对话摘要
      ├── 关键事实
      └── Plan 状态(steps + status + progress)← 子字段

Plan 状态不独立持久化——随 CognitiveSettlement 一同存储。


Plan 中断与恢复

中断

Run 异常终止时(非 end_turn + 有 pending 步骤):

ApplyTermination(plan, "run_interrupted")
  → PlanStatus = terminated
  → TerminationReason = run_interrupted
  → EventPlanInterrupted 事件发出

恢复

下次 Run 开始时,terminated 状态的 Plan 可以恢复:

新 Run 开始 → 读取上次 SessionState
  → Plan.Status == terminated
  → 模型可以继续执行 pending 步骤
  → plan(complete) → ApplyCompletion → completed

terminated → completed 是合法的 resume 迁移。


Plan 补标(INV-PE-14)

Plan 处于 completed 状态时,仍允许步骤终态补标(done/skipped):

模型先做了多步 read/grep/exec
  → plan(complete)
  → 还有步骤是 pending
  → 可以继续 plan(update, task_id=2, status=done) 补标

PlanTerminated 状态则保持锁定,不允许补标。


最佳实践

  1. 让 AI 自然使用 Plan:不需要手动创建
  2. 关注 Plan 面板:实时查看任务进度
  3. 相信实质化验证:0/N 进度可能只是显示延迟
  4. 理解 Nudge:AI 会被提醒继续,但最终决定权在 AI