Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

第20c章:Ultraplan — 远程多代理规划

定位:本章分析 Claude Code 的远程规划能力——将计划阶段卸载到 CCR 远程容器执行。前置依赖:第20章。适用场景:想了解 CC 远程规划能力(CCR 架构、状态机、传送协议)的读者。

为什么需要 Ultraplan

本章前文描述的多 Agent 编排都是本地的——Agent 在用户终端中运行,占用终端的输入输出,上下文窗口与用户共享。Ultraplan 解决的问题是:把计划阶段卸载到远程,让用户终端保持可用。

维度本地 Plan ModeUltraplan
运行位置本地终端CCR(Claude Code on the web)远程容器
模型当前会话模型强制 Opus 4.6(GrowthBook tengu_ultraplan_model 配置)
探索方式单 Agent 顺序探索可选多 Agent 并行探索(视提示词变体)
超时无硬超时30 分钟(GrowthBook tengu_ultraplan_timeout_seconds,默认 1800)
用户终端被阻塞保持可用,可继续其他工作
结果交付直接在会话中执行"远程执行并创建 PR"或"传送回本地终端执行"
审批终端对话框浏览器 PlanModal

架构总览

Ultraplan 由 5 个核心模块组成:

┌──────────────────────────────────────────────────────────────┐
│                    用户终端(本地)                           │
│                                                              │
│  PromptInput.tsx                processUserInput.ts           │
│  ┌─────────────┐              ┌──────────────────┐           │
│  │ 关键字检测   │─→ 彩虹高亮   │ "ultraplan" 替换  │           │
│  │ + 通知气泡   │              │ → /ultraplan 命令  │          │
│  └─────────────┘              └────────┬─────────┘           │
│                                        ↓                     │
│  commands/ultraplan.tsx ──────────────────────────            │
│  ┌─────────────────────────────────────────────┐             │
│  │ launchUltraplan()                           │             │
│  │  ├─ checkRemoteAgentEligibility()           │             │
│  │  ├─ buildUltraplanPrompt(blurb, seed, id)   │             │
│  │  ├─ teleportToRemote() ──→ CCR 会话创建     │             │
│  │  ├─ registerRemoteAgentTask()               │             │
│  │  └─ startDetachedPoll() ──→ 后台轮询        │             │
│  └───────────────────────────┬─────────────────┘             │
│                              ↓                               │
│  utils/ultraplan/ccrSession.ts                               │
│  ┌─────────────────────────────────────────────┐             │
│  │ pollForApprovedExitPlanMode()               │             │
│  │  ├─ 每 3 秒轮询远程会话事件                  │             │
│  │  ├─ ExitPlanModeScanner.ingest() 状态机     │             │
│  │  └─ 阶段检测: running → needs_input → ready │             │
│  └───────────────────────────┬─────────────────┘             │
│                              ↓                               │
│  任务系统 Pill 显示                                           │
│  ◇ ultraplan (运行中)                                        │
│  ◇ ultraplan needs your input (远程空闲)                      │
│  ◆ ultraplan ready (计划就绪)                                │
└──────────────────────────────────────────────────────────────┘
                               ↕ HTTP 轮询
┌──────────────────────────────────────────────────────────────┐
│                   CCR 远程容器                                │
│                                                              │
│  Opus 4.6 + plan mode 权限                                   │
│  ├─ 探索代码库(Glob/Grep/Read)                              │
│  ├─ 可选:Task 工具生成并行子代理                              │
│  ├─ 调用 ExitPlanMode 提交计划                                │
│  └─ 等待用户审批(批准/拒绝/传送回本地)                       │
└──────────────────────────────────────────────────────────────┘

CCR 是什么——"远程工作中"的含义

架构图中的"CCR 远程容器"是 Claude Code Remote(Claude Code on the web)的缩写,本质上是 Anthropic 服务器上运行的一个完整 Claude Code 实例:

你的终端(本地 CLI 客户端)           Anthropic 云端(CCR 容器)
┌──────────────────────┐            ┌────────────────────────────┐
│ 只负责:              │            │ 运行着:                    │
│ · 打包上传代码库      │──HTTP──→   │ · 完整的 Claude Code 实例   │
│ · 显示任务 Pill       │            │ · Opus 4.6 模型(强制)     │
│ · 每 3 秒轮询状态     │←─轮询──    │ · 你的代码库副本(bundle)  │
│ · 接收最终计划        │            │ · Glob/Grep/Read 等工具    │
│                      │            │ · 可选:多个子代理并行探索   │
│ 你可以继续其他工作    │            │ · Plan mode 权限(只读)    │
└──────────────────────┘            └────────────────────────────┘

