Skip to content
源码分析手册

Prompt Suggestions:首轮引导与停轮后下一句预测

在 Claude Code 的交互设计中,Prompt suggestions 并非单一的静态文案,而是一个结合了项目历史缓存与停轮后实时预测的双层提示机制。它通过在输入框中渲染“幽灵文本(Ghost Text)”来引导用户高效操作。

一句话讲清楚

Prompt suggestions 在 Claude Code 里其实由两层性质完全不同的机制构成。

第一层是 首轮占位引导(First-turn Placeholder)。当你刚开启一个全新会话时,输入框里出现的 Try "..." 本质上是基于项目历史的静态推荐。第二层是 停轮后实时预测(Post-turn Prediction)。每当 Claude 完成一轮推理,运行时会另外启动一个轻量级的 Fork Agent,利用此时此刻的 Prompt Cache 去预测“用户下一步最可能问什么”,并作为幽灵文本推送到输入框。

从源码看实现

整个提示系统的实现流程涵盖了从背景探测到实时预测的多个环节:

  1. 项目级示例抽取(Example Commands): 在 claude-code-opensource/src/utils/exampleCommands.ts 中,系统会通过 Git 历史抽取最近高频修改的文件路径。它会过滤掉 lockfile、构建产物等无意义路径,拼装成 how does X work?refactor X 模板存入项目配置。这决定了你看到的“第一屏”占位文案。

  2. 缓存安全快照(Cache-Safe Snapshotting): 在 claude-code-opensource/src/query/stopHooks.ts 中,主线程在生成结束时会先行保存 lastCacheSafeParams。这是为了确保后续的预测 Fork 能复用和主线程完全一致的上下文前缀,从而极致节省预测所需的 Token 成本。

  3. 预测性 Fork 代理(Speculative Prediction): 在 claude-code-opensource/src/services/PromptSuggestion/promptSuggestion.ts 中,若环境支持且缓存命中预估(Cache Cold Check)通过,系统会通过 runForkedAgent 启动预测。为了确保不产生副作用,预测 Agent 会被设置为:

    • 禁工具态(Tools Denied):不允许执行任何工具,只进行文本推理。
    • 跳过转录(Skip Transcript):预测过程不写入本地历史日志。
    • 禁止缓存写(Skip Cache Write):预测产生的 Suffix 绝不污染后续的主会话缓存。
  4. 幽灵文本渲染与竞争(Rendering & Conflict): 预测结果通过 AppState.promptSuggestion 暴露。在 claude-code-opensource/src/components/PromptInput/PromptInput.tsx 中,suggestion 必须与命令补全、文件建议等其他自动完成机制竞争显示位。只有当输入框为空且没有更高优先级的补全项时,Ghost Text 才会浮现。

限制与陷阱

  • 首轮与后轮的区别:首屏 Try "..." 是静态缓存,不耗费即时 Token;而后轮的 suggestion 是由模型实时生成的,依赖 Prompt Cache 降低成本。
  • 抑制条件(Suppression):在 Plan Mode(计划模式)、等待权限审批、或者上一轮发生 API 错误时,预测机制会被主动抑制,以防干扰用户的核心决策。
  • 成本敏感(Cache Cold):如果父请求的上下文没有被成功缓存,系统通常会判定为 cache_cold 并拒绝生成建议,宁愿不给建议也不愿产生额外的冷启动开销。
  • 多重接受行为:除了文档标注的 Tab 键接受外,源码实现中还支持:右箭头键接受、以及在空输入状态下按 Enter 触发“接受并立即提交”。

继续探索

源码锚点

  • claude-code-opensource/src/services/PromptSuggestion/promptSuggestion.ts — 开关判断、预测 Agent 配置与过滤规则。
📄 src/services/PromptSuggestion/promptSuggestion.ts — 开关判断、预测 Agent 配置与过滤规则。L11-13 of 524
typescript
  runForkedAgent,
} from '../../utils/forkedAgent.js'
import type { REPLHookContext } from '../../utils/hooks/postSamplingHooks.js'
  • claude-code-opensource/src/query/stopHooks.ts — 保存缓存安全参数并触发预测任务的入口。
📄 src/query/stopHooks.ts — 保存缓存安全参数并触发预测任务的入口。L42-51 of 474
typescript
const extractMemoriesModule = feature('EXTRACT_MEMORIES')
  ? (require('../services/extractMemories/extractMemories.js') as typeof import('../services/extractMemories/extractMemories.js'))
  : null
const jobClassifierModule = feature('TEMPLATES')
  ? (require('../jobs/classifier.js') as typeof import('../jobs/classifier.js'))
  : null

/* eslint-enable @typescript-eslint/no-require-imports */

import type { QuerySource } from '../constants/querySource.js'
  • claude-code-opensource/src/utils/exampleCommands.ts — 基于 Git 历史生成首轮占位建议的算法。
📄 src/utils/exampleCommands.ts — 基于 Git 历史生成首轮占位建议的算法。L89-118 of 185
typescript
  if (!(await getIsGit())) return []

  try {
    // Collect frequently-modified files, preferring the user's own commits.
    const userEmail = await getGitEmail()

    const logArgs = [
      'log',
      '-n',
      '1000',
      '--pretty=format:',
      '--name-only',
      '--diff-filter=M',
    ]

    const counts = new Map<string, number>()
    const tallyInto = (stdout: string) => {
      for (const line of stdout.split('\n')) {
        const f = line.trim()
        if (f) counts.set(f, (counts.get(f) ?? 0) + 1)
      }
    }

    if (userEmail) {
      const { stdout } = await execFileNoThrowWithCwd(
        'git',
        [...logArgs, `--author=${userEmail}`],
        { cwd: getCwd() },
      )
      tallyInto(stdout)
  • claude-code-opensource/src/components/PromptInput/usePromptInputPlaceholder.ts — 占位引导的渲染逻辑。
📄 src/components/PromptInput/usePromptInputPlaceholder.ts — 占位引导的渲染逻辑。L11-20 of 77
typescript
const proactiveModule =
  feature('PROACTIVE') || feature('KAIROS')
    ? require('../../proactive/index.js')
    : null

type Props = {
  input: string
  submitCount: number
  viewingAgentName?: string
}
  • claude-code-opensource/src/hooks/usePromptSuggestion.ts — 建议的显示、接受与忽略状态 Telemetry 记账。
📄 src/hooks/usePromptSuggestion.ts — 建议的显示、接受与忽略状态 Telemetry 记账。L10-13 of 178
typescript
type Props = {
  inputValue: string
  isAssistantResponding: boolean
}

基于 Claude Code v2.1.88 开源快照的深度分析