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—状态字段:ultraplanSessionUrl、ultraplanPendingChoice 等
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(新环境变量)— 子代理成本引导机制