CCR 容器通过 teleportToRemote() 创建。启动时,你的代码库被打包上传(bundle),远程端获得完整的代码访问能力。远程 Agent Loop 向 Claude API 发请求,与你本地使用 Claude Code 时完全一致——区别在于它用的是 Opus 4.6 模型、运行在 Anthropic 基础设施上、且不占用你的终端。

用户能做什么

触发方式

  1. 关键字触发——在提示词中自然写出 "ultraplan":
    ultraplan 重构认证模块,需要支持 OAuth2 和 API key 两种方式
    
  2. Slash 命令——显式调用 /ultraplan <描述>

前提条件checkRemoteAgentEligibility() 检查):

  • 已通过 OAuth 登录 Claude Code
  • 订阅级别支持远程 Agent(Pro/Max/Team/Enterprise)
  • Feature Flag ULTRAPLAN 已对账户开启(GrowthBook 服务端控制)

判断是否可用:输入包含 "ultraplan" 的文字后,如果关键字出现彩虹高亮并弹出通知"This prompt will launch an ultraplan session in Claude Code on the web",说明功能已开启。无反应则表示 feature flag 未对你的账户启用。

使用流程

1. 输入包含 "ultraplan" 的提示词
2. 确认启动对话框
3. 终端显示 CCR URL,你可以继续其他工作
4. 任务栏 Pill 显示进度:
   ◇ ultraplan               → 远程探索代码库中
   ◇ ultraplan needs your input → 需要你在浏览器中操作
   ◆ ultraplan ready          → 计划就绪,等待审批
5. 在浏览器中审批计划:
   a. 批准 → 远程执行并创建 Pull Request
   b. 拒绝 + 反馈 → 远程根据反馈修改后重新提交
   c. 传送回本地 → 计划回到你的终端中执行
6. 如需中途停止,通过任务系统取消即可

源码位置

文件行数职责
commands/ultraplan.tsx470主命令:启动、轮询、停止、错误处理
utils/ultraplan/ccrSession.ts350轮询状态机、ExitPlanModeScanner、阶段检测
utils/ultraplan/keyword.ts128关键字检测:触发规则、上下文排除
state/AppStateStore.ts状态字段:ultraplanSessionUrlultraplanPendingChoice
tasks/RemoteAgentTask/远程任务注册和生命周期管理
components/PromptInput/PromptInput.tsx关键字彩虹高亮 + 通知气泡

关键字触发系统

用户不需要输入 /ultraplan——只要在提示词中自然地写出 "ultraplan" 即可触发。

// restored-src/src/utils/ultraplan/keyword.ts
export function findUltraplanTriggerPositions(text: string): TriggerPosition[]
export function hasUltraplanKeyword(text: string): boolean
export function replaceUltraplanKeyword(text: string): string

排除规则——以下上下文中的 "ultraplan" 不会触发:

上下文示例原因
引号/反引号中`ultraplan`代码引用
路径中src/ultraplan/foo.ts文件路径
标识符中--ultraplan-modeCLI 参数
文件扩展名前ultraplan.tsx文件名
问号后ultraplan?询问功能而非触发
/ 开头/ultraplan走 slash 命令路径

触发后,processUserInput.ts 将关键字替换为 /ultraplan {rewritten prompt} 并路由到命令处理器。

状态机:生命周期管理

Ultraplan 使用 5 个 AppState 字段管理生命周期:

// restored-src/src/state/AppStateStore.ts
ultraplanLaunching?: boolean         // 启动中(防止重复启动,~5秒窗口)
ultraplanSessionUrl?: string         // 活跃会话 URL(存在时禁用关键字触发)
ultraplanPendingChoice?: {           // 已审批的计划等待用户选择执行位置
  plan: string
  sessionId: string
  taskId: string
}
ultraplanLaunchPending?: {           // 启动前确认对话框状态
  blurb: string
}
isUltraplanMode?: boolean            // 远程端标志(通过 set_permission_mode 设置)

状态转换图

