From 3df1ee7d00203f89f29835830b0e8ea1ea8fab33 Mon Sep 17 00:00:00 2001 From: wenshao Date: Wed, 1 Apr 2026 09:58:08 +0800 Subject: [PATCH 1/7] Add Claude Code Prompt Suggestions deep-dive as standalone doc (+290 lines) New file docs/tools/claude-code/08-prompt-suggestions.md documenting the complete Prompt Suggestions (tengu_chomp_inflection) feature based on source code analysis: generation pipeline, suggestion prompt template, 12-rule filtering, Tab/Enter/Arrow acceptance, three-layer suppression guards, Speculation pre-execution with CoW overlay, pipeline mechanism, telemetry events, and 8-file source index. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../claude-code/08-prompt-suggestions.md | 338 ++++++++++++++++++ 1 file changed, 338 insertions(+) create mode 100644 docs/tools/claude-code/08-prompt-suggestions.md diff --git a/docs/tools/claude-code/08-prompt-suggestions.md b/docs/tools/claude-code/08-prompt-suggestions.md new file mode 100644 index 00000000..bf62a7f1 --- /dev/null +++ b/docs/tools/claude-code/08-prompt-suggestions.md @@ -0,0 +1,338 @@ +# 8. Prompt Suggestions(下一步提示预测) + +> 本文基于源码分析(`services/PromptSuggestion/promptSuggestion.ts` 524 LOC + `services/PromptSuggestion/speculation.ts` 992 LOC + `hooks/usePromptSuggestion.ts` 178 LOC 等共 ~1,700 行),覆盖 suggestion 生成、过滤、交互、遥测和 Speculation 推测执行。 +> +> **功能内部代号**:`tengu_chomp_inflection`(GrowthBook feature flag 名称)。 + +## 8.1 功能概述 + +Claude Code 在每轮 assistant 回复完成后,自动预测用户下一步可能输入的内容,以蓝紫色提示文本显示在输入框中。用户可通过 Tab/Enter 接受,或直接输入覆盖。 + +## 8.2 架构概览 + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ stopHooks.ts │ +│ query/stopHooks.ts#L139 │ +│ 每轮 assistant 回复完成后触发 │ +│ void executePromptSuggestion(stopHookContext) │ +└────────────────────┬────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ executePromptSuggestion() │ +│ services/PromptSuggestion/promptSuggestion.ts#L184 │ +│ 仅处理 querySource === 'repl_main_thread' 的主线程请求 │ +│ │ +│ 1. tryGenerateSuggestion() — 守卫检查 + 生成 + 过滤 │ +│ 2. 写入 AppState.promptSuggestion │ +│ 3. 如果 Speculation 启用 → startSpeculation() │ +└────────────────────┬────────────────────────────────────────────┘ + │ + ┌──────┴──────┐ + ▼ ▼ +┌──────────────────────┐ ┌──────────────────────────────────────┐ +│ usePromptSuggestion │ │ startSpeculation() │ +│ hooks/ │ │ services/PromptSuggestion/ │ +│ usePromptSuggestion │ │ speculation.ts │ +│ .ts │ │ 以 suggestion 为假设输入预执行 agent │ +│ │ │ (仅限 Anthropic 内部用户启用) │ +│ 管理 UI 显示 │ └──────────────────────────────────────┘ +│ Tab/Enter 接受 │ +│ 遥测日志 │ +└──────────────────────┘ +``` + +## 8.3 生成流程 + +### 8.3.1 触发入口 + +每轮 assistant 回复完成后,在 stop hooks 阶段以 fire-and-forget 方式异步发起: + +```typescript +// 源码: query/stopHooks.ts#L138-139 +if (!isEnvDefinedFalsy(process.env.CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION)) { + void executePromptSuggestion(stopHookContext) +} +``` + +`--bare` 模式(`-p` 脚本调用的简化模式)跳过 suggestion 生成。 + +### 8.3.2 API 调用方式 + +源码: `services/PromptSuggestion/promptSuggestion.ts#L294-352` + +调用 `runForkedAgent()` 发起独立的 API 请求: + +- **缓存复用**:复用主对话的 `cacheSafeParams`,刻意不覆盖任何 API 参数(不设 `effortValue`、`maxOutputTokens` 等),以确保命中主对话的 prompt cache +- **独立标记**:`querySource: "prompt_suggestion"`、`forkLabel: "prompt_suggestion"` +- **不写 transcript**:`skipTranscript: true, skipCacheWrite: true` +- **禁止工具**:所有工具调用通过 `canUseTool` 回调拒绝(`behavior: "deny"`),模型只能返回纯文本 + +> **历史教训**:PR #18143 尝试设置 `effort:'low'` 降低 suggestion 成本,结果导致 cache 命中率从 92.7% 暴跌至 61%(45x cache write spike)。billing cache key 包含的参数比文档描述的更多,任何差异都会 bust cache。(源码注释: `promptSuggestion.ts#L308-318`) + +### 8.3.3 Suggestion Prompt + +源码: `services/PromptSuggestion/promptSuggestion.ts#L258-287`,常量 `SUGGESTION_PROMPT` + +``` +[SUGGESTION MODE: Suggest what the user might naturally type next into Claude Code.] + +FIRST: Look at the user's recent messages and original request. + +Your job is to predict what THEY would type - not what you think they should do. + +THE TEST: Would they think "I was just about to type that"? + +EXAMPLES: +User asked "fix the bug and run tests", bug is fixed → "run the tests" +After code written → "try it out" +Claude offers options → suggest the one the user would likely pick, based on conversation +Claude asks to continue → "yes" or "go ahead" +Task complete, obvious follow-up → "commit this" or "push it" +After error or misunderstanding → silence (let them assess/correct) + +Be specific: "run the tests" beats "continue". + +NEVER SUGGEST: +- Evaluative ("looks good", "thanks") +- Questions ("what about...?") +- Claude-voice ("Let me...", "I'll...", "Here's...") +- New ideas they didn't ask about +- Multiple sentences + +Stay silent if the next step isn't obvious from what the user said. + +Format: 2-12 words, match the user's style. Or nothing. + +Reply with ONLY the suggestion, no quotes or explanation. +``` + +Prompt 通过 `PromptVariant` 类型索引(源码: `promptSuggestion.ts#L31-35`),当前仅有 `'user_intent'` 和 `'stated_intent'` 两个变体,均映射到同一模板。 + +## 8.4 过滤机制 + +源码: `services/PromptSuggestion/promptSuggestion.ts#L354-456`,函数 `shouldFilterSuggestion` + +生成的 suggestion 经过 12 条过滤规则严格筛选,不满足条件的被静默丢弃: + +| 过滤规则 | 说明 | 匹配示例 | +|----------|------|----------| +| `done` | 内容恰好为 "done" | `done` | +| `meta_text` | 模型输出元描述而非真实预测 | "nothing to suggest"、"silence"、"nothing found" | +| `meta_wrapped` | 被括号包裹的元推理 | `(silence — ...)`、`[no suggestion]` | +| `error_message` | API 错误信息泄漏 | "api error: ..."、"prompt is too long"、"image was too large" | +| `prefixed_label` | 带 `word: ` 标签前缀 | "Next step: run tests" | +| `too_few_words` | 少于 2 个单词(允许斜杠命令和特定单词) | 单个普通单词(非白名单词) | +| `too_many_words` | 超过 12 个单词 | 过长的句子 | +| `too_long` | ≥100 个字符 | — | +| `multiple_sentences` | 包含多个句子(`/[.!?]\s+[A-Z]/`) | "Do this. Then that." | +| `has_formatting` | 包含换行符或 Markdown 格式 | 含 `\n`、`*`、`**` | +| `evaluative` | 评价性/感谢语句 | "looks good"、"thanks"、"perfect"、"awesome" | +| `claude_voice` | 模型自身语气开头 | "Let me..."、"I'll..."、"Here's..."、"You should..." | + +**单词白名单**(源码: `promptSuggestion.ts#L403-424`,即使只有 1 个单词也不过滤): + +| 类别 | 单词 | +|------|------| +| 肯定词 | yes, yeah, yep, yea, yup, sure, ok, okay | +| 动作词 | push, commit, deploy, stop, continue, check, exit, quit | +| 否定词 | no | + +## 8.5 交互方式 + +| 操作 | 效果 | 遥测 `acceptMethod` | +|------|------|---------------------| +| **Tab** | 接受 suggestion 填入输入框(可继续编辑后再提交) | `tab` | +| **Enter**(输入框为空时) | 接受 suggestion 并直接提交 | `enter` | +| **→**(右箭头) | 接受 suggestion 填入输入框 | — | +| 开始输入其他内容 | suggestion 自动消失,Speculation 被中止 | — | +| 忽略(直接输入新内容提交) | suggestion 在下一轮对话后被新预测替换 | `ignored` | + +接受判定逻辑(源码: `hooks/usePromptSuggestion.ts#L116-117`): +- Tab 按下:`acceptedAt > shownAt` +- 或:用户最终提交内容 === suggestion 文本(空 Enter 场景) + +## 8.6 状态数据结构 + +源码: `state/AppStateStore.ts#L385-393` + +```typescript +promptSuggestion: { + text: string | null // suggestion 文本内容 + promptId: 'user_intent' | 'stated_intent' | null // prompt 变体标识 + shownAt: number // 首次渲染时间戳(用于计算 timeToAcceptMs) + acceptedAt: number // Tab 接受时间戳 + generationRequestId: string | null // 关联的 API 请求 ID(用于 RL 数据集关联) +} +``` + +## 8.7 抑制条件(三层守卫) + +### 8.7.1 初始化守卫 + +源码: `promptSuggestion.ts#L37-94`,函数 `shouldEnablePromptSuggestion` + +| 检查顺序 | 条件 | 结果 | +|----------|------|------| +| 1 | 环境变量 `CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION` 为 falsy | 禁用 | +| 2 | 环境变量 `CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION` 为 truthy | 启用 | +| 3 | GrowthBook flag `tengu_chomp_inflection` 为 false | 禁用 | +| 4 | 非交互模式(`-p`、管道输入、SDK) | 禁用 | +| 5 | Swarm teammate(非 leader) | 禁用 | +| 6 | `settings.promptSuggestionEnabled !== false` | 按设置值 | + +### 8.7.2 运行时守卫 + +源码: `promptSuggestion.ts#L107-119`,函数 `getSuggestionSuppressReason` + +| 条件 | 抑制原因 | +|------|----------| +| `promptSuggestionEnabled === false` | `disabled` | +| 存在待审批的 Worker/Sandbox 权限请求 | `pending_permission` | +| MCP elicitation 队列非空 | `elicitation_active` | +| Plan mode 激活 | `plan_mode` | +| 外部用户且速率限制触发 | `rate_limit` | + +### 8.7.3 生成前守卫 + +源码: `promptSuggestion.ts#L125-182`,函数 `tryGenerateSuggestion` + +| 条件 | 抑制原因 | +|------|----------| +| AbortController 已中止 | `aborted` | +| assistant 回复不足 2 轮 | `early_conversation` | +| 上一条回复是 API 错误 | `last_response_error` | +| 上一条回复未缓存 token 数 > 10,000 | `cache_cold` | + +## 8.8 配置方式 + +| 方式 | 说明 | +|------|------| +| `/config` → "Prompt suggestions" | 交互式配置菜单中切换开关 | +| `settings.json` 中设置 `"promptSuggestionEnabled": false` | 持久化关闭 | +| 环境变量 `CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION=0` | 强制关闭(优先级最高) | +| 环境变量 `CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION=1` | 强制开启(优先级最高) | +| GrowthBook feature flag `tengu_chomp_inflection` | 服务端灰度发布控制 | + +## 8.9 UI 样式 + +suggestion 文本使用主题中的 `suggestion` 颜色渲染: + +| 主题 | 颜色 | +|------|------| +| Light | `rgb(87, 105, 247)`(蓝紫色) | +| Dark | `rgb(177, 185, 249)`(浅蓝紫色) | +| ANSI Light | `ansi:blue` | +| ANSI Dark | `ansi:blueBright` | + +## 8.10 遥测事件 + +### 8.10.1 初始化事件 + +事件名: `tengu_prompt_suggestion_init`(源码: `promptSuggestion.ts#L41-92`) + +| 字段 | 说明 | +|------|------| +| `enabled` | 是否启用 | +| `source` | 决策来源:`env` / `growthbook` / `non_interactive` / `swarm_teammate` / `setting` | + +### 8.10.2 结果事件 + +事件名: `tengu_prompt_suggestion`(源码: `hooks/usePromptSuggestion.ts#L120-157`、`promptSuggestion.ts#L462-523`) + +| 字段 | 说明 | +|------|------| +| `source` | `cli`(TUI)或 `sdk`(API 消费方) | +| `outcome` | `accepted` / `ignored` / `suppressed` | +| `prompt_id` | `user_intent` / `stated_intent` | +| `reason` | 抑制原因(仅 suppressed 时) | +| `acceptMethod` | `tab` / `enter`(仅 CLI 且 accepted 时) | +| `timeToAcceptMs` | 从显示到接受的毫秒数 | +| `timeToIgnoreMs` | 从显示到忽略的毫秒数 | +| `timeToFirstKeystrokeMs` | 从显示到首次按键的毫秒数 | +| `wasFocusedWhenShown` | suggestion 出现时终端是否有焦点 | +| `similarity` | `finalInput.length / suggestion.length`(相似度) | + +> Anthropic 内部用户(`USER_TYPE === 'ant'`)额外记录 `suggestion` 和 `userInput` 原文,用于 RL 数据集训练。 + +## 8.11 Speculation(推测执行) + +Prompt Suggestions 是更深层 **Speculation** 系统的触发器。当 suggestion 生成后,系统立即使用该 suggestion 作为假设的用户输入,预执行一轮 agent 响应。 + +### 8.11.1 启用条件 + +源码: `speculation.ts#L337-343` + +```typescript +export function isSpeculationEnabled(): boolean { + const enabled = + process.env.USER_TYPE === 'ant' && + (getGlobalConfig().speculationEnabled ?? true) + return enabled +} +``` + +> **注意**:Speculation 仅对 Anthropic 内部用户启用(`USER_TYPE === 'ant'`),外部用户仅使用 Prompt Suggestions 文本预测功能。 + +### 8.11.2 核心参数 + +源码: `services/PromptSuggestion/speculation.ts#L58-70` + +```typescript +const MAX_SPECULATION_TURNS = 20 // 最大推测轮数 +const MAX_SPECULATION_MESSAGES = 100 // 最大消息数 + +// 允许在推测中执行的工具 +const WRITE_TOOLS = new Set(['Edit', 'Write', 'NotebookEdit']) +const SAFE_READ_ONLY_TOOLS = new Set([ + 'Read', 'Glob', 'Grep', 'ToolSearch', 'LSP', 'TaskGet', 'TaskList' +]) +``` + +### 8.11.3 文件隔离机制(Copy-on-Write Overlay) + +源码: `speculation.ts#L80-81, #L402-715` + +- 推测执行在独立目录中进行:`$CLAUDE_TEMP_DIR/speculation/{pid}/{id}/` +- 写操作使用 Copy-on-Write:首次写入时将原文件复制到 overlay 目录,后续读写均重定向到 overlay +- CWD 外的写操作被拒绝 +- 接受时:overlay 文件复制回主目录(`copyOverlayToMain`);中止时:overlay 直接删除(`safeRemoveOverlay`) + +### 8.11.4 边界检测 + +`CompletionBoundary` 类型(源码: `state/AppStateStore.ts#L41-50`): + +| 边界类型 | 触发条件 | 行为 | +|----------|----------|------| +| `complete` | agent 自然完成 | 记录 `outputTokens` | +| `bash` | 非只读 Bash 命令 | 中止推测 | +| `edit` | 文件编辑但权限不足(非 `acceptEdits`/`bypassPermissions` 模式) | 中止推测 | +| `denied_tool` | 不在允许列表中的工具 | 中止推测 | + +### 8.11.5 Pipeline 机制 + +源码: `speculation.ts#L345-400`,函数 `generatePipelinedSuggestion` + +推测执行完成后,如果用户尚未做出响应,会立即生成下一轮 suggestion。当用户接受当前 suggestion 时,pipelined suggestion 被提升为新的 suggestion 显示,并启动新一轮 speculation,形成连续的预测→预执行→预测链。 + +``` +用户发送消息 → Claude 回复 + → 生成 suggestion A → 开始 speculation A + → speculation A 完成 → 生成 pipelined suggestion B + → 用户接受 A → 提升 B 为当前 suggestion → 开始 speculation B + → ... +``` + +## 8.12 源码文件索引 + +| 文件 | LOC | 职责 | +|------|-----|------| +| `services/PromptSuggestion/promptSuggestion.ts` | 524 | 核心服务:启用检查、生成、过滤、遥测 | +| `services/PromptSuggestion/speculation.ts` | 992 | 推测执行:overlay 隔离、边界检测、pipeline | +| `hooks/usePromptSuggestion.ts` | 178 | React Hook:UI 状态管理、接受/显示/遥测 | +| `components/PromptInput/PromptInput.tsx` | — | 输入框组件:集成 suggestion 显示与 Enter 接受 | +| `components/PromptInput/useTypeahead.tsx` | — | Tab/→ 键接受与 ghost text 渲染 | +| `state/AppStateStore.ts` | — | 状态定义:`promptSuggestion` + `speculation` + `speculationSessionTimeSavedMs` | +| `query/stopHooks.ts` | — | 入口:在 stop hooks 中 fire-and-forget 调用 | +| `components/Settings/Config.tsx` | — | `/config` 菜单中的开关切换 | From 5f3f954614765ac32a78ee54deb580c5e25b87b2 Mon Sep 17 00:00:00 2001 From: wenshao Date: Wed, 1 Apr 2026 10:04:57 +0800 Subject: [PATCH 2/7] Rename to 10-prompt-suggestions.md (avoid numbering conflict with 08/09) Co-Authored-By: Claude Opus 4.6 (1M context) --- ...uggestions.md => 10-prompt-suggestions.md} | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) rename docs/tools/claude-code/{08-prompt-suggestions.md => 10-prompt-suggestions.md} (95%) diff --git a/docs/tools/claude-code/08-prompt-suggestions.md b/docs/tools/claude-code/10-prompt-suggestions.md similarity index 95% rename from docs/tools/claude-code/08-prompt-suggestions.md rename to docs/tools/claude-code/10-prompt-suggestions.md index bf62a7f1..a7304c4a 100644 --- a/docs/tools/claude-code/08-prompt-suggestions.md +++ b/docs/tools/claude-code/10-prompt-suggestions.md @@ -1,14 +1,14 @@ -# 8. Prompt Suggestions(下一步提示预测) +# 10. Prompt Suggestions(下一步提示预测) > 本文基于源码分析(`services/PromptSuggestion/promptSuggestion.ts` 524 LOC + `services/PromptSuggestion/speculation.ts` 992 LOC + `hooks/usePromptSuggestion.ts` 178 LOC 等共 ~1,700 行),覆盖 suggestion 生成、过滤、交互、遥测和 Speculation 推测执行。 > > **功能内部代号**:`tengu_chomp_inflection`(GrowthBook feature flag 名称)。 -## 8.1 功能概述 +## 功能概述 Claude Code 在每轮 assistant 回复完成后,自动预测用户下一步可能输入的内容,以蓝紫色提示文本显示在输入框中。用户可通过 Tab/Enter 接受,或直接输入覆盖。 -## 8.2 架构概览 +## 架构概览 ``` ┌─────────────────────────────────────────────────────────────────┐ @@ -43,9 +43,9 @@ Claude Code 在每轮 assistant 回复完成后,自动预测用户下一步可 └──────────────────────┘ ``` -## 8.3 生成流程 +## 生成流程 -### 8.3.1 触发入口 +### 触发入口 每轮 assistant 回复完成后,在 stop hooks 阶段以 fire-and-forget 方式异步发起: @@ -58,7 +58,7 @@ if (!isEnvDefinedFalsy(process.env.CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION)) { `--bare` 模式(`-p` 脚本调用的简化模式)跳过 suggestion 生成。 -### 8.3.2 API 调用方式 +### API 调用方式 源码: `services/PromptSuggestion/promptSuggestion.ts#L294-352` @@ -71,7 +71,7 @@ if (!isEnvDefinedFalsy(process.env.CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION)) { > **历史教训**:PR #18143 尝试设置 `effort:'low'` 降低 suggestion 成本,结果导致 cache 命中率从 92.7% 暴跌至 61%(45x cache write spike)。billing cache key 包含的参数比文档描述的更多,任何差异都会 bust cache。(源码注释: `promptSuggestion.ts#L308-318`) -### 8.3.3 Suggestion Prompt +### Suggestion Prompt 源码: `services/PromptSuggestion/promptSuggestion.ts#L258-287`,常量 `SUGGESTION_PROMPT` @@ -110,7 +110,7 @@ Reply with ONLY the suggestion, no quotes or explanation. Prompt 通过 `PromptVariant` 类型索引(源码: `promptSuggestion.ts#L31-35`),当前仅有 `'user_intent'` 和 `'stated_intent'` 两个变体,均映射到同一模板。 -## 8.4 过滤机制 +## 过滤机制 源码: `services/PromptSuggestion/promptSuggestion.ts#L354-456`,函数 `shouldFilterSuggestion` @@ -139,7 +139,7 @@ Prompt 通过 `PromptVariant` 类型索引(源码: `promptSuggestion.ts#L31-35 | 动作词 | push, commit, deploy, stop, continue, check, exit, quit | | 否定词 | no | -## 8.5 交互方式 +## 交互方式 | 操作 | 效果 | 遥测 `acceptMethod` | |------|------|---------------------| @@ -153,7 +153,7 @@ Prompt 通过 `PromptVariant` 类型索引(源码: `promptSuggestion.ts#L31-35 - Tab 按下:`acceptedAt > shownAt` - 或:用户最终提交内容 === suggestion 文本(空 Enter 场景) -## 8.6 状态数据结构 +## 状态数据结构 源码: `state/AppStateStore.ts#L385-393` @@ -167,9 +167,9 @@ promptSuggestion: { } ``` -## 8.7 抑制条件(三层守卫) +## 抑制条件(三层守卫) -### 8.7.1 初始化守卫 +### 初始化守卫 源码: `promptSuggestion.ts#L37-94`,函数 `shouldEnablePromptSuggestion` @@ -182,7 +182,7 @@ promptSuggestion: { | 5 | Swarm teammate(非 leader) | 禁用 | | 6 | `settings.promptSuggestionEnabled !== false` | 按设置值 | -### 8.7.2 运行时守卫 +### 运行时守卫 源码: `promptSuggestion.ts#L107-119`,函数 `getSuggestionSuppressReason` @@ -194,7 +194,7 @@ promptSuggestion: { | Plan mode 激活 | `plan_mode` | | 外部用户且速率限制触发 | `rate_limit` | -### 8.7.3 生成前守卫 +### 生成前守卫 源码: `promptSuggestion.ts#L125-182`,函数 `tryGenerateSuggestion` @@ -205,7 +205,7 @@ promptSuggestion: { | 上一条回复是 API 错误 | `last_response_error` | | 上一条回复未缓存 token 数 > 10,000 | `cache_cold` | -## 8.8 配置方式 +## 配置方式 | 方式 | 说明 | |------|------| @@ -215,7 +215,7 @@ promptSuggestion: { | 环境变量 `CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION=1` | 强制开启(优先级最高) | | GrowthBook feature flag `tengu_chomp_inflection` | 服务端灰度发布控制 | -## 8.9 UI 样式 +## UI 样式 suggestion 文本使用主题中的 `suggestion` 颜色渲染: @@ -226,9 +226,9 @@ suggestion 文本使用主题中的 `suggestion` 颜色渲染: | ANSI Light | `ansi:blue` | | ANSI Dark | `ansi:blueBright` | -## 8.10 遥测事件 +## 遥测事件 -### 8.10.1 初始化事件 +### 初始化事件 事件名: `tengu_prompt_suggestion_init`(源码: `promptSuggestion.ts#L41-92`) @@ -237,7 +237,7 @@ suggestion 文本使用主题中的 `suggestion` 颜色渲染: | `enabled` | 是否启用 | | `source` | 决策来源:`env` / `growthbook` / `non_interactive` / `swarm_teammate` / `setting` | -### 8.10.2 结果事件 +### 结果事件 事件名: `tengu_prompt_suggestion`(源码: `hooks/usePromptSuggestion.ts#L120-157`、`promptSuggestion.ts#L462-523`) @@ -256,11 +256,11 @@ suggestion 文本使用主题中的 `suggestion` 颜色渲染: > Anthropic 内部用户(`USER_TYPE === 'ant'`)额外记录 `suggestion` 和 `userInput` 原文,用于 RL 数据集训练。 -## 8.11 Speculation(推测执行) +## Speculation(推测执行) Prompt Suggestions 是更深层 **Speculation** 系统的触发器。当 suggestion 生成后,系统立即使用该 suggestion 作为假设的用户输入,预执行一轮 agent 响应。 -### 8.11.1 启用条件 +### 启用条件 源码: `speculation.ts#L337-343` @@ -275,7 +275,7 @@ export function isSpeculationEnabled(): boolean { > **注意**:Speculation 仅对 Anthropic 内部用户启用(`USER_TYPE === 'ant'`),外部用户仅使用 Prompt Suggestions 文本预测功能。 -### 8.11.2 核心参数 +### 核心参数 源码: `services/PromptSuggestion/speculation.ts#L58-70` @@ -290,7 +290,7 @@ const SAFE_READ_ONLY_TOOLS = new Set([ ]) ``` -### 8.11.3 文件隔离机制(Copy-on-Write Overlay) +### 文件隔离机制(Copy-on-Write Overlay) 源码: `speculation.ts#L80-81, #L402-715` @@ -299,7 +299,7 @@ const SAFE_READ_ONLY_TOOLS = new Set([ - CWD 外的写操作被拒绝 - 接受时:overlay 文件复制回主目录(`copyOverlayToMain`);中止时:overlay 直接删除(`safeRemoveOverlay`) -### 8.11.4 边界检测 +### 边界检测 `CompletionBoundary` 类型(源码: `state/AppStateStore.ts#L41-50`): @@ -310,7 +310,7 @@ const SAFE_READ_ONLY_TOOLS = new Set([ | `edit` | 文件编辑但权限不足(非 `acceptEdits`/`bypassPermissions` 模式) | 中止推测 | | `denied_tool` | 不在允许列表中的工具 | 中止推测 | -### 8.11.5 Pipeline 机制 +### Pipeline 机制 源码: `speculation.ts#L345-400`,函数 `generatePipelinedSuggestion` @@ -324,7 +324,7 @@ const SAFE_READ_ONLY_TOOLS = new Set([ → ... ``` -## 8.12 源码文件索引 +## 源码文件索引 | 文件 | LOC | 职责 | |------|-----|------| From 84d8d8f898016eb2b3dd32291797d0930880995a Mon Sep 17 00:00:00 2001 From: wenshao Date: Wed, 1 Apr 2026 10:07:57 +0800 Subject: [PATCH 3/7] Add 10-prompt-suggestions to README.md index Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/tools/claude-code/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/tools/claude-code/README.md b/docs/tools/claude-code/README.md index e0f0478d..f70fb6b9 100644 --- a/docs/tools/claude-code/README.md +++ b/docs/tools/claude-code/README.md @@ -13,3 +13,4 @@ | [07-会话与记忆](./07-session.md) | 会话存储、上下文压缩(5 层)、CLAUDE.md 记忆、团队同步、Worktree、文件检查点、MCP | | [08-Remote Control](./08-remote-control.md) | 远程控制架构、会话生命周期、安全纵深、评价优缺点、7 款竞品对比 | | [09-多代理系统](./09-multi-agent.md) | Leader-Worker 协作、Swarm 三后端、Agent 定义、邮箱通信、任务管理、协调模式、远程传送 | +| [10-Prompt Suggestions](./10-prompt-suggestions.md) | 下一步提示预测:生成流程、Prompt 模板、12 条过滤规则、Speculation 推测执行 | From 27e7a610c8bfc94332e74be14c320f2635381a67 Mon Sep 17 00:00:00 2001 From: wenshao Date: Wed, 1 Apr 2026 10:11:37 +0800 Subject: [PATCH 4/7] Address review feedback: add source disclaimer, fix --bare vs -p, fix PR ref MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add "数据来源" disclaimer clarifying source paths are from Claude Code app source (obtained via SEA binary decompilation), not this repo - Fix --bare description: it's a minimal mode, not an alias for -p - Remove bare "PR #18143" reference, rephrase as source code comment citation Co-Authored-By: Claude Opus 4.6 (1M context) --- docs/tools/claude-code/10-prompt-suggestions.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/tools/claude-code/10-prompt-suggestions.md b/docs/tools/claude-code/10-prompt-suggestions.md index a7304c4a..f626ddb3 100644 --- a/docs/tools/claude-code/10-prompt-suggestions.md +++ b/docs/tools/claude-code/10-prompt-suggestions.md @@ -1,6 +1,8 @@ # 10. Prompt Suggestions(下一步提示预测) -> 本文基于源码分析(`services/PromptSuggestion/promptSuggestion.ts` 524 LOC + `services/PromptSuggestion/speculation.ts` 992 LOC + `hooks/usePromptSuggestion.ts` 178 LOC 等共 ~1,700 行),覆盖 suggestion 生成、过滤、交互、遥测和 Speculation 推测执行。 +> 本文基于 Claude Code v2.1.89 源码分析(`services/PromptSuggestion/promptSuggestion.ts` 524 LOC + `services/PromptSuggestion/speculation.ts` 992 LOC + `hooks/usePromptSuggestion.ts` 178 LOC 等共 ~1,700 行),覆盖 suggestion 生成、过滤、交互、遥测和 Speculation 推测执行。 +> +> **数据来源**:文中所有源码路径和行号均引用自 Claude Code 应用源码(非本仓库文件),通过反编译 SEA 二进制获得。源码行数基于 TypeScript 文件的 `wc -l` 统计。 > > **功能内部代号**:`tengu_chomp_inflection`(GrowthBook feature flag 名称)。 @@ -56,7 +58,7 @@ if (!isEnvDefinedFalsy(process.env.CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION)) { } ``` -`--bare` 模式(`-p` 脚本调用的简化模式)跳过 suggestion 生成。 +`--bare` 模式(最小化模式,跳过 hooks、LSP、插件同步等)和 `-p`(非交互管道模式)均跳过 suggestion 生成。 ### API 调用方式 @@ -69,7 +71,7 @@ if (!isEnvDefinedFalsy(process.env.CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION)) { - **不写 transcript**:`skipTranscript: true, skipCacheWrite: true` - **禁止工具**:所有工具调用通过 `canUseTool` 回调拒绝(`behavior: "deny"`),模型只能返回纯文本 -> **历史教训**:PR #18143 尝试设置 `effort:'low'` 降低 suggestion 成本,结果导致 cache 命中率从 92.7% 暴跌至 61%(45x cache write spike)。billing cache key 包含的参数比文档描述的更多,任何差异都会 bust cache。(源码注释: `promptSuggestion.ts#L308-318`) +> **历史教训**:据源码注释(`promptSuggestion.ts#L308-318`),Anthropic 内部曾尝试设置 `effort:'low'` 降低 suggestion 成本,结果导致 cache 命中率从 92.7% 暴跌至 61%(45x cache write spike)。billing cache key 包含的参数比文档描述的更多,任何差异都会 bust cache。 ### Suggestion Prompt From 988465576715ad913840251528733fe0c34e8426 Mon Sep 17 00:00:00 2001 From: wenshao Date: Wed, 1 Apr 2026 10:18:11 +0800 Subject: [PATCH 5/7] Address review: clarify env var three-state logic and timestamp default values - Expand env var table to show three states: explicit falsy, explicit truthy, and unset/empty (falls through to subsequent checks) - Add env var parsing note citing utils/envUtils.ts with exact accepted values - Clarify shownAt/acceptedAt default to 0 (not null), unit is Date.now() ms, and acceptance check uses acceptedAt > shownAt Co-Authored-By: Claude Opus 4.6 (1M context) --- .../claude-code/10-prompt-suggestions.md | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/docs/tools/claude-code/10-prompt-suggestions.md b/docs/tools/claude-code/10-prompt-suggestions.md index f626ddb3..c98f1cff 100644 --- a/docs/tools/claude-code/10-prompt-suggestions.md +++ b/docs/tools/claude-code/10-prompt-suggestions.md @@ -161,14 +161,16 @@ Prompt 通过 `PromptVariant` 类型索引(源码: `promptSuggestion.ts#L31-35 ```typescript promptSuggestion: { - text: string | null // suggestion 文本内容 - promptId: 'user_intent' | 'stated_intent' | null // prompt 变体标识 - shownAt: number // 首次渲染时间戳(用于计算 timeToAcceptMs) - acceptedAt: number // Tab 接受时间戳 + text: string | null // suggestion 文本内容,无 suggestion 时为 null + promptId: 'user_intent' | 'stated_intent' | null // prompt 变体标识,无 suggestion 时为 null + shownAt: number // 首次渲染时间戳(Date.now() ms),未显示时为 0 + acceptedAt: number // Tab 接受时间戳(Date.now() ms),未接受时为 0 generationRequestId: string | null // 关联的 API 请求 ID(用于 RL 数据集关联) } ``` +> **默认值语义**:`shownAt` 和 `acceptedAt` 均为 `number` 类型,以 `0` 表示「未触发」。时间戳单位为 `Date.now()` 返回的毫秒(ms since Unix epoch)。接受判定使用 `acceptedAt > shownAt`(源码: `usePromptSuggestion.ts#L116`),因此 `0 > 0` 为 false 即表示未接受。每次新 suggestion 写入时,两个字段均重置为 `0`。 + ## 抑制条件(三层守卫) ### 初始化守卫 @@ -177,12 +179,15 @@ promptSuggestion: { | 检查顺序 | 条件 | 结果 | |----------|------|------| -| 1 | 环境变量 `CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION` 为 falsy | 禁用 | -| 2 | 环境变量 `CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION` 为 truthy | 启用 | -| 3 | GrowthBook flag `tengu_chomp_inflection` 为 false | 禁用 | -| 4 | 非交互模式(`-p`、管道输入、SDK) | 禁用 | -| 5 | Swarm teammate(非 leader) | 禁用 | -| 6 | `settings.promptSuggestionEnabled !== false` | 按设置值 | +| 1 | 环境变量显式为 falsy(`0`/`false`/`no`/`off`) | 强制禁用 | +| 2 | 环境变量显式为 truthy(`1`/`true`/`yes`/`on`) | 强制启用 | +| 3 | 环境变量未设置或空字符串 → 进入后续判定 | — | +| 4 | GrowthBook flag `tengu_chomp_inflection` 为 false | 禁用 | +| 5 | 非交互模式(`-p`、管道输入、SDK) | 禁用 | +| 6 | Swarm teammate(非 leader) | 禁用 | +| 7 | `settings.promptSuggestionEnabled !== false` | 按设置值 | + +> **环境变量解析**(源码: `utils/envUtils.ts#L32-47`):`isEnvDefinedFalsy()` 仅在变量已设置且值为 `0`/`false`/`no`/`off`(不区分大小写)时返回 true;`isEnvTruthy()` 仅在值为 `1`/`true`/`yes`/`on` 时返回 true。变量未设置(`undefined`)或空字符串时两者均返回 false,继续进入 GrowthBook 等后续判定。 ### 运行时守卫 From 8aa4657689ad47657a2f59c09e38fbb6ea68fcfc Mon Sep 17 00:00:00 2001 From: wenshao Date: Wed, 1 Apr 2026 10:31:47 +0800 Subject: [PATCH 6/7] Address qwen3.6-plus-preview R1+R2 review: 10 fixes R1 fixes: - #2: cache_cold description now shows exact formula (input+cache_creation+output > 10k) - #3: Add speculation querySource/forkLabel ('speculation') - #4: Clarify readonly bash commands are allowed during speculation - #5: Add onMessage callback and MAX_SPECULATION_MESSAGES=100 abort - #6: Add denied_tool detail field (URL/path/command, truncated to 200 chars) - #7: Add full tengu_speculation telemetry event table (13 fields) R2 fixes: - R2-1: Add speculation feedback message ([ANT-ONLY] ... saved) - R2-2: Add prepareMessagesForInjection() cleaning rules (5 steps) - R2-3: Add file state cache merge after speculation accept - R2-4: Add speculation-accept transcript entry Co-Authored-By: Claude Opus 4.6 (1M context) --- .../claude-code/10-prompt-suggestions.md | 51 +++++++++++++++++-- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/docs/tools/claude-code/10-prompt-suggestions.md b/docs/tools/claude-code/10-prompt-suggestions.md index c98f1cff..fc51b417 100644 --- a/docs/tools/claude-code/10-prompt-suggestions.md +++ b/docs/tools/claude-code/10-prompt-suggestions.md @@ -210,7 +210,7 @@ promptSuggestion: { | AbortController 已中止 | `aborted` | | assistant 回复不足 2 轮 | `early_conversation` | | 上一条回复是 API 错误 | `last_response_error` | -| 上一条回复未缓存 token 数 > 10,000 | `cache_cold` | +| 父消息总 token 数(`input_tokens + cache_creation_input_tokens + output_tokens`)> 10,000 | `cache_cold` | ## 配置方式 @@ -313,9 +313,11 @@ const SAFE_READ_ONLY_TOOLS = new Set([ | 边界类型 | 触发条件 | 行为 | |----------|----------|------| | `complete` | agent 自然完成 | 记录 `outputTokens` | -| `bash` | 非只读 Bash 命令 | 中止推测 | +| `bash` | 非只读 Bash 命令(只读命令如 `ls`/`grep`/`cat` 允许执行) | 中止推测 | | `edit` | 文件编辑但权限不足(非 `acceptEdits`/`bypassPermissions` 模式) | 中止推测 | -| `denied_tool` | 不在允许列表中的工具 | 中止推测 | +| `denied_tool` | 不在允许列表中的工具(记录 `detail`: URL/路径/命令,截取前 200 字符) | 中止推测 | + +Speculation 通过 `onMessage` 回调实时追踪消息数量,达到 `MAX_SPECULATION_MESSAGES`(100)时自动中止,防止 speculation 无限运行(源码: `speculation.ts#L637-641`)。 ### Pipeline 机制 @@ -331,6 +333,49 @@ const SAFE_READ_ONLY_TOOLS = new Set([ → ... ``` +Speculation 使用独立的 fork 标签:`querySource: 'speculation'`、`forkLabel: 'speculation'`(源码: `speculation.ts#L633-634`),与 suggestion 生成的 `querySource: 'prompt_suggestion'` 区分。 + +### 接受后处理 + +用户接受 suggestion 后,`handleSpeculationAccept()`(源码: `speculation.ts#L835-991`)执行以下步骤: + +1. **消息清洗**(`prepareMessagesForInjection`,源码: `speculation.ts#L203-271`): + - 过滤 `thinking` 和 `redacted_thinking` 块 + - 移除未成功完成的 `tool_use`/`tool_result` 对 + - 移除中断消息(`INTERRUPT_MESSAGE`) + - 过滤全空白文本消息(避免 API 400 错误) + - 若 speculation 未完成,丢弃尾部 assistant 消息(不支持 prefill 的模型拒绝以 assistant turn 结尾) + +2. **文件状态合并**:将 speculation 读取的文件状态缓存合并到主对话,避免重复读取(源码: `speculation.ts#L910-917`) + +3. **Overlay 回写**:将 overlay 目录中修改的文件复制回主目录 + +4. **反馈消息注入**(仅 `USER_TYPE === 'ant'`,源码: `speculation.ts#L273-308`): + ``` + [ANT-ONLY] Speculated 3 tool uses · 1,234 tokens · +2.1s saved (5.3s this session) + ``` + +5. **Transcript 记录**:写入 `speculation-accept` 条目到 JSONL transcript(源码: `speculation.ts#L784-794`),用于统计会话累计节省时间 + +### Speculation 遥测 + +事件名: `tengu_speculation`(源码: `speculation.ts#L124-153`) + +| 字段 | 说明 | +|------|------| +| `speculation_id` | 推测会话 UUID(前 8 位) | +| `outcome` | `accepted` / `aborted` / `error` | +| `duration_ms` | 推测执行耗时 | +| `suggestion_length` | suggestion 文本长度 | +| `tools_executed` | 成功执行的工具调用数 | +| `completed` | 是否到达边界(`boundary !== null`) | +| `boundary_type` | `complete` / `bash` / `edit` / `denied_tool` | +| `boundary_tool` | 触发边界的工具名 | +| `boundary_detail` | 触发边界的命令/路径(截取前 200 字符) | +| `message_count` | 推测消息总数(仅 accepted 时) | +| `time_saved_ms` | 节省的毫秒数(仅 accepted 时) | +| `is_pipelined` | 是否为 pipeline 产生的推测 | + ## 源码文件索引 | 文件 | LOC | 职责 | From 9db91ee7377836b3d6c52dd4959cc3fe7ce274c5 Mon Sep 17 00:00:00 2001 From: wenshao Date: Wed, 1 Apr 2026 12:23:41 +0800 Subject: [PATCH 7/7] Address qwen3.6-plus-preview R3 review: 7 fixes - R3-1: tools_executed counts tool_result && !is_error, not tool_use - R3-2: getPromptVariant() hardcoded to 'user_intent', stated_intent is reserved - R3-3: Pipeline promotion only on complete boundary, discarded on abort - R3-4: User message injected first for instant visual feedback (step 0) - R3-5: Distinguish acceptSpeculation() vs handleSpeculationAccept() roles - R3-6: Add High Contrast Light/Dark theme colors - R3-7: time_saved_ms = min(acceptedAt, completedAt) - startTime Co-Authored-By: Claude Opus 4.6 (1M context) --- .../tools/claude-code/10-prompt-suggestions.md | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/docs/tools/claude-code/10-prompt-suggestions.md b/docs/tools/claude-code/10-prompt-suggestions.md index fc51b417..716a85d1 100644 --- a/docs/tools/claude-code/10-prompt-suggestions.md +++ b/docs/tools/claude-code/10-prompt-suggestions.md @@ -110,7 +110,7 @@ Format: 2-12 words, match the user's style. Or nothing. Reply with ONLY the suggestion, no quotes or explanation. ``` -Prompt 通过 `PromptVariant` 类型索引(源码: `promptSuggestion.ts#L31-35`),当前仅有 `'user_intent'` 和 `'stated_intent'` 两个变体,均映射到同一模板。 +Prompt 通过 `PromptVariant` 类型索引(源码: `promptSuggestion.ts#L31-35`),定义了 `'user_intent'` 和 `'stated_intent'` 两个变体,均映射到同一模板。`getPromptVariant()` 当前始终硬编码返回 `'user_intent'`,`'stated_intent'` 为预留变体,未被使用。 ## 过滤机制 @@ -230,6 +230,8 @@ suggestion 文本使用主题中的 `suggestion` 颜色渲染: |------|------| | Light | `rgb(87, 105, 247)`(蓝紫色) | | Dark | `rgb(177, 185, 249)`(浅蓝紫色) | +| High Contrast Light | `rgb(51, 102, 255)` | +| High Contrast Dark | `rgb(153, 204, 255)` | | ANSI Light | `ansi:blue` | | ANSI Dark | `ansi:blueBright` | @@ -323,7 +325,7 @@ Speculation 通过 `onMessage` 回调实时追踪消息数量,达到 `MAX_SPEC 源码: `speculation.ts#L345-400`,函数 `generatePipelinedSuggestion` -推测执行完成后,如果用户尚未做出响应,会立即生成下一轮 suggestion。当用户接受当前 suggestion 时,pipelined suggestion 被提升为新的 suggestion 显示,并启动新一轮 speculation,形成连续的预测→预执行→预测链。 +推测执行完成后,如果用户尚未做出响应,会立即生成下一轮 suggestion。当用户接受当前 suggestion 时,**仅在 speculation 自然完成(`boundary.type === 'complete'`)时**,pipelined suggestion 才会被提升为新的 suggestion 显示并启动新一轮 speculation;若 speculation 因 bash/edit/denied_tool 边界中止,pipelined suggestion 会被丢弃(源码: `speculation.ts#L928-929`)。 ``` 用户发送消息 → Claude 回复 @@ -337,7 +339,13 @@ Speculation 使用独立的 fork 标签:`querySource: 'speculation'`、`forkLa ### 接受后处理 -用户接受 suggestion 后,`handleSpeculationAccept()`(源码: `speculation.ts#L835-991`)执行以下步骤: +用户接受 suggestion 后,接受流程由两个函数协作完成: +- `handleSpeculationAccept()`(源码: `speculation.ts#L835-991`):React 层,负责状态更新、消息注入、pipeline promotion +- `acceptSpeculation()`(源码: `speculation.ts#L717-800`):底层,负责 overlay 回写、transcript 记录、`timeSavedMs` 计算 + +执行步骤: + +0. **用户消息优先注入**:立即将用户输入显示在 UI 中,确保即时视觉反馈(源码: `speculation.ts#L875-876`) 1. **消息清洗**(`prepareMessagesForInjection`,源码: `speculation.ts#L203-271`): - 过滤 `thinking` 和 `redacted_thinking` 块 @@ -367,13 +375,13 @@ Speculation 使用独立的 fork 标签:`querySource: 'speculation'`、`forkLa | `outcome` | `accepted` / `aborted` / `error` | | `duration_ms` | 推测执行耗时 | | `suggestion_length` | suggestion 文本长度 | -| `tools_executed` | 成功执行的工具调用数 | +| `tools_executed` | 成功返回结果的工具调用数(计数 `tool_result && !is_error`) | | `completed` | 是否到达边界(`boundary !== null`) | | `boundary_type` | `complete` / `bash` / `edit` / `denied_tool` | | `boundary_tool` | 触发边界的工具名 | | `boundary_detail` | 触发边界的命令/路径(截取前 200 字符) | | `message_count` | 推测消息总数(仅 accepted 时) | -| `time_saved_ms` | 节省的毫秒数(仅 accepted 时) | +| `time_saved_ms` | 从 speculation 开始到 `min(接受时间, 边界完成时间)` 的毫秒数(仅 accepted 时) | | `is_pipelined` | 是否为 pipeline 产生的推测 | ## 源码文件索引