stateDiagram-v2
    [*] --> IDLE
    IDLE --> LAUNCHING: 用户输入 "ultraplan" 关键字
    LAUNCHING --> RUNNING: teleportToRemote() 成功<br/>设置 ultraplanSessionUrl
    LAUNCHING --> IDLE: 启动失败<br/>(认证/资格/网络)

    RUNNING --> RUNNING: phase=running (远程工作中)
    RUNNING --> NEEDS_INPUT: phase=needs_input (远程空闲)
    RUNNING --> PLAN_READY: phase=plan_ready (ExitPlanMode 已调用)
    NEEDS_INPUT --> RUNNING: 远程恢复工作
    NEEDS_INPUT --> PLAN_READY: ExitPlanMode 调用

    PLAN_READY --> REMOTE_EXEC: 用户在浏览器批准 → 远程执行
    PLAN_READY --> PENDING_CHOICE: 用户拒绝 + TELEPORT_SENTINEL
    PLAN_READY --> RUNNING: 用户拒绝 + 反馈 → 远程修改计划

    REMOTE_EXEC --> IDLE: 任务完成,清除 URL
    PENDING_CHOICE --> IDLE: 用户选择"在本地执行"
    PENDING_CHOICE --> RUNNING: 用户选择"继续远程"

    RUNNING --> IDLE: 超时(30min) / 网络失败(5次) / 用户停止

轮询与阶段检测

startDetachedPoll() 以后台 async IIFE 运行,不阻塞终端:

// restored-src/src/utils/ultraplan/ccrSession.ts

const POLL_INTERVAL_MS = 3000             // 每 3 秒轮询
const MAX_CONSECUTIVE_FAILURES = 5        // 连续 5 次网络错误后放弃
const ULTRAPLAN_TIMEOUT_MS = 30 * 60 * 1000  // 30 分钟超时

ExitPlanModeScanner 是纯无状态事件处理器,从远程会话事件流中提取信号:

// 扫描结果类型
type ScanResult =
  | { kind: 'approved'; plan: string }    // 用户批准(远程执行)
  | { kind: 'teleport'; plan: string }    // 用户拒绝 + 传送标记(本地执行)
  | { kind: 'rejected'; id: string }      // 普通拒绝(修改后重新提交)
  | { kind: 'pending' }                   // ExitPlanMode 已调用,等待审批
  | { kind: 'terminated'; subtype: string } // 会话终止
  | { kind: 'unchanged' }                 // 无新信号

阶段检测逻辑

// 判断远程会话当前阶段
const quietIdle =
  (sessionStatus === 'idle' || sessionStatus === 'requires_action') &&
  newEvents.length === 0

const phase: UltraplanPhase = scanner.hasPendingPlan
  ? 'plan_ready'      // ExitPlanMode 已调用,等待浏览器审批
  : quietIdle
    ? 'needs_input'    // 远程空闲,可能需要用户输入
    : 'running'        // 正常工作中

GrowthBook 驱动的提示词变体(v2.1.91 新增)

v2.1.91 引入了 tengu_ultraplan_prompt_identifier 事件,暴露了一个 GrowthBook 控制的提示词变体系统。从 bundle 中提取到至少 3 种提示词变体:

变体 1: simple_plan(默认) — 轻量单代理规划

You're running in a remote planning session.
Run a lightweight planning process, consistent with how you would
in regular plan mode:
- Explore the codebase directly with Glob, Grep, and Read.
- Do not spawn subagents.
When you've settled on an approach, call ExitPlanMode with the plan.

变体 2: 多代理探索 — 使用 Task 工具生成并行子代理

Produce an exceptionally thorough implementation plan using
multi-agent exploration.
Instructions:
1. Use the Task tool to spawn parallel agents to explore different
   aspects of the codebase simultaneously:
   - One agent to understand the relevant existing code and architecture
   - One agent to find all files that will need modification
   - One agent to identify potential risks, edge cases, and dependencies
2. Synthesize their findings into a detailed, step-by-step plan.
3. Use the Task tool to spawn a critique agent to review the plan.
4. Incorporate the critique feedback, then call ExitPlanMode.

变体切换机制

// v2.1.91 bundle 逆向
function getPromptIdentifier(): string {
  // 从 GrowthBook 读取,默认 "simple_plan"
  let id = getFeatureValue('tengu_ultraplan_prompt_identifier', 'simple_plan')
  return isValidId(id) ? id : 'simple_plan'
}

function getTimeout(): number {
  // 从 GrowthBook 读取,默认 1800 秒 (30 分钟)
  return getFeatureValue('tengu_ultraplan_timeout_seconds', 1800) * 1000
}

这意味着 Anthropic 可以通过 GrowthBook A/B 测试不同的规划策略,而无需发版。tengu_ultraplan_config 事件记录每次启动时使用的具体配置组合。

计划传送协议

当用户在浏览器中拒绝计划但选择"传送回本地终端"时,浏览器在反馈中注入一个哨兵字符串:

const ULTRAPLAN_TELEPORT_SENTINEL = '__ULTRAPLAN_TELEPORT_LOCAL__'

远程端的提示词明确指示模型识别此哨兵:

If the feedback contains __ULTRAPLAN_TELEPORT_LOCAL__, DO NOT implement — the plan has been teleported to the user's local terminal. Respond only with "Plan teleported. Return to your terminal to continue."

本地端的 ExitPlanModeScanner 检测到哨兵后,提取计划文本并设置 ultraplanPendingChoice,弹出选择对话框让用户决定在本地执行还是继续远程。

错误处理矩阵

错误原因码发生时机恢复策略
UltraplanPollErrorterminated远程会话异常终止通知用户 + 归档会话
UltraplanPollErrortimeout_pending30 分钟超时,计划已到 pending通知 + 归档
UltraplanPollErrortimeout_no_plan30 分钟超时,ExitPlanMode 从未调用通知 + 归档
UltraplanPollErrornetwork_or_unknown连续 5 次网络错误通知 + 归档
UltraplanPollErrorstopped用户手动停止提前退出,kill 处理归档
启动错误precondition认证/订阅/资格不足通知用户
启动错误bundle_failBundle 创建失败通知用户
启动错误teleport_null远程会话创建返回 null通知用户
启动错误unexpected_error异常归档孤儿会话 + 清除 URL

遥测事件全景

事件来源版本触发时机关键元数据
tengu_ultraplan_keywordv2.1.88用户输入中检测到关键字
tengu_ultraplan_launchedv2.1.88CCR 会话创建成功has_seed_plan, model, prompt_identifier
tengu_ultraplan_approvedv2.1.88计划被批准duration_ms, plan_length, reject_count, execution_target
tengu_ultraplan_awaiting_inputv2.1.88阶段变为 needs_input
tengu_ultraplan_failedv2.1.88轮询错误duration_ms, reason, reject_count
tengu_ultraplan_create_failedv2.1.88启动失败reason, precondition_errors
tengu_ultraplan_modelv2.1.88GrowthBook 配置名模型 ID(默认 Opus 4.6)
tengu_ultraplan_configv2.1.91启动时记录配置组合模型 + 超时 + 提示词变体
tengu_ultraplan_keywordv2.1.91(复用)增强触发追踪
tengu_ultraplan_prompt_identifierv2.1.91GrowthBook 配置名提示词变体 ID
tengu_ultraplan_stoppedv2.1.91用户手动停止
tengu_ultraplan_timeout_secondsv2.1.91GrowthBook 配置名超时秒数(默认 1800)

模式提炼:远程卸载模式(Remote Offloading Pattern)

Ultraplan 体现了一种可复用的架构模式——远程卸载

本地终端                        远程容器
┌──────────┐                   ┌──────────────┐
│ 快速反馈  │───创建会话──→      │ 长时间运行    │
│ 继续可用  │                   │ 高算力模型    │
│          │←──轮询状态──       │ 多代理并行    │
│ Pill 显示 │                   │              │
│ ◇/◆ 状态 │←──计划就绪──      │ ExitPlanMode │
│          │                   │              │
│ 选择执行  │───批准/传送──→    │ 执行/停止     │
└──────────┘                   └──────────────┘

核心设计决策

  1. 异步分离startDetachedPoll() 以 async IIFE 启动,立即返回用户友好消息,不阻塞终端事件循环
  2. 状态机驱动 UI:三相(running/needs_input/plan_ready)映射到任务 Pill 的视觉状态(◇/◆),用户无需打开浏览器即可感知远程进度
  3. 哨兵协议__ULTRAPLAN_TELEPORT_LOCAL__ 利用工具结果文本作为进程间通信通道——简单但有效
  4. GrowthBook 驱动变体:模型、超时、提示词变体均为远程可配的 feature flag,支持 A/B 测试而无需发版
  5. 孤儿防护:所有错误路径都执行 archiveRemoteSession() 归档,防止 CCR 会话泄漏

子代理增强(v2.1.91)

v2.1.91 还新增了多个子代理相关事件,与 Ultraplan 的多代理策略形成互补:

  • tengu_forked_agent_default_turns_exceeded — 分叉代理超过默认回合限制,触发成本控制
  • tengu_subagent_lean_schema_applied — 子代理使用精简 schema(减少上下文占用)
  • tengu_subagent_md_report_blocked — 子代理尝试生成 CLAUDE.md 报告时被阻断(安全边界)
  • tengu_mcp_subagent_prompt — MCP 子代理的提示词注入追踪
  • CLAUDE_CODE_AGENT_COST_STEER(新环境变量)— 子代理成本引导机制